| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // Copyright 2014-2022 Ulrich Kunitz. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package lzma
- import (
- "errors"
- "fmt"
- )
- // uint32LE reads an uint32 integer from a byte slice
- func uint32LE(b []byte) uint32 {
- x := uint32(b[3]) << 24
- x |= uint32(b[2]) << 16
- x |= uint32(b[1]) << 8
- x |= uint32(b[0])
- return x
- }
- // uint64LE converts the uint64 value stored as little endian to an uint64
- // value.
- func uint64LE(b []byte) uint64 {
- x := uint64(b[7]) << 56
- x |= uint64(b[6]) << 48
- x |= uint64(b[5]) << 40
- x |= uint64(b[4]) << 32
- x |= uint64(b[3]) << 24
- x |= uint64(b[2]) << 16
- x |= uint64(b[1]) << 8
- x |= uint64(b[0])
- return x
- }
- // putUint32LE puts an uint32 integer into a byte slice that must have at least
- // a length of 4 bytes.
- func putUint32LE(b []byte, x uint32) {
- b[0] = byte(x)
- b[1] = byte(x >> 8)
- b[2] = byte(x >> 16)
- b[3] = byte(x >> 24)
- }
- // putUint64LE puts the uint64 value into the byte slice as little endian
- // value. The byte slice b must have at least place for 8 bytes.
- func putUint64LE(b []byte, x uint64) {
- b[0] = byte(x)
- b[1] = byte(x >> 8)
- b[2] = byte(x >> 16)
- b[3] = byte(x >> 24)
- b[4] = byte(x >> 32)
- b[5] = byte(x >> 40)
- b[6] = byte(x >> 48)
- b[7] = byte(x >> 56)
- }
- // noHeaderSize defines the value of the length field in the LZMA header.
- const noHeaderSize uint64 = 1<<64 - 1
- // HeaderLen provides the length of the LZMA file header.
- const HeaderLen = 13
- // header represents the header of an LZMA file.
- type header struct {
- properties Properties
- dictCap int
- // uncompressed size; negative value if no size is given
- size int64
- }
- // marshalBinary marshals the header.
- func (h *header) marshalBinary() (data []byte, err error) {
- if err = h.properties.verify(); err != nil {
- return nil, err
- }
- if !(0 <= h.dictCap && int64(h.dictCap) <= MaxDictCap) {
- return nil, fmt.Errorf("lzma: DictCap %d out of range",
- h.dictCap)
- }
- data = make([]byte, 13)
- // property byte
- data[0] = h.properties.Code()
- // dictionary capacity
- putUint32LE(data[1:5], uint32(h.dictCap))
- // uncompressed size
- var s uint64
- if h.size > 0 {
- s = uint64(h.size)
- } else {
- s = noHeaderSize
- }
- putUint64LE(data[5:], s)
- return data, nil
- }
- // unmarshalBinary unmarshals the header.
- func (h *header) unmarshalBinary(data []byte) error {
- if len(data) != HeaderLen {
- return errors.New("lzma.unmarshalBinary: data has wrong length")
- }
- // properties
- var err error
- if h.properties, err = PropertiesForCode(data[0]); err != nil {
- return err
- }
- // dictionary capacity
- h.dictCap = int(uint32LE(data[1:]))
- if h.dictCap < 0 {
- return errors.New(
- "LZMA header: dictionary capacity exceeds maximum " +
- "integer")
- }
- // uncompressed size
- s := uint64LE(data[5:])
- if s == noHeaderSize {
- h.size = -1
- } else {
- h.size = int64(s)
- if h.size < 0 {
- return errors.New(
- "LZMA header: uncompressed size " +
- "out of int64 range")
- }
- }
- return nil
- }
- // validDictCap checks whether the dictionary capacity is correct. This
- // is used to weed out wrong file headers.
- func validDictCap(dictcap int) bool {
- if int64(dictcap) == MaxDictCap {
- return true
- }
- for n := uint(10); n < 32; n++ {
- if dictcap == 1<<n {
- return true
- }
- if dictcap == 1<<n+1<<(n-1) {
- return true
- }
- }
- return false
- }
- // ValidHeader checks for a valid LZMA file header. It allows only
- // dictionary sizes of 2^n or 2^n+2^(n-1) with n >= 10 or 2^32-1. If
- // there is an explicit size it must not exceed 256 GiB. The length of
- // the data argument must be HeaderLen.
- func ValidHeader(data []byte) bool {
- var h header
- if err := h.unmarshalBinary(data); err != nil {
- return false
- }
- if !validDictCap(h.dictCap) {
- return false
- }
- return h.size < 0 || h.size <= 1<<38
- }
|