json.c++ (52328B)
1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #include "json.h" 23 #include <capnp/orphan.h> 24 #include <kj/debug.h> 25 #include <kj/function.h> 26 #include <kj/vector.h> 27 #include <kj/one-of.h> 28 #include <kj/encoding.h> 29 #include <kj/map.h> 30 31 namespace capnp { 32 33 struct JsonCodec::Impl { 34 bool prettyPrint = false; 35 HasMode hasMode = HasMode::NON_NULL; 36 size_t maxNestingDepth = 64; 37 bool rejectUnknownFields = false; 38 39 kj::HashMap<Type, HandlerBase*> typeHandlers; 40 kj::HashMap<StructSchema::Field, HandlerBase*> fieldHandlers; 41 kj::HashMap<Type, kj::Maybe<kj::Own<AnnotatedHandler>>> annotatedHandlers; 42 kj::HashMap<Type, kj::Own<AnnotatedEnumHandler>> annotatedEnumHandlers; 43 44 kj::StringTree encodeRaw(JsonValue::Reader value, uint indent, bool& multiline, 45 bool hasPrefix) const { 46 switch (value.which()) { 47 case JsonValue::NULL_: 48 return kj::strTree("null"); 49 case JsonValue::BOOLEAN: 50 return kj::strTree(value.getBoolean()); 51 case JsonValue::NUMBER: 52 return kj::strTree(value.getNumber()); 53 54 case JsonValue::STRING: 55 return kj::strTree(encodeString(value.getString())); 56 57 case JsonValue::ARRAY: { 58 auto array = value.getArray(); 59 uint subIndent = indent + (array.size() > 1); 60 bool childMultiline = false; 61 auto encodedElements = KJ_MAP(element, array) { 62 return encodeRaw(element, subIndent, childMultiline, false); 63 }; 64 65 return kj::strTree('[', encodeList( 66 kj::mv(encodedElements), childMultiline, indent, multiline, hasPrefix), ']'); 67 } 68 69 case JsonValue::OBJECT: { 70 auto object = value.getObject(); 71 uint subIndent = indent + (object.size() > 1); 72 bool childMultiline = false; 73 kj::StringPtr colon = prettyPrint ? ": " : ":"; 74 auto encodedElements = KJ_MAP(field, object) { 75 return kj::strTree( 76 encodeString(field.getName()), colon, 77 encodeRaw(field.getValue(), subIndent, childMultiline, true)); 78 }; 79 80 return kj::strTree('{', encodeList( 81 kj::mv(encodedElements), childMultiline, indent, multiline, hasPrefix), '}'); 82 } 83 84 case JsonValue::CALL: { 85 auto call = value.getCall(); 86 auto params = call.getParams(); 87 uint subIndent = indent + (params.size() > 1); 88 bool childMultiline = false; 89 auto encodedElements = KJ_MAP(element, params) { 90 return encodeRaw(element, subIndent, childMultiline, false); 91 }; 92 93 return kj::strTree(call.getFunction(), '(', encodeList( 94 kj::mv(encodedElements), childMultiline, indent, multiline, true), ')'); 95 } 96 } 97 98 KJ_FAIL_ASSERT("unknown JsonValue type", static_cast<uint>(value.which())); 99 } 100 101 kj::String encodeString(kj::StringPtr chars) const { 102 static const char HEXDIGITS[] = "0123456789abcdef"; 103 kj::Vector<char> escaped(chars.size() + 3); 104 105 escaped.add('"'); 106 for (char c: chars) { 107 switch (c) { 108 case '\"': escaped.addAll(kj::StringPtr("\\\"")); break; 109 case '\\': escaped.addAll(kj::StringPtr("\\\\")); break; 110 case '\b': escaped.addAll(kj::StringPtr("\\b")); break; 111 case '\f': escaped.addAll(kj::StringPtr("\\f")); break; 112 case '\n': escaped.addAll(kj::StringPtr("\\n")); break; 113 case '\r': escaped.addAll(kj::StringPtr("\\r")); break; 114 case '\t': escaped.addAll(kj::StringPtr("\\t")); break; 115 default: 116 if (static_cast<uint8_t>(c) < 0x20) { 117 escaped.addAll(kj::StringPtr("\\u00")); 118 uint8_t c2 = c; 119 escaped.add(HEXDIGITS[c2 / 16]); 120 escaped.add(HEXDIGITS[c2 % 16]); 121 } else { 122 escaped.add(c); 123 } 124 break; 125 } 126 } 127 escaped.add('"'); 128 escaped.add('\0'); 129 130 return kj::String(escaped.releaseAsArray()); 131 } 132 133 kj::StringTree encodeList(kj::Array<kj::StringTree> elements, 134 bool hasMultilineElement, uint indent, bool& multiline, 135 bool hasPrefix) const { 136 size_t maxChildSize = 0; 137 for (auto& e: elements) maxChildSize = kj::max(maxChildSize, e.size()); 138 139 kj::StringPtr prefix; 140 kj::StringPtr delim; 141 kj::StringPtr suffix; 142 kj::String ownPrefix; 143 kj::String ownDelim; 144 if (!prettyPrint) { 145 // No whitespace. 146 delim = ","; 147 prefix = ""; 148 suffix = ""; 149 } else if ((elements.size() > 1) && (hasMultilineElement || maxChildSize > 50)) { 150 // If the array contained any multi-line elements, OR it contained sufficiently long 151 // elements, then put each element on its own line. 152 auto indentSpace = kj::repeat(' ', (indent + 1) * 2); 153 delim = ownDelim = kj::str(",\n", indentSpace); 154 multiline = true; 155 if (hasPrefix) { 156 // We're producing a multi-line list, and the first line has some garbage in front of it. 157 // Therefore, move the first element to the next line. 158 prefix = ownPrefix = kj::str("\n", indentSpace); 159 } else { 160 prefix = " "; 161 } 162 suffix = " "; 163 } else { 164 // Put everything on one line, but add spacing between elements for legibility. 165 delim = ", "; 166 prefix = ""; 167 suffix = ""; 168 } 169 170 return kj::strTree(prefix, kj::StringTree(kj::mv(elements), delim), suffix); 171 } 172 }; 173 174 JsonCodec::JsonCodec() 175 : impl(kj::heap<Impl>()) {} 176 JsonCodec::~JsonCodec() noexcept(false) {} 177 178 void JsonCodec::setPrettyPrint(bool enabled) { impl->prettyPrint = enabled; } 179 180 void JsonCodec::setMaxNestingDepth(size_t maxNestingDepth) { 181 impl->maxNestingDepth = maxNestingDepth; 182 } 183 184 void JsonCodec::setHasMode(HasMode mode) { impl->hasMode = mode; } 185 186 void JsonCodec::setRejectUnknownFields(bool enabled) { impl->rejectUnknownFields = enabled; } 187 188 kj::String JsonCodec::encode(DynamicValue::Reader value, Type type) const { 189 MallocMessageBuilder message; 190 auto json = message.getRoot<JsonValue>(); 191 encode(value, type, json); 192 return encodeRaw(json); 193 } 194 195 void JsonCodec::decode(kj::ArrayPtr<const char> input, DynamicStruct::Builder output) const { 196 MallocMessageBuilder message; 197 auto json = message.getRoot<JsonValue>(); 198 decodeRaw(input, json); 199 decode(json, output); 200 } 201 202 Orphan<DynamicValue> JsonCodec::decode( 203 kj::ArrayPtr<const char> input, Type type, Orphanage orphanage) const { 204 MallocMessageBuilder message; 205 auto json = message.getRoot<JsonValue>(); 206 decodeRaw(input, json); 207 return decode(json, type, orphanage); 208 } 209 210 kj::String JsonCodec::encodeRaw(JsonValue::Reader value) const { 211 bool multiline = false; 212 return impl->encodeRaw(value, 0, multiline, false).flatten(); 213 } 214 215 void JsonCodec::encode(DynamicValue::Reader input, Type type, JsonValue::Builder output) const { 216 // TODO(someday): For interfaces, check for handlers on superclasses, per documentation... 217 // TODO(someday): For branded types, should we check for handlers on the generic? 218 // TODO(someday): Allow registering handlers for "all structs", "all lists", etc? 219 KJ_IF_MAYBE(handler, impl->typeHandlers.find(type)) { 220 (*handler)->encodeBase(*this, input, output); 221 return; 222 } 223 224 switch (type.which()) { 225 case schema::Type::VOID: 226 output.setNull(); 227 break; 228 case schema::Type::BOOL: 229 output.setBoolean(input.as<bool>()); 230 break; 231 case schema::Type::INT8: 232 case schema::Type::INT16: 233 case schema::Type::INT32: 234 case schema::Type::UINT8: 235 case schema::Type::UINT16: 236 case schema::Type::UINT32: 237 output.setNumber(input.as<double>()); 238 break; 239 case schema::Type::FLOAT32: 240 case schema::Type::FLOAT64: 241 { 242 double value = input.as<double>(); 243 // Inf, -inf and NaN are not allowed in the JSON spec. Storing into string. 244 if (kj::inf() == value) { 245 output.setString("Infinity"); 246 } else if (-kj::inf() == value) { 247 output.setString("-Infinity"); 248 } else if (kj::isNaN(value)) { 249 output.setString("NaN"); 250 } else { 251 output.setNumber(value); 252 } 253 } 254 break; 255 case schema::Type::INT64: 256 output.setString(kj::str(input.as<int64_t>())); 257 break; 258 case schema::Type::UINT64: 259 output.setString(kj::str(input.as<uint64_t>())); 260 break; 261 case schema::Type::TEXT: 262 output.setString(kj::str(input.as<Text>())); 263 break; 264 case schema::Type::DATA: { 265 // Turn into array of byte values. Yep, this is pretty ugly. People really need to override 266 // this with a handler. 267 auto bytes = input.as<Data>(); 268 auto array = output.initArray(bytes.size()); 269 for (auto i: kj::indices(bytes)) { 270 array[i].setNumber(bytes[i]); 271 } 272 break; 273 } 274 case schema::Type::LIST: { 275 auto list = input.as<DynamicList>(); 276 auto elementType = type.asList().getElementType(); 277 auto array = output.initArray(list.size()); 278 for (auto i: kj::indices(list)) { 279 encode(list[i], elementType, array[i]); 280 } 281 break; 282 } 283 case schema::Type::ENUM: { 284 auto e = input.as<DynamicEnum>(); 285 KJ_IF_MAYBE(symbol, e.getEnumerant()) { 286 output.setString(symbol->getProto().getName()); 287 } else { 288 output.setNumber(e.getRaw()); 289 } 290 break; 291 } 292 case schema::Type::STRUCT: { 293 auto structValue = input.as<capnp::DynamicStruct>(); 294 auto nonUnionFields = structValue.getSchema().getNonUnionFields(); 295 296 KJ_STACK_ARRAY(bool, hasField, nonUnionFields.size(), 32, 128); 297 298 uint fieldCount = 0; 299 for (auto i: kj::indices(nonUnionFields)) { 300 fieldCount += (hasField[i] = structValue.has(nonUnionFields[i], impl->hasMode)); 301 } 302 303 // We try to write the union field, if any, in proper order with the rest. 304 auto which = structValue.which(); 305 bool unionFieldIsNull = false; 306 307 KJ_IF_MAYBE(field, which) { 308 // Even if the union field is null, if it is not the default field of the union then we 309 // have to print it anyway. 310 unionFieldIsNull = !structValue.has(*field, impl->hasMode); 311 if (field->getProto().getDiscriminantValue() != 0 || !unionFieldIsNull) { 312 ++fieldCount; 313 } else { 314 which = nullptr; 315 } 316 } 317 318 auto object = output.initObject(fieldCount); 319 320 size_t pos = 0; 321 for (auto i: kj::indices(nonUnionFields)) { 322 auto field = nonUnionFields[i]; 323 KJ_IF_MAYBE(unionField, which) { 324 if (unionField->getIndex() < field.getIndex()) { 325 auto outField = object[pos++]; 326 outField.setName(unionField->getProto().getName()); 327 if (unionFieldIsNull) { 328 outField.initValue().setNull(); 329 } else { 330 encodeField(*unionField, structValue.get(*unionField), outField.initValue()); 331 } 332 which = nullptr; 333 } 334 } 335 if (hasField[i]) { 336 auto outField = object[pos++]; 337 outField.setName(field.getProto().getName()); 338 encodeField(field, structValue.get(field), outField.initValue()); 339 } 340 } 341 if (which != nullptr) { 342 // Union field not printed yet; must be last. 343 auto unionField = KJ_ASSERT_NONNULL(which); 344 auto outField = object[pos++]; 345 outField.setName(unionField.getProto().getName()); 346 if (unionFieldIsNull) { 347 outField.initValue().setNull(); 348 } else { 349 encodeField(unionField, structValue.get(unionField), outField.initValue()); 350 } 351 } 352 KJ_ASSERT(pos == fieldCount); 353 break; 354 } 355 case schema::Type::INTERFACE: 356 KJ_FAIL_REQUIRE("don't know how to JSON-encode capabilities; " 357 "please register a JsonCodec::Handler for this"); 358 case schema::Type::ANY_POINTER: 359 KJ_FAIL_REQUIRE("don't know how to JSON-encode AnyPointer; " 360 "please register a JsonCodec::Handler for this"); 361 } 362 } 363 364 void JsonCodec::encodeField(StructSchema::Field field, DynamicValue::Reader input, 365 JsonValue::Builder output) const { 366 KJ_IF_MAYBE(handler, impl->fieldHandlers.find(field)) { 367 (*handler)->encodeBase(*this, input, output); 368 return; 369 } 370 371 encode(input, field.getType(), output); 372 } 373 374 Orphan<DynamicList> JsonCodec::decodeArray(List<JsonValue>::Reader input, ListSchema type, Orphanage orphanage) const { 375 auto orphan = orphanage.newOrphan(type, input.size()); 376 auto output = orphan.get(); 377 for (auto i: kj::indices(input)) { 378 output.adopt(i, decode(input[i], type.getElementType(), orphanage)); 379 } 380 return orphan; 381 } 382 383 void JsonCodec::decodeObject(JsonValue::Reader input, StructSchema type, Orphanage orphanage, DynamicStruct::Builder output) const { 384 KJ_REQUIRE(input.isObject(), "Expected object value") { return; } 385 for (auto field: input.getObject()) { 386 KJ_IF_MAYBE(fieldSchema, type.findFieldByName(field.getName())) { 387 decodeField(*fieldSchema, field.getValue(), orphanage, output); 388 } else { 389 KJ_REQUIRE(!impl->rejectUnknownFields, "Unknown field", field.getName()); 390 } 391 } 392 } 393 394 void JsonCodec::decodeField(StructSchema::Field fieldSchema, JsonValue::Reader fieldValue, 395 Orphanage orphanage, DynamicStruct::Builder output) const { 396 auto fieldType = fieldSchema.getType(); 397 398 KJ_IF_MAYBE(handler, impl->fieldHandlers.find(fieldSchema)) { 399 output.adopt(fieldSchema, (*handler)->decodeBase(*this, fieldValue, fieldType, orphanage)); 400 } else { 401 output.adopt(fieldSchema, decode(fieldValue, fieldType, orphanage)); 402 } 403 } 404 405 void JsonCodec::decode(JsonValue::Reader input, DynamicStruct::Builder output) const { 406 auto type = output.getSchema(); 407 408 KJ_IF_MAYBE(handler, impl->typeHandlers.find(type)) { 409 return (*handler)->decodeStructBase(*this, input, output); 410 } 411 412 decodeObject(input, type, Orphanage::getForMessageContaining(output), output); 413 } 414 415 Orphan<DynamicValue> JsonCodec::decode( 416 JsonValue::Reader input, Type type, Orphanage orphanage) const { 417 KJ_IF_MAYBE(handler, impl->typeHandlers.find(type)) { 418 return (*handler)->decodeBase(*this, input, type, orphanage); 419 } 420 421 switch(type.which()) { 422 case schema::Type::VOID: 423 return capnp::VOID; 424 case schema::Type::BOOL: 425 switch (input.which()) { 426 case JsonValue::BOOLEAN: 427 return input.getBoolean(); 428 default: 429 KJ_FAIL_REQUIRE("Expected boolean value"); 430 } 431 case schema::Type::INT8: 432 case schema::Type::INT16: 433 case schema::Type::INT32: 434 case schema::Type::INT64: 435 // Relies on range check in DynamicValue::Reader::as<IntType> 436 switch (input.which()) { 437 case JsonValue::NUMBER: 438 return input.getNumber(); 439 case JsonValue::STRING: 440 return input.getString().parseAs<int64_t>(); 441 default: 442 KJ_FAIL_REQUIRE("Expected integer value"); 443 } 444 case schema::Type::UINT8: 445 case schema::Type::UINT16: 446 case schema::Type::UINT32: 447 case schema::Type::UINT64: 448 // Relies on range check in DynamicValue::Reader::as<IntType> 449 switch (input.which()) { 450 case JsonValue::NUMBER: 451 return input.getNumber(); 452 case JsonValue::STRING: 453 return input.getString().parseAs<uint64_t>(); 454 default: 455 KJ_FAIL_REQUIRE("Expected integer value"); 456 } 457 case schema::Type::FLOAT32: 458 case schema::Type::FLOAT64: 459 switch (input.which()) { 460 case JsonValue::NULL_: 461 return kj::nan(); 462 case JsonValue::NUMBER: 463 return input.getNumber(); 464 case JsonValue::STRING: 465 return input.getString().parseAs<double>(); 466 default: 467 KJ_FAIL_REQUIRE("Expected float value"); 468 } 469 case schema::Type::TEXT: 470 switch (input.which()) { 471 case JsonValue::STRING: 472 return orphanage.newOrphanCopy(input.getString()); 473 default: 474 KJ_FAIL_REQUIRE("Expected text value"); 475 } 476 case schema::Type::DATA: 477 switch (input.which()) { 478 case JsonValue::ARRAY: { 479 auto array = input.getArray(); 480 auto orphan = orphanage.newOrphan<Data>(array.size()); 481 auto data = orphan.get(); 482 for (auto i: kj::indices(array)) { 483 auto x = array[i].getNumber(); 484 KJ_REQUIRE(byte(x) == x, "Number in byte array is not an integer in [0, 255]"); 485 data[i] = x; 486 } 487 return kj::mv(orphan); 488 } 489 default: 490 KJ_FAIL_REQUIRE("Expected data value"); 491 } 492 case schema::Type::LIST: 493 switch (input.which()) { 494 case JsonValue::ARRAY: 495 return decodeArray(input.getArray(), type.asList(), orphanage); 496 default: 497 KJ_FAIL_REQUIRE("Expected list value") { break; } 498 return orphanage.newOrphan(type.asList(), 0); 499 } 500 case schema::Type::ENUM: 501 switch (input.which()) { 502 case JsonValue::STRING: 503 return DynamicEnum(type.asEnum().getEnumerantByName(input.getString())); 504 default: 505 KJ_FAIL_REQUIRE("Expected enum value") { break; } 506 return DynamicEnum(type.asEnum(), 0); 507 } 508 case schema::Type::STRUCT: { 509 auto structType = type.asStruct(); 510 auto orphan = orphanage.newOrphan(structType); 511 decodeObject(input, structType, orphanage, orphan.get()); 512 return kj::mv(orphan); 513 } 514 case schema::Type::INTERFACE: 515 KJ_FAIL_REQUIRE("don't know how to JSON-decode capabilities; " 516 "please register a JsonCodec::Handler for this"); 517 case schema::Type::ANY_POINTER: 518 KJ_FAIL_REQUIRE("don't know how to JSON-decode AnyPointer; " 519 "please register a JsonCodec::Handler for this"); 520 } 521 522 KJ_CLANG_KNOWS_THIS_IS_UNREACHABLE_BUT_GCC_DOESNT; 523 } 524 525 // ----------------------------------------------------------------------------- 526 527 namespace { 528 529 class Input { 530 public: 531 Input(kj::ArrayPtr<const char> input) : wrapped(input) {} 532 533 bool exhausted() { 534 return wrapped.size() == 0 || wrapped.front() == '\0'; 535 } 536 537 char nextChar() { 538 KJ_REQUIRE(!exhausted(), "JSON message ends prematurely."); 539 return wrapped.front(); 540 } 541 542 void advance(size_t numBytes = 1) { 543 KJ_REQUIRE(numBytes <= wrapped.size(), "JSON message ends prematurely."); 544 wrapped = kj::arrayPtr(wrapped.begin() + numBytes, wrapped.end()); 545 } 546 547 void advanceTo(const char *newPos) { 548 KJ_REQUIRE(wrapped.begin() <= newPos && newPos < wrapped.end(), 549 "JSON message ends prematurely."); 550 wrapped = kj::arrayPtr(newPos, wrapped.end()); 551 } 552 553 kj::ArrayPtr<const char> consume(size_t numBytes = 1) { 554 auto originalPos = wrapped.begin(); 555 advance(numBytes); 556 557 return kj::arrayPtr(originalPos, wrapped.begin()); 558 } 559 560 void consume(char expected) { 561 char current = nextChar(); 562 KJ_REQUIRE(current == expected, "Unexpected input in JSON message."); 563 564 advance(); 565 } 566 567 void consume(kj::ArrayPtr<const char> expected) { 568 KJ_REQUIRE(wrapped.size() >= expected.size()); 569 570 auto prefix = wrapped.slice(0, expected.size()); 571 KJ_REQUIRE(prefix == expected, "Unexpected input in JSON message."); 572 573 advance(expected.size()); 574 } 575 576 bool tryConsume(char expected) { 577 bool found = !exhausted() && nextChar() == expected; 578 if (found) { advance(); } 579 580 return found; 581 } 582 583 template <typename Predicate> 584 void consumeOne(Predicate&& predicate) { 585 char current = nextChar(); 586 KJ_REQUIRE(predicate(current), "Unexpected input in JSON message."); 587 588 advance(); 589 } 590 591 template <typename Predicate> 592 kj::ArrayPtr<const char> consumeWhile(Predicate&& predicate) { 593 auto originalPos = wrapped.begin(); 594 while (!exhausted() && predicate(nextChar())) { advance(); } 595 596 return kj::arrayPtr(originalPos, wrapped.begin()); 597 } 598 599 template <typename F> // Function<void(Input&)> 600 kj::ArrayPtr<const char> consumeCustom(F&& f) { 601 // Allows consuming in a custom manner without exposing the wrapped ArrayPtr. 602 auto originalPos = wrapped.begin(); 603 f(*this); 604 605 return kj::arrayPtr(originalPos, wrapped.begin()); 606 } 607 608 void consumeWhitespace() { 609 consumeWhile([](char chr) { 610 return ( 611 chr == ' ' || 612 chr == '\n' || 613 chr == '\r' || 614 chr == '\t' 615 ); 616 }); 617 } 618 619 620 private: 621 kj::ArrayPtr<const char> wrapped; 622 623 }; // class Input 624 625 class Parser { 626 public: 627 Parser(size_t maxNestingDepth, kj::ArrayPtr<const char> input) : 628 maxNestingDepth(maxNestingDepth), input(input), nestingDepth(0) {} 629 630 void parseValue(JsonValue::Builder& output) { 631 input.consumeWhitespace(); 632 KJ_DEFER(input.consumeWhitespace()); 633 634 KJ_REQUIRE(!input.exhausted(), "JSON message ends prematurely."); 635 636 switch (input.nextChar()) { 637 case 'n': input.consume(kj::StringPtr("null")); output.setNull(); break; 638 case 'f': input.consume(kj::StringPtr("false")); output.setBoolean(false); break; 639 case 't': input.consume(kj::StringPtr("true")); output.setBoolean(true); break; 640 case '"': parseString(output); break; 641 case '[': parseArray(output); break; 642 case '{': parseObject(output); break; 643 case '-': case '0': case '1': case '2': case '3': 644 case '4': case '5': case '6': case '7': case '8': 645 case '9': parseNumber(output); break; 646 default: KJ_FAIL_REQUIRE("Unexpected input in JSON message."); 647 } 648 } 649 650 void parseNumber(JsonValue::Builder& output) { 651 output.setNumber(consumeNumber().parseAs<double>()); 652 } 653 654 void parseString(JsonValue::Builder& output) { 655 output.setString(consumeQuotedString()); 656 } 657 658 void parseArray(JsonValue::Builder& output) { 659 // TODO(perf): Using orphans leaves holes in the message. It's expected 660 // that a JsonValue is used for interop, and won't be sent or written as a 661 // Cap'n Proto message. This also applies to parseObject below. 662 kj::Vector<Orphan<JsonValue>> values; 663 auto orphanage = Orphanage::getForMessageContaining(output); 664 bool expectComma = false; 665 666 input.consume('['); 667 KJ_REQUIRE(++nestingDepth <= maxNestingDepth, "JSON message nested too deeply."); 668 KJ_DEFER(--nestingDepth); 669 670 while (input.consumeWhitespace(), input.nextChar() != ']') { 671 auto orphan = orphanage.newOrphan<JsonValue>(); 672 auto builder = orphan.get(); 673 674 if (expectComma) { 675 input.consumeWhitespace(); 676 input.consume(','); 677 input.consumeWhitespace(); 678 } 679 680 parseValue(builder); 681 values.add(kj::mv(orphan)); 682 683 expectComma = true; 684 } 685 686 output.initArray(values.size()); 687 auto array = output.getArray(); 688 689 for (auto i : kj::indices(values)) { 690 array.adoptWithCaveats(i, kj::mv(values[i])); 691 } 692 693 input.consume(']'); 694 } 695 696 void parseObject(JsonValue::Builder& output) { 697 kj::Vector<Orphan<JsonValue::Field>> fields; 698 auto orphanage = Orphanage::getForMessageContaining(output); 699 bool expectComma = false; 700 701 input.consume('{'); 702 KJ_REQUIRE(++nestingDepth <= maxNestingDepth, "JSON message nested too deeply."); 703 KJ_DEFER(--nestingDepth); 704 705 while (input.consumeWhitespace(), input.nextChar() != '}') { 706 auto orphan = orphanage.newOrphan<JsonValue::Field>(); 707 auto builder = orphan.get(); 708 709 if (expectComma) { 710 input.consumeWhitespace(); 711 input.consume(','); 712 input.consumeWhitespace(); 713 } 714 715 builder.setName(consumeQuotedString()); 716 717 input.consumeWhitespace(); 718 input.consume(':'); 719 input.consumeWhitespace(); 720 721 auto valueBuilder = builder.getValue(); 722 parseValue(valueBuilder); 723 724 fields.add(kj::mv(orphan)); 725 726 expectComma = true; 727 } 728 729 output.initObject(fields.size()); 730 auto object = output.getObject(); 731 732 for (auto i : kj::indices(fields)) { 733 object.adoptWithCaveats(i, kj::mv(fields[i])); 734 } 735 736 input.consume('}'); 737 } 738 739 bool inputExhausted() { return input.exhausted(); } 740 741 private: 742 kj::String consumeQuotedString() { 743 input.consume('"'); 744 // TODO(perf): Avoid copy / alloc if no escapes encoutered. 745 // TODO(perf): Get statistics on string size and preallocate? 746 kj::Vector<char> decoded; 747 748 do { 749 auto stringValue = input.consumeWhile([](const char chr) { 750 return chr != '"' && chr != '\\'; 751 }); 752 753 decoded.addAll(stringValue); 754 755 if (input.nextChar() == '\\') { // handle escapes. 756 input.advance(); 757 switch(input.nextChar()) { 758 case '"' : decoded.add('"' ); input.advance(); break; 759 case '\\': decoded.add('\\'); input.advance(); break; 760 case '/' : decoded.add('/' ); input.advance(); break; 761 case 'b' : decoded.add('\b'); input.advance(); break; 762 case 'f' : decoded.add('\f'); input.advance(); break; 763 case 'n' : decoded.add('\n'); input.advance(); break; 764 case 'r' : decoded.add('\r'); input.advance(); break; 765 case 't' : decoded.add('\t'); input.advance(); break; 766 case 'u' : 767 input.consume('u'); 768 unescapeAndAppend(input.consume(size_t(4)), decoded); 769 break; 770 default: KJ_FAIL_REQUIRE("Invalid escape in JSON string."); break; 771 } 772 } 773 774 } while(input.nextChar() != '"'); 775 776 input.consume('"'); 777 decoded.add('\0'); 778 779 // TODO(perf): This copy can be eliminated, but I can't find the kj::wayToDoIt(); 780 return kj::String(decoded.releaseAsArray()); 781 } 782 783 kj::String consumeNumber() { 784 auto numArrayPtr = input.consumeCustom([](Input& input) { 785 input.tryConsume('-'); 786 if (!input.tryConsume('0')) { 787 input.consumeOne([](char c) { return '1' <= c && c <= '9'; }); 788 input.consumeWhile([](char c) { return '0' <= c && c <= '9'; }); 789 } 790 791 if (input.tryConsume('.')) { 792 input.consumeWhile([](char c) { return '0' <= c && c <= '9'; }); 793 } 794 795 if (input.tryConsume('e') || input.tryConsume('E')) { 796 input.tryConsume('+') || input.tryConsume('-'); 797 input.consumeWhile([](char c) { return '0' <= c && c <= '9'; }); 798 } 799 }); 800 801 KJ_REQUIRE(numArrayPtr.size() > 0, "Expected number in JSON input."); 802 803 kj::Vector<char> number; 804 number.addAll(numArrayPtr); 805 number.add('\0'); 806 807 return kj::String(number.releaseAsArray()); 808 } 809 810 // TODO(someday): This "interface" is ugly, and won't work if/when surrogates are handled. 811 void unescapeAndAppend(kj::ArrayPtr<const char> hex, kj::Vector<char>& target) { 812 KJ_REQUIRE(hex.size() == 4); 813 int codePoint = 0; 814 815 for (int i = 0; i < 4; ++i) { 816 char c = hex[i]; 817 codePoint <<= 4; 818 819 if ('0' <= c && c <= '9') { 820 codePoint |= c - '0'; 821 } else if ('a' <= c && c <= 'f') { 822 codePoint |= c - 'a'; 823 } else if ('A' <= c && c <= 'F') { 824 codePoint |= c - 'A'; 825 } else { 826 KJ_FAIL_REQUIRE("Invalid hex digit in unicode escape.", c); 827 } 828 } 829 830 if (codePoint < 128) { 831 target.add(0x7f & static_cast<char>(codePoint)); 832 } else { 833 // TODO(perf): This is sorta malloc-heavy... 834 char16_t u = codePoint; 835 target.addAll(kj::decodeUtf16(kj::arrayPtr(&u, 1))); 836 } 837 } 838 839 const size_t maxNestingDepth; 840 Input input; 841 size_t nestingDepth; 842 843 844 }; // class Parser 845 846 } // namespace 847 848 849 void JsonCodec::decodeRaw(kj::ArrayPtr<const char> input, JsonValue::Builder output) const { 850 Parser parser(impl->maxNestingDepth, input); 851 parser.parseValue(output); 852 853 KJ_REQUIRE(parser.inputExhausted(), "Input remains after parsing JSON."); 854 } 855 856 // ----------------------------------------------------------------------------- 857 858 Orphan<DynamicValue> JsonCodec::HandlerBase::decodeBase( 859 const JsonCodec& codec, JsonValue::Reader input, Type type, Orphanage orphanage) const { 860 KJ_FAIL_ASSERT("JSON decoder handler type / value type mismatch"); 861 } 862 void JsonCodec::HandlerBase::decodeStructBase( 863 const JsonCodec& codec, JsonValue::Reader input, DynamicStruct::Builder output) const { 864 KJ_FAIL_ASSERT("JSON decoder handler type / value type mismatch"); 865 } 866 867 void JsonCodec::addTypeHandlerImpl(Type type, HandlerBase& handler) { 868 impl->typeHandlers.upsert(type, &handler, [](HandlerBase*& existing, HandlerBase* replacement) { 869 KJ_REQUIRE(existing == replacement, "type already has a different registered handler"); 870 }); 871 } 872 873 void JsonCodec::addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler) { 874 KJ_REQUIRE(type == field.getType(), 875 "handler type did not match field type for addFieldHandler()"); 876 impl->fieldHandlers.upsert(field, &handler, [](HandlerBase*& existing, HandlerBase* replacement) { 877 KJ_REQUIRE(existing == replacement, "field already has a different registered handler"); 878 }); 879 } 880 881 // ======================================================================================= 882 883 static constexpr uint64_t JSON_NAME_ANNOTATION_ID = 0xfa5b1fd61c2e7c3dull; 884 static constexpr uint64_t JSON_FLATTEN_ANNOTATION_ID = 0x82d3e852af0336bfull; 885 static constexpr uint64_t JSON_DISCRIMINATOR_ANNOTATION_ID = 0xcfa794e8d19a0162ull; 886 static constexpr uint64_t JSON_BASE64_ANNOTATION_ID = 0xd7d879450a253e4bull; 887 static constexpr uint64_t JSON_HEX_ANNOTATION_ID = 0xf061e22f0ae5c7b5ull; 888 889 class JsonCodec::Base64Handler final: public JsonCodec::Handler<capnp::Data> { 890 public: 891 void encode(const JsonCodec& codec, capnp::Data::Reader input, JsonValue::Builder output) const { 892 output.setString(kj::encodeBase64(input)); 893 } 894 895 Orphan<capnp::Data> decode(const JsonCodec& codec, JsonValue::Reader input, 896 Orphanage orphanage) const { 897 return orphanage.newOrphanCopy(capnp::Data::Reader(kj::decodeBase64(input.getString()))); 898 } 899 }; 900 901 class JsonCodec::HexHandler final: public JsonCodec::Handler<capnp::Data> { 902 public: 903 void encode(const JsonCodec& codec, capnp::Data::Reader input, JsonValue::Builder output) const { 904 output.setString(kj::encodeHex(input)); 905 } 906 907 Orphan<capnp::Data> decode(const JsonCodec& codec, JsonValue::Reader input, 908 Orphanage orphanage) const { 909 return orphanage.newOrphanCopy(capnp::Data::Reader(kj::decodeHex(input.getString()))); 910 } 911 }; 912 913 class JsonCodec::AnnotatedHandler final: public JsonCodec::Handler<DynamicStruct> { 914 public: 915 AnnotatedHandler(JsonCodec& codec, StructSchema schema, 916 kj::Maybe<json::DiscriminatorOptions::Reader> discriminator, 917 kj::Maybe<kj::StringPtr> unionDeclName, 918 kj::Vector<Schema>& dependencies) 919 : schema(schema) { 920 auto schemaProto = schema.getProto(); 921 auto typeName = schemaProto.getDisplayName(); 922 923 if (discriminator == nullptr) { 924 // There are two cases of unions: 925 // * Named unions, which are special cases of named groups. In this case, the union may be 926 // annotated by annotating the field. In this case, we receive a non-null `discriminator` 927 // as a constructor parameter, and schemaProto.getAnnotations() must be empty because 928 // it's not possible to annotate a group's type (because the type is anonymous). 929 // * Unnamed unions, of which there can only be one in any particular scope. In this case, 930 // the parent struct type itself is annotated. 931 // So if we received `null` as the constructor parameter, check for annotations on the struct 932 // type. 933 for (auto anno: schemaProto.getAnnotations()) { 934 switch (anno.getId()) { 935 case JSON_DISCRIMINATOR_ANNOTATION_ID: 936 discriminator = anno.getValue().getStruct().getAs<json::DiscriminatorOptions>(); 937 break; 938 } 939 } 940 } 941 942 KJ_IF_MAYBE(d, discriminator) { 943 if (d->hasName()) { 944 unionTagName = d->getName(); 945 } else { 946 unionTagName = unionDeclName; 947 } 948 KJ_IF_MAYBE(u, unionTagName) { 949 fieldsByName.insert(*u, FieldNameInfo { 950 FieldNameInfo::UNION_TAG, 0, 0, nullptr 951 }); 952 } 953 954 if (d->hasValueName()) { 955 fieldsByName.insert(d->getValueName(), FieldNameInfo { 956 FieldNameInfo::UNION_VALUE, 0, 0, nullptr 957 }); 958 } 959 } 960 961 discriminantOffset = schemaProto.getStruct().getDiscriminantOffset(); 962 963 fields = KJ_MAP(field, schema.getFields()) { 964 auto fieldProto = field.getProto(); 965 auto type = field.getType(); 966 auto fieldName = fieldProto.getName(); 967 968 FieldNameInfo nameInfo; 969 nameInfo.index = field.getIndex(); 970 nameInfo.type = FieldNameInfo::NORMAL; 971 nameInfo.prefixLength = 0; 972 973 FieldInfo info; 974 info.name = fieldName; 975 976 kj::Maybe<json::DiscriminatorOptions::Reader> subDiscriminator; 977 bool flattened = false; 978 for (auto anno: field.getProto().getAnnotations()) { 979 switch (anno.getId()) { 980 case JSON_NAME_ANNOTATION_ID: 981 info.name = anno.getValue().getText(); 982 break; 983 case JSON_FLATTEN_ANNOTATION_ID: 984 KJ_REQUIRE(type.isStruct(), "only struct types can be flattened", fieldName, typeName); 985 flattened = true; 986 info.prefix = anno.getValue().getStruct().getAs<json::FlattenOptions>().getPrefix(); 987 break; 988 case JSON_DISCRIMINATOR_ANNOTATION_ID: 989 KJ_REQUIRE(fieldProto.isGroup(), "only unions can have discriminator"); 990 subDiscriminator = anno.getValue().getStruct().getAs<json::DiscriminatorOptions>(); 991 break; 992 case JSON_BASE64_ANNOTATION_ID: { 993 KJ_REQUIRE(field.getType().isData(), "only Data can be marked for base64 encoding"); 994 static Base64Handler handler; 995 codec.addFieldHandler(field, handler); 996 break; 997 } 998 case JSON_HEX_ANNOTATION_ID: { 999 KJ_REQUIRE(field.getType().isData(), "only Data can be marked for hex encoding"); 1000 static HexHandler handler; 1001 codec.addFieldHandler(field, handler); 1002 break; 1003 } 1004 } 1005 } 1006 1007 if (fieldProto.isGroup()) { 1008 // Load group type handler now, even if not flattened, so that we can pass its 1009 // `subDiscriminator`. 1010 kj::Maybe<kj::StringPtr> subFieldName; 1011 if (flattened) { 1012 // If the group was flattened, then we allow its field name to be used as the 1013 // discriminator name, so that the discriminator doesn't have to explicitly specify a 1014 // name. 1015 subFieldName = fieldName; 1016 } 1017 auto& subHandler = codec.loadAnnotatedHandler( 1018 type.asStruct(), subDiscriminator, subFieldName, dependencies); 1019 if (flattened) { 1020 info.flattenHandler = subHandler; 1021 } 1022 } else if (type.isStruct()) { 1023 if (flattened) { 1024 info.flattenHandler = codec.loadAnnotatedHandler( 1025 type.asStruct(), nullptr, nullptr, dependencies); 1026 } 1027 } 1028 1029 bool isUnionMember = fieldProto.getDiscriminantValue() != schema::Field::NO_DISCRIMINANT; 1030 1031 KJ_IF_MAYBE(fh, info.flattenHandler) { 1032 // Set up fieldsByName for each of the child's fields. 1033 for (auto& entry: fh->fieldsByName) { 1034 kj::StringPtr flattenedName; 1035 kj::String ownName; 1036 if (info.prefix.size() > 0) { 1037 ownName = kj::str(info.prefix, entry.key); 1038 flattenedName = ownName; 1039 } else { 1040 flattenedName = entry.key; 1041 } 1042 1043 fieldsByName.upsert(flattenedName, FieldNameInfo { 1044 isUnionMember ? FieldNameInfo::FLATTENED_FROM_UNION : FieldNameInfo::FLATTENED, 1045 field.getIndex(), (uint)info.prefix.size(), kj::mv(ownName) 1046 }, [&](FieldNameInfo& existing, FieldNameInfo&& replacement) { 1047 KJ_REQUIRE(existing.type == FieldNameInfo::FLATTENED_FROM_UNION && 1048 replacement.type == FieldNameInfo::FLATTENED_FROM_UNION, 1049 "flattened members have the same name and are not mutually exclusive"); 1050 }); 1051 } 1052 } 1053 1054 info.nameForDiscriminant = info.name; 1055 1056 if (!flattened) { 1057 bool isUnionWithValueName = false; 1058 if (isUnionMember) { 1059 KJ_IF_MAYBE(d, discriminator) { 1060 if (d->hasValueName()) { 1061 info.name = d->getValueName(); 1062 isUnionWithValueName = true; 1063 } 1064 } 1065 } 1066 1067 if (!isUnionWithValueName) { 1068 fieldsByName.insert(info.name, kj::mv(nameInfo)); 1069 } 1070 } 1071 1072 if (isUnionMember) { 1073 unionTagValues.insert(info.nameForDiscriminant, field); 1074 } 1075 1076 // Look for dependencies that we need to add. 1077 while (type.isList()) type = type.asList().getElementType(); 1078 if (codec.impl->typeHandlers.find(type) == nullptr) { 1079 switch (type.which()) { 1080 case schema::Type::STRUCT: 1081 dependencies.add(type.asStruct()); 1082 break; 1083 case schema::Type::ENUM: 1084 dependencies.add(type.asEnum()); 1085 break; 1086 case schema::Type::INTERFACE: 1087 dependencies.add(type.asInterface()); 1088 break; 1089 default: 1090 break; 1091 } 1092 } 1093 1094 return info; 1095 }; 1096 } 1097 1098 const StructSchema schema; 1099 1100 void encode(const JsonCodec& codec, DynamicStruct::Reader input, 1101 JsonValue::Builder output) const override { 1102 kj::Vector<FlattenedField> flattenedFields; 1103 gatherForEncode(codec, input, nullptr, nullptr, flattenedFields); 1104 1105 auto outs = output.initObject(flattenedFields.size()); 1106 for (auto i: kj::indices(flattenedFields)) { 1107 auto& in = flattenedFields[i]; 1108 auto out = outs[i]; 1109 out.setName(in.name); 1110 KJ_SWITCH_ONEOF(in.type) { 1111 KJ_CASE_ONEOF(type, Type) { 1112 codec.encode(in.value, type, out.initValue()); 1113 } 1114 KJ_CASE_ONEOF(field, StructSchema::Field) { 1115 codec.encodeField(field, in.value, out.initValue()); 1116 } 1117 } 1118 } 1119 } 1120 1121 void decode(const JsonCodec& codec, JsonValue::Reader input, 1122 DynamicStruct::Builder output) const override { 1123 KJ_REQUIRE(input.isObject()); 1124 kj::HashSet<const void*> unionsSeen; 1125 kj::Vector<JsonValue::Field::Reader> retries; 1126 for (auto field: input.getObject()) { 1127 if (!decodeField(codec, field.getName(), field.getValue(), output, unionsSeen)) { 1128 retries.add(field); 1129 } 1130 } 1131 while (!retries.empty()) { 1132 auto retriesCopy = kj::mv(retries); 1133 KJ_ASSERT(retries.empty()); 1134 for (auto field: retriesCopy) { 1135 if (!decodeField(codec, field.getName(), field.getValue(), output, unionsSeen)) { 1136 retries.add(field); 1137 } 1138 } 1139 if (retries.size() == retriesCopy.size()) { 1140 // We made no progress in this iteration. Give up on the remaining fields. 1141 break; 1142 } 1143 } 1144 } 1145 1146 private: 1147 struct FieldInfo { 1148 kj::StringPtr name; 1149 kj::StringPtr nameForDiscriminant; 1150 kj::Maybe<const AnnotatedHandler&> flattenHandler; 1151 kj::StringPtr prefix; 1152 }; 1153 1154 kj::Array<FieldInfo> fields; 1155 // Maps field index -> info about the field 1156 1157 struct FieldNameInfo { 1158 enum { 1159 NORMAL, 1160 // This is a normal field with the given `index`. 1161 1162 FLATTENED, 1163 // This is a field of a flattened inner struct or group (that is not in a union). `index` 1164 // is the field index of the particular struct/group field. 1165 1166 UNION_TAG, 1167 // The parent struct is a flattened union, and this field is the discriminant tag. It is a 1168 // string field whose name determines the union type. `index` is not used. 1169 1170 FLATTENED_FROM_UNION, 1171 // The parent struct is a flattened union, and some of the union's members are flattened 1172 // structs or groups, and this field is possibly a member of one or more of them. `index` 1173 // is not used, because it's possible that the same field name appears in multiple variants. 1174 // Instead, the parser must find the union tag, and then can descend and attempt to parse 1175 // the field in the context of whichever variant is selected. 1176 1177 UNION_VALUE 1178 // This field is the value of a discriminated union that has `valueName` set. 1179 } type; 1180 1181 uint index; 1182 // For `NORMAL` and `FLATTENED`, the index of the field in schema.getFields(). 1183 1184 uint prefixLength; 1185 kj::String ownName; 1186 }; 1187 1188 kj::HashMap<kj::StringPtr, FieldNameInfo> fieldsByName; 1189 // Maps JSON names to info needed to parse them. 1190 1191 kj::HashMap<kj::StringPtr, StructSchema::Field> unionTagValues; 1192 // If the parent struct is a flattened union, it has a tag field which is a string with one of 1193 // these values. The map maps to the union member to set. 1194 1195 kj::Maybe<kj::StringPtr> unionTagName; 1196 // If the parent struct is a flattened union, the name of the "tag" field. 1197 1198 uint discriminantOffset; 1199 // Shortcut for schema.getProto().getStruct().getDiscriminantOffset(), used in a hack to identify 1200 // which unions have been seen. 1201 1202 struct FlattenedField { 1203 kj::String ownName; 1204 kj::StringPtr name; 1205 kj::OneOf<StructSchema::Field, Type> type; 1206 DynamicValue::Reader value; 1207 1208 FlattenedField(kj::StringPtr prefix, kj::StringPtr name, 1209 kj::OneOf<StructSchema::Field, Type> type, DynamicValue::Reader value) 1210 : ownName(prefix.size() > 0 ? kj::str(prefix, name) : nullptr), 1211 name(prefix.size() > 0 ? ownName : name), 1212 type(type), value(value) {} 1213 }; 1214 1215 void gatherForEncode(const JsonCodec& codec, DynamicValue::Reader input, 1216 kj::StringPtr prefix, kj::StringPtr morePrefix, 1217 kj::Vector<FlattenedField>& flattenedFields) const { 1218 kj::String ownPrefix; 1219 if (morePrefix.size() > 0) { 1220 if (prefix.size() > 0) { 1221 ownPrefix = kj::str(prefix, morePrefix); 1222 prefix = ownPrefix; 1223 } else { 1224 prefix = morePrefix; 1225 } 1226 } 1227 1228 auto reader = input.as<DynamicStruct>(); 1229 auto schema = reader.getSchema(); 1230 for (auto field: schema.getNonUnionFields()) { 1231 auto& info = fields[field.getIndex()]; 1232 if (!reader.has(field, codec.impl->hasMode)) { 1233 // skip 1234 } else KJ_IF_MAYBE(handler, info.flattenHandler) { 1235 handler->gatherForEncode(codec, reader.get(field), prefix, info.prefix, flattenedFields); 1236 } else { 1237 flattenedFields.add(FlattenedField { 1238 prefix, info.name, field, reader.get(field) }); 1239 } 1240 } 1241 1242 KJ_IF_MAYBE(which, reader.which()) { 1243 auto& info = fields[which->getIndex()]; 1244 KJ_IF_MAYBE(tag, unionTagName) { 1245 flattenedFields.add(FlattenedField { 1246 prefix, *tag, Type(schema::Type::TEXT), Text::Reader(info.nameForDiscriminant) }); 1247 } 1248 1249 KJ_IF_MAYBE(handler, info.flattenHandler) { 1250 handler->gatherForEncode(codec, reader.get(*which), prefix, info.prefix, flattenedFields); 1251 } else { 1252 auto type = which->getType(); 1253 if (type.which() == schema::Type::VOID && unionTagName != nullptr) { 1254 // When we have an explicit union discriminant, we don't need to encode void fields. 1255 } else { 1256 flattenedFields.add(FlattenedField { 1257 prefix, info.name, *which, reader.get(*which) }); 1258 } 1259 } 1260 } 1261 } 1262 1263 bool decodeField(const JsonCodec& codec, kj::StringPtr name, JsonValue::Reader value, 1264 DynamicStruct::Builder output, kj::HashSet<const void*>& unionsSeen) const { 1265 KJ_ASSERT(output.getSchema() == schema); 1266 1267 KJ_IF_MAYBE(info, fieldsByName.find(name)) { 1268 switch (info->type) { 1269 case FieldNameInfo::NORMAL: { 1270 auto field = output.getSchema().getFields()[info->index]; 1271 codec.decodeField(field, value, Orphanage::getForMessageContaining(output), output); 1272 return true; 1273 } 1274 case FieldNameInfo::FLATTENED: 1275 return KJ_ASSERT_NONNULL(fields[info->index].flattenHandler) 1276 .decodeField(codec, name.slice(info->prefixLength), value, 1277 output.get(output.getSchema().getFields()[info->index]).as<DynamicStruct>(), 1278 unionsSeen); 1279 case FieldNameInfo::UNION_TAG: { 1280 KJ_REQUIRE(value.isString(), "Expected string value."); 1281 1282 // Mark that we've seen a union tag for this struct. 1283 const void* ptr = getUnionInstanceIdentifier(output); 1284 KJ_IF_MAYBE(field, unionTagValues.find(value.getString())) { 1285 // clear() has the side-effect of activating this member of the union, without 1286 // allocating any objects. 1287 output.clear(*field); 1288 unionsSeen.insert(ptr); 1289 } 1290 return true; 1291 } 1292 case FieldNameInfo::FLATTENED_FROM_UNION: { 1293 const void* ptr = getUnionInstanceIdentifier(output); 1294 if (unionsSeen.contains(ptr)) { 1295 auto variant = KJ_ASSERT_NONNULL(output.which()); 1296 return KJ_ASSERT_NONNULL(fields[variant.getIndex()].flattenHandler) 1297 .decodeField(codec, name.slice(info->prefixLength), value, 1298 output.get(variant).as<DynamicStruct>(), unionsSeen); 1299 } else { 1300 // We haven't seen the union tag yet, so we can't parse this field yet. Try again later. 1301 return false; 1302 } 1303 } 1304 case FieldNameInfo::UNION_VALUE: { 1305 const void* ptr = getUnionInstanceIdentifier(output); 1306 if (unionsSeen.contains(ptr)) { 1307 auto variant = KJ_ASSERT_NONNULL(output.which()); 1308 codec.decodeField(variant, value, Orphanage::getForMessageContaining(output), output); 1309 return true; 1310 } else { 1311 // We haven't seen the union tag yet, so we can't parse this field yet. Try again later. 1312 return false; 1313 } 1314 } 1315 } 1316 1317 KJ_UNREACHABLE; 1318 } else { 1319 // Ignore undefined field -- unless the flag is set to reject them. 1320 KJ_REQUIRE(!codec.impl->rejectUnknownFields, "Unknown field", name); 1321 return true; 1322 } 1323 } 1324 1325 const void* getUnionInstanceIdentifier(DynamicStruct::Builder obj) const { 1326 // Gets a value uniquely identifying an instance of a union. 1327 // HACK: We return a poniter to the union's discriminant within the underlying buffer. 1328 return reinterpret_cast<const uint16_t*>( 1329 AnyStruct::Reader(obj.asReader()).getDataSection().begin()) + discriminantOffset; 1330 } 1331 }; 1332 1333 class JsonCodec::AnnotatedEnumHandler final: public JsonCodec::Handler<DynamicEnum> { 1334 public: 1335 AnnotatedEnumHandler(EnumSchema schema): schema(schema) { 1336 auto enumerants = schema.getEnumerants(); 1337 auto builder = kj::heapArrayBuilder<kj::StringPtr>(enumerants.size()); 1338 1339 for (auto e: enumerants) { 1340 auto proto = e.getProto(); 1341 kj::StringPtr name = proto.getName(); 1342 1343 for (auto anno: proto.getAnnotations()) { 1344 switch (anno.getId()) { 1345 case JSON_NAME_ANNOTATION_ID: 1346 name = anno.getValue().getText(); 1347 break; 1348 } 1349 } 1350 1351 builder.add(name); 1352 nameToValue.insert(name, e.getIndex()); 1353 } 1354 1355 valueToName = builder.finish(); 1356 } 1357 1358 void encode(const JsonCodec& codec, DynamicEnum input, JsonValue::Builder output) const override { 1359 KJ_IF_MAYBE(e, input.getEnumerant()) { 1360 KJ_ASSERT(e->getIndex() < valueToName.size()); 1361 output.setString(valueToName[e->getIndex()]); 1362 } else { 1363 output.setNumber(input.getRaw()); 1364 } 1365 } 1366 1367 DynamicEnum decode(const JsonCodec& codec, JsonValue::Reader input) const override { 1368 if (input.isNumber()) { 1369 return DynamicEnum(schema, static_cast<uint16_t>(input.getNumber())); 1370 } else { 1371 uint16_t val = KJ_REQUIRE_NONNULL(nameToValue.find(input.getString()), 1372 "invalid enum value", input.getString()); 1373 return DynamicEnum(schema.getEnumerants()[val]); 1374 } 1375 } 1376 1377 private: 1378 EnumSchema schema; 1379 kj::Array<kj::StringPtr> valueToName; 1380 kj::HashMap<kj::StringPtr, uint16_t> nameToValue; 1381 }; 1382 1383 class JsonCodec::JsonValueHandler final: public JsonCodec::Handler<DynamicStruct> { 1384 public: 1385 void encode(const JsonCodec& codec, DynamicStruct::Reader input, 1386 JsonValue::Builder output) const override { 1387 #if _MSC_VER && !defined(__clang__) 1388 // TODO(msvc): Hack to work around missing AnyStruct::Builder constructor on MSVC. 1389 rawCopy(input, toDynamic(output)); 1390 #else 1391 rawCopy(input, kj::mv(output)); 1392 #endif 1393 } 1394 1395 void decode(const JsonCodec& codec, JsonValue::Reader input, 1396 DynamicStruct::Builder output) const override { 1397 rawCopy(input, kj::mv(output)); 1398 } 1399 1400 private: 1401 void rawCopy(AnyStruct::Reader input, AnyStruct::Builder output) const { 1402 // HACK: Manually copy using AnyStruct, so that if JsonValue's definition changes, this code 1403 // doesn't need to be updated. However, note that if JsonValue ever adds new fields that 1404 // change its size, and the input struct is a newer version than the output, we may lose 1405 // the new fields. Technically the "correct" thing to do would be to allocate the output 1406 // struct to be exactly the same size as the input, but JsonCodec's Handler interface is 1407 // not designed to allow that -- it passes in an already-allocated builder. Oops. 1408 auto dataIn = input.getDataSection(); 1409 auto dataOut = output.getDataSection(); 1410 memcpy(dataOut.begin(), dataIn.begin(), kj::min(dataOut.size(), dataIn.size())); 1411 1412 auto ptrIn = input.getPointerSection(); 1413 auto ptrOut = output.getPointerSection(); 1414 for (auto i: kj::zeroTo(kj::min(ptrIn.size(), ptrOut.size()))) { 1415 ptrOut[i].set(ptrIn[i]); 1416 } 1417 } 1418 }; 1419 1420 JsonCodec::AnnotatedHandler& JsonCodec::loadAnnotatedHandler( 1421 StructSchema schema, kj::Maybe<json::DiscriminatorOptions::Reader> discriminator, 1422 kj::Maybe<kj::StringPtr> unionDeclName, kj::Vector<Schema>& dependencies) { 1423 auto& entry = impl->annotatedHandlers.upsert(schema, nullptr, 1424 [&](kj::Maybe<kj::Own<AnnotatedHandler>>& existing, auto dummy) { 1425 KJ_ASSERT(existing != nullptr, 1426 "cyclic JSON flattening detected", schema.getProto().getDisplayName()); 1427 }); 1428 1429 KJ_IF_MAYBE(v, entry.value) { 1430 // Already exists. 1431 return **v; 1432 } else { 1433 // Not seen before. 1434 auto newHandler = kj::heap<AnnotatedHandler>( 1435 *this, schema, discriminator, unionDeclName, dependencies); 1436 auto& result = *newHandler; 1437 1438 // Map may have changed, so we have to look up again. 1439 KJ_ASSERT_NONNULL(impl->annotatedHandlers.find(schema)) = kj::mv(newHandler); 1440 1441 addTypeHandler(schema, result); 1442 return result; 1443 }; 1444 } 1445 1446 void JsonCodec::handleByAnnotation(Schema schema) { 1447 switch (schema.getProto().which()) { 1448 case schema::Node::STRUCT: { 1449 if (schema.getProto().getId() == capnp::typeId<JsonValue>()) { 1450 // Special handler for JsonValue. 1451 static JsonValueHandler GLOBAL_HANDLER; 1452 addTypeHandler(schema.asStruct(), GLOBAL_HANDLER); 1453 } else { 1454 kj::Vector<Schema> dependencies; 1455 loadAnnotatedHandler(schema.asStruct(), nullptr, nullptr, dependencies); 1456 for (auto dep: dependencies) { 1457 handleByAnnotation(dep); 1458 } 1459 } 1460 break; 1461 } 1462 case schema::Node::ENUM: { 1463 auto enumSchema = schema.asEnum(); 1464 impl->annotatedEnumHandlers.findOrCreate(enumSchema, [&]() { 1465 auto handler = kj::heap<AnnotatedEnumHandler>(enumSchema); 1466 addTypeHandler(enumSchema, *handler); 1467 return kj::HashMap<Type, kj::Own<AnnotatedEnumHandler>>::Entry { 1468 enumSchema, kj::mv(handler) }; 1469 }); 1470 break; 1471 } 1472 default: 1473 break; 1474 } 1475 } 1476 1477 } // namespace capnp