void parseField()

in thrift/lib/cpp2/util/DebugString.cpp [431:715]


void parseField(
    TType expectedType,
    folly::StringPiece extendedExpectedType,
    Tokenizer& tokenizer,
    ProtocolWriter* outProtoWriter) {
  auto getNextOrThrow = [&]() -> folly::StringPiece {
    auto sp = tokenizer.getNextToken();
    if (sp.empty()) {
      throw TProtocolException(
          TProtocolException::INVALID_DATA, "Unexpected end of data");
    }
    return sp;
  };

  switch (expectedType) {
    case TType::T_BOOL: {
      auto tok = getNextOrThrow();
      if (tok == "true") {
        if (outProtoWriter) {
          outProtoWriter->writeBool(true);
        }
      } else if (tok == "false") {
        if (outProtoWriter) {
          outProtoWriter->writeBool(false);
        }
      } else {
        throw TProtocolException(
            TProtocolException::INVALID_DATA,
            fmt::format("Bad bool token {}", tok));
      }
      break;
    }
    case TType::T_BYTE: {
      int8_t val = folly::to<int8_t>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeByte(val);
      }
      break;
    }
    case TType::T_I16: {
      int16_t val = folly::to<int16_t>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeI16(val);
      }
      break;
    }
    case TType::T_I32: {
      int32_t val = folly::to<int32_t>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeI32(val);
      }
      break;
    }
    case TType::T_I64: {
      int64_t val = folly::to<int64_t>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeI64(val);
      }
      break;
    }
    case TType::T_DOUBLE: {
      double val = folly::to<double>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeDouble(val);
      }
      break;
    }
    case TType::T_FLOAT: {
      float val = folly::to<float>(getNextOrThrow());
      if (outProtoWriter) {
        outProtoWriter->writeFloat(val);
      }
      break;
    }
    case TType::T_STRING: {
      auto tok = getNextOrThrow();
      if (tok.size() < 2 || tok.front() != '"' || tok.back() != '"') {
        throw TProtocolException(
            TProtocolException::INVALID_DATA,
            fmt::format("Bad string token {}", tok));
      }
      tok.pop_back();
      tok.pop_front();
      std::string unescaped;
      folly::cUnescape(tok, unescaped);
      if (outProtoWriter) {
        outProtoWriter->writeString(unescaped);
      }
      break;
    }
    case TType::T_STRUCT: {
      auto tok = getNextOrThrow();
      if (tok == "struct") {
        tok = getNextOrThrow();
      }
      if (tok != "{") {
        throw TProtocolException(
            TProtocolException::INVALID_DATA,
            fmt::format("Expected struct '{{' at {}", tok));
      }
      if (outProtoWriter) {
        outProtoWriter->writeStructBegin("");
      }
      for (;;) {
        tok = getNextOrThrow();
        if (tok == "}") {
          if (outProtoWriter) {
            outProtoWriter->writeFieldStop();
            outProtoWriter->writeStructEnd();
          }
          break;
        }
        if (tok.back() != ':') {
          throw TProtocolException(
              TProtocolException::INVALID_DATA,
              fmt::format("Expected tag: {}", tok));
        }
        tok.pop_back();
        int16_t tagnum = folly::to<int16_t>(tok);
        folly::StringPiece fullType = getNextOrThrow();
        TType typeId = parseSimpleTypeLabel(fullType);
        if (typeId == TType::T_STOP) {
          throw TProtocolException(
              TProtocolException::INVALID_DATA,
              fmt::format("Unexpected type {}", fullType));
        }
        if (getNextOrThrow() != "=") {
          throw TProtocolException(
              TProtocolException::INVALID_DATA, "Missing = delim in struct");
        }
        if (outProtoWriter) {
          outProtoWriter->writeFieldBegin("", typeId, tagnum);
        }
        parseField(typeId, fullType, tokenizer, outProtoWriter);
        if (outProtoWriter) {
          outProtoWriter->writeFieldEnd();
        }
      }
      break;
    }
    case TType::T_LIST:
    case TType::T_SET: {
      folly::StringPiece elemTypeStr = getInnerTypeLabel(extendedExpectedType);
      TType elemTypeId = parseSimpleTypeLabel(elemTypeStr);
      if (elemTypeId == TType::T_STOP) {
        throw TProtocolException(
            TProtocolException::INVALID_DATA,
            fmt::format("Cannot parse element type in {}", elemTypeStr));
      }
      folly::StringPiece openDelim, closeDelim;
      if (expectedType == TType::T_LIST) {
        openDelim = "[";
        closeDelim = "]";
      } else {
        openDelim = "{";
        closeDelim = "}";
      }
      if (getNextOrThrow() != openDelim) {
        throw TProtocolException(
            TProtocolException::INVALID_DATA, "Missing open delimiter");
      }

      // Count the number of elements. Unfortunately, the involves parsing the
      // data. We parse to a null writer, and copy the tokenizer.
      size_t numElems = 0;
      auto tokenizerCopy = tokenizer; // cheap
      for (;;) {
        auto peekTokenizer = tokenizerCopy;
        auto delim = peekTokenizer.getNextToken();
        if (delim.empty()) {
          throw TProtocolException(
              TProtocolException::INVALID_DATA, "Premature end of stream");
        }
        if (delim == closeDelim) {
          break;
        }
        parseField(
            elemTypeId,
            elemTypeStr,
            tokenizerCopy,
            static_cast<ProtocolWriter*>(nullptr));
        numElems++;
      }

      if (outProtoWriter) {
        if (expectedType == TType::T_LIST) {
          outProtoWriter->writeListBegin(elemTypeId, numElems);
        } else {
          outProtoWriter->writeSetBegin(elemTypeId, numElems);
        }
      }

      // Parse the actual elements
      for (size_t i = 0; i < numElems; ++i) {
        parseField(elemTypeId, elemTypeStr, tokenizer, outProtoWriter);
      }

      if (getNextOrThrow() != closeDelim) {
        throw TProtocolException(
            TProtocolException::INVALID_DATA, "Missing closing delimiter");
      }

      if (outProtoWriter) {
        if (expectedType == TType::T_LIST) {
          outProtoWriter->writeListEnd();
        } else {
          outProtoWriter->writeSetEnd();
        }
      }
      break;
    }

    case TType::T_MAP: {
      std::pair<folly::StringPiece, folly::StringPiece> keyValSplit =
          splitMapKeyValueType(getInnerTypeLabel(extendedExpectedType));
      TType keyTypeId = parseSimpleTypeLabel(keyValSplit.first);
      TType valTypeId = parseSimpleTypeLabel(keyValSplit.second);
      if (keyTypeId == TType::T_STOP || valTypeId == TType::T_STOP) {
        throw TProtocolException(
            TProtocolException::INVALID_DATA,
            fmt::format("Cannot parse map types: {}", extendedExpectedType));
      }
      if (getNextOrThrow() != "{") {
        throw TProtocolException(
            TProtocolException::INVALID_DATA, "Missing { delimiter in map");
      }

      // Need to count the elements, which requires a pre-pass.
      // Parse with a tokenizerCopy and discard the results.
      auto tokenizerCopy = tokenizer; // cheap
      size_t numElems = 0;
      for (;;) {
        auto peekTokenizer = tokenizerCopy;
        auto delim = peekTokenizer.getNextToken();
        if (delim.empty()) {
          throw TProtocolException(
              TProtocolException::INVALID_DATA, "Premature end of stream");
        }
        if (delim == "}") {
          break;
        }
        parseField(
            keyTypeId,
            keyValSplit.first,
            tokenizerCopy,
            static_cast<ProtocolWriter*>(nullptr));
        if (tokenizerCopy.getNextToken() != ":") {
          throw TProtocolException(
              TProtocolException::INVALID_DATA, "Missing : delimiter");
        }
        parseField(
            valTypeId,
            keyValSplit.second,
            tokenizerCopy,
            static_cast<ProtocolWriter*>(nullptr));
        numElems++;
      }

      if (outProtoWriter) {
        outProtoWriter->writeMapBegin(keyTypeId, valTypeId, numElems);
      }

      // Now, go back and actually parse the map.
      for (size_t i = 0; i < numElems; ++i) {
        parseField(keyTypeId, keyValSplit.first, tokenizer, outProtoWriter);
        if (getNextOrThrow() != ":") {
          throw TProtocolException(
              TProtocolException::INVALID_DATA, "Missing : delimiter");
        }
        parseField(valTypeId, keyValSplit.second, tokenizer, outProtoWriter);
      }
      if (getNextOrThrow() != "}") {
        throw TProtocolException(
            TProtocolException::INVALID_DATA, "Missing } delimiter");
      }
      if (outProtoWriter) {
        outProtoWriter->writeMapEnd();
      }
      break;
    }
    default:
      TProtocolException::throwInvalidSkipType(expectedType);
      break;
  }
}