gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

lz4sconvert.go (11155B)


      1 // Copyright (c) 2022 Klaus Post. 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 
      5 package s2
      6 
      7 import (
      8 	"encoding/binary"
      9 	"fmt"
     10 )
     11 
     12 // LZ4sConverter provides conversion from LZ4s.
     13 // (Intel modified LZ4 Blocks)
     14 // https://cdrdv2-public.intel.com/743912/743912-qat-programmers-guide-v2.0.pdf
     15 // LZ4s is a variant of LZ4 block format. LZ4s should be considered as an intermediate compressed block format.
     16 // The LZ4s format is selected when the application sets the compType to CPA_DC_LZ4S in CpaDcSessionSetupData.
     17 // The LZ4s block returned by the IntelĀ® QAT hardware can be used by an external
     18 // software post-processing to generate other compressed data formats.
     19 // The following table lists the differences between LZ4 and LZ4s block format. LZ4s block format uses
     20 // the same high-level formatting as LZ4 block format with the following encoding changes:
     21 // For Min Match of 4 bytes, Copy length value 1-15 means length 4-18 with 18 bytes adding an extra byte.
     22 // ONLY "Min match of 4 bytes" is supported.
     23 type LZ4sConverter struct {
     24 }
     25 
     26 // ConvertBlock will convert an LZ4s block and append it as an S2
     27 // block without block length to dst.
     28 // The uncompressed size is returned as well.
     29 // dst must have capacity to contain the entire compressed block.
     30 func (l *LZ4sConverter) ConvertBlock(dst, src []byte) ([]byte, int, error) {
     31 	if len(src) == 0 {
     32 		return dst, 0, nil
     33 	}
     34 	const debug = false
     35 	const inline = true
     36 	const lz4MinMatch = 3
     37 
     38 	s, d := 0, len(dst)
     39 	dst = dst[:cap(dst)]
     40 	if !debug && hasAmd64Asm {
     41 		res, sz := cvtLZ4sBlockAsm(dst[d:], src)
     42 		if res < 0 {
     43 			const (
     44 				errCorrupt     = -1
     45 				errDstTooSmall = -2
     46 			)
     47 			switch res {
     48 			case errCorrupt:
     49 				return nil, 0, ErrCorrupt
     50 			case errDstTooSmall:
     51 				return nil, 0, ErrDstTooSmall
     52 			default:
     53 				return nil, 0, fmt.Errorf("unexpected result: %d", res)
     54 			}
     55 		}
     56 		if d+sz > len(dst) {
     57 			return nil, 0, ErrDstTooSmall
     58 		}
     59 		return dst[:d+sz], res, nil
     60 	}
     61 
     62 	dLimit := len(dst) - 10
     63 	var lastOffset uint16
     64 	var uncompressed int
     65 	if debug {
     66 		fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst))
     67 	}
     68 
     69 	for {
     70 		if s >= len(src) {
     71 			return dst[:d], 0, ErrCorrupt
     72 		}
     73 		// Read literal info
     74 		token := src[s]
     75 		ll := int(token >> 4)
     76 		ml := int(lz4MinMatch + (token & 0xf))
     77 
     78 		// If upper nibble is 15, literal length is extended
     79 		if token >= 0xf0 {
     80 			for {
     81 				s++
     82 				if s >= len(src) {
     83 					if debug {
     84 						fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src))
     85 					}
     86 					return dst[:d], 0, ErrCorrupt
     87 				}
     88 				val := src[s]
     89 				ll += int(val)
     90 				if val != 255 {
     91 					break
     92 				}
     93 			}
     94 		}
     95 		// Skip past token
     96 		if s+ll >= len(src) {
     97 			if debug {
     98 				fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src))
     99 			}
    100 			return nil, 0, ErrCorrupt
    101 		}
    102 		s++
    103 		if ll > 0 {
    104 			if d+ll > dLimit {
    105 				return nil, 0, ErrDstTooSmall
    106 			}
    107 			if debug {
    108 				fmt.Printf("emit %d literals\n", ll)
    109 			}
    110 			d += emitLiteralGo(dst[d:], src[s:s+ll])
    111 			s += ll
    112 			uncompressed += ll
    113 		}
    114 
    115 		// Check if we are done...
    116 		if ml == lz4MinMatch {
    117 			if s == len(src) {
    118 				break
    119 			}
    120 			// 0 bytes.
    121 			continue
    122 		}
    123 		// 2 byte offset
    124 		if s >= len(src)-2 {
    125 			if debug {
    126 				fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2)
    127 			}
    128 			return nil, 0, ErrCorrupt
    129 		}
    130 		offset := binary.LittleEndian.Uint16(src[s:])
    131 		s += 2
    132 		if offset == 0 {
    133 			if debug {
    134 				fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s)
    135 			}
    136 			return nil, 0, ErrCorrupt
    137 		}
    138 		if int(offset) > uncompressed {
    139 			if debug {
    140 				fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed)
    141 			}
    142 			return nil, 0, ErrCorrupt
    143 		}
    144 
    145 		if ml == lz4MinMatch+15 {
    146 			for {
    147 				if s >= len(src) {
    148 					if debug {
    149 						fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
    150 					}
    151 					return nil, 0, ErrCorrupt
    152 				}
    153 				val := src[s]
    154 				s++
    155 				ml += int(val)
    156 				if val != 255 {
    157 					if s >= len(src) {
    158 						if debug {
    159 							fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
    160 						}
    161 						return nil, 0, ErrCorrupt
    162 					}
    163 					break
    164 				}
    165 			}
    166 		}
    167 		if offset == lastOffset {
    168 			if debug {
    169 				fmt.Printf("emit repeat, length: %d, offset: %d\n", ml, offset)
    170 			}
    171 			if !inline {
    172 				d += emitRepeat16(dst[d:], offset, ml)
    173 			} else {
    174 				length := ml
    175 				dst := dst[d:]
    176 				for len(dst) > 5 {
    177 					// Repeat offset, make length cheaper
    178 					length -= 4
    179 					if length <= 4 {
    180 						dst[0] = uint8(length)<<2 | tagCopy1
    181 						dst[1] = 0
    182 						d += 2
    183 						break
    184 					}
    185 					if length < 8 && offset < 2048 {
    186 						// Encode WITH offset
    187 						dst[1] = uint8(offset)
    188 						dst[0] = uint8(offset>>8)<<5 | uint8(length)<<2 | tagCopy1
    189 						d += 2
    190 						break
    191 					}
    192 					if length < (1<<8)+4 {
    193 						length -= 4
    194 						dst[2] = uint8(length)
    195 						dst[1] = 0
    196 						dst[0] = 5<<2 | tagCopy1
    197 						d += 3
    198 						break
    199 					}
    200 					if length < (1<<16)+(1<<8) {
    201 						length -= 1 << 8
    202 						dst[3] = uint8(length >> 8)
    203 						dst[2] = uint8(length >> 0)
    204 						dst[1] = 0
    205 						dst[0] = 6<<2 | tagCopy1
    206 						d += 4
    207 						break
    208 					}
    209 					const maxRepeat = (1 << 24) - 1
    210 					length -= 1 << 16
    211 					left := 0
    212 					if length > maxRepeat {
    213 						left = length - maxRepeat + 4
    214 						length = maxRepeat - 4
    215 					}
    216 					dst[4] = uint8(length >> 16)
    217 					dst[3] = uint8(length >> 8)
    218 					dst[2] = uint8(length >> 0)
    219 					dst[1] = 0
    220 					dst[0] = 7<<2 | tagCopy1
    221 					if left > 0 {
    222 						d += 5 + emitRepeat16(dst[5:], offset, left)
    223 						break
    224 					}
    225 					d += 5
    226 					break
    227 				}
    228 			}
    229 		} else {
    230 			if debug {
    231 				fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset)
    232 			}
    233 			if !inline {
    234 				d += emitCopy16(dst[d:], offset, ml)
    235 			} else {
    236 				length := ml
    237 				dst := dst[d:]
    238 				for len(dst) > 5 {
    239 					// Offset no more than 2 bytes.
    240 					if length > 64 {
    241 						off := 3
    242 						if offset < 2048 {
    243 							// emit 8 bytes as tagCopy1, rest as repeats.
    244 							dst[1] = uint8(offset)
    245 							dst[0] = uint8(offset>>8)<<5 | uint8(8-4)<<2 | tagCopy1
    246 							length -= 8
    247 							off = 2
    248 						} else {
    249 							// Emit a length 60 copy, encoded as 3 bytes.
    250 							// Emit remaining as repeat value (minimum 4 bytes).
    251 							dst[2] = uint8(offset >> 8)
    252 							dst[1] = uint8(offset)
    253 							dst[0] = 59<<2 | tagCopy2
    254 							length -= 60
    255 						}
    256 						// Emit remaining as repeats, at least 4 bytes remain.
    257 						d += off + emitRepeat16(dst[off:], offset, length)
    258 						break
    259 					}
    260 					if length >= 12 || offset >= 2048 {
    261 						// Emit the remaining copy, encoded as 3 bytes.
    262 						dst[2] = uint8(offset >> 8)
    263 						dst[1] = uint8(offset)
    264 						dst[0] = uint8(length-1)<<2 | tagCopy2
    265 						d += 3
    266 						break
    267 					}
    268 					// Emit the remaining copy, encoded as 2 bytes.
    269 					dst[1] = uint8(offset)
    270 					dst[0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
    271 					d += 2
    272 					break
    273 				}
    274 			}
    275 			lastOffset = offset
    276 		}
    277 		uncompressed += ml
    278 		if d > dLimit {
    279 			return nil, 0, ErrDstTooSmall
    280 		}
    281 	}
    282 
    283 	return dst[:d], uncompressed, nil
    284 }
    285 
    286 // ConvertBlockSnappy will convert an LZ4s block and append it
    287 // as a Snappy block without block length to dst.
    288 // The uncompressed size is returned as well.
    289 // dst must have capacity to contain the entire compressed block.
    290 func (l *LZ4sConverter) ConvertBlockSnappy(dst, src []byte) ([]byte, int, error) {
    291 	if len(src) == 0 {
    292 		return dst, 0, nil
    293 	}
    294 	const debug = false
    295 	const lz4MinMatch = 3
    296 
    297 	s, d := 0, len(dst)
    298 	dst = dst[:cap(dst)]
    299 	// Use assembly when possible
    300 	if !debug && hasAmd64Asm {
    301 		res, sz := cvtLZ4sBlockSnappyAsm(dst[d:], src)
    302 		if res < 0 {
    303 			const (
    304 				errCorrupt     = -1
    305 				errDstTooSmall = -2
    306 			)
    307 			switch res {
    308 			case errCorrupt:
    309 				return nil, 0, ErrCorrupt
    310 			case errDstTooSmall:
    311 				return nil, 0, ErrDstTooSmall
    312 			default:
    313 				return nil, 0, fmt.Errorf("unexpected result: %d", res)
    314 			}
    315 		}
    316 		if d+sz > len(dst) {
    317 			return nil, 0, ErrDstTooSmall
    318 		}
    319 		return dst[:d+sz], res, nil
    320 	}
    321 
    322 	dLimit := len(dst) - 10
    323 	var uncompressed int
    324 	if debug {
    325 		fmt.Printf("convert block start: len(src): %d, len(dst):%d \n", len(src), len(dst))
    326 	}
    327 
    328 	for {
    329 		if s >= len(src) {
    330 			return nil, 0, ErrCorrupt
    331 		}
    332 		// Read literal info
    333 		token := src[s]
    334 		ll := int(token >> 4)
    335 		ml := int(lz4MinMatch + (token & 0xf))
    336 
    337 		// If upper nibble is 15, literal length is extended
    338 		if token >= 0xf0 {
    339 			for {
    340 				s++
    341 				if s >= len(src) {
    342 					if debug {
    343 						fmt.Printf("error reading ll: s (%d) >= len(src) (%d)\n", s, len(src))
    344 					}
    345 					return nil, 0, ErrCorrupt
    346 				}
    347 				val := src[s]
    348 				ll += int(val)
    349 				if val != 255 {
    350 					break
    351 				}
    352 			}
    353 		}
    354 		// Skip past token
    355 		if s+ll >= len(src) {
    356 			if debug {
    357 				fmt.Printf("error literals: s+ll (%d+%d) >= len(src) (%d)\n", s, ll, len(src))
    358 			}
    359 			return nil, 0, ErrCorrupt
    360 		}
    361 		s++
    362 		if ll > 0 {
    363 			if d+ll > dLimit {
    364 				return nil, 0, ErrDstTooSmall
    365 			}
    366 			if debug {
    367 				fmt.Printf("emit %d literals\n", ll)
    368 			}
    369 			d += emitLiteralGo(dst[d:], src[s:s+ll])
    370 			s += ll
    371 			uncompressed += ll
    372 		}
    373 
    374 		// Check if we are done...
    375 		if ml == lz4MinMatch {
    376 			if s == len(src) {
    377 				break
    378 			}
    379 			// 0 bytes.
    380 			continue
    381 		}
    382 		// 2 byte offset
    383 		if s >= len(src)-2 {
    384 			if debug {
    385 				fmt.Printf("s (%d) >= len(src)-2 (%d)", s, len(src)-2)
    386 			}
    387 			return nil, 0, ErrCorrupt
    388 		}
    389 		offset := binary.LittleEndian.Uint16(src[s:])
    390 		s += 2
    391 		if offset == 0 {
    392 			if debug {
    393 				fmt.Printf("error: offset 0, ml: %d, len(src)-s: %d\n", ml, len(src)-s)
    394 			}
    395 			return nil, 0, ErrCorrupt
    396 		}
    397 		if int(offset) > uncompressed {
    398 			if debug {
    399 				fmt.Printf("error: offset (%d)> uncompressed (%d)\n", offset, uncompressed)
    400 			}
    401 			return nil, 0, ErrCorrupt
    402 		}
    403 
    404 		if ml == lz4MinMatch+15 {
    405 			for {
    406 				if s >= len(src) {
    407 					if debug {
    408 						fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
    409 					}
    410 					return nil, 0, ErrCorrupt
    411 				}
    412 				val := src[s]
    413 				s++
    414 				ml += int(val)
    415 				if val != 255 {
    416 					if s >= len(src) {
    417 						if debug {
    418 							fmt.Printf("error reading ml: s (%d) >= len(src) (%d)\n", s, len(src))
    419 						}
    420 						return nil, 0, ErrCorrupt
    421 					}
    422 					break
    423 				}
    424 			}
    425 		}
    426 		if debug {
    427 			fmt.Printf("emit copy, length: %d, offset: %d\n", ml, offset)
    428 		}
    429 		length := ml
    430 		// d += emitCopyNoRepeat(dst[d:], int(offset), ml)
    431 		for length > 0 {
    432 			if d >= dLimit {
    433 				return nil, 0, ErrDstTooSmall
    434 			}
    435 
    436 			// Offset no more than 2 bytes.
    437 			if length > 64 {
    438 				// Emit a length 64 copy, encoded as 3 bytes.
    439 				dst[d+2] = uint8(offset >> 8)
    440 				dst[d+1] = uint8(offset)
    441 				dst[d+0] = 63<<2 | tagCopy2
    442 				length -= 64
    443 				d += 3
    444 				continue
    445 			}
    446 			if length >= 12 || offset >= 2048 || length < 4 {
    447 				// Emit the remaining copy, encoded as 3 bytes.
    448 				dst[d+2] = uint8(offset >> 8)
    449 				dst[d+1] = uint8(offset)
    450 				dst[d+0] = uint8(length-1)<<2 | tagCopy2
    451 				d += 3
    452 				break
    453 			}
    454 			// Emit the remaining copy, encoded as 2 bytes.
    455 			dst[d+1] = uint8(offset)
    456 			dst[d+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
    457 			d += 2
    458 			break
    459 		}
    460 		uncompressed += ml
    461 		if d > dLimit {
    462 			return nil, 0, ErrDstTooSmall
    463 		}
    464 	}
    465 
    466 	return dst[:d], uncompressed, nil
    467 }