| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- // Copyright 2024 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- // Package zstd provides a high-level API for reading and writing zstd-compressed data.
- // It supports both regular and seekable zstd streams.
- // It's not a new wheel, but a wrapper around the zstd and zstd-seekable-format-go packages.
- package zstd
-
- import (
- "errors"
- "io"
-
- seekable "github.com/SaveTheRbtz/zstd-seekable-format-go/pkg"
- "github.com/klauspost/compress/zstd"
- )
-
- type Writer zstd.Encoder
-
- var _ io.WriteCloser = (*Writer)(nil)
-
- // NewWriter returns a new zstd writer.
- func NewWriter(w io.Writer, opts ...WriterOption) (*Writer, error) {
- zstdW, err := zstd.NewWriter(w, opts...)
- if err != nil {
- return nil, err
- }
- return (*Writer)(zstdW), nil
- }
-
- func (w *Writer) Write(p []byte) (int, error) {
- return (*zstd.Encoder)(w).Write(p)
- }
-
- func (w *Writer) Close() error {
- return (*zstd.Encoder)(w).Close()
- }
-
- type Reader zstd.Decoder
-
- var _ io.ReadCloser = (*Reader)(nil)
-
- // NewReader returns a new zstd reader.
- func NewReader(r io.Reader, opts ...ReaderOption) (*Reader, error) {
- zstdR, err := zstd.NewReader(r, opts...)
- if err != nil {
- return nil, err
- }
- return (*Reader)(zstdR), nil
- }
-
- func (r *Reader) Read(p []byte) (int, error) {
- return (*zstd.Decoder)(r).Read(p)
- }
-
- func (r *Reader) Close() error {
- (*zstd.Decoder)(r).Close() // no error returned
- return nil
- }
-
- type SeekableWriter struct {
- buf []byte
- n int
- w seekable.Writer
- }
-
- var _ io.WriteCloser = (*SeekableWriter)(nil)
-
- // NewSeekableWriter returns a zstd writer to compress data to seekable format.
- // blockSize is an important parameter, it should be decided according to the actual business requirements.
- // If it's too small, the compression ratio could be very bad, even no compression at all.
- // If it's too large, it could cost more traffic when reading the data partially from underlying storage.
- func NewSeekableWriter(w io.Writer, blockSize int, opts ...WriterOption) (*SeekableWriter, error) {
- zstdW, err := zstd.NewWriter(nil, opts...)
- if err != nil {
- return nil, err
- }
-
- seekableW, err := seekable.NewWriter(w, zstdW)
- if err != nil {
- return nil, err
- }
-
- return &SeekableWriter{
- buf: make([]byte, blockSize),
- w: seekableW,
- }, nil
- }
-
- func (w *SeekableWriter) Write(p []byte) (int, error) {
- written := 0
- for len(p) > 0 {
- n := copy(w.buf[w.n:], p)
- w.n += n
- written += n
- p = p[n:]
-
- if w.n == len(w.buf) {
- if _, err := w.w.Write(w.buf); err != nil {
- return written, err
- }
- w.n = 0
- }
- }
- return written, nil
- }
-
- func (w *SeekableWriter) Close() error {
- if w.n > 0 {
- if _, err := w.w.Write(w.buf[:w.n]); err != nil {
- return err
- }
- }
- return w.w.Close()
- }
-
- type SeekableReader struct {
- r seekable.Reader
- c func() error
- }
-
- var _ io.ReadSeekCloser = (*SeekableReader)(nil)
-
- // NewSeekableReader returns a zstd reader to decompress data from seekable format.
- func NewSeekableReader(r io.ReadSeeker, opts ...ReaderOption) (*SeekableReader, error) {
- zstdR, err := zstd.NewReader(nil, opts...)
- if err != nil {
- return nil, err
- }
-
- seekableR, err := seekable.NewReader(r, zstdR)
- if err != nil {
- return nil, err
- }
-
- ret := &SeekableReader{
- r: seekableR,
- }
- if closer, ok := r.(io.Closer); ok {
- ret.c = closer.Close
- }
-
- return ret, nil
- }
-
- func (r *SeekableReader) Read(p []byte) (int, error) {
- return r.r.Read(p)
- }
-
- func (r *SeekableReader) Seek(offset int64, whence int) (int64, error) {
- return r.r.Seek(offset, whence)
- }
-
- func (r *SeekableReader) Close() error {
- return errors.Join(
- func() error {
- if r.c != nil {
- return r.c()
- }
- return nil
- }(),
- r.r.Close(),
- )
- }
|