1 | /* |
2 | * Copyright 2016-present Facebook, Inc. |
3 | * |
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 |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
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. |
15 | */ |
16 | |
17 | #include <folly/experimental/bser/Bser.h> |
18 | |
19 | #include <folly/String.h> |
20 | #include <folly/io/Cursor.h> |
21 | |
22 | using namespace folly; |
23 | using folly::io::Cursor; |
24 | |
25 | namespace folly { |
26 | namespace bser { |
27 | static dynamic parseBser(Cursor& curs); |
28 | |
29 | template <typename... ARGS> |
30 | [[noreturn]] static void throwDecodeError(Cursor& curs, ARGS&&... args) { |
31 | throw BserDecodeError(folly::to<std::string>( |
32 | std::forward<ARGS>(args)..., |
33 | " with " , |
34 | curs.length(), |
35 | " bytes remaining in cursor" )); |
36 | } |
37 | |
38 | static int64_t decodeInt(Cursor& curs) { |
39 | auto enc = (BserType)curs.read<int8_t>(); |
40 | switch (enc) { |
41 | case BserType::Int8: |
42 | return curs.read<int8_t>(); |
43 | case BserType::Int16: |
44 | return curs.read<int16_t>(); |
45 | case BserType::Int32: |
46 | return curs.read<int32_t>(); |
47 | case BserType::Int64: |
48 | return curs.read<int64_t>(); |
49 | default: |
50 | throwDecodeError( |
51 | curs, "invalid integer encoding detected (" , (int8_t)enc, ")" ); |
52 | } |
53 | } |
54 | |
55 | static std::string decodeString(Cursor& curs) { |
56 | auto len = decodeInt(curs); |
57 | std::string str; |
58 | |
59 | if (len < 0) { |
60 | throw std::range_error("string length must not be negative" ); |
61 | } |
62 | |
63 | // We could use Cursor::readFixedString() here, but we'd like |
64 | // to throw our own exception with some increased diagnostics. |
65 | str.resize(len); |
66 | |
67 | // The start of the string data, mutable. |
68 | auto* dest = &str[0]; |
69 | |
70 | auto pulled = curs.pullAtMost(dest, len); |
71 | if (pulled != size_t(len)) { |
72 | // Saw this case when decodeHeader was returning the incorrect length |
73 | // and we were splitting off too few bytes from the IOBufQueue |
74 | throwDecodeError( |
75 | curs, |
76 | "no data available while decoding a string, header was " |
77 | "not decoded properly" ); |
78 | } |
79 | |
80 | return str; |
81 | } |
82 | |
83 | static dynamic decodeArray(Cursor& curs) { |
84 | dynamic arr = dynamic::array(); |
85 | auto size = decodeInt(curs); |
86 | while (size-- > 0) { |
87 | arr.push_back(parseBser(curs)); |
88 | } |
89 | return arr; |
90 | } |
91 | |
92 | static dynamic decodeObject(Cursor& curs) { |
93 | dynamic obj = dynamic::object; |
94 | auto size = decodeInt(curs); |
95 | while (size-- > 0) { |
96 | if ((BserType)curs.read<int8_t>() != BserType::String) { |
97 | throwDecodeError(curs, "expected String" ); |
98 | } |
99 | auto key = decodeString(curs); |
100 | obj[key] = parseBser(curs); |
101 | } |
102 | return obj; |
103 | } |
104 | |
105 | static dynamic decodeTemplate(Cursor& curs) { |
106 | dynamic arr = folly::dynamic::array; |
107 | |
108 | // List of property names |
109 | if ((BserType)curs.read<int8_t>() != BserType::Array) { |
110 | throw std::runtime_error("Expected array encoding for property names" ); |
111 | } |
112 | auto names = decodeArray(curs); |
113 | |
114 | auto size = decodeInt(curs); |
115 | |
116 | while (size-- > 0) { |
117 | dynamic obj = dynamic::object; |
118 | |
119 | for (auto& name : names) { |
120 | auto bytes = curs.peekBytes(); |
121 | if ((BserType)bytes.at(0) == BserType::Skip) { |
122 | obj[name.getString()] = nullptr; |
123 | curs.skipAtMost(1); |
124 | continue; |
125 | } |
126 | |
127 | obj[name.getString()] = parseBser(curs); |
128 | } |
129 | |
130 | arr.push_back(std::move(obj)); |
131 | } |
132 | |
133 | return arr; |
134 | } |
135 | |
136 | static dynamic parseBser(Cursor& curs) { |
137 | switch ((BserType)curs.read<int8_t>()) { |
138 | case BserType::Int8: |
139 | return curs.read<int8_t>(); |
140 | case BserType::Int16: |
141 | return curs.read<int16_t>(); |
142 | case BserType::Int32: |
143 | return curs.read<int32_t>(); |
144 | case BserType::Int64: |
145 | return curs.read<int64_t>(); |
146 | case BserType::Real: { |
147 | double dval; |
148 | curs.pull((void*)&dval, sizeof(dval)); |
149 | return dval; |
150 | } |
151 | case BserType::Null: |
152 | return nullptr; |
153 | case BserType::True: |
154 | return (bool)true; |
155 | case BserType::False: |
156 | return (bool)false; |
157 | case BserType::String: |
158 | return decodeString(curs); |
159 | case BserType::Array: |
160 | return decodeArray(curs); |
161 | case BserType::Object: |
162 | return decodeObject(curs); |
163 | case BserType::Template: |
164 | return decodeTemplate(curs); |
165 | case BserType::Skip: |
166 | throw std::runtime_error( |
167 | "Skip not valid at this location in the bser stream" ); |
168 | default: |
169 | throw std::runtime_error("invalid bser encoding" ); |
170 | } |
171 | } |
172 | |
173 | static size_t (Cursor& curs) { |
174 | char [sizeof(kMagic)]; |
175 | curs.pull(header, sizeof(header)); |
176 | if (memcmp(header, kMagic, sizeof(kMagic))) { |
177 | throw std::runtime_error("invalid BSER magic header" ); |
178 | } |
179 | |
180 | auto enc = (BserType)curs.peekBytes().at(0); |
181 | size_t int_size; |
182 | switch (enc) { |
183 | case BserType::Int8: |
184 | int_size = 1; |
185 | break; |
186 | case BserType::Int16: |
187 | int_size = 2; |
188 | break; |
189 | case BserType::Int32: |
190 | int_size = 4; |
191 | break; |
192 | case BserType::Int64: |
193 | int_size = 8; |
194 | break; |
195 | default: |
196 | int_size = 0; |
197 | } |
198 | |
199 | return int_size + 3 /* magic + int type */ + decodeInt(curs); |
200 | } |
201 | |
202 | size_t decodePduLength(const folly::IOBuf* buf) { |
203 | Cursor curs(buf); |
204 | return decodeHeader(curs); |
205 | } |
206 | |
207 | folly::dynamic parseBser(const IOBuf* buf) { |
208 | Cursor curs(buf); |
209 | |
210 | decodeHeader(curs); |
211 | return parseBser(curs); |
212 | } |
213 | |
214 | folly::dynamic parseBser(ByteRange str) { |
215 | auto buf = IOBuf::wrapBuffer(str.data(), str.size()); |
216 | return parseBser(&*buf); |
217 | } |
218 | |
219 | folly::dynamic parseBser(StringPiece str) { |
220 | return parseBser(ByteRange((uint8_t*)str.data(), str.size())); |
221 | } |
222 | } // namespace bser |
223 | } // namespace folly |
224 | |
225 | /* vim:ts=2:sw=2:et: |
226 | */ |
227 | |