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