Index: content/browser/android/string_message_codec.cc |
diff --git a/content/browser/android/string_message_codec.cc b/content/browser/android/string_message_codec.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..aa258875ef539ec64df030c2bb84a4a9943c0865 |
--- /dev/null |
+++ b/content/browser/android/string_message_codec.cc |
@@ -0,0 +1,153 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/android/string_message_codec.h" |
+ |
+#include <vector> |
+ |
+#include "base/logging.h" |
+ |
+namespace content { |
+namespace { |
+ |
+const uint32_t kVarIntShift = 7; |
+const uint32_t kVarIntMask = (1 << kVarIntShift) - 1; |
+ |
+const uint8_t kVersionTag = 0xFF; |
+const uint8_t kPaddingTag = '\0'; |
+const uint8_t kOneByteStringTag = '"'; |
+const uint8_t kTwoByteStringTag = 'c'; |
+ |
+const uint32_t kVersion = 10; |
+ |
+static size_t BytesNeededForUint32(uint32_t value) { |
+ size_t result = 0; |
+ do { |
+ result++; |
+ value >>= kVarIntShift; |
+ } while (value); |
+ return result; |
+} |
+ |
+void WriteUint8(uint8_t value, std::vector<uint8_t>* buffer) { |
+ buffer->push_back(value); |
+} |
+ |
+void WriteUint32(uint32_t value, std::vector<uint8_t>* buffer) { |
+ for (;;) { |
+ uint8_t b = (value & kVarIntMask); |
+ value >>= kVarIntShift; |
+ if (!value) { |
+ WriteUint8(b, buffer); |
+ break; |
+ } |
+ WriteUint8(b | (1 << kVarIntShift), buffer); |
+ } |
+} |
+ |
+void WriteBytes(const char* bytes, size_t num_bytes, |
+ std::vector<uint8_t>* buffer) { |
+ buffer->insert(buffer->end(), bytes, bytes + num_bytes); |
+} |
+ |
+bool ReadUint8(const uint8_t** ptr, const uint8_t* end, uint8_t* value) { |
+ if (*ptr >= end) |
+ return false; |
+ *value = *(*ptr)++; |
+ return true; |
+} |
+ |
+bool ReadUint32(const uint8_t** ptr, const uint8_t* end, uint32_t* value) { |
+ *value = 0; |
+ uint8_t current_byte; |
+ int shift = 0; |
+ do { |
+ if (*ptr >= end) |
+ return false; |
+ current_byte = *(*ptr)++; |
+ *value |= (static_cast<uint32_t>(current_byte & kVarIntMask) << shift); |
+ shift += kVarIntShift; |
+ } while (current_byte & (1 << kVarIntShift)); |
+ return true; |
+} |
+ |
+bool ContainsOnlyLatin1(const base::string16& data) { |
+ base::char16 x = 0; |
+ for (base::char16 c : data) |
+ x |= c; |
+ return !(x & 0xFF00); |
+} |
+ |
+} // namespace |
+ |
+base::string16 EncodeStringMessage(const base::string16& data) { |
+ std::vector<uint8_t> buffer; |
+ WriteUint8(kVersionTag, &buffer); |
+ WriteUint32(kVersion, &buffer); |
+ |
+ if (ContainsOnlyLatin1(data)) { |
+ std::string data_latin1(data.begin(), data.end()); |
+ WriteUint8(kOneByteStringTag, &buffer); |
+ WriteUint32(data_latin1.size(), &buffer); |
+ WriteBytes(data_latin1.c_str(), data_latin1.size(), &buffer); |
+ } else { |
+ size_t num_bytes = data.size() * sizeof(base::char16); |
+ if ((buffer.size() + 1 + BytesNeededForUint32(num_bytes)) & 1) |
+ WriteUint8(kPaddingTag, &buffer); |
+ WriteUint8(kTwoByteStringTag, &buffer); |
+ WriteUint32(num_bytes, &buffer); |
+ WriteBytes(reinterpret_cast<const char*>(data.data()), num_bytes, &buffer); |
+ } |
+ |
+ base::string16 result; |
+ size_t result_num_bytes = (buffer.size() + 1) & ~1; |
+ result.resize(result_num_bytes / 2); |
+ uint8_t* destination = reinterpret_cast<uint8_t*>(&result[0]); |
+ memcpy(destination, &buffer[0], buffer.size()); |
+ return result; |
+} |
+ |
+bool DecodeStringMessage(const base::string16& encoded_data, |
+ base::string16* result) { |
+ size_t num_bytes = encoded_data.size() * 2; |
+ |
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&encoded_data[0]); |
+ const uint8_t* end = ptr + num_bytes; |
+ |
+ uint8_t tag; |
+ if (!ReadUint8(&ptr, end, &tag) || tag != kVersionTag) |
+ return false; |
+ |
+ uint32_t version; |
+ if (!ReadUint32(&ptr, end, &version)) |
+ return false; |
+ |
+ do { |
+ if (!ReadUint8(&ptr, end, &tag)) |
+ return false; |
+ } while (tag == kPaddingTag); |
+ |
+ switch (tag) { |
+ case kOneByteStringTag: { |
+ uint32_t num_bytes; |
+ if (!ReadUint32(&ptr, end, &num_bytes)) |
+ return false; |
+ result->assign(reinterpret_cast<const char*>(ptr), |
+ reinterpret_cast<const char*>(ptr) + num_bytes); |
+ return true; |
+ } |
+ case kTwoByteStringTag: { |
+ uint32_t num_bytes; |
+ if (!ReadUint32(&ptr, end, &num_bytes)) |
+ return false; |
+ result->assign(reinterpret_cast<const base::char16*>(ptr), num_bytes / 2); |
+ return true; |
+ } |
+ } |
+ |
+ DLOG(WARNING) << "Unexpected tag: " << tag; |
+ return false; |
+} |
+ |
+} // namespace content |