mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
For ScalarTraits, a buffer was always created on the heap to which the scalar string value was written just to be copied to the output buffer again. In case the value already exists in a memory buffer it is way cheaper to avoid the heap allocation and copy it straight to the output buffer.
304 lines
7.8 KiB
C++
304 lines
7.8 KiB
C++
//===--- JSONSerialization.cpp - JSON serialization support ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Basic/JSONSerialization.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Format.h"
|
|
|
|
using namespace swift::json;
|
|
using namespace swift;
|
|
|
|
unsigned Output::beginArray() {
|
|
StateStack.push_back(ArrayFirstValue);
|
|
Stream << '[';
|
|
return 0;
|
|
}
|
|
|
|
bool Output::preflightElement(unsigned, void *&) {
|
|
if (StateStack.back() != ArrayFirstValue) {
|
|
assert(StateStack.back() == ArrayOtherValue && "We must be in a sequence!");
|
|
Stream << ',';
|
|
}
|
|
if (PrettyPrint) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Output::postflightElement(void*) {
|
|
if (StateStack.back() == ArrayFirstValue) {
|
|
StateStack.pop_back();
|
|
StateStack.push_back(ArrayOtherValue);
|
|
}
|
|
}
|
|
|
|
void Output::endArray() {
|
|
bool HadContent = StateStack.back() != ArrayFirstValue;
|
|
StateStack.pop_back();
|
|
if (PrettyPrint && HadContent) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << ']';
|
|
}
|
|
|
|
bool Output::canElideEmptyArray() {
|
|
if (StateStack.size() < 2)
|
|
return true;
|
|
if (StateStack.back() != ObjectFirstKey)
|
|
return true;
|
|
State checkedState = StateStack[StateStack.size() - 2];
|
|
return (checkedState != ArrayFirstValue && checkedState != ArrayOtherValue);
|
|
}
|
|
|
|
void Output::beginObject() {
|
|
StateStack.push_back(ObjectFirstKey);
|
|
Stream << "{";
|
|
}
|
|
|
|
void Output::endObject() {
|
|
bool HadContent = StateStack.back() != ObjectFirstKey;
|
|
StateStack.pop_back();
|
|
if (PrettyPrint && HadContent) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << "}";
|
|
}
|
|
|
|
bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault,
|
|
bool &UseDefault, void *&) {
|
|
UseDefault = false;
|
|
if (Required || !SameAsDefault) {
|
|
if (StateStack.back() != ObjectFirstKey) {
|
|
assert(StateStack.back() == ObjectOtherKey && "We must be in an object!");
|
|
Stream << ',';
|
|
}
|
|
if (PrettyPrint) {
|
|
Stream << '\n';
|
|
indent();
|
|
}
|
|
Stream << '"' << Key << "\":";
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::postflightKey(void*) {
|
|
if (StateStack.back() == ObjectFirstKey) {
|
|
StateStack.pop_back();
|
|
StateStack.push_back(ObjectOtherKey);
|
|
}
|
|
}
|
|
|
|
void Output::beginEnumScalar() {
|
|
EnumerationMatchFound = false;
|
|
}
|
|
|
|
bool Output::matchEnumScalar(const char *Str, bool Match) {
|
|
if (Match && !EnumerationMatchFound) {
|
|
StringRef StrRef(Str);
|
|
scalarString(StrRef, true);
|
|
EnumerationMatchFound = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::endEnumScalar() {
|
|
if (!EnumerationMatchFound)
|
|
llvm_unreachable("bad runtime enum value");
|
|
}
|
|
|
|
bool Output::beginBitSetScalar(bool &DoClear) {
|
|
Stream << '[';
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
NeedBitValueComma = false;
|
|
DoClear = false;
|
|
return true;
|
|
}
|
|
|
|
bool Output::bitSetMatch(const char *Str, bool Matches) {
|
|
if (Matches) {
|
|
if (NeedBitValueComma) {
|
|
Stream << ',';
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
}
|
|
StringRef StrRef(Str);
|
|
scalarString(StrRef, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Output::endBitSetScalar() {
|
|
if (PrettyPrint)
|
|
Stream << ' ';
|
|
Stream << ']';
|
|
}
|
|
|
|
void Output::scalarString(StringRef &S, bool MustQuote) {
|
|
if (MustQuote) {
|
|
Stream << '"';
|
|
for (unsigned char c : S) {
|
|
// According to the JSON standard, the following characters must be
|
|
// escaped:
|
|
// - Quotation mark (U+0022)
|
|
// - Reverse solidus (U+005C)
|
|
// - Control characters (U+0000 to U+001F)
|
|
// We need to check for these and escape them if present.
|
|
//
|
|
// Since these are represented by a single byte in UTF8 (and will not be
|
|
// present in any multi-byte UTF8 representations), we can just switch on
|
|
// the value of the current byte.
|
|
//
|
|
// Any other bytes present in the string should therefore be emitted
|
|
// as-is, without any escaping.
|
|
switch (c) {
|
|
// First, check for characters for which JSON has custom escape sequences.
|
|
case '"':
|
|
Stream << '\\' << '"';
|
|
break;
|
|
case '\\':
|
|
Stream << '\\' << '\\';
|
|
break;
|
|
case '/':
|
|
Stream << '\\' << '/';
|
|
break;
|
|
case '\b':
|
|
Stream << '\\' << 'b';
|
|
break;
|
|
case '\f':
|
|
Stream << '\\' << 'f';
|
|
break;
|
|
case '\n':
|
|
Stream << '\\' << 'n';
|
|
break;
|
|
case '\r':
|
|
Stream << '\\' << 'r';
|
|
break;
|
|
case '\t':
|
|
Stream << '\\' << 't';
|
|
break;
|
|
default:
|
|
// Otherwise, check to see if the current byte is a control character.
|
|
if (c <= '\x1F') {
|
|
// Since we have a control character, we need to escape it using
|
|
// JSON's only valid escape sequence: \uxxxx (where x is a hex digit).
|
|
|
|
// The upper two digits for control characters are always 00.
|
|
Stream << "\\u00";
|
|
|
|
// Convert the current character into hexadecimal digits.
|
|
Stream << llvm::hexdigit((c >> 4) & 0xF);
|
|
Stream << llvm::hexdigit((c >> 0) & 0xF);
|
|
} else {
|
|
// This isn't a control character, so we don't need to escape it.
|
|
// As a result, emit it directly; if it's part of a multi-byte UTF8
|
|
// representation, all bytes will be emitted in this fashion.
|
|
Stream << c;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Stream << '"';
|
|
}
|
|
else
|
|
Stream << S;
|
|
}
|
|
|
|
void Output::null() {
|
|
Stream << "null";
|
|
}
|
|
|
|
void Output::indent() {
|
|
Stream.indent(StateStack.size() * 2);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// traits for built-in types
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
StringRef ScalarReferenceTraits<bool>::stringRef(const bool &Val) {
|
|
return (Val ? "true" : "false");
|
|
}
|
|
|
|
StringRef ScalarReferenceTraits<StringRef>::stringRef(const StringRef &Val) {
|
|
return Val;
|
|
}
|
|
|
|
StringRef
|
|
ScalarReferenceTraits<std::string>::stringRef(const std::string &Val) {
|
|
return Val;
|
|
}
|
|
|
|
void ScalarTraits<uint8_t>::output(const uint8_t &Val,
|
|
raw_ostream &Out) {
|
|
// use temp uin32_t because ostream thinks uint8_t is a character
|
|
uint32_t Num = Val;
|
|
Out << Num;
|
|
}
|
|
|
|
void ScalarTraits<uint16_t>::output(const uint16_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<uint32_t>::output(const uint32_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
#if defined(_MSC_VER)
|
|
void ScalarTraits<unsigned long>::output(const unsigned long &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
#endif
|
|
|
|
void ScalarTraits<uint64_t>::output(const uint64_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int8_t>::output(const int8_t &Val, raw_ostream &Out) {
|
|
// use temp in32_t because ostream thinks int8_t is a character
|
|
int32_t Num = Val;
|
|
Out << Num;
|
|
}
|
|
|
|
void ScalarTraits<int16_t>::output(const int16_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int32_t>::output(const int32_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<int64_t>::output(const int64_t &Val,
|
|
raw_ostream &Out) {
|
|
Out << Val;
|
|
}
|
|
|
|
void ScalarTraits<double>::output(const double &Val, raw_ostream &Out) {
|
|
Out << llvm::format("%g", Val);
|
|
}
|
|
|
|
void ScalarTraits<float>::output(const float &Val, raw_ostream &Out) {
|
|
Out << llvm::format("%g", Val);
|
|
}
|