main
Raw Download raw file
  1// Copyright 2014-2022 Ulrich Kunitz. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5package lzma
  6
  7import (
  8	"errors"
  9	"fmt"
 10)
 11
 12// uint32LE reads an uint32 integer from a byte slice
 13func uint32LE(b []byte) uint32 {
 14	x := uint32(b[3]) << 24
 15	x |= uint32(b[2]) << 16
 16	x |= uint32(b[1]) << 8
 17	x |= uint32(b[0])
 18	return x
 19}
 20
 21// uint64LE converts the uint64 value stored as little endian to an uint64
 22// value.
 23func uint64LE(b []byte) uint64 {
 24	x := uint64(b[7]) << 56
 25	x |= uint64(b[6]) << 48
 26	x |= uint64(b[5]) << 40
 27	x |= uint64(b[4]) << 32
 28	x |= uint64(b[3]) << 24
 29	x |= uint64(b[2]) << 16
 30	x |= uint64(b[1]) << 8
 31	x |= uint64(b[0])
 32	return x
 33}
 34
 35// putUint32LE puts an uint32 integer into a byte slice that must have at least
 36// a length of 4 bytes.
 37func putUint32LE(b []byte, x uint32) {
 38	b[0] = byte(x)
 39	b[1] = byte(x >> 8)
 40	b[2] = byte(x >> 16)
 41	b[3] = byte(x >> 24)
 42}
 43
 44// putUint64LE puts the uint64 value into the byte slice as little endian
 45// value. The byte slice b must have at least place for 8 bytes.
 46func putUint64LE(b []byte, x uint64) {
 47	b[0] = byte(x)
 48	b[1] = byte(x >> 8)
 49	b[2] = byte(x >> 16)
 50	b[3] = byte(x >> 24)
 51	b[4] = byte(x >> 32)
 52	b[5] = byte(x >> 40)
 53	b[6] = byte(x >> 48)
 54	b[7] = byte(x >> 56)
 55}
 56
 57// noHeaderSize defines the value of the length field in the LZMA header.
 58const noHeaderSize uint64 = 1<<64 - 1
 59
 60// HeaderLen provides the length of the LZMA file header.
 61const HeaderLen = 13
 62
 63// header represents the header of an LZMA file.
 64type header struct {
 65	properties Properties
 66	dictCap    int
 67	// uncompressed size; negative value if no size is given
 68	size int64
 69}
 70
 71// marshalBinary marshals the header.
 72func (h *header) marshalBinary() (data []byte, err error) {
 73	if err = h.properties.verify(); err != nil {
 74		return nil, err
 75	}
 76	if !(0 <= h.dictCap && int64(h.dictCap) <= MaxDictCap) {
 77		return nil, fmt.Errorf("lzma: DictCap %d out of range",
 78			h.dictCap)
 79	}
 80
 81	data = make([]byte, 13)
 82
 83	// property byte
 84	data[0] = h.properties.Code()
 85
 86	// dictionary capacity
 87	putUint32LE(data[1:5], uint32(h.dictCap))
 88
 89	// uncompressed size
 90	var s uint64
 91	if h.size > 0 {
 92		s = uint64(h.size)
 93	} else {
 94		s = noHeaderSize
 95	}
 96	putUint64LE(data[5:], s)
 97
 98	return data, nil
 99}
100
101// unmarshalBinary unmarshals the header.
102func (h *header) unmarshalBinary(data []byte) error {
103	if len(data) != HeaderLen {
104		return errors.New("lzma.unmarshalBinary: data has wrong length")
105	}
106
107	// properties
108	var err error
109	if h.properties, err = PropertiesForCode(data[0]); err != nil {
110		return err
111	}
112
113	// dictionary capacity
114	h.dictCap = int(uint32LE(data[1:]))
115	if h.dictCap < 0 {
116		return errors.New(
117			"LZMA header: dictionary capacity exceeds maximum " +
118				"integer")
119	}
120
121	// uncompressed size
122	s := uint64LE(data[5:])
123	if s == noHeaderSize {
124		h.size = -1
125	} else {
126		h.size = int64(s)
127		if h.size < 0 {
128			return errors.New(
129				"LZMA header: uncompressed size " +
130					"out of int64 range")
131		}
132	}
133
134	return nil
135}
136
137// validDictCap checks whether the dictionary capacity is correct. This
138// is used to weed out wrong file headers.
139func validDictCap(dictcap int) bool {
140	if int64(dictcap) == MaxDictCap {
141		return true
142	}
143	for n := uint(10); n < 32; n++ {
144		if dictcap == 1<<n {
145			return true
146		}
147		if dictcap == 1<<n+1<<(n-1) {
148			return true
149		}
150	}
151	return false
152}
153
154// ValidHeader checks for a valid LZMA file header. It allows only
155// dictionary sizes of 2^n or 2^n+2^(n-1) with n >= 10 or 2^32-1. If
156// there is an explicit size it must not exceed 256 GiB. The length of
157// the data argument must be HeaderLen.
158func ValidHeader(data []byte) bool {
159	var h header
160	if err := h.unmarshalBinary(data); err != nil {
161		return false
162	}
163	if !validDictCap(h.dictCap) {
164		return false
165	}
166	return h.size < 0 || h.size <= 1<<38
167}