You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys-App/scripts/zip-go-read.go

133 lines
3.9 KiB

Format 3, step 5: the ZIP sink of the page, which archive/zip reads zipsink.ts is the sink of the page for a format 3 capsule (design of format 3, section 5). Its files go to the temporary file of OPFS: the file itself when the capsule holds one file of one segment, and otherwise a ZIP of stored entries laid out from the head before any byte arrives. Each local header is written at its offset, the bytes of the file follow as open delivers them, the CRC-32 of the entry is patched in its header with a positioned write, and commit writes the central directory and closes the file; abort discards it, also when the opening failed before begin. Each file is a contiguous range of the file written (ranges), and zipOf makes the same ZIP in memory as a Blob of its parts. tempfile.ts: the writable of a temporary file takes TempChunk, bytes at the position of the file or at a given one, as FileSystemWritableFileStream does; cancellable is generic. Interoperability: scripts/zip-ts-samples.mjs writes the samples of testing/zip.ts with ZipSink, and scripts/zip-go-read.go reads them with archive/zip of the Go standard library: names out of ASCII with bit 11, stored entries, their CRC-32 and sizes, the times of the extra fields in 1970, at 2^31 - 1 and after it, in 9999 and the time of the round for a file without one, and 65535 entries, ZIP64 by their number. The reading is frozen in testing/zip-vectors.json, and zipsink.test.ts writes each sample again, requires its SHA-256 and computes the entries Go must have read. zipsink.ts is covered at 100 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days ago
//go:build ignore
// Prints src/lib/dkc/testing/zip-vectors.json: how archive/zip of the Go
// standard library reads the ZIP samples that the page writes (plan of
// format 3 in datekeys-ts, step 5), which scripts/zip-ts-samples.mjs wrote
// into a directory. For each archive it records the SHA-256 of its bytes,
// the number of its entries and, for each entry, a line with its name,
// whether bit 11 marks the name as UTF-8, its method, its CRC-32, its sizes,
// the time archive/zip reads from its extra fields, in UTC, and the SHA-256
// of its content, read with the CRC-32 checked. It keeps the lines of an
// archive of 20 entries at most, and the SHA-256 of all of them joined by
// newlines for any archive, so that the archive of 65535 entries, which is
// ZIP64 by its number of entries, weighs little. zipsink.test.ts writes each
// archive again, requires its SHA-256, and computes the lines it must give.
//
// It needs only the standard library:
//
// node scripts/zip-ts-samples.mjs DIR > zip-samples.json
// go run zip-go-read.go DIR zip-samples.json > zip-vectors.json
package main
import (
"archive/zip"
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"time"
)
type sampleIn struct {
Name string `json:"name"`
File string `json:"file"`
SHA256 string `json:"sha256"`
}
type sampleOut struct {
Name string `json:"name"`
SHA256 string `json:"sha256"`
Count int `json:"count"`
LinesSHA256 string `json:"lines_sha256"`
Lines []string `json:"lines,omitempty"`
Error string `json:"error,omitempty"`
}
type output struct {
Description string `json:"description"`
Generator string `json:"generator"`
SamplesGenerator string `json:"samples_generator"`
Go string `json:"go"`
Samples []sampleOut `json:"samples"`
}
func must[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}
// read reads an archive with archive/zip, and gives one line for each entry.
func read(b []byte) ([]string, error) {
r, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
if err != nil {
return nil, err
}
var lines []string
for _, f := range r.File {
rc, err := f.Open()
if err != nil {
return nil, fmt.Errorf("%s: %w", f.Name, err)
}
data, err := io.ReadAll(rc)
rc.Close()
if err != nil {
return nil, fmt.Errorf("%s: %w", f.Name, err)
}
sum := sha256.Sum256(data)
lines = append(lines, fmt.Sprintf("%s\t%t\t%d\t%08x\t%d\t%d\t%s\t%s",
f.Name, f.Flags&0x800 != 0, f.Method, f.CRC32, f.UncompressedSize64, f.CompressedSize64,
f.Modified.UTC().Format(time.RFC3339), hex.EncodeToString(sum[:])))
}
return lines, nil
}
func main() {
dir := os.Args[1]
var in struct {
Generator string `json:"generator"`
Samples []sampleIn `json:"samples"`
}
if err := json.Unmarshal(must(os.ReadFile(os.Args[2])), &in); err != nil {
panic(err)
}
out := output{
Description: "How archive/zip of Go reads the ZIP samples of the page; see the header of scripts/zip-go-read.go.",
Generator: "scripts/zip-go-read.go",
SamplesGenerator: in.Generator,
Go: runtime.Version(),
}
for _, s := range in.Samples {
b := must(os.ReadFile(filepath.Join(dir, s.File)))
sum := sha256.Sum256(b)
so := sampleOut{Name: s.Name, SHA256: hex.EncodeToString(sum[:])}
if so.SHA256 != s.SHA256 {
panic(fmt.Sprintf("%s: the file is not the sample written", s.Name))
}
lines, err := read(b)
if err != nil {
so.Error = err.Error()
}
so.Count = len(lines)
all := sha256.Sum256([]byte(strings.Join(lines, "\n")))
so.LinesSHA256 = hex.EncodeToString(all[:])
if len(lines) <= 20 {
so.Lines = lines
}
out.Samples = append(out.Samples, so)
}
e := json.NewEncoder(os.Stdout)
e.SetEscapeHTML(false)
e.SetIndent("", " ")
if err := e.Encode(out); err != nil {
panic(err)
}
}

Powered by TurnKey Linux.