Problem: every RTL function calls defer t.EndProc() which does recover(). 50K SEEK loop = 250K recover() calls = ~12ms wasted. Solution: EndProcFast() skips recover (only needs endFrame restore). Applied to ALL RTL functions in strings.go, rdd.go, missing.go, database.go. EndProc() with recover kept for generated PRG code (needs BEGIN SEQUENCE). Analysis (50K sequential SEEK breakdown): Go NTX Seek direct: 7ms (faster than Harbour 27ms!) PRG VM overhead: 38ms (Frame + RTL calls + key generation) Key generation: 25ms (Str+LTrim+PadL+PadR = 5 RTL Frame/EndProc per iter) With EndProcFast: RTL overhead reduced ~30%. CDX SCOPE: 2ms (Harbour 4ms — 2x FASTER!) 82/82 stress PASS. 14 packages ALL PASS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
133 lines
2.4 KiB
Go
133 lines
2.4 KiB
Go
// Copyright (c) 2026 Charles KWON OhJun (charleskwonohjun@gmail.com)
|
|
// All rights reserved.
|
|
|
|
// RDD-related RTL functions: EOF(), BOF(), Found(), RecNo(), RecCount(), Deleted().
|
|
// Optimized: no Frame/EndProc for 0-param functions (called millions of times in loops).
|
|
package hbrtl
|
|
|
|
import (
|
|
"five/hbrt"
|
|
"five/hbrdd"
|
|
)
|
|
|
|
func rtlEOF(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
t.PushBool(area.EOF())
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
t.PushBool(true)
|
|
t.RetValue()
|
|
}
|
|
|
|
func rtlBOF(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
t.PushBool(area.BOF())
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
t.PushBool(true)
|
|
t.RetValue()
|
|
}
|
|
|
|
func rtlFound(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
t.PushBool(area.Found())
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
t.PushBool(false)
|
|
t.RetValue()
|
|
}
|
|
|
|
func rtlRecNo(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
t.RetInt(int64(area.RecNo()))
|
|
return
|
|
}
|
|
}
|
|
t.RetInt(0)
|
|
}
|
|
|
|
func rtlRecCount(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
rc, _ := area.RecCount()
|
|
t.RetInt(int64(rc))
|
|
return
|
|
}
|
|
}
|
|
t.RetInt(0)
|
|
}
|
|
|
|
func rtlDeleted(t *hbrt.Thread) {
|
|
t.Frame(0, 0)
|
|
defer t.EndProcFast()
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
t.PushBool(area.Deleted())
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
t.PushBool(false)
|
|
t.RetValue()
|
|
}
|
|
|
|
func rtlFieldGet(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProcFast()
|
|
n := int(t.Local(1).AsNumInt())
|
|
if wa := getWA(t); wa != nil {
|
|
if area := wa.Current(); area != nil {
|
|
val, err := area.GetValue(n - 1) // 1-based → 0-based
|
|
if err == nil {
|
|
t.PushValue(val)
|
|
t.RetValue()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
t.PushNil()
|
|
t.RetValue()
|
|
}
|
|
|
|
// getWA returns the WorkAreaManager with cached type assertion.
|
|
var waCache = struct {
|
|
iface interface{}
|
|
wam *hbrdd.WorkAreaManager
|
|
}{}
|
|
|
|
func getWA(t *hbrt.Thread) *hbrdd.WorkAreaManager {
|
|
if t.WA == nil {
|
|
return nil
|
|
}
|
|
if t.WA == waCache.iface {
|
|
return waCache.wam
|
|
}
|
|
wa, ok := t.WA.(*hbrdd.WorkAreaManager)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
waCache.iface = t.WA
|
|
waCache.wam = wa
|
|
return wa
|
|
}
|