summaryrefslogtreecommitdiff
path: root/vendor/github.com/ulikunitz/xz/lzma/header.go
blob: 04276c81634095fc16b6217d3d4fb873d1f00f5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright 2014-2021 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
}