xref: /aosp_15_r20/external/image_io/src/base/data_context.cc (revision ca0779eb572efbbfda2e47f806647c3c7eeea8c3)
1 #include "image_io/base/data_context.h"
2 
3 #include <algorithm>
4 #include <cctype>
5 #include <iomanip>
6 #include <sstream>
7 #include <string>
8 
9 #include "image_io/base/byte_data.h"
10 
11 namespace photos_editing_formats {
12 namespace image_io {
13 
14 namespace {
15 
AddNames(const std::list<std::string> & name_list,std::stringstream * ss)16 void AddNames(const std::list<std::string>& name_list, std::stringstream* ss) {
17   for (const auto& name : name_list) {
18     *ss << name << ":";
19   }
20 }
21 
22 }  // namespace
23 
GetInvalidLocationAndRangeErrorText() const24 std::string DataContext::GetInvalidLocationAndRangeErrorText() const {
25   std::stringstream ss;
26   ss << "Invalid location:" << location_ << " range:[" << range_.GetBegin()
27      << "," << range_.GetEnd() << ") segment_range:["
28      << segment_.GetDataRange().GetBegin() << ","
29      << segment_.GetDataRange().GetEnd() << ")";
30   return GetErrorText(ss.str(), "");
31 }
32 
GetErrorText(const std::string & error_description,const std::string & expectation_description) const33 std::string DataContext::GetErrorText(
34     const std::string& error_description,
35     const std::string& expectation_description) const {
36   std::list<std::string> none;
37   return GetErrorText(none, none, error_description, expectation_description);
38 }
39 
GetErrorText(const std::list<std::string> & prefix_name_list,const std::list<std::string> & postfix_name_list,const std::string & error_description,const std::string & expectation_description) const40 std::string DataContext::GetErrorText(
41     const std::list<std::string>& prefix_name_list,
42     const std::list<std::string>& postfix_name_list,
43     const std::string& error_description,
44     const std::string& expectation_description) const {
45   const std::string kContinue("- ");
46   std::stringstream ss;
47 
48   // Write error description if present.
49   if (!error_description.empty()) {
50     ss << error_description << std::endl;
51   }
52 
53   // Write name:name:... if present.
54   std::string names_string =
55       GetNamesString(prefix_name_list, postfix_name_list);
56   if (!names_string.empty()) {
57     ss << kContinue << names_string << std::endl;
58   }
59 
60   // Get the line:XX part of the line string.
61   DataLine data_line;
62   std::string line_number_string;
63   if (IsValidLocationAndRange()) {
64     data_line = line_info_map_.GetDataLine(location_);
65     line_number_string = GetLineNumberString(data_line);
66   }
67 
68   // Get the line_string related ranges and the line string.
69   DataRange clipped_range, line_range;
70   size_t spaces_before_caret = line_number_string.length();
71   GetClippedAndLineRange(data_line, &clipped_range, &line_range);
72   std::string line_string =
73       GetLineString(clipped_range, line_range, &spaces_before_caret);
74 
75   // Write the line string
76   ss << kContinue << line_number_string << line_string << std::endl;
77 
78   // Write the caret and expectation description
79   size_t spaces_count = location_ + spaces_before_caret - line_range.GetBegin();
80   std::string spaces(spaces_count, ' ');
81   ss << kContinue << spaces << '^';
82   if (!expectation_description.empty()) {
83     ss << "expected:" << expectation_description;
84   }
85   return ss.str();
86 }
87 
GetNamesString(const std::list<std::string> & prefix_name_list,const std::list<std::string> & postfix_name_list) const88 std::string DataContext::GetNamesString(
89     const std::list<std::string>& prefix_name_list,
90     const std::list<std::string>& postfix_name_list) const {
91   std::stringstream ss;
92   if (!prefix_name_list.empty() || !name_list_.empty() ||
93       !postfix_name_list.empty()) {
94     AddNames(prefix_name_list, &ss);
95     AddNames(name_list_, &ss);
96     AddNames(postfix_name_list, &ss);
97   }
98   return ss.str();
99 }
100 
GetLineNumberString(const DataLine & data_line) const101 std::string DataContext::GetLineNumberString(const DataLine& data_line) const {
102   std::stringstream liness;
103   liness << "line:";
104   if (data_line.number == 0) {
105     liness << "?:";
106   } else {
107     liness << data_line.number << ":";
108   }
109   return liness.str();
110 }
111 
GetClippedAndLineRange(const DataLine & data_line,DataRange * clipped_range,DataRange * line_range) const112 void DataContext::GetClippedAndLineRange(const DataLine& data_line,
113                                          DataRange* clipped_range,
114                                          DataRange* line_range) const {
115   // Lines could be really long, so provide some sane limits: some kLimit chars
116   // on either side of the current location.
117   const size_t kLimit = 25;
118   size_t line_begin, line_end;
119   *clipped_range = data_line.range.IsValid()
120                        ? range_.GetIntersection(data_line.range)
121                        : range_;
122   if (clipped_range->IsValid() && clipped_range->Contains(location_)) {
123     line_begin = (clipped_range->GetBegin() + kLimit < location_)
124                      ? location_ - kLimit
125                      : clipped_range->GetBegin();
126     line_end = std::min(line_begin + 2 * kLimit, clipped_range->GetEnd());
127   } else {
128     line_begin = location_;
129     line_end = std::min(location_ + 2 * kLimit, range_.GetEnd());
130     *clipped_range = DataRange(line_begin, line_end);
131   }
132   *line_range = DataRange(line_begin, line_end);
133 }
134 
GetLineString(const DataRange & clipped_range,const DataRange & line_range,size_t * spaces_before_caret) const135 std::string DataContext::GetLineString(const DataRange& clipped_range,
136                                        const DataRange& line_range,
137                                        size_t* spaces_before_caret) const {
138   std::stringstream ss;
139   if (!IsValidLocationAndRange()) {
140     ss << "Invalid location or range";
141     return ss.str();
142   }
143 
144   const char* cbytes =
145       reinterpret_cast<const char*>(segment_.GetBuffer(line_range.GetBegin()));
146   if (cbytes != nullptr) {
147     if (line_range.GetBegin() != clipped_range.GetBegin()) {
148       ss << "...";
149       *spaces_before_caret += 3;
150     }
151     for (size_t index = 0; index < line_range.GetLength(); ++index) {
152       char cbyte = cbytes[index];
153       if (isprint(cbyte)) {
154         ss << cbyte;
155       } else {
156         ss << "\\x" << ByteData::Byte2Hex(cbyte);
157         if (index + line_range.GetBegin() < location_) {
158           *spaces_before_caret += 4;
159         }
160       }
161     }
162     if (line_range.GetEnd() != clipped_range.GetEnd()) {
163       ss << "...";
164     }
165   }
166   return ss.str();
167 }
168 
169 }  // namespace image_io
170 }  // namespace photos_editing_formats
171