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

10 KiB
Raw Blame History

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종)

위치 계산 공식

// 방향에 따른 레코드 락 위치:
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

파일 락 (전체 테이블 잠금)

// 파일 락 위치 = 베이스 위치
// 파일 락 크기 = 스키마별 상수 (위 표 참조)
fileLockPos  = lockBasePos
fileLockSize = scheme.fileLockSize

레코드 락 (개별 레코드 잠금)

// 레코드 락 위치 = 방향에 따라 계산
// 레코드 락 크기 = 1 byte (VFP noTags는 recordLen)
recLockPos = calculateRecLockPos(scheme, recNo)
recLockSize = scheme.recLockSize

헤더 락 (APPEND 시)

// 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

블록 오프셋 계산

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 정밀 분석