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>
This commit is contained in:
403
docs/dbf-engine-spec.md
Normal file
403
docs/dbf-engine-spec.md
Normal file
@@ -0,0 +1,403 @@
|
||||
# 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 정밀 분석 |
|
||||
Reference in New Issue
Block a user