Files
five/docs/dbf-engine-spec.md
Charles KWON OhJun 59568f3301 Five v0.9 — Harbour + Go fusion language
- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline
- Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch
- RTL: 351 Harbour-compatible functions
- RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization
- Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec)
- HB_FUNC API: Full Harbour C API compatible Go bridge
- Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT
- Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST
- Macro Compiler: Runtime AST parsing and evaluation
- Debugger: TUI debugger with source display, breakpoints, stepping
- FRB: Native + Pcode dual mode runtime binary
- Tests: 13 packages ALL PASS

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 09:41:50 +09:00

404 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Five DBF Engine Specification
> Harbour DBF 소스 코드 정밀 분석 결과
> 바이트 레벨 포맷 호환을 위한 구현 사양
>
> Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
> All rights reserved.
>
> Source reference: /mnt/d/harbour-core/src/rdd/dbf1.c, include/hbdbf.h
---
## 1. DBF 헤더 (32 bytes)
```
Offset Size Field Description
────── ──── ───── ──────────
0 1 bVersion DB type: 0x03=std, 0x83=DBT, 0xF5=FPT, 0x30/31/32=VFP
1 1 bYear Last update year (year - 1900)
2 1 bMonth Last update month (1-12)
3 1 bDay Last update day (1-31)
4 4 ulRecCount Record count (LE uint32)
8 2 uiHeaderLen Total header length including terminators (LE uint16)
10 2 uiRecordLen Record length including deletion flag (LE uint16)
12 2 bReserved1 Reserved (0x00)
14 1 bTransaction 1=in transaction
15 1 bEncrypted 1=encrypted
16 12 bReserved2 Multi-user/LAN (12 bytes)
28 1 bHasTags 0x01=production index, 0x02=memo (VFP)
29 1 bCodePage Code page identifier
30 2 bReserved3 Reserved (0x00)
```
### Version byte values
```
0x03 Standard DBF III
0x04 DBF IV (reserved)
0x30 VFP (Visual FoxPro)
0x31 VFP + autoincrement
0x32 VFP + varchar/varbinary
0x83 DBF III + DBT memo
0xF5 DBF + FPT memo
0x8B DBF IV + DBT memo
0x06 Encrypted DBF
0x86 Encrypted + DBT
0xE6 Encrypted + SMT
0xF6 Encrypted + SMT
```
---
## 2. 필드 디스크립터 (32 bytes per field)
```
Offset Size Field Description
────── ──── ───── ──────────
0 11 bName Field name (null-terminated, space-padded)
11 1 bType Type char: C, N, L, D, M, I, B, T, @, +, =, ^, Y, Z, Q, V, P, W, G
12 4 bReserved1 Reserved (VFP: offset in record)
16 1 bLen Field length (max 255)
17 1 bDec Decimal places
18 1 bFieldFlags 0x01=system, 0x02=nullable, 0x04=binary
19 4 bCounter Autoincrement counter (LE)
23 1 bStep Autoincrement step
24 7 bReserved2 Reserved
31 1 bHasTag Has index tag (VFP)
```
### 헤더 종결자
```
- 필드 디스크립터 이후 0x0D (carriage return) 1 byte 종결자
- VFP: 추가 263 bytes 백필러 가능
- headerLen = 32(header) + fieldCount*32 + 1(0x0D) [+ backlink]
```
---
## 3. 필드 타입별 바이트 포맷
### C (Character)
```
저장: 원시 바이트, 우측 공백 패딩
읽기: 후행 공백 보존 (SET EXACT에 따라)
최대: 65535 bytes (bLen + bDec*256)
```
### N (Numeric)
```
저장: ASCII 문자열, 우측 정렬, 좌측 공백 패딩
형식: " -123.45" (폭 = bLen, 소수점 = bDec)
음수: '-' 기호 포함
빈 값: 모두 공백 (" ")
```
### L (Logical)
```
저장: 1 byte
참: 'T', 't', 'Y', 'y' (모두 true로 인식)
거짓: 'F', 'f', 'N', 'n' (모두 false로 인식)
미정: ' ' (space) = NIL
```
### D (Date)
```
표준 (bLen=8): "YYYYMMDD" ASCII (빈 날짜 = " ")
짧은 형식 (bLen=3): LE uint24 packed date
VFP (bLen=4): LE uint32 Julian day
```
### M (Memo)
```
bLen=4: LE uint32 block number
bLen=10: ASCII block number (우측 공백)
block number 0 = 빈 메모
```
### I (Integer — VFP)
```
bLen=1: signed int8
bLen=2: signed int16 LE
bLen=3: signed int24 LE
bLen=4: signed int32 LE
bLen=8: signed int64 LE
```
### B (Double — VFP)
```
bLen=8: IEEE 754 double (8 bytes LE)
```
### @ (Timestamp — VFP)
```
bLen=8: 4 bytes date (LE int32) + 4 bytes time (LE int32)
date = Julian day number
time = milliseconds since midnight
```
### = (Modtime)
```
동일 형식: @ (Timestamp)
자동 업데이트됨
```
### + (Autoincrement)
```
bLen=1/2/3/4/8: 부호 있는 정수 LE
자동 증가
```
### Y (Currency)
```
bLen=8: LE int64, 암묵적 4자리 소수점 (value / 10000.0)
```
### ^ (RowVersion)
```
bLen=8: LE uint64, 자동 버전 증가
```
---
## 4. 레코드 레이아웃
```
Byte 0: Deletion flag (' '=active, '*'=deleted)
Byte 1..N: Field data (각 필드가 연속 배치)
레코드 오프셋 = headerLen + (recNo - 1) * recordLen
EOF 마커 위치 = headerLen + recordLen * recordCount
EOF 값 = 0x1A
```
### 레코드 번호
```
1-based (첫 레코드 = 1)
0 = BOF (유효하지 않은 레코드)
recordCount + 1 = 유령 레코드 (APPEND용)
```
---
## 5. 락 스키마 (6종)
### 위치 계산 공식
```go
// 방향에 따른 레코드 락 위치:
switch direction {
case +1: // forward
lockPos = basePos + recNo
case -1: // backward (VFP with tags)
lockPos = basePos - recNo
case 2: // at record (VFP no tags)
lockPos = basePos + (recNo-1)*recordLen + headerLen
}
```
### 스키마별 상수
```
스키마 베이스 위치 방향 파일락 크기 레코드락 크기
───────── ────────────────────── ──── ──────────── ──────────
DB_DBFLOCK_CLIPPER
1,000,000,000 +1 294,967,295 1 byte
DB_DBFLOCK_CLIPPER2
4,000,000,000 +1 294,967,295 1 byte
DB_DBFLOCK_COMIX
1,000,000,000 +1 1 1 byte
DB_DBFLOCK_VFP (hasTags)
0x7FFFFFFE -1 0x07FFFFFF 1 byte
DB_DBFLOCK_VFP (noTags)
0x40000000 +2 0x3FFFFFFF recordLen
DB_DBFLOCK_HB32
4,000,000,000 +1 294,967,295 1 byte
DB_DBFLOCK_HB64
0x7F00000000000000 +1 0x00000000FFFFFFFE 1 byte
```
### 파일 락 (전체 테이블 잠금)
```go
// 파일 락 위치 = 베이스 위치
// 파일 락 크기 = 스키마별 상수 (위 표 참조)
fileLockPos = lockBasePos
fileLockSize = scheme.fileLockSize
```
### 레코드 락 (개별 레코드 잠금)
```go
// 레코드 락 위치 = 방향에 따라 계산
// 레코드 락 크기 = 1 byte (VFP noTags는 recordLen)
recLockPos = calculateRecLockPos(scheme, recNo)
recLockSize = scheme.recLockSize
```
### 헤더 락 (APPEND 시)
```go
// APPEND BLANK 시 다른 프로세스의 동시 APPEND 방지
headerLockPos = lockBasePos // 파일 락과 같은 위치
headerLockSize = 1
```
---
## 6. FPT 메모 파일
### FPT 헤더 (512 bytes)
```
Offset Size Field Description
────── ──── ───── ──────────
0 4 nextBlock Next free block (BE uint32)
4 2 reserved1 Reserved
6 2 blockSize Block size in bytes (BE uint16)
8 504 reserved2 Reserved (VFP: contains GC info at offset 536)
```
### 메모 블록 구조
```
Offset Size Field Description
────── ──── ───── ──────────
0 4 type Block type (BE uint32): 0=picture, 1=memo, 2=object
4 4 size Data size in bytes (BE uint32)
8 N data Actual memo data
```
### 블록 오프셋 계산
```go
blockOffset = int64(blockNumber) * int64(blockSize)
```
### 기본 블록 크기
```
DBT (dBASE): 512 bytes
FPT (FoxPro): 64 bytes (최소)
SMT (SIx): 32 bytes
```
---
## 7. OPEN 처리 순서
```
1. 파일 열기 (fOpen or fCreate)
2. 헤더 32 bytes 읽기
3. uiHeaderLen 검증 (>= 66, headerLen % 32 == 0 or 1)
4. 필드 디스크립터 읽기 (fieldCount = (headerLen - 32 - 1) / 32)
5. 각 필드의 offset 계산:
pFieldOffset[0] = 1 (deletion flag 이후)
pFieldOffset[i+1] = pFieldOffset[i] + field[i].bLen
6. recordLen 검증: pFieldOffset[fieldCount] == recordLen
7. 레코드 버퍼 할당 (recordLen bytes)
8. shared 모드면 recCount 재계산:
recCount = (fileSize - headerLen) / recordLen
9. 인덱스 파일이 있으면 자동 열기 (bHasTags 체크)
10. 메모 파일이 있으면 열기 (version byte 체크)
```
---
## 8. APPEND BLANK 처리
```
1. 파일 락/헤더 락 획득
2. shared 모드면 recCount 재계산
3. recCount++
4. 새 레코드 오프셋 = headerLen + (recCount - 1) * recordLen
5. 레코드 버퍼를 공백(' ')으로 초기화
6. 파일에 쓰기
7. EOF 마커(0x1A) 쓰기
8. 헤더의 recCount 갱신
9. 현재 위치를 새 레코드로 설정
10. 락 해제
```
---
## 9. PACK 처리
```
1. 배타적 잠금 필수 (fShared이면 에러)
2. 열린 인덱스 모두 닫기
3. ulRecOut = 0 (출력 카운터)
4. FOR recNo = 1 TO recCount:
레코드 읽기
IF NOT deleted:
ulRecOut++
레코드를 ulRecOut 위치에 쓰기
5. recCount = ulRecOut
6. 파일 크기 조정 (truncate)
7. EOF 마커 쓰기
8. 헤더 갱신
9. 인덱스 재빌드 (REINDEX)
```
---
## 10. 헤더 갱신 로직
```
갱신 시점:
- APPEND BLANK 이후
- PACK 이후
- CLOSE 시 (fUpdateHeader 플래그가 설정되어 있으면)
- FLUSH 시
갱신 내용:
- bYear/bMonth/bDay: 현재 날짜
- ulRecCount: 현재 레코드 수
- 32 bytes를 파일 오프셋 0에 쓰기
```
---
## 11. Go 구현 체크리스트
```
[ ] DBF 헤더 읽기/쓰기 (32 bytes, LE)
[ ] 필드 디스크립터 읽기/쓰기 (32 bytes × N)
[ ] 필드 타입별 GET (C, N, L, D, M, I, B, @, +, =, ^, Y)
[ ] 필드 타입별 PUT (역방향)
[ ] 레코드 오프셋 계산 (headerLen + (recNo-1) * recordLen)
[ ] 삭제 플래그 관리 (pRecord[0] = ' ' or '*')
[ ] EOF 마커 (0x1A) 읽기/쓰기
[ ] OPEN: 헤더 → 필드 → 오프셋 배열 → 버퍼 할당
[ ] CLOSE: 플러시 → 헤더 갱신 → 파일 닫기
[ ] APPEND BLANK: 락 → recCount++ → 빈 레코드 쓰기 → EOF → 헤더 갱신
[ ] DELETE/RECALL: pRecord[0] 변경
[ ] PACK: 배타적 → 순차 재작성 → truncate → 재인덱스
[ ] 6종 락 스키마 전부 구현
[ ] shared 모드에서 recCount 재계산
[ ] FPT 메모: 헤더 → 블록 읽기/쓰기
[ ] Harbour/Clipper로 생성한 DBF ↔ Five로 읽기 호환 테스트
[ ] Five로 생성한 DBF ↔ Harbour/Clipper로 읽기 호환 테스트
```
---
## 변경 이력
| 날짜 | 변경 내용 |
|------|----------|
| 2026-03-28 | 초기 작성. Harbour dbf1.c/hbdbf.h 정밀 분석 |