From 1d817808b775c784afc447422c19dd01c14fb159 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Wed, 10 Apr 2024 03:52:30 +0300 Subject: [PATCH] Invent roundripping between `BitStreamer` state and byte stream position --- .../bitstreams/BitStreamPosition.h | 77 +++++++++++++++++++ src/librawspeed/bitstreams/CMakeLists.txt | 1 + .../bitstreams/BitVacuumerLSBTest.cpp | 44 +++++++++++ .../bitstreams/BitVacuumerMSB16Test.cpp | 44 +++++++++++ .../bitstreams/BitVacuumerMSB32Test.cpp | 44 +++++++++++ .../bitstreams/BitVacuumerMSBTest.cpp | 44 +++++++++++ 6 files changed, 254 insertions(+) create mode 100644 src/librawspeed/bitstreams/BitStreamPosition.h diff --git a/src/librawspeed/bitstreams/BitStreamPosition.h b/src/librawspeed/bitstreams/BitStreamPosition.h new file mode 100644 index 000000000..20bea0876 --- /dev/null +++ b/src/librawspeed/bitstreams/BitStreamPosition.h @@ -0,0 +1,77 @@ +/* + RawSpeed - RAW file decoder. + + Copyright (C) 2024 Roman Lebedev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "adt/Casts.h" +#include "adt/Invariant.h" +#include "bitstreams/BitStreams.h" +#include "common/Common.h" + +namespace rawspeed { + +template struct BitStreamTraits; + +template struct BitStreamPosition { + int pos; + int fillLevel; +}; + +template struct ByteStreamPosition { + int bytePos; + int numBitsToSkip; +}; + +template +ByteStreamPosition getAsByteStreamPosition(BitStreamPosition state) { + const int MinByteStepMultiple = BitStreamTraits::MinLoadStepByteMultiple; + + invariant(state.pos >= 0); + invariant(state.pos % MinByteStepMultiple == 0); + invariant(state.fillLevel >= 0); + + auto numBytesRemainingInCache = + implicit_cast(roundUpDivision(state.fillLevel, CHAR_BIT)); + invariant(numBytesRemainingInCache >= 0); + invariant(numBytesRemainingInCache <= state.pos); + + auto numBytesToBacktrack = implicit_cast( + roundUp(numBytesRemainingInCache, MinByteStepMultiple)); + invariant(numBytesToBacktrack >= 0); + invariant(numBytesToBacktrack <= state.pos); + invariant(numBytesToBacktrack % MinByteStepMultiple == 0); + + auto numBitsToBacktrack = CHAR_BIT * numBytesToBacktrack; + invariant(numBitsToBacktrack >= 0); + + ByteStreamPosition res; + invariant(state.pos >= numBytesToBacktrack); + res.bytePos = state.pos - numBytesToBacktrack; + invariant(numBitsToBacktrack >= state.fillLevel); + res.numBitsToSkip = numBitsToBacktrack - state.fillLevel; + + invariant(res.bytePos >= 0); + invariant(res.bytePos <= state.pos); + invariant(res.bytePos % MinByteStepMultiple == 0); + invariant(res.numBitsToSkip >= 0); + return res; +} + +} // namespace rawspeed diff --git a/src/librawspeed/bitstreams/CMakeLists.txt b/src/librawspeed/bitstreams/CMakeLists.txt index 1f8131c7d..9e9952aa3 100644 --- a/src/librawspeed/bitstreams/CMakeLists.txt +++ b/src/librawspeed/bitstreams/CMakeLists.txt @@ -8,6 +8,7 @@ FILE(GLOB SOURCES "BitStreamMSB.h" "BitStreamMSB16.h" "BitStreamMSB32.h" + "BitStreamPosition.h" "BitStreamer.cpp" "BitStreamer.h" "BitStreamerJPEG.h" diff --git a/test/librawspeed/bitstreams/BitVacuumerLSBTest.cpp b/test/librawspeed/bitstreams/BitVacuumerLSBTest.cpp index 4b3c053a3..1ceab4c1e 100644 --- a/test/librawspeed/bitstreams/BitVacuumerLSBTest.cpp +++ b/test/librawspeed/bitstreams/BitVacuumerLSBTest.cpp @@ -22,6 +22,7 @@ #include "adt/Array1DRef.h" #include "adt/Casts.h" #include "adt/PartitioningOutputIterator.h" +#include "bitstreams/BitStreamPosition.h" #include "bitstreams/BitStreamerLSB.h" #include #include @@ -250,6 +251,49 @@ TEST(BitVacuumerLSBTest, LoadPos) { } } +TEST(BitVacuumerLSBTest, DependencyBreaking) { + std::vector bitstream; + + using BitStreamer = BitStreamerLSB; + using BitStreamerTraits = BitStreamer::Traits; + + constexpr int numByteElts = 33; + + { + auto bsInserter = PartitioningOutputIterator(std::back_inserter(bitstream)); + using BitVacuumer = BitVacuumerLSB; + auto bv = BitVacuumer(bsInserter); + + for (int e = 0; e != numByteElts + BitStreamerTraits::MaxProcessBytes; ++e) + bv.put(e, 8); + } + constexpr int numBitsTotal = CHAR_BIT * numByteElts; + + auto fullInput = + Array1DRef(bitstream.data(), implicit_cast(bitstream.size())); + + for (int numBitsToSkip = 0; numBitsToSkip <= numBitsTotal; ++numBitsToSkip) { + const int numBitsRemaining = numBitsTotal - numBitsToSkip; + auto bsRef = BitStreamer(fullInput); + bsRef.fill(); + bsRef.skipManyBits(numBitsToSkip); + + BitStreamPosition state; + state.pos = bsRef.getInputPosition(); + state.fillLevel = bsRef.getFillLevel(); + const auto bsPos = getAsByteStreamPosition(state); + + auto rebasedInput = + fullInput.getCrop(bsPos.bytePos, fullInput.size() - bsPos.bytePos) + .getAsArray1DRef(); + auto bsRebased = BitStreamer(rebasedInput); + if (bsPos.numBitsToSkip != 0) + bsRebased.skipBits(bsPos.numBitsToSkip); + for (int i = 0; i != numBitsRemaining; ++i) + ASSERT_THAT(bsRebased.getBits(1), bsRef.getBits(1)); + } +} + } // namespace } // namespace rawspeed diff --git a/test/librawspeed/bitstreams/BitVacuumerMSB16Test.cpp b/test/librawspeed/bitstreams/BitVacuumerMSB16Test.cpp index 26864f9f3..eba301fb9 100644 --- a/test/librawspeed/bitstreams/BitVacuumerMSB16Test.cpp +++ b/test/librawspeed/bitstreams/BitVacuumerMSB16Test.cpp @@ -22,6 +22,7 @@ #include "adt/Array1DRef.h" #include "adt/Casts.h" #include "adt/PartitioningOutputIterator.h" +#include "bitstreams/BitStreamPosition.h" #include "bitstreams/BitStreamerMSB16.h" #include #include @@ -247,6 +248,49 @@ TEST(BitVacuumerMSB16Test, LoadPos) { } } +TEST(BitVacuumerMSB16Test, DependencyBreaking) { + std::vector bitstream; + + using BitStreamer = BitStreamerMSB16; + using BitStreamerTraits = BitStreamer::Traits; + + constexpr int numByteElts = 33; + + { + auto bsInserter = PartitioningOutputIterator(std::back_inserter(bitstream)); + using BitVacuumer = BitVacuumerMSB16; + auto bv = BitVacuumer(bsInserter); + + for (int e = 0; e != numByteElts + BitStreamerTraits::MaxProcessBytes; ++e) + bv.put(e, 8); + } + constexpr int numBitsTotal = CHAR_BIT * numByteElts; + + auto fullInput = + Array1DRef(bitstream.data(), implicit_cast(bitstream.size())); + + for (int numBitsToSkip = 0; numBitsToSkip <= numBitsTotal; ++numBitsToSkip) { + const int numBitsRemaining = numBitsTotal - numBitsToSkip; + auto bsRef = BitStreamer(fullInput); + bsRef.fill(); + bsRef.skipManyBits(numBitsToSkip); + + BitStreamPosition state; + state.pos = bsRef.getInputPosition(); + state.fillLevel = bsRef.getFillLevel(); + const auto bsPos = getAsByteStreamPosition(state); + + auto rebasedInput = + fullInput.getCrop(bsPos.bytePos, fullInput.size() - bsPos.bytePos) + .getAsArray1DRef(); + auto bsRebased = BitStreamer(rebasedInput); + if (bsPos.numBitsToSkip != 0) + bsRebased.skipBits(bsPos.numBitsToSkip); + for (int i = 0; i != numBitsRemaining; ++i) + ASSERT_THAT(bsRebased.getBits(1), bsRef.getBits(1)); + } +} + } // namespace } // namespace rawspeed diff --git a/test/librawspeed/bitstreams/BitVacuumerMSB32Test.cpp b/test/librawspeed/bitstreams/BitVacuumerMSB32Test.cpp index d75b52fea..bcb6067c1 100644 --- a/test/librawspeed/bitstreams/BitVacuumerMSB32Test.cpp +++ b/test/librawspeed/bitstreams/BitVacuumerMSB32Test.cpp @@ -22,6 +22,7 @@ #include "adt/Array1DRef.h" #include "adt/Casts.h" #include "adt/PartitioningOutputIterator.h" +#include "bitstreams/BitStreamPosition.h" #include "bitstreams/BitStreamerMSB32.h" #include #include @@ -250,6 +251,49 @@ TEST(BitVacuumerMSB32Test, LoadPos) { } } +TEST(BitVacuumerMSB32Test, DependencyBreaking) { + std::vector bitstream; + + using BitStreamer = BitStreamerMSB32; + using BitStreamerTraits = BitStreamer::Traits; + + constexpr int numByteElts = 33; + + { + auto bsInserter = PartitioningOutputIterator(std::back_inserter(bitstream)); + using BitVacuumer = BitVacuumerMSB32; + auto bv = BitVacuumer(bsInserter); + + for (int e = 0; e != numByteElts + BitStreamerTraits::MaxProcessBytes; ++e) + bv.put(e, 8); + } + constexpr int numBitsTotal = CHAR_BIT * numByteElts; + + auto fullInput = + Array1DRef(bitstream.data(), implicit_cast(bitstream.size())); + + for (int numBitsToSkip = 0; numBitsToSkip <= numBitsTotal; ++numBitsToSkip) { + const int numBitsRemaining = numBitsTotal - numBitsToSkip; + auto bsRef = BitStreamer(fullInput); + bsRef.fill(); + bsRef.skipManyBits(numBitsToSkip); + + BitStreamPosition state; + state.pos = bsRef.getInputPosition(); + state.fillLevel = bsRef.getFillLevel(); + const auto bsPos = getAsByteStreamPosition(state); + + auto rebasedInput = + fullInput.getCrop(bsPos.bytePos, fullInput.size() - bsPos.bytePos) + .getAsArray1DRef(); + auto bsRebased = BitStreamer(rebasedInput); + if (bsPos.numBitsToSkip != 0) + bsRebased.skipBits(bsPos.numBitsToSkip); + for (int i = 0; i != numBitsRemaining; ++i) + ASSERT_THAT(bsRebased.getBits(1), bsRef.getBits(1)); + } +} + } // namespace } // namespace rawspeed diff --git a/test/librawspeed/bitstreams/BitVacuumerMSBTest.cpp b/test/librawspeed/bitstreams/BitVacuumerMSBTest.cpp index 35869b05d..066942d61 100644 --- a/test/librawspeed/bitstreams/BitVacuumerMSBTest.cpp +++ b/test/librawspeed/bitstreams/BitVacuumerMSBTest.cpp @@ -22,6 +22,7 @@ #include "adt/Array1DRef.h" #include "adt/Casts.h" #include "adt/PartitioningOutputIterator.h" +#include "bitstreams/BitStreamPosition.h" #include "bitstreams/BitStreamerMSB.h" #include #include @@ -250,6 +251,49 @@ TEST(BitVacuumerMSBTest, LoadPos) { } } +TEST(BitVacuumerMSBTest, DependencyBreaking) { + std::vector bitstream; + + using BitStreamer = BitStreamerMSB; + using BitStreamerTraits = BitStreamer::Traits; + + constexpr int numByteElts = 33; + + { + auto bsInserter = PartitioningOutputIterator(std::back_inserter(bitstream)); + using BitVacuumer = BitVacuumerMSB; + auto bv = BitVacuumer(bsInserter); + + for (int e = 0; e != numByteElts + BitStreamerTraits::MaxProcessBytes; ++e) + bv.put(e, 8); + } + constexpr int numBitsTotal = CHAR_BIT * numByteElts; + + auto fullInput = + Array1DRef(bitstream.data(), implicit_cast(bitstream.size())); + + for (int numBitsToSkip = 0; numBitsToSkip <= numBitsTotal; ++numBitsToSkip) { + const int numBitsRemaining = numBitsTotal - numBitsToSkip; + auto bsRef = BitStreamer(fullInput); + bsRef.fill(); + bsRef.skipManyBits(numBitsToSkip); + + BitStreamPosition state; + state.pos = bsRef.getInputPosition(); + state.fillLevel = bsRef.getFillLevel(); + const auto bsPos = getAsByteStreamPosition(state); + + auto rebasedInput = + fullInput.getCrop(bsPos.bytePos, fullInput.size() - bsPos.bytePos) + .getAsArray1DRef(); + auto bsRebased = BitStreamer(rebasedInput); + if (bsPos.numBitsToSkip != 0) + bsRebased.skipBits(bsPos.numBitsToSkip); + for (int i = 0; i != numBitsRemaining; ++i) + ASSERT_THAT(bsRebased.getBits(1), bsRef.getBits(1)); + } +} + } // namespace } // namespace rawspeed