New Five-native HTTP / ZIP / XML primitives so PRG code can do
HTTPS fetch, ZIP container reads, and streaming XML row extraction
without dropping into BEGINDUMP. FV_ prefix marks Five-original
RTL (distinct from Harbour-inherited HB_ surface).
FV_HTTPGET(cUrl [, hOpts]) / FV_HTTPPOST(cUrl, cBody [, hOpts])
hOpts: { headers: {=>}, timeout: nSec, tls_legacy: .T./.F. }
Result: { status, body, error, headers }
tls_legacy re-enables TLS_RSA cipher suites for legacy
endpoints (DART OpenAPI pins them).
FV_ZIPENTRIES(cZipBytes) / FV_ZIPREAD(cZipBytes, cEntryName)
Read ZIP archives held in memory (e.g. from FV_HTTPGET).
FV_XML_ROWS(cXml, cRowTag)
Streaming reader for repeating-record XML. Each row becomes a
flat hash of immediate-child element name -> text. Verified
against DART corpCode.xml: 30 MB / 118k rows in seconds, no
full-tree allocation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
// zipx.go — Five-native ZIP container access (FV_ZIPENTRIES / FV_ZIPREAD).
|
|
//
|
|
// Reads an in-memory ZIP archive. No filesystem path — callers that
|
|
// downloaded the ZIP via FV_HTTPGET hand the raw bytes here directly,
|
|
// which is exactly the DART corpCode flow.
|
|
|
|
package hbrtl
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"io"
|
|
|
|
"five/hbrt"
|
|
)
|
|
|
|
// FV_ZIPENTRIES(cZipBytes) -> [ { name, size }, ... ]
|
|
// Returns an empty array if the input isn't a valid ZIP.
|
|
func FvZipEntries(t *hbrt.Thread) {
|
|
t.Frame(1, 0)
|
|
defer t.EndProc()
|
|
|
|
data := []byte(t.Local(1).AsString())
|
|
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
|
if err != nil {
|
|
t.RetVal(hbrt.MakeArrayFrom(nil))
|
|
return
|
|
}
|
|
items := make([]hbrt.Value, 0, len(zr.File))
|
|
for _, f := range zr.File {
|
|
row := &hbrt.HbHash{}
|
|
row.Append(hbrt.MakeString("name"), hbrt.MakeString(f.Name))
|
|
row.Append(hbrt.MakeString("size"), hbrt.MakeInt(int(f.UncompressedSize64)))
|
|
items = append(items, hbrt.MakeHashFrom(row))
|
|
}
|
|
t.RetVal(hbrt.MakeArrayFrom(items))
|
|
}
|
|
|
|
// FV_ZIPREAD(cZipBytes, cEntryName) -> cContents
|
|
// Returns "" if the ZIP is invalid or the entry isn't present.
|
|
func FvZipRead(t *hbrt.Thread) {
|
|
t.Frame(2, 0)
|
|
defer t.EndProc()
|
|
|
|
data := []byte(t.Local(1).AsString())
|
|
name := t.Local(2).AsString()
|
|
zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
|
if err != nil {
|
|
t.RetString("")
|
|
return
|
|
}
|
|
for _, f := range zr.File {
|
|
if f.Name != name {
|
|
continue
|
|
}
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
t.RetString("")
|
|
return
|
|
}
|
|
buf, err := io.ReadAll(rc)
|
|
rc.Close()
|
|
if err != nil {
|
|
t.RetString("")
|
|
return
|
|
}
|
|
t.RetVal(hbrt.MakeStringBytes(buf))
|
|
return
|
|
}
|
|
t.RetString("")
|
|
}
|