1 #include "image_io/extras/base64_decoder_data_destination.h"
2 
3 #include <memory>
4 #include <sstream>
5 #include <vector>
6 
7 #include "image_io/base/data_segment.h"
8 #include "image_io/base/message_handler.h"
9 #include <modp_b64/modp_b64.h>
10 
11 namespace photos_editing_formats {
12 namespace image_io {
13 
14 using std::shared_ptr;
15 using std::unique_ptr;
16 using std::vector;
17 
18 // Set this flag to 1 for debugging output.
19 #define PHOTOS_EDITING_FORMATS_IMAGE_IO_EXTRAS_BASE64_DECODER_DATA_DEST_DEBUG 0
20 
21 /// A helper function to adjust the parameters for the base64 decoder function
22 /// that are used by the Base64DecoderDataDestination to those that are required
23 /// to call the modp_b64_decode function.
24 /// @param src The source bytes to decode.
25 /// @param len The number of source bytes to decode.
26 /// @param out The output buffer to receive the decoded bytes, assumed to be
27 ///     large enough (which the Base64DecoderDataDestination code does).
28 /// @param pad_count The number of pad characters detected at the end of the
29 ///     src buffer.
30 /// @return The number of decoded bytes placed in the out buffer.
base64_decode(const Byte * src,size_t len,Byte * out,size_t * pad_count)31 static size_t base64_decode(const Byte* src, size_t len, Byte* out,
32                             size_t* pad_count) {
33   // The base64 encoding is described at https://en.wikipedia.org/wiki/Base64.
34   // It uses these 64 printable characters: [0-9], [a-z], [A-Z], + and /. Since
35   // each character can represent 6 bits, 4 encoded characters can be used to
36   // represent 3 decoded bytes (6*4 = 3*8). There is the possibility that up to
37   // two padding bytes have to be added to the src that is encoded to ensure
38   // that the total number of encoded bytes is evenly divisible by 3. The = char
39   // is used for the purpose of completing the multiple-of-4 encoded bytes. The
40   // = may appear only at the end of the buffer being decoded, or else its an
41   // error.
42   const char kPadChar = '=';
43   if (len > 2 && src[len - 1] == kPadChar && src[len - 2] == kPadChar) {
44     // If the final two chars of the src buffer are pads then pad count is 2.
45     *pad_count = 2;
46   } else if (len > 1 && src[len - 1] == kPadChar) {
47     // If the final char of the src buffer is a pad then pad count is 1.
48     *pad_count = 1;
49   } else {
50     *pad_count = 0;
51   }
52   int bytes_decoded = modp_b64_decode(reinterpret_cast<char*>(out),
53                                       reinterpret_cast<const char*>(src),
54                                       static_cast<int>(len));
55   return bytes_decoded > 0 ? bytes_decoded : 0;
56 }
57 
StartTransfer()58 void Base64DecoderDataDestination::StartTransfer() {
59   next_destination_->StartTransfer();
60 }
61 
Transfer(const DataRange & transfer_range,const DataSegment & data_segment)62 DataDestination::TransferStatus Base64DecoderDataDestination::Transfer(
63     const DataRange& transfer_range, const DataSegment& data_segment) {
64   const Byte* encoded_buffer =
65       data_segment.GetBuffer(transfer_range.GetBegin());
66   if (!encoded_buffer || !transfer_range.IsValid() || HasError()) {
67     return kTransferError;
68   }
69 
70   // If there are left over bytes from the last call, steal enough bytes from
71   // the current encoded buffer to make up chunk's worth. If there are no more
72   // bytes in the encoded buffer (must be a small buffer) then we're done.
73 #if PHOTOS_EDITING_FORMATS_IMAGE_IO_EXTRAS_BASE64_DECODER_DATA_DEST_DEBUG
74   std::stringstream sstream1;
75   sstream1 << "  " << leftover_bytes_.size() << " bytes left over";
76   MessageHandler::Get()->ReportMessage(MessageHandler::kStatus, sstream1.str());
77 #endif  // PHOTOS_EDITING_FORMATS_IMAGE_IO_EXTRAS_BASE64_DECODER_DATA_DEST_DEBUG
78   size_t number_stolen_bytes = 0;
79   std::vector<Byte> leftover_and_stolen_bytes;
80   if (!leftover_bytes_.empty()) {
81     // Note that because of the way the leftover_bytes are captured at the end
82     // of this function, leftover_bytes.size() will be in the range [0:4). The
83     // number_stolen_bytes is always less than or equal to the number of bytes
84     // in the transfer_range. If the transfer_range happens to be small, and
85     // the leftover_bytes.size() + number_stolen_bytes does not equal 4, then
86     // no decoding can be done, and so the function just returns kTransferOk,
87     // indicating that the transfer operation should continue. The next call to
88     // Transfer() will either have enough bytes avaiable to be stolen so that
89     // the bytes can be decoded, or the process of premature return will be
90     // repeated, up to 3 times, worst case, where the transfer_range length is
91     // 1 each time Transfer is called.
92     number_stolen_bytes =
93         std::min(transfer_range.GetLength(), 4 - leftover_bytes_.size() % 4);
94     leftover_bytes_.insert(leftover_bytes_.end(), encoded_buffer,
95                            encoded_buffer + number_stolen_bytes);
96     if (number_stolen_bytes == transfer_range.GetLength() &&
97         leftover_bytes_.size() % 4) {
98       return kTransferOk;
99     }
100     using std::swap;
101     swap(leftover_and_stolen_bytes, leftover_bytes_);
102   }
103 
104   // Figure out the size of the buffer to hold the decoded bytes. When computing
105   // the number_remaining_bytes, note that number_stolen_bytes is 0 if there are
106   // no leftover_bytes, or in the range [1:3], and if the transfer_range length
107   // equals the number_stolen_bytes, then the execution does not get to this
108   // point, but rather the function returns in the above code block. Thus it is
109   // safe to subtract number_stolen_bytes from the transfer_range's length to
110   // obtain a (guarenteed) positive value for number_remaining_bytes.
111   size_t number_remaining_bytes =
112       transfer_range.GetLength() - number_stolen_bytes;
113   size_t number_leftover_and_stolen_decoded_bytes =
114       leftover_and_stolen_bytes.size() / 4 * 3;
115   size_t number_remaining_chunks = number_remaining_bytes / 4;
116   size_t number_remaining_decoded_bytes = number_remaining_chunks * 3;
117   size_t decoded_buffer_length =
118       number_leftover_and_stolen_decoded_bytes + number_remaining_decoded_bytes;
119   unique_ptr<Byte[]> decoded_buffer(new Byte[decoded_buffer_length]);
120 
121   // Decode the left over and stolen bytes first.
122   size_t pad_count1 = 0;
123   size_t total_bytes_decoded = 0;
124   if (number_leftover_and_stolen_decoded_bytes) {
125     total_bytes_decoded = base64_decode(leftover_and_stolen_bytes.data(),
126                                         leftover_and_stolen_bytes.size(),
127                                         decoded_buffer.get(), &pad_count1);
128     if (total_bytes_decoded + pad_count1 !=
129         number_leftover_and_stolen_decoded_bytes) {
130       if (message_handler_) {
131         message_handler_->ReportMessage(Message::kDecodingError, "");
132       }
133       has_error_ = true;
134       return kTransferError;
135     }
136   }
137 
138   // Decode the remaining bytes from the encoded buffer.
139   size_t pad_count2 = 0;
140   if (number_remaining_decoded_bytes) {
141     size_t number_bytes_decoded = base64_decode(
142         encoded_buffer + number_stolen_bytes, number_remaining_chunks * 4,
143         decoded_buffer.get() + total_bytes_decoded, &pad_count2);
144     total_bytes_decoded += number_bytes_decoded;
145     if (total_bytes_decoded + pad_count1 + pad_count2 !=
146         decoded_buffer_length) {
147       if (message_handler_) {
148         message_handler_->ReportMessage(Message::kDecodingError, "");
149       }
150       has_error_ = true;
151       return kTransferError;
152     }
153   }
154 
155   // Capture any new left over bytes. The number_new_leftover_bytes will always
156   // be in the range [0:4).
157   size_t number_processed_bytes =
158       number_stolen_bytes + number_remaining_chunks * 4;
159   size_t number_new_leftover_bytes =
160       transfer_range.GetLength() - number_processed_bytes;
161   if (number_new_leftover_bytes) {
162     leftover_bytes_.insert(
163         leftover_bytes_.end(), encoded_buffer + number_processed_bytes,
164         encoded_buffer + number_processed_bytes + number_new_leftover_bytes);
165   }
166 
167 #if PHOTOS_EDITING_FORMATS_IMAGE_IO_EXTRAS_BASE64_DECODER_DATA_DEST_DEBUG
168   std::stringstream sstream2;
169   sstream2 << "  " << leftover_bytes_.size() << " new bytes left over";
170   MessageHandler::Get()->ReportMessage(Message::kStatus, sstream2.str());
171 #endif  // PHOTOS_EDITING_FORMATS_IMAGE_IO_EXTRAS_BASE64_DECODER_DATA_DEST_DEBUG
172 
173   // And call the next stage
174   size_t decoded_location = next_decoded_location_;
175   next_decoded_location_ += (total_bytes_decoded);
176   DataRange decoded_range(decoded_location, next_decoded_location_);
177   shared_ptr<DataSegment> decoded_data_segment =
178       DataSegment::Create(decoded_range, decoded_buffer.release());
179   return next_destination_->Transfer(decoded_range, *decoded_data_segment);
180 }
181 
FinishTransfer()182 void Base64DecoderDataDestination::FinishTransfer() {
183   if (leftover_bytes_.size() % 4) {
184     if (message_handler_) {
185       message_handler_->ReportMessage(Message::kDecodingError, "");
186     }
187     has_error_ = true;
188   }
189   next_destination_->FinishTransfer();
190 }
191 
192 }  // namespace image_io
193 }  // namespace photos_editing_formats
194