2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/io/Cursor.h>
18 #include <folly/String.h>
20 using namespace folly;
21 using folly::io::Cursor;
25 static dynamic parseBser(Cursor& curs);
27 template <typename... ARGS>
28 [[noreturn]] static void throwDecodeError(Cursor& curs, ARGS&&... args) {
29 throw BserDecodeError(folly::to<std::string>(std::forward<ARGS>(args)...,
32 " bytes remaining in cursor"));
35 static int64_t decodeInt(Cursor& curs) {
36 auto enc = (BserType)curs.read<int8_t>();
39 return curs.read<int8_t>();
41 return curs.read<int16_t>();
43 return curs.read<int32_t>();
45 return curs.read<int64_t>();
48 curs, "invalid integer encoding detected (", (int8_t)enc, ")");
52 static std::string decodeString(Cursor& curs) {
53 auto len = decodeInt(curs);
57 throw std::range_error("string length must not be negative");
61 size_t available = curs.length();
62 while (available < (size_t)len) {
64 // Saw this case when we decodeHeader was returning the incorrect length
65 // and we were splitting off too few bytes from the IOBufQueue
66 throwDecodeError(curs,
67 "no data available while decoding a string, header was "
68 "not decoded properly");
70 str.append(reinterpret_cast<const char*>(curs.data()), available);
71 curs.skipAtMost(available);
73 available = curs.length();
76 str.append(reinterpret_cast<const char*>(curs.data()), len);
81 static dynamic decodeArray(Cursor& curs) {
82 dynamic arr = dynamic::array();
83 auto size = decodeInt(curs);
85 arr.push_back(parseBser(curs));
90 static dynamic decodeObject(Cursor& curs) {
91 dynamic obj = dynamic::object;
92 auto size = decodeInt(curs);
94 if ((BserType)curs.read<int8_t>() != BserType::String) {
95 throwDecodeError(curs, "expected String");
97 auto key = decodeString(curs);
98 obj[key] = parseBser(curs);
103 static dynamic decodeTemplate(Cursor& curs) {
104 std::vector<dynamic> arr;
106 // List of property names
107 if ((BserType)curs.read<int8_t>() != BserType::Array) {
108 throw std::runtime_error("Expected array encoding for property names");
110 auto names = decodeArray(curs);
112 auto size = decodeInt(curs);
116 dynamic obj = dynamic::object;
118 for (auto& name : names) {
119 auto bytes = curs.peekBytes();
120 if ((BserType)bytes.at(0) == BserType::Skip) {
121 obj[name.getString()] = nullptr;
126 obj[name.getString()] = parseBser(curs);
129 arr.emplace_back(std::move(obj));
132 return dynamic(std::move(arr));
135 static dynamic parseBser(Cursor& curs) {
136 switch ((BserType)curs.read<int8_t>()) {
138 return curs.read<int8_t>();
139 case BserType::Int16:
140 return curs.read<int16_t>();
141 case BserType::Int32:
142 return curs.read<int32_t>();
143 case BserType::Int64:
144 return curs.read<int64_t>();
145 case BserType::Real: {
147 curs.pull((void*)&dval, sizeof(dval));
154 case BserType::False:
156 case BserType::String:
157 return decodeString(curs);
158 case BserType::Array:
159 return decodeArray(curs);
160 case BserType::Object:
161 return decodeObject(curs);
162 case BserType::Template:
163 return decodeTemplate(curs);
165 throw std::runtime_error(
166 "Skip not valid at this location in the bser stream");
168 throw std::runtime_error("invalid bser encoding");
172 static size_t decodeHeader(Cursor& curs) {
173 char header[sizeof(kMagic)];
174 curs.pull(header, sizeof(header));
175 if (memcmp(header, kMagic, sizeof(kMagic))) {
176 throw std::runtime_error("invalid BSER magic header");
179 auto enc = (BserType)curs.peekBytes().at(0);
185 case BserType::Int16:
188 case BserType::Int32:
191 case BserType::Int64:
198 return int_size + 3 /* magic + int type */ + decodeInt(curs);
201 size_t decodePduLength(const folly::IOBuf* buf) {
203 return decodeHeader(curs);
206 folly::dynamic parseBser(const IOBuf* buf) {
210 return parseBser(curs);
213 folly::dynamic parseBser(ByteRange str) {
214 auto buf = IOBuf::wrapBuffer(str.data(), str.size());
215 return parseBser(&*buf);
218 folly::dynamic parseBser(StringPiece str) {
219 return parseBser(ByteRange((uint8_t*)str.data(), str.size()));