Skip to content

Commit ce1b0d6

Browse files
committed
fix: 修正 BitStream byte stuffing 和 RST marker 處理錯誤
- 修正 0xFF 0x00 byte stuffing 未跳過 0x00 的嚴重 bug - 修正 RST marker (0xFF 0xD0-0xD7) 未正確跳過的問題 - 重構 BitStream:引入 32-bit 緩衝區機制提升效能 - 新增 precision 驗證和清理未使用變數
1 parent 7f18fdb commit ce1b0d6

3 files changed

Lines changed: 89 additions & 81 deletions

File tree

src/cpp/bitstream.cpp

Lines changed: 74 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,114 +5,117 @@
55
namespace jpeg {
66

77
BitStream::BitStream(const uint8_t* data, size_t size)
8-
: data_(data), size_(size), byte_pos_(0), bit_pos_(0) {
8+
: data_(data), size_(size), byte_pos_(0), bit_buffer_(0), bits_in_buffer_(0) {
99
if (!data || size == 0) {
1010
throw std::invalid_argument("BitStream: invalid data or size");
1111
}
1212
}
1313

14-
uint16_t BitStream::readBits(int n_bits) {
15-
if (n_bits <= 0 || n_bits > 16) {
16-
throw std::invalid_argument("BitStream: n_bits must be between 1 and 16");
17-
}
18-
19-
uint16_t result = 0;
20-
21-
for (int i = 0; i < n_bits; ++i) {
22-
// update byte and bit positions
23-
if (bit_pos_ == 8) {
24-
byte_pos_++;
25-
bit_pos_ = 0;
26-
}
27-
28-
// 檢查是否超出範圍
29-
if (byte_pos_ >= size_) {
30-
throw std::runtime_error("BitStream: unexpected end of data");
31-
}
32-
33-
// 處理 byte stuffing: 如果當前 byte 是 0xFF
34-
uint8_t current_byte = data_[byte_pos_];
35-
if (current_byte == 0xFF && bit_pos_ == 0) {
36-
// 檢查下一個 byte
37-
if (byte_pos_ + 1 < size_) {
38-
uint8_t next_byte = data_[byte_pos_ + 1];
14+
void BitStream::fillBuffer() {
15+
while (bits_in_buffer_ <= 24 && byte_pos_ < size_) {
16+
uint8_t byte = data_[byte_pos_++];
17+
18+
// 處理 byte stuffing: 如果是 0xFF,檢查下一個 byte
19+
if (byte == 0xFF) {
20+
if (byte_pos_ < size_) {
21+
uint8_t next_byte = data_[byte_pos_];
3922
if (next_byte == 0x00) {
40-
// 這是 byte stuffing,跳過 0x00
41-
// 繼續使用 0xFF
23+
// byte stuffing (FF 00),跳過 00
24+
byte_pos_++;
4225
} else if (next_byte >= 0xD0 && next_byte <= 0xD7) {
43-
// RST marker,這裡簡單處理,繼續讀取
44-
// 更完整的實作應該處理 restart interval
26+
// 是 RST marker (FF Dx),跳過整個 marker
27+
byte_pos_++;
28+
continue; // 不要將 0xFF 放入緩衝區,繼續讀取下一個 byte
4529
} else {
46-
// 其他 marker,可能是資料結束
47-
// 目前先簡單拋出異常
48-
throw std::runtime_error("BitStream: unexpected marker");
30+
// 其他 marker,可能是錯誤或資料結束
31+
// 根據 JPEG 規範,這裡應該是資料結束,但我們保守地放入 0xFF
4932
}
5033
}
5134
}
52-
53-
// 讀取一個 bit (MSB first)
54-
int bit = (current_byte >> (7 - bit_pos_)) & 1;
55-
result = (result << 1) | bit;
56-
bit_pos_++;
35+
36+
bit_buffer_ = (bit_buffer_ << 8) | byte;
37+
bits_in_buffer_ += 8;
5738
}
58-
59-
return result;
6039
}
6140

62-
uint16_t BitStream::peekBits(int n_bits) {
63-
// 保存當前狀態
64-
size_t saved_byte_pos = byte_pos_;
65-
int saved_bit_pos = bit_pos_;
41+
uint16_t BitStream::readBits(int n_bits) {
42+
if (n_bits <= 0 || n_bits > 16) {
43+
throw std::invalid_argument("BitStream: n_bits must be between 1 and 16");
44+
}
45+
46+
if (bits_in_buffer_ < n_bits) {
47+
fillBuffer();
48+
}
49+
50+
if (bits_in_buffer_ < n_bits) {
51+
throw std::runtime_error("BitStream: unexpected end of data");
52+
}
6653

67-
uint16_t result = readBits(n_bits);
54+
uint16_t result = bit_buffer_ >> (bits_in_buffer_ - n_bits);
6855

69-
// 恢復狀態
70-
byte_pos_ = saved_byte_pos;
71-
bit_pos_ = saved_bit_pos;
56+
bits_in_buffer_ -= n_bits;
57+
bit_buffer_ &= (1U << bits_in_buffer_) - 1;
7258

7359
return result;
7460
}
7561

62+
uint16_t BitStream::peekBits(int n_bits) {
63+
if (n_bits <= 0 || n_bits > 16) {
64+
throw std::invalid_argument("BitStream: n_bits must be between 1 and 16");
65+
}
66+
67+
if (bits_in_buffer_ < n_bits) {
68+
fillBuffer();
69+
}
70+
71+
if (bits_in_buffer_ < n_bits) {
72+
throw std::runtime_error("BitStream: unexpected end of data");
73+
}
74+
75+
return bit_buffer_ >> (bits_in_buffer_ - n_bits);
76+
}
77+
7678
void BitStream::skipBits(int n_bits) {
77-
readBits(n_bits);
79+
if (bits_in_buffer_ >= n_bits) {
80+
bits_in_buffer_ -= n_bits;
81+
bit_buffer_ &= (1U << bits_in_buffer_) - 1;
82+
} else {
83+
n_bits -= bits_in_buffer_;
84+
bits_in_buffer_ = 0;
85+
bit_buffer_ = 0;
86+
87+
// 這裡可以優化,但為了簡單起見,先這樣
88+
size_t bytes_to_skip = n_bits / 8;
89+
byte_pos_ += bytes_to_skip;
90+
n_bits %= 8;
91+
92+
if (n_bits > 0) {
93+
readBits(n_bits);
94+
}
95+
}
7896
}
7997

8098
bool BitStream::hasMoreData() const {
81-
return byte_pos_ < size_;
99+
return bits_in_buffer_ > 0 || byte_pos_ < size_;
82100
}
83101

84102
size_t BitStream::getBitPosition() const {
85-
return byte_pos_ * 8 + bit_pos_;
103+
return byte_pos_ * 8 - bits_in_buffer_;
86104
}
87105

88106
void BitStream::reset(size_t byte_pos, int bit_pos) {
89-
if (byte_pos >= size_) {
107+
if (byte_pos > size_) { // allow reset to end
90108
throw std::out_of_range("BitStream: byte_pos out of range");
91109
}
92110
if (bit_pos < 0 || bit_pos >= 8) {
93111
throw std::out_of_range("BitStream: bit_pos must be 0-7");
94112
}
95113
byte_pos_ = byte_pos;
96-
bit_pos_ = bit_pos;
97-
}
98-
99-
uint8_t BitStream::getNextByte() {
100-
if (byte_pos_ >= size_) {
101-
throw std::runtime_error("BitStream: unexpected end of data");
102-
}
103-
104-
uint8_t byte = data_[byte_pos_++];
105-
106-
// 處理 byte stuffing
107-
if (byte == 0xFF && byte_pos_ < size_) {
108-
uint8_t next = data_[byte_pos_];
109-
if (next == 0x00) {
110-
// Byte stuffing: 跳過 0x00
111-
byte_pos_++;
112-
}
114+
bits_in_buffer_ = 0;
115+
bit_buffer_ = 0;
116+
if (bit_pos > 0) {
117+
readBits(bit_pos); // 讀取並丟棄,以對齊 bit 位置
113118
}
114-
115-
return byte;
116119
}
117120

118121
} // namespace jpeg

src/cpp/bitstream.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,14 @@ class BitStream {
6060
const uint8_t* data_; // 原始資料指標
6161
size_t size_; // 資料大小
6262
size_t byte_pos_; // 當前位元組位置
63-
int bit_pos_; // 當前 bit 位置 (0-7, 0 = MSB)
63+
64+
uint32_t bit_buffer_; // 位元緩衝區
65+
int bits_in_buffer_; // 緩衝區中剩餘的 bit 數量
6466

6567
/**
66-
* 讀取下一個 byte,處理 byte stuffing
67-
* @return 下一個有效的 byte
68+
* 填充緩衝區,處理 byte stuffing
6869
*/
69-
uint8_t getNextByte();
70+
void fillBuffer();
7071
};
7172

7273
} // namespace jpeg

src/cpp/decoder.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,13 @@ bool JPEGDecoder::processDQT() {
153153
}
154154

155155
bool JPEGDecoder::processSOF0() {
156-
uint16_t length = readWord();
157-
158-
uint8_t precision = readByte(); // 通常是 8
156+
readWord(); // length
157+
158+
uint8_t precision = readByte(); // precision (通常是 8)
159+
if (precision != 8) {
160+
throw std::runtime_error("Only 8-bit precision is supported");
161+
}
162+
159163
height_ = readWord();
160164
width_ = readWord();
161165
num_components_ = readByte();
@@ -206,13 +210,13 @@ bool JPEGDecoder::processDHT() {
206210
}
207211

208212
bool JPEGDecoder::processDRI() {
209-
uint16_t length = readWord();
213+
readWord(); // length
210214
restart_interval_ = readWord();
211215
return true;
212216
}
213217

214218
bool JPEGDecoder::processSOS() {
215-
uint16_t length = readWord();
219+
readWord(); // length
216220

217221
int num_components = readByte();
218222

@@ -412,4 +416,4 @@ void JPEGDecoder::upsample(const std::vector<std::vector<uint8_t>>& y_blocks,
412416
}
413417
}
414418

415-
} // namespace jpeg
419+
} // namespace jpeg

0 commit comments

Comments
 (0)