File: util.go

package info (click to toggle)
golang-github-dsnet-compress 0.0.2~git20230904.39efe44%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,724 kB
  • sloc: sh: 108; makefile: 5
file content (179 lines) | stat: -rw-r--r-- 4,350 bytes parent folder | download
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
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

// Package testutil is a collection of testing helper methods.
package testutil

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
)

// ResizeData resizes the input. If n < 0, then the original input will be
// returned as is. If n <= len(input), then the input slice will be truncated.
// However, if n > len(input), then the input will be replicated to fill in
// the missing bytes, but each replicated string will be XORed by some byte
// mask to avoid favoring algorithms with large LZ77 windows.
//
// If n > len(input), then len(input) must be > 0.
func ResizeData(input []byte, n int) []byte {
	if n < 0 {
		return input
	}
	if len(input) >= n {
		return input[:n]
	}
	if len(input) == 0 {
		panic("unable to replicate an empty string")
	}

	var mask byte
	output := make([]byte, n)
	for i := range output {
		idx := i % len(input)
		output[i] = input[idx] ^ mask
		if idx == len(input)-1 {
			mask++
		}
	}
	return output
}

// MustLoadFile must load a file or else panics.
func MustLoadFile(file string) []byte {
	b, err := ioutil.ReadFile(file)
	if err != nil {
		panic(err)
	}
	return b
}

// MustDecodeHex must decode a hexadecimal string or else panics.
func MustDecodeHex(s string) []byte {
	b, err := hex.DecodeString(s)
	if err != nil {
		panic(err)
	}
	return b
}

// MustDecodeBitGen must decode a BitGen formatted string or else panics.
func MustDecodeBitGen(s string) []byte {
	b, err := DecodeBitGen(s)
	if err != nil {
		panic(err)
	}
	return b
}

// BytesCompare compares inputs a and b and reports whether they are equal.
//
// If they are not equal, it returns two one-line strings that are
// representative of the differences between the two strings.
// The output will be quoted strings if it seems like the data is text,
// otherwise, it will use hexadecimal strings.
//
// Example usage:
//
//	if got, want, ok := testutil.BytesCompare(output, v.output); !ok {
//		t.Errorf("output mismatch:\ngot  %s\nwant %s", got, want)
//	}
func BytesCompare(a, b []byte) (sa, sb string, ok bool) {
	if ok = bytes.Equal(a, b); ok {
		return
	}

	commonPrefix := func(a, b []byte) int {
		if len(a) > len(b) {
			a, b = b, a
		}
		for i := range a {
			if a[i] != b[i] {
				return i
			}
		}
		return len(a)
	}

	formatter := func(a, b []byte, format string, trimHead, maxLen int) (sa, sb string) {
		trimHead -= maxLen / 2 // Always provide context of equal bytes
		if trimHead < 0 {
			trimHead = 0
		}
		if trimHead > (len(a) - maxLen) {
			trimHead = (len(a) - maxLen)
		}
		if trimHead > (len(b) - maxLen) {
			trimHead = (len(b) - maxLen)
		}

		var head, atail, btail string
		if trimHead > 0 {
			a = a[trimHead:]
			b = b[trimHead:]
			head = fmt.Sprintf("(%d bytes)...", trimHead)
		}
		if len(a) > maxLen {
			atail = fmt.Sprintf("...(%d bytes)", len(a)-maxLen)
			a = a[:maxLen]
		}
		if len(b) > maxLen {
			btail = fmt.Sprintf("...(%d bytes)", len(b)-maxLen)
			b = b[:maxLen]
		}
		sa = fmt.Sprintf("%s"+format+"%s", head, a, atail)
		sb = fmt.Sprintf("%s"+format+"%s", head, b, btail)
		return sa, sb
	}

	const maxLen = 64
	n := commonPrefix(a, b)
	sa, sb = formatter(a, b, "%q", n, maxLen) // Favor quoted output, first
	if s := sa + sb; strings.Count(s, `\u`)+strings.Count(s, `\x`) > maxLen/8 {
		sa, sb = formatter(a, b, "%x", n, maxLen/2) // Fallback to hex, next
	}
	return sa, sb, false
}

// BuggyReader returns Err after N bytes have been read from R.
type BuggyReader struct {
	R   io.Reader
	N   int64 // Number of valid bytes to read
	Err error // Return this error after N bytes
}

func (br *BuggyReader) Read(buf []byte) (int, error) {
	if int64(len(buf)) > br.N {
		buf = buf[:br.N]
	}
	n, err := br.R.Read(buf)
	br.N -= int64(n)
	if err == nil && br.N <= 0 {
		return n, br.Err
	}
	return n, err
}

// BuggyWriter returns Err after N bytes have been written to W.
type BuggyWriter struct {
	W   io.Writer
	N   int64 // Number of valid bytes to write
	Err error // Return this error after N bytes
}

func (bw *BuggyWriter) Write(buf []byte) (int, error) {
	if int64(len(buf)) > bw.N {
		buf = buf[:bw.N]
	}
	n, err := bw.W.Write(buf)
	bw.N -= int64(n)
	if err == nil && bw.N <= 0 {
		return n, bw.Err
	}
	return n, err
}