# DbaMessageInserter — 적응형 DEFLATE 블록 선택

DBA 메시지 삽입 전용 프로젝트입니다. 복원기 코드는 포함하지 않습니다.

이번 버전은 실데이터 블록을 더 이상 `Dynamic → Fixed → Stored` 순서로 강제하지 않습니다. 입력을 일정 크기의 데이터 구간으로 나눈 뒤, 각 구간마다 **Stored, Fixed Huffman, Dynamic Huffman 후보의 정확한 비트 수를 계산하고 가장 작은 후보를 기록**합니다.

NPNCB의 개수, 삽입 위치, DBA 메시지 기록 방식은 이전 버전과 같습니다.

## 실행 흐름

1. ZIP으로 만들 입력 파일 선택
2. 한 줄 UTF-8 메시지 입력
3. 각 데이터 블록 뒤에 추가할 NPNCB 개수 입력
4. 출력 ZIP 경로 선택
5. 각 데이터 구간의 Stored/Fixed/Dynamic 후보 분석
6. 가장 작은 데이터 블록 형식 선택
7. 각 데이터 블록 뒤에 기존 방식 그대로 NPNCB 삽입
8. 모든 NPNCB의 DBA 용량 계산
9. 저장 가능한 메시지 비트를 DBA 순서대로 기록
10. 표준 `ZipArchive` 압축 해제로 원본 검증

```powershell
dotnet run --project .\DbaMessageInserter.csproj
```

명령행 실행:

```powershell
dotnet run -c Release -- "input.bin" "output.zip" "DBA 메시지" 32
```

마지막 값은 각 데이터 블록 뒤에 넣을 NPNCB 개수입니다.

## 블록 자동 선택

`Program.cs`는 다음 계획을 만듭니다.

```csharp
List<DeflateBlockSpec> plan = DeflateBlockPlanner.BuildAdaptivePlan(
    input.Length,
    DataBlockSize,
    npncbCount,
    includeNpncbAfterFinalDataBlock: true);
```

계획상의 데이터 블록 형식은 `DeflateBlockType.Auto`입니다. `RawDeflateEncoder`는 해당 블록을 기록할 실제 비트 위치에서 세 후보를 분석합니다.

```text
Stored  = 3비트 헤더 + 바이트 정렬 + LEN/NLEN 32비트 + 원본 데이터
Fixed   = 3비트 헤더 + Fixed Huffman LZ77 토큰 + EOB
Dynamic = 3비트 헤더 + 동적 트리 헤더 + Dynamic Huffman LZ77 토큰 + EOB
```

Stored 후보는 현재 스트림 위치에 따라 정렬 패딩 길이가 달라지므로, 단순히 바이트 수를 추정하지 않고 현재 `startBit`을 이용해 계산합니다.

후보 중 실제 비트 수가 가장 작은 형식만 출력됩니다. 실행 로그에는 다음 값이 함께 표시됩니다.

```text
Requested  Selected  Candidates S/F/D
Auto       Dynamic   131112/1767/1182
Auto       Stored    131112/138311/131389
Auto       Fixed     128/98/194
```

따라서 압축하기 어려운 데이터는 Stored, 작은 블록이나 Fixed가 유리한 데이터는 Fixed, 충분히 반복되거나 심볼 분포가 치우친 데이터는 Dynamic이 선택될 수 있습니다.

## Dynamic Huffman 구현

Dynamic 후보는 고정된 예제용 트리를 재사용하지 않습니다. 각 데이터 블록의 실제 LZ77 토큰 빈도로 다음 트리를 새로 만듭니다.

- literal/length tree
- distance tree
- code-length alphabet tree

`LengthLimitedHuffmanBuilder.cs`는 package-merge 알고리즘으로 DEFLATE 제한을 만족하는 길이 제한 Huffman 코드를 만듭니다.

```text
literal/length 최대 길이: 15비트
distance 최대 길이:      15비트
code-length 최대 길이:    7비트
```

Dynamic 헤더의 연속 코드 길이는 16, 17, 18 반복 심볼로 축약해서 기록합니다. `HLIT`, `HDIST`, `HCLEN`과 헤더 자체의 비트 수도 Dynamic 후보 비용에 포함됩니다.

Fixed와 Dynamic 후보는 동일한 greedy LZ77 토큰 스트림을 사용하므로, 비교 결과는 Huffman 표현과 Dynamic 헤더 비용의 차이를 정확하게 반영합니다.

## NPNCB와 DBA는 이전과 동일

`DeflateBlockPlan.cs`의 삽입 지점은 그대로 유지했습니다.

```csharp
if (npncbCountAfterEachDataBlock > 0 &&
    (hasAnotherDataBlock || includeNpncbAfterFinalDataBlock))
{
    AddNpncbTrain(plan, npncbCountAfterEachDataBlock);
}
```

`RawDeflateEncoder.cs`의 `WriteNpncbBlock`은 다음 순서로 기록합니다.

```text
BFINAL       1 bit
BTYPE=00     2 bits
DBA          0..7 bits
LEN          00 00
NLEN         FF FF
```

DBA 슬롯이 메시지보다 많으면 남는 비트는 0입니다. 메시지는 여러 NPNCB의 DBA를 하나의 연속 비트 공간으로 사용하며, 각 UTF-8 바이트를 LSB-first로 기록합니다.

## 조정 지점

`Program.cs`:

```csharp
private const int DataBlockSize = 16 * 1024;
private const int DefaultNpncbCountAfterEachDataBlock = 32;
```

`DataBlockSize`는 자동 선택의 평가 단위이자 물리적 데이터 블록의 최대 크기입니다. 값을 줄이면 데이터 특성 변화에 더 자주 대응하지만 Dynamic 헤더와 NPNCB 오버헤드가 증가합니다.

핵심 파일:

```text
DeflateBlockPlan.cs              Auto 데이터 블록 및 NPNCB 계획
RawDeflateEncoder.cs             세 후보 비용 계산, 실제 선택 및 기록
LengthLimitedHuffmanBuilder.cs   길이 제한 Huffman 트리 생성
Lz77Matcher.cs                   공통 greedy LZ77 토큰 생성
DbaMessageEncoder.cs             UTF-8 메시지와 DBA carrier 생성
```

## 범위

이 예제는 블록 **형식**을 적응적으로 선택하지만, production zlib처럼 블록 경계를 모든 바이트 위치에서 탐색하거나 여러 LZ77 파싱 결과를 비교하지는 않습니다. 데이터 경계는 `DataBlockSize` 단위이며, 각 경계 안에서 이 인코더가 실제로 출력할 Stored/Fixed/Dynamic 후보 중 최솟값을 선택합니다.
