github.com/ethereum/go-ethereum@v1.14.3/internal/era/e2store/e2store.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package e2store
    18  
    19  import (
    20  	"encoding/binary"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  )
    25  
    26  const (
    27  	headerSize     = 8
    28  	valueSizeLimit = 1024 * 1024 * 50
    29  )
    30  
    31  // Entry is a variable-length-data record in an e2store.
    32  type Entry struct {
    33  	Type  uint16
    34  	Value []byte
    35  }
    36  
    37  // Writer writes entries using e2store encoding.
    38  // For more information on this format, see:
    39  // https://github.com/status-im/nimbus-eth2/blob/stable/docs/e2store.md
    40  type Writer struct {
    41  	w io.Writer
    42  }
    43  
    44  // NewWriter returns a new Writer that writes to w.
    45  func NewWriter(w io.Writer) *Writer {
    46  	return &Writer{w}
    47  }
    48  
    49  // Write writes a single e2store entry to w.
    50  // An entry is encoded in a type-length-value format. The first 8 bytes of the
    51  // record store the type (2 bytes), the length (4 bytes), and some reserved
    52  // data (2 bytes). The remaining bytes store b.
    53  func (w *Writer) Write(typ uint16, b []byte) (int, error) {
    54  	buf := make([]byte, headerSize)
    55  	binary.LittleEndian.PutUint16(buf, typ)
    56  	binary.LittleEndian.PutUint32(buf[2:], uint32(len(b)))
    57  
    58  	// Write header.
    59  	if n, err := w.w.Write(buf); err != nil {
    60  		return n, err
    61  	}
    62  	// Write value, return combined write size.
    63  	n, err := w.w.Write(b)
    64  	return n + headerSize, err
    65  }
    66  
    67  // A Reader reads entries from an e2store-encoded file.
    68  // For more information on this format, see
    69  // https://github.com/status-im/nimbus-eth2/blob/stable/docs/e2store.md
    70  type Reader struct {
    71  	r      io.ReaderAt
    72  	offset int64
    73  }
    74  
    75  // NewReader returns a new Reader that reads from r.
    76  func NewReader(r io.ReaderAt) *Reader {
    77  	return &Reader{r, 0}
    78  }
    79  
    80  // Read reads one Entry from r.
    81  func (r *Reader) Read() (*Entry, error) {
    82  	var e Entry
    83  	n, err := r.ReadAt(&e, r.offset)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	r.offset += int64(n)
    88  	return &e, nil
    89  }
    90  
    91  // ReadAt reads one Entry from r at the specified offset.
    92  func (r *Reader) ReadAt(entry *Entry, off int64) (int, error) {
    93  	typ, length, err := r.ReadMetadataAt(off)
    94  	if err != nil {
    95  		return 0, err
    96  	}
    97  	entry.Type = typ
    98  
    99  	// Check length bounds.
   100  	if length > valueSizeLimit {
   101  		return headerSize, fmt.Errorf("item larger than item size limit %d: have %d", valueSizeLimit, length)
   102  	}
   103  	if length == 0 {
   104  		return headerSize, nil
   105  	}
   106  
   107  	// Read value.
   108  	val := make([]byte, length)
   109  	if n, err := r.r.ReadAt(val, off+headerSize); err != nil {
   110  		n += headerSize
   111  		// An entry with a non-zero length should not return EOF when
   112  		// reading the value.
   113  		if err == io.EOF {
   114  			return n, io.ErrUnexpectedEOF
   115  		}
   116  		return n, err
   117  	}
   118  	entry.Value = val
   119  	return int(headerSize + length), nil
   120  }
   121  
   122  // ReaderAt returns an io.Reader delivering value data for the entry at
   123  // the specified offset. If the entry type does not match the expected type, an
   124  // error is returned.
   125  func (r *Reader) ReaderAt(expectedType uint16, off int64) (io.Reader, int, error) {
   126  	// problem = need to return length+headerSize not just value length via section reader
   127  	typ, length, err := r.ReadMetadataAt(off)
   128  	if err != nil {
   129  		return nil, headerSize, err
   130  	}
   131  	if typ != expectedType {
   132  		return nil, headerSize, fmt.Errorf("wrong type, want %d have %d", expectedType, typ)
   133  	}
   134  	if length > valueSizeLimit {
   135  		return nil, headerSize, fmt.Errorf("item larger than item size limit %d: have %d", valueSizeLimit, length)
   136  	}
   137  	return io.NewSectionReader(r.r, off+headerSize, int64(length)), headerSize + int(length), nil
   138  }
   139  
   140  // LengthAt reads the header at off and returns the total length of the entry,
   141  // including header.
   142  func (r *Reader) LengthAt(off int64) (int64, error) {
   143  	_, length, err := r.ReadMetadataAt(off)
   144  	if err != nil {
   145  		return 0, err
   146  	}
   147  	return int64(length) + headerSize, nil
   148  }
   149  
   150  // ReadMetadataAt reads the header metadata at the given offset.
   151  func (r *Reader) ReadMetadataAt(off int64) (typ uint16, length uint32, err error) {
   152  	b := make([]byte, headerSize)
   153  	if n, err := r.r.ReadAt(b, off); err != nil {
   154  		if err == io.EOF && n > 0 {
   155  			return 0, 0, io.ErrUnexpectedEOF
   156  		}
   157  		return 0, 0, err
   158  	}
   159  	typ = binary.LittleEndian.Uint16(b)
   160  	length = binary.LittleEndian.Uint32(b[2:])
   161  
   162  	// Check reserved bytes of header.
   163  	if b[6] != 0 || b[7] != 0 {
   164  		return 0, 0, errors.New("reserved bytes are non-zero")
   165  	}
   166  
   167  	return typ, length, nil
   168  }
   169  
   170  // Find returns the first entry with the matching type.
   171  func (r *Reader) Find(want uint16) (*Entry, error) {
   172  	var (
   173  		off    int64
   174  		typ    uint16
   175  		length uint32
   176  		err    error
   177  	)
   178  	for {
   179  		typ, length, err = r.ReadMetadataAt(off)
   180  		if err == io.EOF {
   181  			return nil, io.EOF
   182  		} else if err != nil {
   183  			return nil, err
   184  		}
   185  		if typ == want {
   186  			var e Entry
   187  			if _, err := r.ReadAt(&e, off); err != nil {
   188  				return nil, err
   189  			}
   190  			return &e, nil
   191  		}
   192  		off += int64(headerSize + length)
   193  	}
   194  }
   195  
   196  // FindAll returns all entries with the matching type.
   197  func (r *Reader) FindAll(want uint16) ([]*Entry, error) {
   198  	var (
   199  		off     int64
   200  		typ     uint16
   201  		length  uint32
   202  		entries []*Entry
   203  		err     error
   204  	)
   205  	for {
   206  		typ, length, err = r.ReadMetadataAt(off)
   207  		if err == io.EOF {
   208  			return entries, nil
   209  		} else if err != nil {
   210  			return entries, err
   211  		}
   212  		if typ == want {
   213  			e := new(Entry)
   214  			if _, err := r.ReadAt(e, off); err != nil {
   215  				return entries, err
   216  			}
   217  			entries = append(entries, e)
   218  		}
   219  		off += int64(headerSize + length)
   220  	}
   221  }