view pkg/mesh/meshserialize.go @ 5695:ef80748ae4f3 sr-v2

Finish deserializing v2 of the mesh index.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 12 Feb 2024 21:23:55 +0100
parents d920f0fa2f04
children
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package mesh

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/binary"
	"fmt"
	"io"

	"gemma.intevation.de/gemma/pkg/log"
)

// Version is current version of the format the mesh index is stored in.
const Version = 2

// magicHeader is a magic header to tell if the data blob is a mesh index.
const magicHeader = "SR3D"

// coalesceVersion returns the most recent version if version is less or equal zero.
func coalesceVersion(version int) int {
	if version > 0 {
		return version
	}
	return Version
}

// OptimizeForSerialization apply version specific storage optimizations
// before serialization.
func (s *STRTree) OptimizeForSerialization(version int) {
	version = coalesceVersion(version)
	if version == 2 {
		s.optimizeForSerializationV2()
	}
}

// Bytes serializes this tree to a byte slice.
func (s *STRTree) Bytes(version int) ([]byte, int, error) {
	var buf bytes.Buffer
	w, err := gzip.NewWriterLevel(&buf, gzip.BestSpeed)
	if err != nil {
		return nil, 0, err
	}
	version = coalesceVersion(version)
	if err := s.serializeVn(w, version); err != nil {
		return nil, 0, err
	}
	if err := w.Close(); err != nil {
		return nil, 0, err
	}
	return buf.Bytes(), version, nil
}

func (s *STRTree) serializeVn(w io.Writer, version int) error {
	switch version {
	case 1:
		return s.serializeV1(w)
	case 2:
		return s.serializeV2(w)
	default:
		return fmt.Errorf("cannot serialize mesh version %d", version)
	}
}

// FromBytes restores a STRTree from a binary representation.
func (s *STRTree) FromBytes(data []byte, version int) error {
	version = coalesceVersion(version)
	r, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		return err
	}
	return s.deserialize(bufio.NewReader(r), version)
}

func (s *STRTree) deserialize(r *bufio.Reader, version int) error {
	header, err := r.Peek(8)
	if err != nil {
		return err
	}
	if bytes.HasPrefix(header, []byte(magicHeader)) {
		realVersion := int(binary.LittleEndian.Uint32(header[4:]))
		if realVersion != version {
			return fmt.Errorf("mesh version mismatch: Have %d expect %d",
				realVersion, version)
		}
		// Skip the header
		if _, err := r.Discard(8); err != nil {
			return err
		}
		return s.deserializeVn(r, realVersion)
	}
	return s.deserializeV1(r)
}

func (s *STRTree) deserializeVn(r *bufio.Reader, version int) error {
	switch version {
	case 1:
		return s.deserializeV1(r)
	case 2:
		return s.deserializeV2(r)
	default:
		return fmt.Errorf("cannot deserialize mesh version %d", version)
	}
}

// serializer is a generic function to apply a chain of
// serializer/deserializer functions to an object given a reader or writer.
func serializer[T, S any](
	s S,
	t T,
	fns ...func(T, S) error,
) error {
	for _, fn := range fns {
		if err := fn(t, s); err != nil {
			return err
		}
	}
	return nil
}

func (s *STRTree) serializeEntries(w io.Writer) error {
	return binary.Write(w, binary.LittleEndian, uint8(s.Entries))
}

func (s *STRTree) deserializeEntries(r *bufio.Reader) error {
	var numEntries uint8
	if err := binary.Read(r, binary.LittleEndian, &numEntries); err != nil {
		return err
	}
	s.Entries = int(numEntries)
	return nil
}

func (t *Tin) serializeExtent(w io.Writer) error {
	if err := t.Min.Write(w); err != nil {
		return err
	}
	return t.Max.Write(w)
}

func (t *Tin) deserializeExtent(r *bufio.Reader) error {
	if err := t.Min.Read(r); err != nil {
		return err
	}
	if err := t.Max.Read(r); err != nil {
		return err
	}
	log.Infof("BBOX: [[%f, %f, %f], [%f, %f, %f]]\n",
		t.Min.X, t.Min.Y, t.Min.Z,
		t.Max.X, t.Max.Y, t.Max.Z)
	return nil
}

func (t *Tin) serializeEPSG(w io.Writer) error {
	return binary.Write(w, binary.LittleEndian, t.EPSG)
}

func (t *Tin) deserializeEPSG(r *bufio.Reader) error {
	if err := binary.Read(r, binary.LittleEndian, &t.EPSG); err != nil {
		return err
	}
	log.Infof("EPSG: %d\n", t.EPSG)
	return nil
}

func serializeHeader() func(*STRTree, io.Writer) error {
	return func(_ *STRTree, w io.Writer) error {
		_, err := w.Write([]byte(magicHeader))
		return err
	}
}

func serializeVersion(version int) func(*STRTree, io.Writer) error {
	return func(_ *STRTree, w io.Writer) error {
		return binary.Write(w, binary.LittleEndian, uint32(version))
	}
}