feat: initial release
Assisted-by: GLM 5.3 Flash
This commit is contained in:
+165
@@ -0,0 +1,165 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package io
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sourcedock.dev/petrbalvin/tensor/internal/core"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestMapFloatsRoundTrip writes floats natively, maps them back, and
|
||||
// checks spot values far apart, which is exactly the sparse-touch
|
||||
// pattern big-file mapping exists for.
|
||||
func TestMapFloatsRoundTrip(t *testing.T) {
|
||||
const n = 4096
|
||||
path := filepath.Join(t.TempDir(), "values.bin")
|
||||
values := make([]float64, n)
|
||||
for i := range n {
|
||||
values[i] = math.Sin(float64(i)) * 1e6
|
||||
}
|
||||
if err := SaveNativeFloats(path, values); err != nil {
|
||||
t.Fatalf("SaveNativeFloats: %v", err)
|
||||
}
|
||||
a, release, err := MapFloats(path, 0, n)
|
||||
if err != nil {
|
||||
t.Fatalf("MapFloats: %v", err)
|
||||
}
|
||||
if a.Len() != n || a.Dtype() != core.Float {
|
||||
t.Fatalf("mapped array %s of length %d", a.Dtype(), a.Len())
|
||||
}
|
||||
for _, i := range []int{0, 1, 999, 2048, n - 1} {
|
||||
if a.FloatAt(i) != values[i] {
|
||||
t.Fatalf("value %d = %.14g, want %.14g", i, a.FloatAt(i), values[i])
|
||||
}
|
||||
}
|
||||
// A reshape keeps the view: strides read straight from the mapping.
|
||||
square, err := core.Reshape(a, 64, 64)
|
||||
if err != nil {
|
||||
t.Fatalf("Reshape: %v", err)
|
||||
}
|
||||
if square.FloatAt(3*64+7) != values[3*64+7] {
|
||||
t.Fatal("the reshaped view lost the mapping")
|
||||
}
|
||||
if err := release(); err != nil {
|
||||
t.Fatalf("release: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMapFloat32sAndInts covers the other two element types.
|
||||
func TestMapFloat32sAndInts(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
const n = 100
|
||||
f32path := filepath.Join(dir, "f32.bin")
|
||||
var buf []byte
|
||||
var word [4]byte
|
||||
want32 := make([]float32, n)
|
||||
for i := range n {
|
||||
want32[i] = float32(i) / 7
|
||||
binary.NativeEndian.PutUint32(word[:], math.Float32bits(want32[i]))
|
||||
buf = append(buf, word[:]...)
|
||||
}
|
||||
if err := os.WriteFile(f32path, buf, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a, release, err := MapFloat32s(f32path, 0, n)
|
||||
if err != nil {
|
||||
t.Fatalf("MapFloat32s: %v", err)
|
||||
}
|
||||
if a.RawFloat32s()[42] != want32[42] {
|
||||
t.Fatalf("f32[42] = %v, want %v", a.RawFloat32s()[42], want32[42])
|
||||
}
|
||||
if err := release(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ipath := filepath.Join(dir, "i64.bin")
|
||||
var ibuf []byte
|
||||
var iword [8]byte
|
||||
wantInt := make([]int64, n)
|
||||
for i := range n {
|
||||
wantInt[i] = int64(i) * 1_000_000
|
||||
binary.NativeEndian.PutUint64(iword[:], uint64(wantInt[i]))
|
||||
ibuf = append(ibuf, iword[:]...)
|
||||
}
|
||||
if err := os.WriteFile(ipath, ibuf, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, releaseInt, err := MapInts(ipath, 0, n)
|
||||
if err != nil {
|
||||
t.Fatalf("MapInts: %v", err)
|
||||
}
|
||||
if b.RawInts()[13] != wantInt[13] {
|
||||
t.Fatalf("i64[13] = %d, want %d", b.RawInts()[13], wantInt[13])
|
||||
}
|
||||
if err := releaseInt(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMapFloatsOffset maps from a non-zero offset inside a file that
|
||||
// carries a small header first. The header is a multiple of the
|
||||
// element size: the typed view the reader builds is built with unsafe
|
||||
// and must be aligned.
|
||||
func TestMapFloatsOffset(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "headered.bin")
|
||||
header := []byte("TENSOR HEADER 24 BYTES!!")
|
||||
if len(header)%8 != 0 {
|
||||
t.Fatalf("the test header is %d bytes, want a multiple of 8", len(header))
|
||||
}
|
||||
values := []float64{3.5, 1.25, -9}
|
||||
var buf []byte
|
||||
buf = append(buf, header...)
|
||||
var word [8]byte
|
||||
for _, v := range values {
|
||||
binary.NativeEndian.PutUint64(word[:], math.Float64bits(v))
|
||||
buf = append(buf, word[:]...)
|
||||
}
|
||||
if err := os.WriteFile(path, buf, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a, release, err := MapFloats(path, int64(len(header)), 3)
|
||||
if err != nil {
|
||||
t.Fatalf("MapFloats: %v", err)
|
||||
}
|
||||
for i, want := range values {
|
||||
if a.FloatAt(i) != want {
|
||||
t.Fatalf("value %d = %.14g, want %.14g", i, a.FloatAt(i), want)
|
||||
}
|
||||
}
|
||||
if err := release(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMapErrors pins the validation contract: short files, negative
|
||||
// offsets, zero counts and directories are errors, never mappings.
|
||||
func TestMapErrors(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "small.bin")
|
||||
if err := SaveNativeFloats(path, []float64{1, 2, 3}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := MapFloats(path, 4, 1); err == nil {
|
||||
t.Fatal("expected an error for an offset that misaligns the elements")
|
||||
}
|
||||
if _, _, err := MapFloats(path, 0, 4); err == nil {
|
||||
t.Fatal("expected an error when the file is too short")
|
||||
}
|
||||
if _, _, err := MapFloats(path, -8, 2); err == nil {
|
||||
t.Fatal("expected an error for a negative offset")
|
||||
}
|
||||
if _, _, err := MapFloats(path, 0, 0); err == nil {
|
||||
t.Fatal("expected an error for a zero count")
|
||||
}
|
||||
if _, _, err := MapFloats(dir, 0, 1); err == nil {
|
||||
t.Fatal("expected an error for a directory")
|
||||
}
|
||||
if _, _, err := MapFloats(filepath.Join(dir, "missing.bin"), 0, 1); err == nil {
|
||||
t.Fatal("expected an error for a missing file")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user