diff --git a/Package.swift b/Package.swift index fb154e45..9712deb4 100644 --- a/Package.swift +++ b/Package.swift @@ -514,6 +514,7 @@ let package = Package( "internal/platform/feature_flags_test.cc", "internal/platform/cancelable_alarm_test.cc", "internal/platform/crypto_test.cc", + "internal/platform/base_input_stream_test.cc", "internal/platform/byte_array_test.cc", "internal/platform/bluetooth_utils_test.cc", "internal/platform/credential_storage_impl_test.cc", diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 2af6d684..1c53d60d 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -397,6 +397,7 @@ cc_test( cc_test( name = "platform_util_test", srcs = [ + "base_input_stream_test.cc", "byte_utils_test.cc", ], deps = [ diff --git a/internal/platform/base_input_stream.cc b/internal/platform/base_input_stream.cc index 3740e89b..7392ce9b 100644 --- a/internal/platform/base_input_stream.cc +++ b/internal/platform/base_input_stream.cc @@ -38,6 +38,27 @@ ExceptionOr BaseInputStream::Read(std::int64_t size) { } } +std::optional BaseInputStream::ReadBits(int bits) { + if (bits > 8) { + return std::nullopt; + } + if (bits_unused_ == 0) { + if (!IsAvailable(1)) { + return std::nullopt; + } + bits_buffer_ = (uint8_t)buffer_.data()[position_++]; + bits_unused_ = 8; + } + if (bits_unused_ < bits) { + return std::nullopt; + } + + uint8_t mask = (1 << bits) - 1; + uint8_t value = (bits_buffer_ >> (bits_unused_ - bits)) & mask; + bits_unused_ -= bits; + return value; +} + std::optional BaseInputStream::ReadUint8() { constexpr int byte_size = sizeof(std::uint8_t); std::optional read_bytes = ReadBytes(byte_size); @@ -153,6 +174,10 @@ std::optional BaseInputStream::ReadInt64() { } std::optional BaseInputStream::ReadBytes(int size) { + if (bits_unused_ != 0) { + return std::nullopt; + } + ExceptionOr read_bytes_result = Read(size); if (!read_bytes_result.ok()) { return std::nullopt; diff --git a/internal/platform/base_input_stream.h b/internal/platform/base_input_stream.h index d5474986..0f5fd1cf 100644 --- a/internal/platform/base_input_stream.h +++ b/internal/platform/base_input_stream.h @@ -47,6 +47,9 @@ class BaseInputStream : public InputStream { return {Exception::kSuccess}; } + // Reads less than 8 bits from the stream, returning the value if available. + // The read bits cannot across a byte boundary. + std::optional ReadBits(int bits); std::optional ReadUint8(); std::optional ReadInt8(); std::optional ReadUint16(); @@ -62,6 +65,8 @@ class BaseInputStream : public InputStream { } private: + uint8_t bits_unused_{0}; + uint8_t bits_buffer_{0}; ByteArray &buffer_; size_t position_{0}; }; diff --git a/internal/platform/base_input_stream_test.cc b/internal/platform/base_input_stream_test.cc new file mode 100644 index 00000000..a7466ebe --- /dev/null +++ b/internal/platform/base_input_stream_test.cc @@ -0,0 +1,65 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "internal/platform/base_input_stream.h" + +#include +#include + +#include "gtest/gtest.h" +#include "internal/platform/byte_array.h" + +namespace nearby { +namespace { + +TEST(BaseInputStreamTest, ReadBits) { + std::string data{static_cast(0b01011100)}; + ByteArray byte_array(data); + BaseInputStream stream{byte_array}; + EXPECT_EQ(stream.ReadBits(1), 0); + EXPECT_EQ(stream.ReadBits(2), 2); + EXPECT_EQ(stream.ReadBits(3), 7); + EXPECT_FALSE(stream.ReadBits(5).has_value()); + EXPECT_EQ(stream.ReadBits(2), 0); + EXPECT_FALSE(stream.ReadBits(1).has_value()); +} + +TEST(BaseInputStreamTest, ReadBitsExceedsByteBoundary) { + std::string data = "ab"; + ByteArray byte_array(data); + BaseInputStream stream{byte_array}; + EXPECT_FALSE(stream.ReadBits(9).has_value()); + EXPECT_EQ(stream.ReadBits(1), 0); + EXPECT_FALSE(stream.ReadInt16().has_value()); +} + +TEST(BaseInputStreamTest, ReadUintValues) { + std::string data = "\xff\xf1\x0f\x0e\x01\x02\x01\x01\x01\x02\x03\x04\x05\x06"; + ByteArray byte_array(data); + BaseInputStream stream{byte_array}; + EXPECT_EQ(stream.ReadUint16(), 0xfff1); + EXPECT_EQ(stream.ReadUint32(), 0x0f0e0102); + EXPECT_EQ(stream.ReadUint64(), 0x0101010203040506); +} + +TEST(BaseInputStreamTest, ReadIntValues) { + std::string data = "\xff\xf1\x0f\x0e\x01\x02\x01\x01\x01\x02\x03\x04\x05\x06"; + ByteArray byte_array(data); + BaseInputStream stream{byte_array}; + EXPECT_EQ(stream.ReadInt16(), static_cast(0xfff1)); + EXPECT_EQ(stream.ReadInt32(), static_cast(0x0f0e0102)); + EXPECT_EQ(stream.ReadInt64(), static_cast(0x0101010203040506)); +} + +} // namespace +} // namespace nearby