bool CJsonOverUTTPReader::ReadMessage(CUTTPReader& reader) { for (;;) switch (reader.GetNextEvent()) { case CUTTPReader::eChunkPart: switch (m_State) { case eExpectNextToken: m_State = eReadingString; m_CurrentChunk.assign(reader.GetChunkPart(), reader.GetChunkPartSize()); break; case eReadingString: m_CurrentChunk.append(reader.GetChunkPart(), reader.GetChunkPartSize()); break; case eReadingDouble: // The underlying transport protocol guarantees // that m_Double boundaries will not be exceeded. memcpy(m_DoublePtr, reader.GetChunkPart(), reader.GetChunkPartSize()); m_DoublePtr += reader.GetChunkPartSize(); break; default: /* case eMessageComplete: */ goto ThrowUnexpectedTrailingToken; } break; case CUTTPReader::eChunk: switch (m_State) { case eExpectNextToken: m_CurrentChunk.assign(reader.GetChunkPart(), reader.GetChunkPartSize()); break; case eReadingString: m_State = eExpectNextToken; m_CurrentChunk.append(reader.GetChunkPart(), reader.GetChunkPartSize()); break; case eReadingDouble: m_State = eExpectNextToken; memcpy(m_DoublePtr, reader.GetChunkPart(), reader.GetChunkPartSize()); if (m_DoubleEndianness != DOUBLE_PREFIX) reverse(reinterpret_cast<char*>(&m_Double), reinterpret_cast<char*>(&m_Double + 1)); if (!x_AddNewNode(CJsonNode::NewDoubleNode(m_Double))) m_State = eMessageComplete; continue; default: /* case eMessageComplete: */ goto ThrowUnexpectedTrailingToken; } if (!m_CurrentNode) { m_CurrentNode = CJsonNode::NewStringNode(m_CurrentChunk); m_State = eMessageComplete; } else if (m_HashValueIsExpected) { m_HashValueIsExpected = false; m_CurrentNode.SetString(m_HashKey, m_CurrentChunk); } else // The current node is either a JSON object or an array, // because if it was a non-container node, m_State would // be eMessageComplete. if (m_CurrentNode.IsArray()) m_CurrentNode.AppendString(m_CurrentChunk); else { m_HashKey = m_CurrentChunk; m_HashValueIsExpected = true; } break; case CUTTPReader::eControlSymbol: { char control_symbol = reader.GetControlSymbol(); if (control_symbol == '\n') { if (m_State != eMessageComplete) { NCBI_THROW(CJsonOverUTTPException, eUnexpectedEOM, "JSON-over-UTTP: Unexpected end of message"); } return true; } if (m_State != eExpectNextToken) goto ThrowChunkContinuationExpected; switch (control_symbol) { case '[': case '{': { CJsonNode new_node(control_symbol == '[' ? CJsonNode::NewArrayNode() : CJsonNode::NewObjectNode()); if (x_AddNewNode(new_node)) { m_NodeStack.push_back(m_CurrentNode); m_CurrentNode = new_node; } } break; case ']': case '}': if (!m_CurrentNode || (control_symbol == ']') ^ m_CurrentNode.IsArray()) { NCBI_THROW(CJsonOverUTTPException, eUnexpectedClosingBracket, "JSON-over-UTTP: Unexpected closing bracket"); } if (m_NodeStack.empty()) m_State = eMessageComplete; else { m_CurrentNode = m_NodeStack.back(); m_NodeStack.pop_back(); } break; case 'D': case 'd': switch (reader.ReadRawData(sizeof(double))) { default: /* case CUTTPReader::eEndOfBuffer: */ m_State = eReadingDouble; m_DoubleEndianness = control_symbol; m_DoublePtr = reinterpret_cast<char*>(&m_Double); return false; case CUTTPReader::eChunkPart: m_State = eReadingDouble; m_DoubleEndianness = control_symbol; memcpy(&m_Double, reader.GetChunkPart(), reader.GetChunkPartSize()); m_DoublePtr = reinterpret_cast<char*>(&m_Double) + reader.GetChunkPartSize(); break; case CUTTPReader::eChunk: _ASSERT(reader.GetChunkPartSize() == sizeof(double)); if (control_symbol == DOUBLE_PREFIX) memcpy(&m_Double, reader.GetChunkPart(), sizeof(double)); else { // Copy the entire chunk to // m_Double in reverse order. const char* src = reader.GetChunkPart(); char* dst = reinterpret_cast<char*>(&m_Double + 1); int count = sizeof(double); do *--dst = *src++; while (--count > 0); } if (!x_AddNewNode(CJsonNode::NewDoubleNode(m_Double))) m_State = eMessageComplete; } break; case 'Y': if (!x_AddNewNode(CJsonNode::NewBooleanNode(true))) m_State = eMessageComplete; break; case 'N': if (!x_AddNewNode(CJsonNode::NewBooleanNode(false))) m_State = eMessageComplete; break; case 'U': if (!x_AddNewNode(CJsonNode::NewNullNode())) m_State = eMessageComplete; break; default: NCBI_THROW_FMT(CJsonOverUTTPException, eUnknownControlSymbol, "JSON-over-UTTP: Unknown control symbol '" << NStr::PrintableString(CTempString(&control_symbol, 1)) << '\''); } } break; case CUTTPReader::eNumber: switch (m_State) { case eExpectNextToken: if (!x_AddNewNode(CJsonNode::NewIntegerNode(reader.GetNumber()))) m_State = eMessageComplete; break; case eMessageComplete: goto ThrowUnexpectedTrailingToken; default: goto ThrowChunkContinuationExpected; } break; case CUTTPReader::eEndOfBuffer: return false; default: /* case CUTTPReader::eFormatError: */ NCBI_THROW(CJsonOverUTTPException, eUTTPFormatError, "JSON-over-UTTP: UTTP format error"); } ThrowUnexpectedTrailingToken: NCBI_THROW(CJsonOverUTTPException, eUnexpectedTrailingToken, "JSON-over-UTTP: Received a token while expected EOM"); ThrowChunkContinuationExpected: NCBI_THROW(CJsonOverUTTPException, eChunkContinuationExpected, "JSON-over-UTTP: Chunk continuation expected"); }