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