Consistently have the namespace closing comment
[folly.git] / folly / experimental / bser / Dump.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
17 #include <folly/experimental/bser/Bser.h>
18
19 #include <folly/io/Cursor.h>
20
21 using namespace folly;
22 using folly::io::QueueAppender;
23 using folly::bser::serialization_opts;
24
25 namespace folly {
26 namespace bser {
27
28 const uint8_t kMagic[2] = {0, 1};
29
30 static void bserEncode(
31     dynamic const& dyn,
32     QueueAppender& appender,
33     const serialization_opts& opts);
34
35 serialization_opts::serialization_opts()
36     : sort_keys(false), growth_increment(8192) {}
37
38 static const dynamic* getTemplate(
39     const serialization_opts& opts,
40     dynamic const& dynArray) {
41   if (!opts.templates.hasValue()) {
42     return nullptr;
43   }
44   const auto& templates = opts.templates.value();
45   const auto it = templates.find(&dynArray);
46   if (it == templates.end()) {
47     return nullptr;
48   }
49   return &it->second;
50 }
51
52 static void bserEncodeInt(int64_t ival, QueueAppender& appender) {
53   /* Return the smallest size int that can store the value */
54   auto size =
55       ((ival == ((int8_t)ival))
56            ? 1
57            : (ival == ((int16_t)ival)) ? 2 : (ival == ((int32_t)ival)) ? 4 : 8);
58
59   switch (size) {
60     case 1:
61       appender.write((int8_t)BserType::Int8);
62       appender.write(int8_t(ival));
63       return;
64     case 2:
65       appender.write((int8_t)BserType::Int16);
66       appender.write(int16_t(ival));
67       return;
68     case 4:
69       appender.write((int8_t)BserType::Int32);
70       appender.write(int32_t(ival));
71       return;
72     case 8:
73       appender.write((int8_t)BserType::Int64);
74       appender.write(ival);
75       return;
76     default:
77       throw std::runtime_error("impossible integer size");
78   }
79 }
80
81 static void bserEncodeString(folly::StringPiece str, QueueAppender& appender) {
82   appender.write((int8_t)BserType::String);
83   bserEncodeInt(int64_t(str.size()), appender);
84   appender.push((uint8_t*)str.data(), str.size());
85 }
86
87 static void bserEncodeArraySimple(
88     dynamic const& dyn,
89     QueueAppender& appender,
90     const serialization_opts& opts) {
91   appender.write((int8_t)BserType::Array);
92   bserEncodeInt(int64_t(dyn.size()), appender);
93   for (const auto& ele : dyn) {
94     bserEncode(ele, appender, opts);
95   }
96 }
97
98 static void bserEncodeArray(
99     dynamic const& dyn,
100     QueueAppender& appender,
101     const serialization_opts& opts) {
102   auto templ = getTemplate(opts, dyn);
103   if (UNLIKELY(templ != nullptr)) {
104     appender.write((int8_t)BserType::Template);
105
106     // Emit the list of property names
107     bserEncodeArraySimple(*templ, appender, opts);
108
109     // The number of objects in the array
110     bserEncodeInt(int64_t(dyn.size()), appender);
111
112     // For each object in the array
113     for (const auto& ele : dyn) {
114       // For each key in the template
115       for (const auto& name : *templ) {
116         if (auto found = ele.get_ptr(name)) {
117           if (found->isNull()) {
118             // Prefer to Skip rather than encode a null value for
119             // compatibility with the other bser implementations
120             appender.write((int8_t)BserType::Skip);
121           } else {
122             bserEncode(*found, appender, opts);
123           }
124         } else {
125           appender.write((int8_t)BserType::Skip);
126         }
127       }
128     }
129     return;
130   }
131
132   bserEncodeArraySimple(dyn, appender, opts);
133 }
134
135 static void bserEncodeObject(
136     dynamic const& dyn,
137     QueueAppender& appender,
138     const serialization_opts& opts) {
139   appender.write((int8_t)BserType::Object);
140   bserEncodeInt(int64_t(dyn.size()), appender);
141
142   if (opts.sort_keys) {
143     std::vector<std::pair<dynamic, dynamic>> sorted(
144         dyn.items().begin(), dyn.items().end());
145     std::sort(sorted.begin(), sorted.end());
146     for (const auto& item : sorted) {
147       bserEncode(item.first, appender, opts);
148       bserEncode(item.second, appender, opts);
149     }
150   } else {
151     for (const auto& item : dyn.items()) {
152       bserEncode(item.first, appender, opts);
153       bserEncode(item.second, appender, opts);
154     }
155   }
156 }
157
158 static void bserEncode(
159     dynamic const& dyn,
160     QueueAppender& appender,
161     const serialization_opts& opts) {
162   switch (dyn.type()) {
163     case dynamic::Type::NULLT:
164       appender.write((int8_t)BserType::Null);
165       return;
166     case dynamic::Type::BOOL:
167       appender.write(
168           (int8_t)(dyn.getBool() ? BserType::True : BserType::False));
169       return;
170     case dynamic::Type::DOUBLE: {
171       double dval = dyn.getDouble();
172       appender.write((int8_t)BserType::Real);
173       appender.write(dval);
174       return;
175     }
176     case dynamic::Type::INT64:
177       bserEncodeInt(dyn.getInt(), appender);
178       return;
179     case dynamic::Type::OBJECT:
180       bserEncodeObject(dyn, appender, opts);
181       return;
182     case dynamic::Type::ARRAY:
183       bserEncodeArray(dyn, appender, opts);
184       return;
185     case dynamic::Type::STRING:
186       bserEncodeString(dyn.getString(), appender);
187       return;
188   }
189 }
190
191 std::unique_ptr<folly::IOBuf> toBserIOBuf(
192     folly::dynamic const& dyn,
193     const serialization_opts& opts) {
194   IOBufQueue q(IOBufQueue::cacheChainLength());
195   uint8_t hdrbuf[sizeof(kMagic) + 1 + sizeof(int64_t)];
196
197   // Reserve some headroom for the overall PDU size; we'll fill this in
198   // after we've serialized the data and know the length
199   auto firstbuf = IOBuf::create(opts.growth_increment);
200   firstbuf->advance(sizeof(hdrbuf));
201   q.append(std::move(firstbuf));
202
203   // encode the value
204   QueueAppender appender(&q, opts.growth_increment);
205   bserEncode(dyn, appender, opts);
206
207   // compute the length
208   auto len = q.chainLength();
209   if (len > uint64_t(std::numeric_limits<int64_t>::max())) {
210     throw std::range_error(folly::to<std::string>(
211         "serialized data size ", len, " is too large to represent as BSER"));
212   }
213
214   // This is a bit verbose, but it computes a header that is appropriate
215   // to the size of the serialized data
216
217   memcpy(hdrbuf, kMagic, sizeof(kMagic));
218   size_t hdrlen = sizeof(kMagic) + 1;
219   auto magicptr = hdrbuf + sizeof(kMagic);
220   auto lenptr = hdrbuf + hdrlen;
221
222   if (len > uint64_t(std::numeric_limits<int32_t>::max())) {
223     *magicptr = (int8_t)BserType::Int64;
224     *(int64_t*)lenptr = (int64_t)len;
225     hdrlen += sizeof(int64_t);
226   } else if (len > uint64_t(std::numeric_limits<int16_t>::max())) {
227     *magicptr = (int8_t)BserType::Int32;
228     *(int32_t*)lenptr = (int32_t)len;
229     hdrlen += sizeof(int32_t);
230   } else if (len > uint64_t(std::numeric_limits<int8_t>::max())) {
231     *magicptr = (int8_t)BserType::Int16;
232     *(int16_t*)lenptr = (int16_t)len;
233     hdrlen += sizeof(int16_t);
234   } else {
235     *magicptr = (int8_t)BserType::Int8;
236     *(int8_t*)lenptr = (int8_t)len;
237     hdrlen += sizeof(int8_t);
238   }
239
240   // and place the data in the headroom
241   q.prepend(hdrbuf, hdrlen);
242
243   return q.move();
244 }
245
246 fbstring toBser(dynamic const& dyn, const serialization_opts& opts) {
247   auto buf = toBserIOBuf(dyn, opts);
248   return buf->moveToFbString();
249 }
250 } // namespace bser
251 } // namespace folly
252
253 /* vim:ts=2:sw=2:et:
254  */