TEST(SliceContainerTest, FromUInt8Longer) { std::shared_ptr<Builder> builder = BuildValue("[\"the eagle has landed\",\"test\",\"qux\"]"); uint8_t const* begin = builder->start(); Slice slice = builder->slice(); SliceContainer sb(begin, slice.byteSize()); ASSERT_EQ(sb.byteSize(), slice.byteSize()); ASSERT_EQ(0, memcmp(sb.data(), builder->start(), slice.byteSize())); ASSERT_TRUE(sb.slice().isArray()); ASSERT_NE(sb.data(), begin); }
TEST(SliceContainerTest, FromSlice) { std::shared_ptr<Builder> builder = BuildValue("\"this is a string of 20 bytes\""); Slice slice = builder->slice(); SliceContainer sb(slice); ASSERT_EQ(sb.byteSize(), slice.byteSize()); ASSERT_EQ(0, memcmp(sb.data(), slice.begin(), slice.byteSize())); ASSERT_EQ(29UL, sb.byteSize()); ASSERT_TRUE(sb.slice().isString()); ASSERT_EQ("this is a string of 20 bytes", sb.slice().copyString()); ASSERT_NE(sb.data(), slice.begin()); }
TEST(SliceContainerTest, FromChar) { std::shared_ptr<Builder> builder = BuildValue("null"); uint8_t const* begin = builder->start(); Slice slice = builder->slice(); SliceContainer sb(reinterpret_cast<char const*>(begin), slice.byteSize()); ASSERT_EQ(sb.byteSize(), slice.byteSize()); ASSERT_EQ(0, memcmp(sb.data(), builder->start(), slice.byteSize())); ASSERT_EQ(1UL, sb.byteSize()); ASSERT_TRUE(sb.slice().isNull()); ASSERT_NE(sb.data(), begin); }
TEST(SliceContainerTest, MoveAssign) { std::shared_ptr<Builder> builder = BuildValue("\"this is a string of 20 bytes\""); Slice slice = builder->slice(); SliceContainer sb(slice.begin(), slice.byteSize()); SliceContainer sb2(slice.begin(), slice.byteSize()); sb2 = std::move(sb); ASSERT_TRUE(sb.slice().isNone()); // must be empty now ASSERT_EQ(1UL, sb.slice().byteSize()); ASSERT_EQ(29UL, sb2.byteSize()); ASSERT_TRUE(sb2.slice().isString()); ASSERT_EQ("this is a string of 20 bytes", sb2.slice().copyString()); ASSERT_NE(sb.slice().begin(), sb2.slice().begin()); }
TEST(SliceContainerTest, FromSliceLonger) { std::string s("[-1"); for (size_t i = 0; i < 2000; ++i) { s.push_back(','); s.append(std::to_string(i)); } s.push_back(']'); std::shared_ptr<Builder> builder = BuildValue(s); Slice slice = builder->slice(); SliceContainer sb(slice); ASSERT_EQ(sb.byteSize(), slice.byteSize()); ASSERT_EQ(0, memcmp(sb.data(), builder->start(), slice.byteSize())); ASSERT_TRUE(sb.slice().isArray()); ASSERT_NE(sb.data(), slice.begin()); }
Slice Slice::getFromCompactObject(std::string const& attribute) const { ObjectIterator it(*this); while (it.valid()) { Slice key = it.key(); if (key.makeKey().isEqualString(attribute)) { return Slice(key.start() + key.byteSize()); } it.next(); } // not found return Slice(); }
TEST(SliceContainerTest, SizeLengthByteSize) { std::shared_ptr<Builder> builder = BuildValue("\"this is a string of 20 bytes\""); Slice slice = builder->slice(); SliceContainer sb(slice.begin(), slice.byteSize()); ASSERT_TRUE(sb.slice().isString()); ASSERT_EQ(29UL, sb.size()); ASSERT_EQ(29UL, sb.length()); ASSERT_EQ(29UL, sb.byteSize()); ASSERT_EQ(sb.data(), sb.begin()); Slice empty; sb = SliceContainer(empty); ASSERT_EQ(1UL, sb.size()); ASSERT_EQ(1UL, sb.length()); ASSERT_EQ(1UL, sb.byteSize()); ASSERT_EQ(sb.data(), sb.begin()); }
// look for the specified attribute inside an Object // returns a Slice(ValueType::None) if not found Slice Slice::get(std::string const& attribute) const { if (!isObject()) { throw Exception(Exception::InvalidValueType, "Expecting Object"); } auto const h = head(); if (h == 0x0a) { // special case, empty object return Slice(); } if (h == 0x14) { // compact Object return getFromCompactObject(attribute); } ValueLength const offsetSize = indexEntrySize(h); ValueLength end = readInteger<ValueLength>(_start + 1, offsetSize); // read number of items ValueLength n; if (offsetSize < 8) { n = readInteger<ValueLength>(_start + 1 + offsetSize, offsetSize); } else { n = readInteger<ValueLength>(_start + end - offsetSize, offsetSize); } if (n == 1) { // Just one attribute, there is no index table! Slice key = Slice(_start + findDataOffset(h)); if (key.isString()) { if (key.isEqualString(attribute)) { return Slice(key.start() + key.byteSize()); } // fall through to returning None Slice below } else if (key.isSmallInt() || key.isUInt()) { // translate key if (Options::Defaults.attributeTranslator == nullptr) { throw Exception(Exception::NeedAttributeTranslator); } if (key.translateUnchecked().isEqualString(attribute)) { return Slice(key.start() + key.byteSize()); } } // no match or invalid key type return Slice(); } ValueLength const ieBase = end - n * offsetSize - (offsetSize == 8 ? offsetSize : 0); // only use binary search for attributes if we have at least this many entries // otherwise we'll always use the linear search static ValueLength const SortedSearchEntriesThreshold = 4; bool const isSorted = (h >= 0x0b && h <= 0x0e); if (isSorted && n >= SortedSearchEntriesThreshold) { // This means, we have to handle the special case n == 1 only // in the linear search! return searchObjectKeyBinary(attribute, ieBase, offsetSize, n); } return searchObjectKeyLinear(attribute, ieBase, offsetSize, n); }