mirror of https://github.com/ginuerzh/gost
55 changed files with 2988 additions and 4637 deletions
@ -1,79 +0,0 @@ |
|||
[](https://godoc.org/github.com/aead/chacha20) |
|||
|
|||
## The ChaCha20 stream cipher |
|||
|
|||
ChaCha is a stream cipher family created by Daniel J. Bernstein. |
|||
The most common ChaCha cipher is ChaCha20 (20 rounds). ChaCha20 is standardized in [RFC 7539](https://tools.ietf.org/html/rfc7539 "RFC 7539"). |
|||
|
|||
This package provides implementations of three ChaCha versions: |
|||
- ChaCha20 with a 64 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination) |
|||
- ChaCha20 with a 96 bit nonce (can en/decrypt up to 2^32 * 64 bytes ~ 256 GB for one key-nonce combination) |
|||
- XChaCha20 with a 192 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination) |
|||
|
|||
Furthermore the chacha subpackage implements ChaCha20/12 and ChaCha20/8. |
|||
These versions use 12 or 8 rounds instead of 20. |
|||
But it's recommended to use ChaCha20 (with 20 rounds) - it will be fast enough for almost all purposes. |
|||
|
|||
### Installation |
|||
Install in your GOPATH: `go get -u github.com/aead/chacha20` |
|||
|
|||
### Requirements |
|||
All go versions >= 1.5.3 are supported. |
|||
Please notice, that the amd64 AVX2 asm implementation requires go1.7 or newer. |
|||
|
|||
### Performance |
|||
|
|||
#### AMD64 |
|||
Hardware: Intel i7-6500U 2.50GHz x 2 |
|||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic |
|||
Go version: 1.8.0 |
|||
``` |
|||
AVX2 |
|||
name speed cpb |
|||
ChaCha20_64-4 573MB/s ± 0% 4.16 |
|||
ChaCha20_1K-4 2.19GB/s ± 0% 1.06 |
|||
XChaCha20_64-4 261MB/s ± 0% 9.13 |
|||
XChaCha20_1K-4 1.69GB/s ± 4% 1.37 |
|||
XORKeyStream64-4 474MB/s ± 2% 5.02 |
|||
XORKeyStream1K-4 2.09GB/s ± 1% 1.11 |
|||
XChaCha20_XORKeyStream64-4 262MB/s ± 0% 9.09 |
|||
XChaCha20_XORKeyStream1K-4 1.71GB/s ± 1% 1.36 |
|||
|
|||
SSSE3 |
|||
name speed cpb |
|||
ChaCha20_64-4 583MB/s ± 0% 4.08 |
|||
ChaCha20_1K-4 1.15GB/s ± 1% 2.02 |
|||
XChaCha20_64-4 267MB/s ± 0% 8.92 |
|||
XChaCha20_1K-4 984MB/s ± 5% 2.42 |
|||
XORKeyStream64-4 492MB/s ± 1% 4.84 |
|||
XORKeyStream1K-4 1.10GB/s ± 5% 2.11 |
|||
XChaCha20_XORKeyStream64-4 266MB/s ± 0% 8.96 |
|||
XChaCha20_XORKeyStream1K-4 1.00GB/s ± 2% 2.32 |
|||
``` |
|||
#### 386 |
|||
Hardware: Intel i7-6500U 2.50GHz x 2 |
|||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic |
|||
Go version: 1.8.0 |
|||
``` |
|||
SSSE3 |
|||
name speed cpb |
|||
ChaCha20_64-4 570MB/s ± 0% 4.18 |
|||
ChaCha20_1K-4 650MB/s ± 0% 3.66 |
|||
XChaCha20_64-4 223MB/s ± 0% 10.69 |
|||
XChaCha20_1K-4 584MB/s ± 1% 4.08 |
|||
XORKeyStream64-4 392MB/s ± 1% 6.08 |
|||
XORKeyStream1K-4 629MB/s ± 1% 3.79 |
|||
XChaCha20_XORKeyStream64-4 222MB/s ± 0% 10.73 |
|||
XChaCha20_XORKeyStream1K-4 585MB/s ± 0% 4.07 |
|||
|
|||
SSE2 |
|||
name speed cpb |
|||
ChaCha20_64-4 509MB/s ± 0% 4.68 |
|||
ChaCha20_1K-4 553MB/s ± 2% 4.31 |
|||
XChaCha20_64-4 201MB/s ± 0% 11.86 |
|||
XChaCha20_1K-4 498MB/s ± 4% 4.78 |
|||
XORKeyStream64-4 359MB/s ± 1% 6.64 |
|||
XORKeyStream1K-4 545MB/s ± 0% 4.37 |
|||
XChaCha20_XORKeyStream64-4 201MB/s ± 1% 11.86 |
|||
XChaCha20_XORKeyStream1K-4 507MB/s ± 0% 4.70 |
|||
``` |
|||
@ -1,176 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// Package chacha implements some low-level functions of the
|
|||
// ChaCha cipher family.
|
|||
package chacha // import "github.com/aead/chacha20/chacha"
|
|||
|
|||
import ( |
|||
"encoding/binary" |
|||
"errors" |
|||
) |
|||
|
|||
const ( |
|||
// NonceSize is the size of the ChaCha20 nonce in bytes.
|
|||
NonceSize = 8 |
|||
|
|||
// INonceSize is the size of the IETF-ChaCha20 nonce in bytes.
|
|||
INonceSize = 12 |
|||
|
|||
// XNonceSize is the size of the XChaCha20 nonce in bytes.
|
|||
XNonceSize = 24 |
|||
|
|||
// KeySize is the size of the key in bytes.
|
|||
KeySize = 32 |
|||
) |
|||
|
|||
var ( |
|||
useSSE2 bool |
|||
useSSSE3 bool |
|||
useAVX2 bool |
|||
) |
|||
|
|||
var ( |
|||
errKeySize = errors.New("chacha20/chacha: bad key length") |
|||
errInvalidNonce = errors.New("chacha20/chacha: bad nonce length") |
|||
) |
|||
|
|||
func setup(state *[64]byte, nonce, key []byte) (err error) { |
|||
if len(key) != KeySize { |
|||
err = errKeySize |
|||
return |
|||
} |
|||
var Nonce [16]byte |
|||
switch len(nonce) { |
|||
case NonceSize: |
|||
copy(Nonce[8:], nonce) |
|||
initialize(state, key, &Nonce) |
|||
case INonceSize: |
|||
copy(Nonce[4:], nonce) |
|||
initialize(state, key, &Nonce) |
|||
case XNonceSize: |
|||
var tmpKey [32]byte |
|||
var hNonce [16]byte |
|||
|
|||
copy(hNonce[:], nonce[:16]) |
|||
copy(tmpKey[:], key) |
|||
hChaCha20(&tmpKey, &hNonce, &tmpKey) |
|||
copy(Nonce[8:], nonce[16:]) |
|||
initialize(state, tmpKey[:], &Nonce) |
|||
|
|||
// BUG(aead): A "good" compiler will remove this (optimizations)
|
|||
// But using the provided key instead of tmpKey,
|
|||
// will change the key (-> probably confuses users)
|
|||
for i := range tmpKey { |
|||
tmpKey[i] = 0 |
|||
} |
|||
default: |
|||
err = errInvalidNonce |
|||
} |
|||
return |
|||
} |
|||
|
|||
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// The rounds argument specifies the number of rounds performed for keystream
|
|||
// generation - valid values are 8, 12 or 20. The src and dst may be the same slice
|
|||
// but otherwise should not overlap. If len(dst) < len(src) this function panics.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
|
|||
func XORKeyStream(dst, src, nonce, key []byte, rounds int) { |
|||
if rounds != 20 && rounds != 12 && rounds != 8 { |
|||
panic("chacha20/chacha: bad number of rounds") |
|||
} |
|||
if len(dst) < len(src) { |
|||
panic("chacha20/chacha: dst buffer is to small") |
|||
} |
|||
if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) { |
|||
panic("chacha20/chacha: src is too large") |
|||
} |
|||
|
|||
var block, state [64]byte |
|||
if err := setup(&state, nonce, key); err != nil { |
|||
panic(err) |
|||
} |
|||
xorKeyStream(dst, src, &block, &state, rounds) |
|||
} |
|||
|
|||
// Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r.
|
|||
type Cipher struct { |
|||
state, block [64]byte |
|||
off int |
|||
rounds int // 20 for ChaCha20
|
|||
noncesize int |
|||
} |
|||
|
|||
// NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r
|
|||
// (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
|
|||
func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) { |
|||
if rounds != 20 && rounds != 12 && rounds != 8 { |
|||
panic("chacha20/chacha: bad number of rounds") |
|||
} |
|||
|
|||
c := new(Cipher) |
|||
if err := setup(&(c.state), nonce, key); err != nil { |
|||
return nil, err |
|||
} |
|||
c.rounds = rounds |
|||
|
|||
if len(nonce) == INonceSize { |
|||
c.noncesize = INonceSize |
|||
} else { |
|||
c.noncesize = NonceSize |
|||
} |
|||
|
|||
return c, nil |
|||
} |
|||
|
|||
// XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice
|
|||
// but otherwise should not overlap. If len(dst) < len(src) the function panics.
|
|||
func (c *Cipher) XORKeyStream(dst, src []byte) { |
|||
if len(dst) < len(src) { |
|||
panic("chacha20/chacha: dst buffer is to small") |
|||
} |
|||
|
|||
if c.off > 0 { |
|||
n := len(c.block[c.off:]) |
|||
if len(src) <= n { |
|||
for i, v := range src { |
|||
dst[i] = v ^ c.block[c.off] |
|||
c.off++ |
|||
} |
|||
if c.off == 64 { |
|||
c.off = 0 |
|||
} |
|||
return |
|||
} |
|||
|
|||
for i, v := range c.block[c.off:] { |
|||
dst[i] = src[i] ^ v |
|||
} |
|||
src = src[n:] |
|||
dst = dst[n:] |
|||
c.off = 0 |
|||
} |
|||
|
|||
c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds) |
|||
} |
|||
|
|||
// SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher.
|
|||
// This function always skips the unused keystream of the current 64 byte block.
|
|||
func (c *Cipher) SetCounter(ctr uint64) { |
|||
if c.noncesize == INonceSize { |
|||
binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr)) |
|||
} else { |
|||
binary.LittleEndian.PutUint64(c.state[48:], ctr) |
|||
} |
|||
c.off = 0 |
|||
} |
|||
@ -1,542 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build go1.7,amd64,!gccgo,!appengine,!nacl |
|||
|
|||
#include "textflag.h" |
|||
|
|||
DATA ·sigma_AVX<>+0x00(SB)/4, $0x61707865 |
|||
DATA ·sigma_AVX<>+0x04(SB)/4, $0x3320646e |
|||
DATA ·sigma_AVX<>+0x08(SB)/4, $0x79622d32 |
|||
DATA ·sigma_AVX<>+0x0C(SB)/4, $0x6b206574 |
|||
GLOBL ·sigma_AVX<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·one_AVX<>+0x00(SB)/8, $1 |
|||
DATA ·one_AVX<>+0x08(SB)/8, $0 |
|||
GLOBL ·one_AVX<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·one_AVX2<>+0x00(SB)/8, $0 |
|||
DATA ·one_AVX2<>+0x08(SB)/8, $0 |
|||
DATA ·one_AVX2<>+0x10(SB)/8, $1 |
|||
DATA ·one_AVX2<>+0x18(SB)/8, $0 |
|||
GLOBL ·one_AVX2<>(SB), (NOPTR+RODATA), $32 |
|||
|
|||
DATA ·two_AVX2<>+0x00(SB)/8, $2 |
|||
DATA ·two_AVX2<>+0x08(SB)/8, $0 |
|||
DATA ·two_AVX2<>+0x10(SB)/8, $2 |
|||
DATA ·two_AVX2<>+0x18(SB)/8, $0 |
|||
GLOBL ·two_AVX2<>(SB), (NOPTR+RODATA), $32 |
|||
|
|||
DATA ·rol16_AVX2<>+0x00(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16_AVX2<>+0x08(SB)/8, $0x0D0C0F0E09080B0A |
|||
DATA ·rol16_AVX2<>+0x10(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16_AVX2<>+0x18(SB)/8, $0x0D0C0F0E09080B0A |
|||
GLOBL ·rol16_AVX2<>(SB), (NOPTR+RODATA), $32 |
|||
|
|||
DATA ·rol8_AVX2<>+0x00(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8_AVX2<>+0x08(SB)/8, $0x0E0D0C0F0A09080B |
|||
DATA ·rol8_AVX2<>+0x10(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8_AVX2<>+0x18(SB)/8, $0x0E0D0C0F0A09080B |
|||
GLOBL ·rol8_AVX2<>(SB), (NOPTR+RODATA), $32 |
|||
|
|||
#define ROTL(n, t, v) \ |
|||
VPSLLD $n, v, t; \ |
|||
VPSRLD $(32-n), v, v; \ |
|||
VPXOR v, t, v |
|||
|
|||
#define CHACHA_QROUND(v0, v1, v2, v3, t, c16, c8) \ |
|||
VPADDD v0, v1, v0; \ |
|||
VPXOR v3, v0, v3; \ |
|||
VPSHUFB c16, v3, v3; \ |
|||
VPADDD v2, v3, v2; \ |
|||
VPXOR v1, v2, v1; \ |
|||
ROTL(12, t, v1); \ |
|||
VPADDD v0, v1, v0; \ |
|||
VPXOR v3, v0, v3; \ |
|||
VPSHUFB c8, v3, v3; \ |
|||
VPADDD v2, v3, v2; \ |
|||
VPXOR v1, v2, v1; \ |
|||
ROTL(7, t, v1) |
|||
|
|||
#define CHACHA_SHUFFLE(v1, v2, v3) \ |
|||
VPSHUFD $0x39, v1, v1; \ |
|||
VPSHUFD $0x4E, v2, v2; \ |
|||
VPSHUFD $-109, v3, v3 |
|||
|
|||
#define XOR_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \ |
|||
VMOVDQU (0+off)(src), t0; \ |
|||
VPERM2I128 $32, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (0+off)(dst); \ |
|||
VMOVDQU (32+off)(src), t0; \ |
|||
VPERM2I128 $32, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (32+off)(dst); \ |
|||
VMOVDQU (64+off)(src), t0; \ |
|||
VPERM2I128 $49, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (64+off)(dst); \ |
|||
VMOVDQU (96+off)(src), t0; \ |
|||
VPERM2I128 $49, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (96+off)(dst) |
|||
|
|||
#define XOR_UPPER_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \ |
|||
VMOVDQU (0+off)(src), t0; \ |
|||
VPERM2I128 $32, v1, v0, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (0+off)(dst); \ |
|||
VMOVDQU (32+off)(src), t0; \ |
|||
VPERM2I128 $32, v3, v2, t1; \ |
|||
VPXOR t0, t1, t0; \ |
|||
VMOVDQU t0, (32+off)(dst); \ |
|||
|
|||
#define EXTRACT_LOWER(dst, v0, v1, v2, v3, t0) \ |
|||
VPERM2I128 $49, v1, v0, t0; \ |
|||
VMOVDQU t0, 0(dst); \ |
|||
VPERM2I128 $49, v3, v2, t0; \ |
|||
VMOVDQU t0, 32(dst) |
|||
|
|||
#define XOR_AVX(dst, src, off, v0, v1, v2, v3, t0) \ |
|||
VPXOR 0+off(src), v0, t0; \ |
|||
VMOVDQU t0, 0+off(dst); \ |
|||
VPXOR 16+off(src), v1, t0; \ |
|||
VMOVDQU t0, 16+off(dst); \ |
|||
VPXOR 32+off(src), v2, t0; \ |
|||
VMOVDQU t0, 32+off(dst); \ |
|||
VPXOR 48+off(src), v3, t0; \ |
|||
VMOVDQU t0, 48+off(dst) |
|||
|
|||
#define TWO 0(SP) |
|||
#define C16 32(SP) |
|||
#define C8 64(SP) |
|||
#define STATE_0 96(SP) |
|||
#define STATE_1 128(SP) |
|||
#define STATE_2 160(SP) |
|||
#define STATE_3 192(SP) |
|||
#define TMP_0 224(SP) |
|||
#define TMP_1 256(SP) |
|||
|
|||
// func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamAVX2(SB), 4, $320-80 |
|||
MOVQ dst_base+0(FP), DI |
|||
MOVQ src_base+24(FP), SI |
|||
MOVQ src_len+32(FP), CX |
|||
MOVQ block+48(FP), BX |
|||
MOVQ state+56(FP), AX |
|||
MOVQ rounds+64(FP), DX |
|||
|
|||
MOVQ SP, R8 |
|||
ADDQ $32, SP |
|||
ANDQ $-32, SP |
|||
|
|||
VMOVDQU 0(AX), Y2 |
|||
VMOVDQU 32(AX), Y3 |
|||
VPERM2I128 $0x22, Y2, Y0, Y0 |
|||
VPERM2I128 $0x33, Y2, Y1, Y1 |
|||
VPERM2I128 $0x22, Y3, Y2, Y2 |
|||
VPERM2I128 $0x33, Y3, Y3, Y3 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
VMOVDQU ·one_AVX2<>(SB), Y4 |
|||
VPADDD Y4, Y3, Y3 |
|||
|
|||
VMOVDQA Y0, STATE_0 |
|||
VMOVDQA Y1, STATE_1 |
|||
VMOVDQA Y2, STATE_2 |
|||
VMOVDQA Y3, STATE_3 |
|||
|
|||
VMOVDQU ·rol16_AVX2<>(SB), Y4 |
|||
VMOVDQU ·rol8_AVX2<>(SB), Y5 |
|||
VMOVDQU ·two_AVX2<>(SB), Y6 |
|||
VMOVDQA Y4, Y14 |
|||
VMOVDQA Y5, Y15 |
|||
VMOVDQA Y4, C16 |
|||
VMOVDQA Y5, C8 |
|||
VMOVDQA Y6, TWO |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $192 |
|||
JBE between_64_and_192 |
|||
CMPQ CX, $320 |
|||
JBE between_192_and_320 |
|||
CMPQ CX, $448 |
|||
JBE between_320_and_448 |
|||
|
|||
at_least_512: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VPADDQ TWO, Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y7, Y11 |
|||
VMOVDQA Y0, Y12 |
|||
VMOVDQA Y1, Y13 |
|||
VMOVDQA Y2, Y14 |
|||
VPADDQ TWO, Y11, Y15 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_512: |
|||
VMOVDQA Y8, TMP_0 |
|||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y8, C16, C8) |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y8, C16, C8) |
|||
VMOVDQA TMP_0, Y8 |
|||
VMOVDQA Y0, TMP_0 |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y0, C16, C8) |
|||
CHACHA_QROUND(Y12, Y13, Y14, Y15, Y0, C16, C8) |
|||
CHACHA_SHUFFLE(Y1, Y2, Y3) |
|||
CHACHA_SHUFFLE(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE(Y9, Y10, Y11) |
|||
CHACHA_SHUFFLE(Y13, Y14, Y15) |
|||
|
|||
CHACHA_QROUND(Y12, Y13, Y14, Y15, Y0, C16, C8) |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y0, C16, C8) |
|||
VMOVDQA TMP_0, Y0 |
|||
VMOVDQA Y8, TMP_0 |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y8, C16, C8) |
|||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y8, C16, C8) |
|||
VMOVDQA TMP_0, Y8 |
|||
CHACHA_SHUFFLE(Y3, Y2, Y1) |
|||
CHACHA_SHUFFLE(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE(Y11, Y10, Y9) |
|||
CHACHA_SHUFFLE(Y15, Y14, Y13) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_512 |
|||
|
|||
VMOVDQA Y12, TMP_0 |
|||
VMOVDQA Y13, TMP_1 |
|||
VPADDD STATE_0, Y0, Y0 |
|||
VPADDD STATE_1, Y1, Y1 |
|||
VPADDD STATE_2, Y2, Y2 |
|||
VPADDD STATE_3, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13) |
|||
VMOVDQA STATE_0, Y0 |
|||
VMOVDQA STATE_1, Y1 |
|||
VMOVDQA STATE_2, Y2 |
|||
VMOVDQA STATE_3, Y3 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD TMP_0, Y0, Y12 |
|||
VPADDD TMP_1, Y1, Y13 |
|||
VPADDD Y2, Y14, Y14 |
|||
VPADDD Y3, Y15, Y15 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $512 |
|||
JB less_than_512 |
|||
|
|||
XOR_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5) |
|||
VMOVDQA Y3, STATE_3 |
|||
ADDQ $512, SI |
|||
ADDQ $512, DI |
|||
SUBQ $512, CX |
|||
CMPQ CX, $448 |
|||
JA at_least_512 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
VMOVDQA C16, Y14 |
|||
VMOVDQA C8, Y15 |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $192 |
|||
JBE between_64_and_192 |
|||
CMPQ CX, $320 |
|||
JBE between_192_and_320 |
|||
JMP between_320_and_448 |
|||
|
|||
less_than_512: |
|||
XOR_UPPER_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5) |
|||
EXTRACT_LOWER(BX, Y12, Y13, Y14, Y15, Y4) |
|||
ADDQ $448, SI |
|||
ADDQ $448, DI |
|||
SUBQ $448, CX |
|||
JMP finalize |
|||
|
|||
between_320_and_448: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VPADDQ TWO, Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y7, Y11 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_384: |
|||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y1, Y2, Y3) |
|||
CHACHA_SHUFFLE(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE(Y9, Y10, Y11) |
|||
CHACHA_QROUND(Y0, Y1, Y2, Y3, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y3, Y2, Y1) |
|||
CHACHA_SHUFFLE(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE(Y11, Y10, Y9) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_384 |
|||
|
|||
VPADDD STATE_0, Y0, Y0 |
|||
VPADDD STATE_1, Y1, Y1 |
|||
VPADDD STATE_2, Y2, Y2 |
|||
VPADDD STATE_3, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13) |
|||
VMOVDQA STATE_0, Y0 |
|||
VMOVDQA STATE_1, Y1 |
|||
VMOVDQA STATE_2, Y2 |
|||
VMOVDQA STATE_3, Y3 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $384 |
|||
JB less_than_384 |
|||
|
|||
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
SUBQ $384, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $384, SI |
|||
ADDQ $384, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_384: |
|||
XOR_UPPER_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12) |
|||
ADDQ $320, SI |
|||
ADDQ $320, DI |
|||
SUBQ $320, CX |
|||
JMP finalize |
|||
|
|||
between_192_and_320: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VMOVDQA Y3, Y7 |
|||
VMOVDQA Y0, Y8 |
|||
VMOVDQA Y1, Y9 |
|||
VMOVDQA Y2, Y10 |
|||
VPADDQ TWO, Y3, Y11 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_256: |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y5, Y6, Y7) |
|||
CHACHA_SHUFFLE(Y9, Y10, Y11) |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_QROUND(Y8, Y9, Y10, Y11, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y7, Y6, Y5) |
|||
CHACHA_SHUFFLE(Y11, Y10, Y9) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_256 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
VPADDQ TWO, Y3, Y3 |
|||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
VPADDD Y0, Y8, Y8 |
|||
VPADDD Y1, Y9, Y9 |
|||
VPADDD Y2, Y10, Y10 |
|||
VPADDD Y3, Y11, Y11 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $256 |
|||
JB less_than_256 |
|||
|
|||
XOR_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
SUBQ $256, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $256, SI |
|||
ADDQ $256, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_256: |
|||
XOR_UPPER_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12) |
|||
ADDQ $192, SI |
|||
ADDQ $192, DI |
|||
SUBQ $192, CX |
|||
JMP finalize |
|||
|
|||
between_64_and_192: |
|||
VMOVDQA Y0, Y4 |
|||
VMOVDQA Y1, Y5 |
|||
VMOVDQA Y2, Y6 |
|||
VMOVDQA Y3, Y7 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_128: |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y5, Y6, Y7) |
|||
CHACHA_QROUND(Y4, Y5, Y6, Y7, Y13, Y14, Y15) |
|||
CHACHA_SHUFFLE(Y7, Y6, Y5) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_128 |
|||
|
|||
VPADDD Y0, Y4, Y4 |
|||
VPADDD Y1, Y5, Y5 |
|||
VPADDD Y2, Y6, Y6 |
|||
VPADDD Y3, Y7, Y7 |
|||
VPADDQ TWO, Y3, Y3 |
|||
|
|||
CMPQ CX, $128 |
|||
JB less_than_128 |
|||
|
|||
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
SUBQ $128, CX |
|||
TESTQ CX, CX |
|||
JE done |
|||
|
|||
ADDQ $128, SI |
|||
ADDQ $128, DI |
|||
JMP between_0_and_64 |
|||
|
|||
less_than_128: |
|||
XOR_UPPER_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13) |
|||
EXTRACT_LOWER(BX, Y4, Y5, Y6, Y7, Y13) |
|||
ADDQ $64, SI |
|||
ADDQ $64, DI |
|||
SUBQ $64, CX |
|||
JMP finalize |
|||
|
|||
between_0_and_64: |
|||
VMOVDQA X0, X4 |
|||
VMOVDQA X1, X5 |
|||
VMOVDQA X2, X6 |
|||
VMOVDQA X3, X7 |
|||
|
|||
MOVQ DX, R9 |
|||
|
|||
chacha_loop_64: |
|||
CHACHA_QROUND(X4, X5, X6, X7, X13, X14, X15) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_QROUND(X4, X5, X6, X7, X13, X14, X15) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
SUBQ $2, R9 |
|||
JA chacha_loop_64 |
|||
|
|||
VPADDD X0, X4, X4 |
|||
VPADDD X1, X5, X5 |
|||
VPADDD X2, X6, X6 |
|||
VPADDD X3, X7, X7 |
|||
VMOVDQU ·one_AVX<>(SB), X0 |
|||
VPADDQ X0, X3, X3 |
|||
|
|||
CMPQ CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR_AVX(DI, SI, 0, X4, X5, X6, X7, X13) |
|||
SUBQ $64, CX |
|||
JMP done |
|||
|
|||
less_than_64: |
|||
VMOVDQU X4, 0(BX) |
|||
VMOVDQU X5, 16(BX) |
|||
VMOVDQU X6, 32(BX) |
|||
VMOVDQU X7, 48(BX) |
|||
|
|||
finalize: |
|||
XORQ R11, R11 |
|||
XORQ R12, R12 |
|||
MOVQ CX, BP |
|||
|
|||
xor_loop: |
|||
MOVB 0(SI), R11 |
|||
MOVB 0(BX), R12 |
|||
XORQ R11, R12 |
|||
MOVB R12, 0(DI) |
|||
INCQ SI |
|||
INCQ BX |
|||
INCQ DI |
|||
DECQ BP |
|||
JA xor_loop |
|||
|
|||
done: |
|||
VMOVDQU X3, 48(AX) |
|||
VZEROUPPER |
|||
MOVQ R8, SP |
|||
MOVQ CX, ret+72(FP) |
|||
RET |
|||
|
|||
// func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20AVX(SB), 4, $0-24 |
|||
MOVQ out+0(FP), DI |
|||
MOVQ nonce+8(FP), AX |
|||
MOVQ key+16(FP), BX |
|||
|
|||
VMOVDQU ·sigma_AVX<>(SB), X0 |
|||
VMOVDQU 0(BX), X1 |
|||
VMOVDQU 16(BX), X2 |
|||
VMOVDQU 0(AX), X3 |
|||
VMOVDQU ·rol16_AVX2<>(SB), X5 |
|||
VMOVDQU ·rol8_AVX2<>(SB), X6 |
|||
|
|||
MOVQ $20, CX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_QROUND(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
SUBQ $2, CX |
|||
JNZ chacha_loop |
|||
|
|||
VMOVDQU X0, 0(DI) |
|||
VMOVDQU X3, 16(DI) |
|||
VZEROUPPER |
|||
RET |
|||
|
|||
// func supportsAVX2() bool |
|||
TEXT ·supportsAVX2(SB), 4, $0-1 |
|||
MOVQ runtime·support_avx(SB), AX |
|||
MOVQ runtime·support_avx2(SB), BX |
|||
ANDQ AX, BX |
|||
MOVB BX, ret+0(FP) |
|||
RET |
|||
@ -1,67 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl
|
|||
|
|||
package chacha |
|||
|
|||
import "encoding/binary" |
|||
|
|||
func init() { |
|||
useSSE2 = supportsSSE2() |
|||
useSSSE3 = supportsSSSE3() |
|||
useAVX2 = false |
|||
} |
|||
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) { |
|||
binary.LittleEndian.PutUint32(state[0:], sigma[0]) |
|||
binary.LittleEndian.PutUint32(state[4:], sigma[1]) |
|||
binary.LittleEndian.PutUint32(state[8:], sigma[2]) |
|||
binary.LittleEndian.PutUint32(state[12:], sigma[3]) |
|||
copy(state[16:], key[:]) |
|||
copy(state[48:], nonce[:]) |
|||
} |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func supportsSSE2() bool |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func supportsSSSE3() bool |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chacha_386.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
if useSSSE3 { |
|||
hChaCha20SSSE3(out, nonce, key) |
|||
} else if useSSE2 { |
|||
hChaCha20SSE2(out, nonce, key) |
|||
} else { |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
if useSSSE3 { |
|||
return xorKeyStreamSSSE3(dst, src, block, state, rounds) |
|||
} else if useSSE2 { |
|||
return xorKeyStreamSSE2(dst, src, block, state, rounds) |
|||
} |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
@ -1,311 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build 386,!gccgo,!appengine,!nacl |
|||
|
|||
#include "textflag.h" |
|||
|
|||
DATA ·sigma<>+0x00(SB)/4, $0x61707865 |
|||
DATA ·sigma<>+0x04(SB)/4, $0x3320646e |
|||
DATA ·sigma<>+0x08(SB)/4, $0x79622d32 |
|||
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574 |
|||
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·one<>+0x00(SB)/8, $1 |
|||
DATA ·one<>+0x08(SB)/8, $0 |
|||
GLOBL ·one<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A |
|||
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B |
|||
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
#define ROTL_SSE2(n, t, v) \ |
|||
MOVO v, t; \ |
|||
PSLLL $n, t; \ |
|||
PSRLL $(32-n), v; \ |
|||
PXOR t, v |
|||
|
|||
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t0) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE2(16, t0, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(12, t0, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE2(8, t0, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(7, t0, v1) |
|||
|
|||
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t0, r16, r8) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r16, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(12, t0, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r8, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(7, t0, v1) |
|||
|
|||
#define CHACHA_SHUFFLE(v1, v2, v3) \ |
|||
PSHUFL $0x39, v1, v1; \ |
|||
PSHUFL $0x4E, v2, v2; \ |
|||
PSHUFL $0x93, v3, v3 |
|||
|
|||
#define XOR(dst, src, off, v0, v1, v2, v3, t0) \ |
|||
MOVOU 0+off(src), t0; \ |
|||
PXOR v0, t0; \ |
|||
MOVOU t0, 0+off(dst); \ |
|||
MOVOU 16+off(src), t0; \ |
|||
PXOR v1, t0; \ |
|||
MOVOU t0, 16+off(dst); \ |
|||
MOVOU 32+off(src), t0; \ |
|||
PXOR v2, t0; \ |
|||
MOVOU t0, 32+off(dst); \ |
|||
MOVOU 48+off(src), t0; \ |
|||
PXOR v3, t0; \ |
|||
MOVOU t0, 48+off(dst) |
|||
|
|||
#define FINALIZE(dst, src, block, len, t0, t1) \ |
|||
XORL t0, t0; \ |
|||
XORL t1, t1; \ |
|||
finalize: \ |
|||
MOVB 0(src), t0; \ |
|||
MOVB 0(block), t1; \ |
|||
XORL t0, t1; \ |
|||
MOVB t1, 0(dst); \ |
|||
INCL src; \ |
|||
INCL block; \ |
|||
INCL dst; \ |
|||
DECL len; \ |
|||
JA finalize \ |
|||
|
|||
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamSSE2(SB), 4, $0-40 |
|||
MOVL dst_base+0(FP), DI |
|||
MOVL src_base+12(FP), SI |
|||
MOVL src_len+16(FP), CX |
|||
MOVL state+28(FP), AX |
|||
MOVL rounds+32(FP), DX |
|||
|
|||
MOVOU 0(AX), X0 |
|||
MOVOU 16(AX), X1 |
|||
MOVOU 32(AX), X2 |
|||
MOVOU 48(AX), X3 |
|||
|
|||
TESTL CX, CX |
|||
JZ done |
|||
|
|||
at_least_64: |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
|
|||
MOVL DX, BX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
SUBL $2, BX |
|||
JA chacha_loop |
|||
|
|||
MOVOU 0(AX), X0 |
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
MOVOU ·one<>(SB), X0 |
|||
PADDQ X0, X3 |
|||
|
|||
CMPL CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 0, X4, X5, X6, X7, X0) |
|||
MOVOU 0(AX), X0 |
|||
ADDL $64, SI |
|||
ADDL $64, DI |
|||
SUBL $64, CX |
|||
JNZ at_least_64 |
|||
|
|||
less_than_64: |
|||
MOVL CX, BP |
|||
TESTL BP, BP |
|||
JZ done |
|||
|
|||
MOVL block+24(FP), BX |
|||
MOVOU X4, 0(BX) |
|||
MOVOU X5, 16(BX) |
|||
MOVOU X6, 32(BX) |
|||
MOVOU X7, 48(BX) |
|||
FINALIZE(DI, SI, BX, BP, AX, DX) |
|||
|
|||
done: |
|||
MOVL state+28(FP), AX |
|||
MOVOU X3, 48(AX) |
|||
MOVL CX, ret+36(FP) |
|||
RET |
|||
|
|||
// func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamSSSE3(SB), 4, $64-40 |
|||
MOVL dst_base+0(FP), DI |
|||
MOVL src_base+12(FP), SI |
|||
MOVL src_len+16(FP), CX |
|||
MOVL state+28(FP), AX |
|||
MOVL rounds+32(FP), DX |
|||
|
|||
MOVOU 48(AX), X3 |
|||
TESTL CX, CX |
|||
JZ done |
|||
|
|||
MOVL SP, BP |
|||
ADDL $16, SP |
|||
ANDL $-16, SP |
|||
|
|||
MOVOU ·one<>(SB), X0 |
|||
MOVOU 16(AX), X1 |
|||
MOVOU 32(AX), X2 |
|||
MOVO X0, 0(SP) |
|||
MOVO X1, 16(SP) |
|||
MOVO X2, 32(SP) |
|||
|
|||
MOVOU 0(AX), X0 |
|||
MOVOU ·rol16<>(SB), X1 |
|||
MOVOU ·rol8<>(SB), X2 |
|||
|
|||
at_least_64: |
|||
MOVO X0, X4 |
|||
MOVO 16(SP), X5 |
|||
MOVO 32(SP), X6 |
|||
MOVO X3, X7 |
|||
|
|||
MOVL DX, BX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X0, X1, X2) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X0, X1, X2) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
SUBL $2, BX |
|||
JA chacha_loop |
|||
|
|||
MOVOU 0(AX), X0 |
|||
PADDL X0, X4 |
|||
PADDL 16(SP), X5 |
|||
PADDL 32(SP), X6 |
|||
PADDL X3, X7 |
|||
PADDQ 0(SP), X3 |
|||
|
|||
CMPL CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 0, X4, X5, X6, X7, X0) |
|||
MOVOU 0(AX), X0 |
|||
ADDL $64, SI |
|||
ADDL $64, DI |
|||
SUBL $64, CX |
|||
JNZ at_least_64 |
|||
|
|||
less_than_64: |
|||
MOVL BP, SP |
|||
MOVL CX, BP |
|||
TESTL BP, BP |
|||
JE done |
|||
|
|||
MOVL block+24(FP), BX |
|||
MOVOU X4, 0(BX) |
|||
MOVOU X5, 16(BX) |
|||
MOVOU X6, 32(BX) |
|||
MOVOU X7, 48(BX) |
|||
FINALIZE(DI, SI, BX, BP, AX, DX) |
|||
|
|||
done: |
|||
MOVL state+28(FP), AX |
|||
MOVOU X3, 48(AX) |
|||
MOVL CX, ret+36(FP) |
|||
RET |
|||
|
|||
// func supportsSSE2() bool |
|||
TEXT ·supportsSSE2(SB), NOSPLIT, $0-1 |
|||
XORL AX, AX |
|||
INCL AX |
|||
CPUID |
|||
SHRL $26, DX |
|||
ANDL $1, DX |
|||
MOVB DX, ret+0(FP) |
|||
RET |
|||
|
|||
// func supportsSSSE3() bool |
|||
TEXT ·supportsSSSE3(SB), NOSPLIT, $0-1 |
|||
XORL AX, AX |
|||
INCL AX |
|||
CPUID |
|||
SHRL $9, CX |
|||
ANDL $1, CX |
|||
MOVB CX, ret+0(FP) |
|||
RET |
|||
|
|||
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSE2(SB), 4, $0-12 |
|||
MOVL out+0(FP), DI |
|||
MOVL nonce+4(FP), AX |
|||
MOVL key+8(FP), BX |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0(BX), X1 |
|||
MOVOU 16(BX), X2 |
|||
MOVOU 0(AX), X3 |
|||
|
|||
MOVL $20, CX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
SUBL $2, CX |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0(DI) |
|||
MOVOU X3, 16(DI) |
|||
RET |
|||
|
|||
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSSE3(SB), 4, $0-12 |
|||
MOVL out+0(FP), DI |
|||
MOVL nonce+4(FP), AX |
|||
MOVL key+8(FP), BX |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0(BX), X1 |
|||
MOVOU 16(BX), X2 |
|||
MOVOU 0(AX), X3 |
|||
MOVOU ·rol16<>(SB), X5 |
|||
MOVOU ·rol8<>(SB), X6 |
|||
|
|||
MOVL $20, CX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
SUBL $2, CX |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0(DI) |
|||
MOVOU X3, 16(DI) |
|||
RET |
|||
@ -1,788 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved. |
|||
// Use of this source code is governed by a license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
// +build amd64,!gccgo,!appengine,!nacl |
|||
|
|||
#include "textflag.h" |
|||
|
|||
DATA ·sigma<>+0x00(SB)/4, $0x61707865 |
|||
DATA ·sigma<>+0x04(SB)/4, $0x3320646e |
|||
DATA ·sigma<>+0x08(SB)/4, $0x79622d32 |
|||
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574 |
|||
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·one<>+0x00(SB)/8, $1 |
|||
DATA ·one<>+0x08(SB)/8, $0 |
|||
GLOBL ·one<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302 |
|||
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A |
|||
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003 |
|||
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B |
|||
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16 |
|||
|
|||
#define ROTL_SSE2(n, t, v) \ |
|||
MOVO v, t; \ |
|||
PSLLL $n, t; \ |
|||
PSRLL $(32-n), v; \ |
|||
PXOR t, v |
|||
|
|||
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t0) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE2(16, t0, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(12, t0, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
ROTL_SSE2(8, t0, v3); \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(7, t0, v1) |
|||
|
|||
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t0, r16, r8) \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r16, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(12, t0, v1); \ |
|||
PADDL v1, v0; \ |
|||
PXOR v0, v3; \ |
|||
PSHUFB r8, v3; \ |
|||
PADDL v3, v2; \ |
|||
PXOR v2, v1; \ |
|||
ROTL_SSE2(7, t0, v1) |
|||
|
|||
#define CHACHA_SHUFFLE(v1, v2, v3) \ |
|||
PSHUFL $0x39, v1, v1; \ |
|||
PSHUFL $0x4E, v2, v2; \ |
|||
PSHUFL $0x93, v3, v3 |
|||
|
|||
#define XOR(dst, src, off, v0, v1, v2, v3, t0) \ |
|||
MOVOU 0+off(src), t0; \ |
|||
PXOR v0, t0; \ |
|||
MOVOU t0, 0+off(dst); \ |
|||
MOVOU 16+off(src), t0; \ |
|||
PXOR v1, t0; \ |
|||
MOVOU t0, 16+off(dst); \ |
|||
MOVOU 32+off(src), t0; \ |
|||
PXOR v2, t0; \ |
|||
MOVOU t0, 32+off(dst); \ |
|||
MOVOU 48+off(src), t0; \ |
|||
PXOR v3, t0; \ |
|||
MOVOU t0, 48+off(dst) |
|||
|
|||
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamSSE2(SB), 4, $112-80 |
|||
MOVQ dst_base+0(FP), DI |
|||
MOVQ src_base+24(FP), SI |
|||
MOVQ src_len+32(FP), CX |
|||
MOVQ block+48(FP), BX |
|||
MOVQ state+56(FP), AX |
|||
MOVQ rounds+64(FP), DX |
|||
|
|||
MOVQ SP, R9 |
|||
ADDQ $16, SP |
|||
ANDQ $-16, SP |
|||
|
|||
MOVOU 0(AX), X0 |
|||
MOVOU 16(AX), X1 |
|||
MOVOU 32(AX), X2 |
|||
MOVOU 48(AX), X3 |
|||
MOVOU ·one<>(SB), X15 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
|
|||
CMPQ CX, $128 |
|||
JBE between_64_and_128 |
|||
|
|||
MOVO X0, 0(SP) |
|||
MOVO X1, 16(SP) |
|||
MOVO X2, 32(SP) |
|||
MOVO X3, 48(SP) |
|||
MOVO X15, 64(SP) |
|||
|
|||
CMPQ CX, $192 |
|||
JBE between_128_and_192 |
|||
|
|||
MOVQ $192, R14 |
|||
|
|||
at_least_256: |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
PADDQ 64(SP), X7 |
|||
MOVO X0, X12 |
|||
MOVO X1, X13 |
|||
MOVO X2, X14 |
|||
MOVO X7, X15 |
|||
PADDQ 64(SP), X15 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X15, X11 |
|||
PADDQ 64(SP), X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_256: |
|||
MOVO X8, 80(SP) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X8) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X8) |
|||
MOVO 80(SP), X8 |
|||
|
|||
MOVO X0, 80(SP) |
|||
CHACHA_QROUND_SSE2(X12, X13, X14, X15, X0) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X0) |
|||
MOVO 80(SP), X0 |
|||
|
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X13, X14, X15) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
|
|||
MOVO X8, 80(SP) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X8) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X8) |
|||
MOVO 80(SP), X8 |
|||
|
|||
MOVO X0, 80(SP) |
|||
CHACHA_QROUND_SSE2(X12, X13, X14, X15, X0) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X0) |
|||
MOVO 80(SP), X0 |
|||
|
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X15, X14, X13) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_256 |
|||
|
|||
MOVO X8, 80(SP) |
|||
|
|||
PADDL 0(SP), X0 |
|||
PADDL 16(SP), X1 |
|||
PADDL 32(SP), X2 |
|||
PADDL 48(SP), X3 |
|||
XOR(DI, SI, 0, X0, X1, X2, X3, X8) |
|||
|
|||
MOVO 0(SP), X0 |
|||
MOVO 16(SP), X1 |
|||
MOVO 32(SP), X2 |
|||
MOVO 48(SP), X3 |
|||
PADDQ 64(SP), X3 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ 64(SP), X3 |
|||
XOR(DI, SI, 64, X4, X5, X6, X7, X8) |
|||
|
|||
MOVO 64(SP), X5 |
|||
MOVO 80(SP), X8 |
|||
|
|||
PADDL X0, X12 |
|||
PADDL X1, X13 |
|||
PADDL X2, X14 |
|||
PADDL X3, X15 |
|||
PADDQ X5, X3 |
|||
XOR(DI, SI, 128, X12, X13, X14, X15, X4) |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X5, X3 |
|||
|
|||
CMPQ CX, $256 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 192, X8, X9, X10, X11, X4) |
|||
MOVO X3, 48(SP) |
|||
ADDQ $256, SI |
|||
ADDQ $256, DI |
|||
SUBQ $256, CX |
|||
CMPQ CX, $192 |
|||
JA at_least_256 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
MOVO 64(SP), X15 |
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $128 |
|||
JBE between_64_and_128 |
|||
|
|||
between_128_and_192: |
|||
MOVQ $128, R14 |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
PADDQ X15, X7 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X7, X11 |
|||
PADDQ X15, X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_192: |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X12) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X12) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_192 |
|||
|
|||
PADDL 0(SP), X0 |
|||
PADDL 16(SP), X1 |
|||
PADDL 32(SP), X2 |
|||
PADDL 48(SP), X3 |
|||
XOR(DI, SI, 0, X0, X1, X2, X3, X12) |
|||
|
|||
MOVO 0(SP), X0 |
|||
MOVO 16(SP), X1 |
|||
MOVO 32(SP), X2 |
|||
MOVO 48(SP), X3 |
|||
PADDQ X15, X3 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ X15, X3 |
|||
XOR(DI, SI, 64, X4, X5, X6, X7, X12) |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
|
|||
CMPQ CX, $192 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 128, X8, X9, X10, X11, X12) |
|||
SUBQ $192, CX |
|||
JMP done |
|||
|
|||
between_64_and_128: |
|||
MOVQ $64, R14 |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X3, X11 |
|||
PADDQ X15, X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_128: |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X12) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_128 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ X15, X3 |
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
XOR(DI, SI, 0, X4, X5, X6, X7, X12) |
|||
|
|||
CMPQ CX, $128 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 64, X8, X9, X10, X11, X12) |
|||
SUBQ $128, CX |
|||
JMP done |
|||
|
|||
between_0_and_64: |
|||
MOVQ $0, R14 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X3, X11 |
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_64: |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSE2(X8, X9, X10, X11, X12) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_64 |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
CMPQ CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 0, X8, X9, X10, X11, X12) |
|||
SUBQ $64, CX |
|||
JMP done |
|||
|
|||
less_than_64: |
|||
// R14 contains the num of bytes already xor'd |
|||
ADDQ R14, SI |
|||
ADDQ R14, DI |
|||
SUBQ R14, CX |
|||
MOVOU X8, 0(BX) |
|||
MOVOU X9, 16(BX) |
|||
MOVOU X10, 32(BX) |
|||
MOVOU X11, 48(BX) |
|||
XORQ R11, R11 |
|||
XORQ R12, R12 |
|||
MOVQ CX, BP |
|||
|
|||
xor_loop: |
|||
MOVB 0(SI), R11 |
|||
MOVB 0(BX), R12 |
|||
XORQ R11, R12 |
|||
MOVB R12, 0(DI) |
|||
INCQ SI |
|||
INCQ BX |
|||
INCQ DI |
|||
DECQ BP |
|||
JA xor_loop |
|||
|
|||
done: |
|||
MOVOU X3, 48(AX) |
|||
MOVQ R9, SP |
|||
MOVQ CX, ret+72(FP) |
|||
RET |
|||
|
|||
// func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
TEXT ·xorKeyStreamSSSE3(SB), 4, $144-80 |
|||
MOVQ dst_base+0(FP), DI |
|||
MOVQ src_base+24(FP), SI |
|||
MOVQ src_len+32(FP), CX |
|||
MOVQ block+48(FP), BX |
|||
MOVQ state+56(FP), AX |
|||
MOVQ rounds+64(FP), DX |
|||
|
|||
MOVQ SP, R9 |
|||
ADDQ $16, SP |
|||
ANDQ $-16, SP |
|||
|
|||
MOVOU 0(AX), X0 |
|||
MOVOU 16(AX), X1 |
|||
MOVOU 32(AX), X2 |
|||
MOVOU 48(AX), X3 |
|||
MOVOU ·rol16<>(SB), X13 |
|||
MOVOU ·rol8<>(SB), X14 |
|||
MOVOU ·one<>(SB), X15 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
|
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
|
|||
CMPQ CX, $128 |
|||
JBE between_64_and_128 |
|||
|
|||
MOVO X0, 0(SP) |
|||
MOVO X1, 16(SP) |
|||
MOVO X2, 32(SP) |
|||
MOVO X3, 48(SP) |
|||
MOVO X15, 64(SP) |
|||
|
|||
CMPQ CX, $192 |
|||
JBE between_128_and_192 |
|||
|
|||
MOVO X13, 96(SP) |
|||
MOVO X14, 112(SP) |
|||
MOVQ $192, R14 |
|||
|
|||
at_least_256: |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
PADDQ 64(SP), X7 |
|||
MOVO X0, X12 |
|||
MOVO X1, X13 |
|||
MOVO X2, X14 |
|||
MOVO X7, X15 |
|||
PADDQ 64(SP), X15 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X15, X11 |
|||
PADDQ 64(SP), X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_256: |
|||
MOVO X8, 80(SP) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X8, 96(SP), 112(SP)) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X8, 96(SP), 112(SP)) |
|||
MOVO 80(SP), X8 |
|||
|
|||
MOVO X0, 80(SP) |
|||
CHACHA_QROUND_SSSE3(X12, X13, X14, X15, X0, 96(SP), 112(SP)) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X0, 96(SP), 112(SP)) |
|||
MOVO 80(SP), X0 |
|||
|
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X13, X14, X15) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
|
|||
MOVO X8, 80(SP) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X8, 96(SP), 112(SP)) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X8, 96(SP), 112(SP)) |
|||
MOVO 80(SP), X8 |
|||
|
|||
MOVO X0, 80(SP) |
|||
CHACHA_QROUND_SSSE3(X12, X13, X14, X15, X0, 96(SP), 112(SP)) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X0, 96(SP), 112(SP)) |
|||
MOVO 80(SP), X0 |
|||
|
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X15, X14, X13) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_256 |
|||
|
|||
MOVO X8, 80(SP) |
|||
|
|||
PADDL 0(SP), X0 |
|||
PADDL 16(SP), X1 |
|||
PADDL 32(SP), X2 |
|||
PADDL 48(SP), X3 |
|||
XOR(DI, SI, 0, X0, X1, X2, X3, X8) |
|||
MOVO 0(SP), X0 |
|||
MOVO 16(SP), X1 |
|||
MOVO 32(SP), X2 |
|||
MOVO 48(SP), X3 |
|||
PADDQ 64(SP), X3 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ 64(SP), X3 |
|||
XOR(DI, SI, 64, X4, X5, X6, X7, X8) |
|||
|
|||
MOVO 64(SP), X5 |
|||
MOVO 80(SP), X8 |
|||
|
|||
PADDL X0, X12 |
|||
PADDL X1, X13 |
|||
PADDL X2, X14 |
|||
PADDL X3, X15 |
|||
PADDQ X5, X3 |
|||
XOR(DI, SI, 128, X12, X13, X14, X15, X4) |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X5, X3 |
|||
|
|||
CMPQ CX, $256 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 192, X8, X9, X10, X11, X4) |
|||
MOVO X3, 48(SP) |
|||
ADDQ $256, SI |
|||
ADDQ $256, DI |
|||
SUBQ $256, CX |
|||
CMPQ CX, $192 |
|||
JA at_least_256 |
|||
|
|||
TESTQ CX, CX |
|||
JZ done |
|||
MOVOU ·rol16<>(SB), X13 |
|||
MOVOU ·rol8<>(SB), X14 |
|||
MOVO 64(SP), X15 |
|||
CMPQ CX, $64 |
|||
JBE between_0_and_64 |
|||
CMPQ CX, $128 |
|||
JBE between_64_and_128 |
|||
|
|||
between_128_and_192: |
|||
MOVQ $128, R14 |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
PADDQ X15, X7 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X7, X11 |
|||
PADDQ X15, X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_192: |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_192 |
|||
|
|||
PADDL 0(SP), X0 |
|||
PADDL 16(SP), X1 |
|||
PADDL 32(SP), X2 |
|||
PADDL 48(SP), X3 |
|||
XOR(DI, SI, 0, X0, X1, X2, X3, X12) |
|||
|
|||
MOVO 0(SP), X0 |
|||
MOVO 16(SP), X1 |
|||
MOVO 32(SP), X2 |
|||
MOVO 48(SP), X3 |
|||
PADDQ X15, X3 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ X15, X3 |
|||
XOR(DI, SI, 64, X4, X5, X6, X7, X12) |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
|
|||
CMPQ CX, $192 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 128, X8, X9, X10, X11, X12) |
|||
SUBQ $192, CX |
|||
JMP done |
|||
|
|||
between_64_and_128: |
|||
MOVQ $64, R14 |
|||
MOVO X0, X4 |
|||
MOVO X1, X5 |
|||
MOVO X2, X6 |
|||
MOVO X3, X7 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X3, X11 |
|||
PADDQ X15, X11 |
|||
|
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_128: |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X5, X6, X7) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSSE3(X4, X5, X6, X7, X12, X13, X14) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X7, X6, X5) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_128 |
|||
|
|||
PADDL X0, X4 |
|||
PADDL X1, X5 |
|||
PADDL X2, X6 |
|||
PADDL X3, X7 |
|||
PADDQ X15, X3 |
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
XOR(DI, SI, 0, X4, X5, X6, X7, X12) |
|||
|
|||
CMPQ CX, $128 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 64, X8, X9, X10, X11, X12) |
|||
SUBQ $128, CX |
|||
JMP done |
|||
|
|||
between_0_and_64: |
|||
MOVQ $0, R14 |
|||
MOVO X0, X8 |
|||
MOVO X1, X9 |
|||
MOVO X2, X10 |
|||
MOVO X3, X11 |
|||
MOVQ DX, R8 |
|||
|
|||
chacha_loop_64: |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X9, X10, X11) |
|||
CHACHA_QROUND_SSSE3(X8, X9, X10, X11, X12, X13, X14) |
|||
CHACHA_SHUFFLE(X11, X10, X9) |
|||
SUBQ $2, R8 |
|||
JA chacha_loop_64 |
|||
|
|||
PADDL X0, X8 |
|||
PADDL X1, X9 |
|||
PADDL X2, X10 |
|||
PADDL X3, X11 |
|||
PADDQ X15, X3 |
|||
CMPQ CX, $64 |
|||
JB less_than_64 |
|||
|
|||
XOR(DI, SI, 0, X8, X9, X10, X11, X12) |
|||
SUBQ $64, CX |
|||
JMP done |
|||
|
|||
less_than_64: |
|||
// R14 contains the num of bytes already xor'd |
|||
ADDQ R14, SI |
|||
ADDQ R14, DI |
|||
SUBQ R14, CX |
|||
MOVOU X8, 0(BX) |
|||
MOVOU X9, 16(BX) |
|||
MOVOU X10, 32(BX) |
|||
MOVOU X11, 48(BX) |
|||
XORQ R11, R11 |
|||
XORQ R12, R12 |
|||
MOVQ CX, BP |
|||
|
|||
xor_loop: |
|||
MOVB 0(SI), R11 |
|||
MOVB 0(BX), R12 |
|||
XORQ R11, R12 |
|||
MOVB R12, 0(DI) |
|||
INCQ SI |
|||
INCQ BX |
|||
INCQ DI |
|||
DECQ BP |
|||
JA xor_loop |
|||
|
|||
done: |
|||
MOVQ R9, SP |
|||
MOVOU X3, 48(AX) |
|||
MOVQ CX, ret+72(FP) |
|||
RET |
|||
|
|||
// func supportsSSSE3() bool |
|||
TEXT ·supportsSSSE3(SB), NOSPLIT, $0-1 |
|||
XORQ AX, AX |
|||
INCQ AX |
|||
CPUID |
|||
SHRQ $9, CX |
|||
ANDQ $1, CX |
|||
MOVB CX, ret+0(FP) |
|||
RET |
|||
|
|||
// func initialize(state *[64]byte, key []byte, nonce *[16]byte) |
|||
TEXT ·initialize(SB), 4, $0-40 |
|||
MOVQ state+0(FP), DI |
|||
MOVQ key+8(FP), AX |
|||
MOVQ nonce+32(FP), BX |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0(AX), X1 |
|||
MOVOU 16(AX), X2 |
|||
MOVOU 0(BX), X3 |
|||
|
|||
MOVOU X0, 0(DI) |
|||
MOVOU X1, 16(DI) |
|||
MOVOU X2, 32(DI) |
|||
MOVOU X3, 48(DI) |
|||
RET |
|||
|
|||
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSE2(SB), 4, $0-24 |
|||
MOVQ out+0(FP), DI |
|||
MOVQ nonce+8(FP), AX |
|||
MOVQ key+16(FP), BX |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0(BX), X1 |
|||
MOVOU 16(BX), X2 |
|||
MOVOU 0(AX), X3 |
|||
|
|||
MOVQ $20, CX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
SUBQ $2, CX |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0(DI) |
|||
MOVOU X3, 16(DI) |
|||
RET |
|||
|
|||
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
TEXT ·hChaCha20SSSE3(SB), 4, $0-24 |
|||
MOVQ out+0(FP), DI |
|||
MOVQ nonce+8(FP), AX |
|||
MOVQ key+16(FP), BX |
|||
|
|||
MOVOU ·sigma<>(SB), X0 |
|||
MOVOU 0(BX), X1 |
|||
MOVOU 16(BX), X2 |
|||
MOVOU 0(AX), X3 |
|||
MOVOU ·rol16<>(SB), X5 |
|||
MOVOU ·rol8<>(SB), X6 |
|||
|
|||
MOVQ $20, CX |
|||
|
|||
chacha_loop: |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X1, X2, X3) |
|||
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6) |
|||
CHACHA_SHUFFLE(X3, X2, X1) |
|||
SUBQ $2, CX |
|||
JNZ chacha_loop |
|||
|
|||
MOVOU X0, 0(DI) |
|||
MOVOU X3, 16(DI) |
|||
RET |
|||
@ -1,319 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
package chacha |
|||
|
|||
import "encoding/binary" |
|||
|
|||
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574} |
|||
|
|||
func xorKeyStreamGeneric(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
for len(src) >= 64 { |
|||
chachaGeneric(block, state, rounds) |
|||
|
|||
for i, v := range block { |
|||
dst[i] = src[i] ^ v |
|||
} |
|||
src = src[64:] |
|||
dst = dst[64:] |
|||
} |
|||
|
|||
n := len(src) |
|||
if n > 0 { |
|||
chachaGeneric(block, state, rounds) |
|||
for i, v := range src { |
|||
dst[i] = v ^ block[i] |
|||
} |
|||
} |
|||
return n |
|||
} |
|||
|
|||
func chachaGeneric(dst *[64]byte, state *[64]byte, rounds int) { |
|||
v00 := binary.LittleEndian.Uint32(state[0:]) |
|||
v01 := binary.LittleEndian.Uint32(state[4:]) |
|||
v02 := binary.LittleEndian.Uint32(state[8:]) |
|||
v03 := binary.LittleEndian.Uint32(state[12:]) |
|||
v04 := binary.LittleEndian.Uint32(state[16:]) |
|||
v05 := binary.LittleEndian.Uint32(state[20:]) |
|||
v06 := binary.LittleEndian.Uint32(state[24:]) |
|||
v07 := binary.LittleEndian.Uint32(state[28:]) |
|||
v08 := binary.LittleEndian.Uint32(state[32:]) |
|||
v09 := binary.LittleEndian.Uint32(state[36:]) |
|||
v10 := binary.LittleEndian.Uint32(state[40:]) |
|||
v11 := binary.LittleEndian.Uint32(state[44:]) |
|||
v12 := binary.LittleEndian.Uint32(state[48:]) |
|||
v13 := binary.LittleEndian.Uint32(state[52:]) |
|||
v14 := binary.LittleEndian.Uint32(state[56:]) |
|||
v15 := binary.LittleEndian.Uint32(state[60:]) |
|||
|
|||
s00, s01, s02, s03, s04, s05, s06, s07 := v00, v01, v02, v03, v04, v05, v06, v07 |
|||
s08, s09, s10, s11, s12, s13, s14, s15 := v08, v09, v10, v11, v12, v13, v14, v15 |
|||
|
|||
for i := 0; i < rounds; i += 2 { |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
} |
|||
|
|||
v00 += s00 |
|||
v01 += s01 |
|||
v02 += s02 |
|||
v03 += s03 |
|||
v04 += s04 |
|||
v05 += s05 |
|||
v06 += s06 |
|||
v07 += s07 |
|||
v08 += s08 |
|||
v09 += s09 |
|||
v10 += s10 |
|||
v11 += s11 |
|||
v12 += s12 |
|||
v13 += s13 |
|||
v14 += s14 |
|||
v15 += s15 |
|||
|
|||
s12++ |
|||
binary.LittleEndian.PutUint32(state[48:], s12) |
|||
if s12 == 0 { // indicates overflow
|
|||
s13++ |
|||
binary.LittleEndian.PutUint32(state[52:], s13) |
|||
} |
|||
|
|||
binary.LittleEndian.PutUint32(dst[0:], v00) |
|||
binary.LittleEndian.PutUint32(dst[4:], v01) |
|||
binary.LittleEndian.PutUint32(dst[8:], v02) |
|||
binary.LittleEndian.PutUint32(dst[12:], v03) |
|||
binary.LittleEndian.PutUint32(dst[16:], v04) |
|||
binary.LittleEndian.PutUint32(dst[20:], v05) |
|||
binary.LittleEndian.PutUint32(dst[24:], v06) |
|||
binary.LittleEndian.PutUint32(dst[28:], v07) |
|||
binary.LittleEndian.PutUint32(dst[32:], v08) |
|||
binary.LittleEndian.PutUint32(dst[36:], v09) |
|||
binary.LittleEndian.PutUint32(dst[40:], v10) |
|||
binary.LittleEndian.PutUint32(dst[44:], v11) |
|||
binary.LittleEndian.PutUint32(dst[48:], v12) |
|||
binary.LittleEndian.PutUint32(dst[52:], v13) |
|||
binary.LittleEndian.PutUint32(dst[56:], v14) |
|||
binary.LittleEndian.PutUint32(dst[60:], v15) |
|||
} |
|||
|
|||
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
v00 := sigma[0] |
|||
v01 := sigma[1] |
|||
v02 := sigma[2] |
|||
v03 := sigma[3] |
|||
v04 := binary.LittleEndian.Uint32(key[0:]) |
|||
v05 := binary.LittleEndian.Uint32(key[4:]) |
|||
v06 := binary.LittleEndian.Uint32(key[8:]) |
|||
v07 := binary.LittleEndian.Uint32(key[12:]) |
|||
v08 := binary.LittleEndian.Uint32(key[16:]) |
|||
v09 := binary.LittleEndian.Uint32(key[20:]) |
|||
v10 := binary.LittleEndian.Uint32(key[24:]) |
|||
v11 := binary.LittleEndian.Uint32(key[28:]) |
|||
v12 := binary.LittleEndian.Uint32(nonce[0:]) |
|||
v13 := binary.LittleEndian.Uint32(nonce[4:]) |
|||
v14 := binary.LittleEndian.Uint32(nonce[8:]) |
|||
v15 := binary.LittleEndian.Uint32(nonce[12:]) |
|||
|
|||
for i := 0; i < 20; i += 2 { |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v00 += v04 |
|||
v12 ^= v00 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v08 += v12 |
|||
v04 ^= v08 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v01 += v05 |
|||
v13 ^= v01 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v09 += v13 |
|||
v05 ^= v09 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v02 += v06 |
|||
v14 ^= v02 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v10 += v14 |
|||
v06 ^= v10 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v03 += v07 |
|||
v15 ^= v03 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v11 += v15 |
|||
v07 ^= v11 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 16) | (v15 >> 16) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 12) | (v05 >> 20) |
|||
v00 += v05 |
|||
v15 ^= v00 |
|||
v15 = (v15 << 8) | (v15 >> 24) |
|||
v10 += v15 |
|||
v05 ^= v10 |
|||
v05 = (v05 << 7) | (v05 >> 25) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 16) | (v12 >> 16) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 12) | (v06 >> 20) |
|||
v01 += v06 |
|||
v12 ^= v01 |
|||
v12 = (v12 << 8) | (v12 >> 24) |
|||
v11 += v12 |
|||
v06 ^= v11 |
|||
v06 = (v06 << 7) | (v06 >> 25) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 16) | (v13 >> 16) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 12) | (v07 >> 20) |
|||
v02 += v07 |
|||
v13 ^= v02 |
|||
v13 = (v13 << 8) | (v13 >> 24) |
|||
v08 += v13 |
|||
v07 ^= v08 |
|||
v07 = (v07 << 7) | (v07 >> 25) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 16) | (v14 >> 16) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 12) | (v04 >> 20) |
|||
v03 += v04 |
|||
v14 ^= v03 |
|||
v14 = (v14 << 8) | (v14 >> 24) |
|||
v09 += v14 |
|||
v04 ^= v09 |
|||
v04 = (v04 << 7) | (v04 >> 25) |
|||
} |
|||
|
|||
binary.LittleEndian.PutUint32(out[0:], v00) |
|||
binary.LittleEndian.PutUint32(out[4:], v01) |
|||
binary.LittleEndian.PutUint32(out[8:], v02) |
|||
binary.LittleEndian.PutUint32(out[12:], v03) |
|||
binary.LittleEndian.PutUint32(out[16:], v12) |
|||
binary.LittleEndian.PutUint32(out[20:], v13) |
|||
binary.LittleEndian.PutUint32(out[24:], v14) |
|||
binary.LittleEndian.PutUint32(out[28:], v15) |
|||
} |
|||
@ -1,56 +0,0 @@ |
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build amd64,!gccgo,!appengine,!nacl,!go1.7
|
|||
|
|||
package chacha |
|||
|
|||
func init() { |
|||
useSSE2 = true |
|||
useSSSE3 = supportsSSSE3() |
|||
useAVX2 = false |
|||
} |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func supportsSSSE3() bool |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
if useSSSE3 { |
|||
hChaCha20SSSE3(out, nonce, key) |
|||
} else if useSSE2 { // on amd64 this is always true - used to test generic on amd64
|
|||
hChaCha20SSE2(out, nonce, key) |
|||
} else { |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
if useSSSE3 { |
|||
return xorKeyStreamSSSE3(dst, src, block, state, rounds) |
|||
} else if useSSE2 { // on amd64 this is always true - used to test generic on amd64
|
|||
return xorKeyStreamSSE2(dst, src, block, state, rounds) |
|||
} |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
@ -1,72 +0,0 @@ |
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build go1.7,amd64,!gccgo,!appengine,!nacl
|
|||
|
|||
package chacha |
|||
|
|||
func init() { |
|||
useSSE2 = true |
|||
useSSSE3 = supportsSSSE3() |
|||
useAVX2 = supportsAVX2() |
|||
} |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func supportsSSSE3() bool |
|||
|
|||
// This function is implemented in chachaAVX2_amd64.s
|
|||
//go:noescape
|
|||
func supportsAVX2() bool |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chachaAVX2_amd64.s
|
|||
//go:noescape
|
|||
func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte) |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chacha_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
// This function is implemented in chachaAVX2_amd64.s
|
|||
//go:noescape
|
|||
func xorKeyStreamAVX2(dst, src []byte, block, state *[64]byte, rounds int) int |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
if useAVX2 { |
|||
hChaCha20AVX(out, nonce, key) |
|||
} else if useSSSE3 { |
|||
hChaCha20SSSE3(out, nonce, key) |
|||
} else if useSSE2 { // on amd64 this is always true - neccessary for testing generic on amd64
|
|||
hChaCha20SSE2(out, nonce, key) |
|||
} else { |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
if useAVX2 { |
|||
return xorKeyStreamAVX2(dst, src, block, state, rounds) |
|||
} else if useSSSE3 { |
|||
return xorKeyStreamSSSE3(dst, src, block, state, rounds) |
|||
} else if useSSE2 { // on amd64 this is always true - neccessary for testing generic on amd64
|
|||
return xorKeyStreamSSE2(dst, src, block, state, rounds) |
|||
} |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// +build !amd64,!386 gccgo appengine nacl
|
|||
|
|||
package chacha |
|||
|
|||
import "encoding/binary" |
|||
|
|||
func initialize(state *[64]byte, key []byte, nonce *[16]byte) { |
|||
binary.LittleEndian.PutUint32(state[0:], sigma[0]) |
|||
binary.LittleEndian.PutUint32(state[4:], sigma[1]) |
|||
binary.LittleEndian.PutUint32(state[8:], sigma[2]) |
|||
binary.LittleEndian.PutUint32(state[12:], sigma[3]) |
|||
copy(state[16:], key[:]) |
|||
copy(state[48:], nonce[:]) |
|||
} |
|||
|
|||
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int { |
|||
return xorKeyStreamGeneric(dst, src, block, state, rounds) |
|||
} |
|||
|
|||
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { |
|||
hChaCha20Generic(out, nonce, key) |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
|||
// Use of this source code is governed by a license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
// Package chacha20 implements the ChaCha20 / XChaCha20 stream chipher.
|
|||
// Notice that one specific key-nonce combination must be unique for all time.
|
|||
//
|
|||
// There are three versions of ChaCha20:
|
|||
// - ChaCha20 with a 64 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
|||
// - ChaCha20 with a 96 bit nonce (en/decrypt up to 2^32 * 64 bytes (~256 GB) for one key-nonce combination)
|
|||
// - XChaCha20 with a 192 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
|
|||
package chacha20 // import "github.com/aead/chacha20"
|
|||
|
|||
import ( |
|||
"crypto/cipher" |
|||
|
|||
"github.com/aead/chacha20/chacha" |
|||
) |
|||
|
|||
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// Src and dst may be the same slice but otherwise should not overlap.
|
|||
// If len(dst) < len(src) this function panics.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
|
|||
func XORKeyStream(dst, src, nonce, key []byte) { |
|||
chacha.XORKeyStream(dst, src, nonce, key, 20) |
|||
} |
|||
|
|||
// NewCipher returns a new cipher.Stream implementing a ChaCha20 version.
|
|||
// The nonce must be unique for one key for all time.
|
|||
// The length of the nonce determinds the version of ChaCha20:
|
|||
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
|
|||
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
|
|||
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
|
|||
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
|
|||
func NewCipher(nonce, key []byte) (cipher.Stream, error) { |
|||
return chacha.NewCipher(nonce, key, 20) |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
MIT License |
|||
|
|||
Copyright (c) 2017 ginuerzh |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
@ -1,2 +0,0 @@ |
|||
# pht |
|||
Plain HTTP Tunnel - Tunnel over HTTP using only GET and POST requests, NO Websocket, NO CONNECT method. |
|||
@ -1,162 +0,0 @@ |
|||
package pht |
|||
|
|||
import ( |
|||
"bufio" |
|||
"bytes" |
|||
"encoding/base64" |
|||
"errors" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net" |
|||
"net/http" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
type Client struct { |
|||
Host string |
|||
Key string |
|||
httpClient *http.Client |
|||
manager *sessionManager |
|||
} |
|||
|
|||
func NewClient(host, key string) *Client { |
|||
return &Client{ |
|||
Host: host, |
|||
Key: key, |
|||
httpClient: &http.Client{}, |
|||
manager: newSessionManager(), |
|||
} |
|||
} |
|||
|
|||
func (c *Client) Dial() (net.Conn, error) { |
|||
r, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s%s", c.Host, tokenURI), nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
r.Header.Set("Authorization", "key="+c.Key) |
|||
resp, err := c.httpClient.Do(r) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
defer resp.Body.Close() |
|||
|
|||
if resp.StatusCode != http.StatusOK { |
|||
return nil, errors.New(resp.Status) |
|||
} |
|||
|
|||
data, err := ioutil.ReadAll(resp.Body) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
token := strings.TrimPrefix(string(data), "token=") |
|||
if token == "" { |
|||
return nil, errors.New("invalid token") |
|||
} |
|||
|
|||
session := newSession(0, 0) |
|||
c.manager.SetSession(token, session) |
|||
|
|||
go c.sendDataLoop(token) |
|||
go c.recvDataLoop(token) |
|||
|
|||
return newConn(session), nil |
|||
} |
|||
|
|||
func (c *Client) sendDataLoop(token string) error { |
|||
session := c.manager.GetSession(token) |
|||
if session == nil { |
|||
return errors.New("invalid token") |
|||
} |
|||
|
|||
for { |
|||
select { |
|||
case b, ok := <-session.wchan: |
|||
var data string |
|||
if len(b) > 0 { |
|||
data = base64.StdEncoding.EncodeToString(b) |
|||
} |
|||
r, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%s%s", c.Host, pushURI), bytes.NewBufferString(data+"\n")) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
r.Header.Set("Authorization", fmt.Sprintf("key=%s; token=%s", c.Key, token)) |
|||
if !ok { |
|||
c.manager.DelSession(token) |
|||
resp, err := c.httpClient.Do(r) |
|||
if err != nil { // TODO: retry
|
|||
return err |
|||
} |
|||
resp.Body.Close() |
|||
return nil // session is closed
|
|||
} |
|||
|
|||
resp, err := c.httpClient.Do(r) |
|||
if err != nil { // TODO: retry
|
|||
return err |
|||
} |
|||
resp.Body.Close() |
|||
if resp.StatusCode != http.StatusOK { |
|||
return errors.New(resp.Status) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (c *Client) recvDataLoop(token string) error { |
|||
session := c.manager.GetSession(token) |
|||
if session == nil { |
|||
return errors.New("invalid token") |
|||
} |
|||
|
|||
for { |
|||
err := c.recvData(token, session) |
|||
if err != nil { |
|||
close(session.rchan) |
|||
c.manager.DelSession(token) |
|||
return err |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (c *Client) recvData(token string, s *session) error { |
|||
r, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s%s", c.Host, pollURI), nil) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
r.Header.Set("Authorization", fmt.Sprintf("key=%s; token=%s", c.Key, token)) |
|||
resp, err := c.httpClient.Do(r) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer resp.Body.Close() |
|||
if resp.StatusCode != http.StatusOK { |
|||
return errors.New(resp.Status) |
|||
} |
|||
|
|||
scanner := bufio.NewScanner(resp.Body) |
|||
for scanner.Scan() { |
|||
select { |
|||
case <-s.closed: |
|||
return errors.New("session closed") |
|||
default: |
|||
} |
|||
|
|||
b, err := base64.StdEncoding.DecodeString(scanner.Text()) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
select { |
|||
case s.rchan <- b: |
|||
case <-s.closed: |
|||
return errors.New("session closed") |
|||
case <-time.After(time.Second * 90): |
|||
return errors.New("timeout") |
|||
} |
|||
|
|||
if err := scanner.Err(); err != nil { |
|||
return err |
|||
} |
|||
} |
|||
return nil |
|||
} |
|||
@ -1,133 +0,0 @@ |
|||
package pht |
|||
|
|||
import ( |
|||
"errors" |
|||
"io" |
|||
"net" |
|||
"time" |
|||
) |
|||
|
|||
type conn struct { |
|||
session *session |
|||
rb []byte // read buffer
|
|||
remoteAddr net.Addr |
|||
localAddr net.Addr |
|||
rTimer, wTimer *time.Timer |
|||
closed chan interface{} |
|||
} |
|||
|
|||
func newConn(session *session) *conn { |
|||
conn := &conn{ |
|||
session: session, |
|||
rTimer: time.NewTimer(time.Hour * 65535), |
|||
wTimer: time.NewTimer(time.Hour * 65535), |
|||
closed: make(chan interface{}), |
|||
} |
|||
conn.rTimer.Stop() |
|||
conn.wTimer.Stop() |
|||
|
|||
return conn |
|||
} |
|||
|
|||
func (conn *conn) Read(b []byte) (n int, err error) { |
|||
select { |
|||
case <-conn.closed: |
|||
err = errors.New("read: use of closed network connection") |
|||
return |
|||
default: |
|||
} |
|||
|
|||
if len(conn.rb) > 0 { |
|||
n = copy(b, conn.rb) |
|||
conn.rb = conn.rb[n:] |
|||
return |
|||
} |
|||
|
|||
select { |
|||
case data, ok := <-conn.session.rchan: |
|||
if !ok { |
|||
err = io.EOF |
|||
return |
|||
} |
|||
n = copy(b, data) |
|||
conn.rb = data[n:] |
|||
case <-conn.rTimer.C: |
|||
err = errors.New("read timeout") |
|||
case <-conn.closed: |
|||
err = io.EOF |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (conn *conn) Write(b []byte) (n int, err error) { |
|||
select { |
|||
case <-conn.closed: |
|||
err = errors.New("write: use of closed network connection") |
|||
return |
|||
default: |
|||
} |
|||
|
|||
if len(b) == 0 { |
|||
return |
|||
} |
|||
|
|||
data := make([]byte, len(b)) |
|||
copy(data, b) |
|||
|
|||
select { |
|||
case conn.session.wchan <- data: |
|||
n = len(b) |
|||
case <-conn.wTimer.C: |
|||
err = errors.New("write timeout") |
|||
case <-conn.closed: |
|||
err = errors.New("connection is closed") |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (conn *conn) Close() error { |
|||
close(conn.closed) |
|||
close(conn.session.closed) |
|||
close(conn.session.wchan) |
|||
return nil |
|||
} |
|||
|
|||
func (conn *conn) LocalAddr() net.Addr { |
|||
return conn.localAddr |
|||
} |
|||
|
|||
func (conn *conn) RemoteAddr() net.Addr { |
|||
return conn.remoteAddr |
|||
} |
|||
|
|||
func (conn *conn) SetReadDeadline(t time.Time) error { |
|||
if t.IsZero() { |
|||
conn.rTimer.Stop() |
|||
return nil |
|||
} |
|||
conn.rTimer.Reset(t.Sub(time.Now())) |
|||
return nil |
|||
} |
|||
|
|||
func (conn *conn) SetWriteDeadline(t time.Time) error { |
|||
if t.IsZero() { |
|||
conn.wTimer.Stop() |
|||
return nil |
|||
} |
|||
conn.wTimer.Reset(t.Sub(time.Now())) |
|||
return nil |
|||
} |
|||
|
|||
func (conn *conn) SetDeadline(t time.Time) error { |
|||
if t.IsZero() { |
|||
conn.rTimer.Stop() |
|||
conn.wTimer.Stop() |
|||
return nil |
|||
} |
|||
d := t.Sub(time.Now()) |
|||
conn.rTimer.Reset(d) |
|||
conn.wTimer.Reset(d) |
|||
return nil |
|||
} |
|||
@ -1,199 +0,0 @@ |
|||
package pht |
|||
|
|||
import ( |
|||
"bufio" |
|||
"encoding/base64" |
|||
"fmt" |
|||
"net" |
|||
"net/http" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
const ( |
|||
tokenURI = "/token" |
|||
pushURI = "/push" |
|||
pollURI = "/poll" |
|||
) |
|||
|
|||
type Server struct { |
|||
Addr string |
|||
Key string |
|||
Handler func(net.Conn) |
|||
manager *sessionManager |
|||
} |
|||
|
|||
func (s *Server) ListenAndServe() error { |
|||
s.manager = newSessionManager() |
|||
|
|||
mux := http.NewServeMux() |
|||
mux.Handle(tokenURI, http.HandlerFunc(s.tokenHandler)) |
|||
mux.Handle(pushURI, http.HandlerFunc(s.pushHandler)) |
|||
mux.Handle(pollURI, http.HandlerFunc(s.pollHandler)) |
|||
|
|||
return http.ListenAndServe(s.Addr, mux) |
|||
} |
|||
|
|||
func (s *Server) tokenHandler(w http.ResponseWriter, r *http.Request) { |
|||
if r.Method != http.MethodPost { |
|||
w.WriteHeader(http.StatusMethodNotAllowed) |
|||
return |
|||
} |
|||
|
|||
m := parseAuth(r.Header.Get("Authorization")) |
|||
if m["key"] != s.Key { |
|||
w.WriteHeader(http.StatusForbidden) |
|||
return |
|||
} |
|||
|
|||
token, session, err := s.manager.NewSession(0, 0) |
|||
if err != nil { |
|||
w.WriteHeader(http.StatusInternalServerError) |
|||
return |
|||
} |
|||
|
|||
conn, err := s.upgrade(session, r) |
|||
if err != nil { |
|||
s.manager.DelSession(token) |
|||
w.WriteHeader(http.StatusInternalServerError) |
|||
return |
|||
} |
|||
|
|||
if s.Handler != nil { |
|||
go s.Handler(conn) |
|||
} |
|||
|
|||
w.Write([]byte(fmt.Sprintf("token=%s", token))) |
|||
} |
|||
|
|||
func (s *Server) pushHandler(w http.ResponseWriter, r *http.Request) { |
|||
if r.Method != http.MethodPost { |
|||
w.WriteHeader(http.StatusMethodNotAllowed) |
|||
return |
|||
} |
|||
|
|||
m := parseAuth(r.Header.Get("Authorization")) |
|||
if m["key"] != s.Key { |
|||
w.WriteHeader(http.StatusForbidden) |
|||
return |
|||
} |
|||
|
|||
token := m["token"] |
|||
session := s.manager.GetSession(token) |
|||
if session == nil { |
|||
w.WriteHeader(http.StatusUnauthorized) |
|||
return |
|||
} |
|||
|
|||
br := bufio.NewReader(r.Body) |
|||
data, err := br.ReadString('\n') |
|||
if err != nil { |
|||
s.manager.DelSession(token) |
|||
close(session.rchan) |
|||
w.WriteHeader(http.StatusInternalServerError) |
|||
return |
|||
} |
|||
|
|||
data = strings.TrimSuffix(data, "\n") |
|||
if len(data) == 0 { |
|||
s.manager.DelSession(token) |
|||
close(session.rchan) |
|||
return |
|||
} |
|||
|
|||
b, err := base64.StdEncoding.DecodeString(data) |
|||
if err != nil { |
|||
s.manager.DelSession(token) |
|||
close(session.rchan) |
|||
return |
|||
} |
|||
|
|||
select { |
|||
case <-session.closed: |
|||
s.manager.DelSession(token) |
|||
return |
|||
case session.rchan <- b: |
|||
w.WriteHeader(http.StatusOK) |
|||
case <-time.After(time.Second * 90): |
|||
s.manager.DelSession(token) |
|||
w.WriteHeader(http.StatusRequestTimeout) |
|||
} |
|||
} |
|||
|
|||
func (s *Server) pollHandler(w http.ResponseWriter, r *http.Request) { |
|||
if r.Method != http.MethodGet { |
|||
w.WriteHeader(http.StatusMethodNotAllowed) |
|||
return |
|||
} |
|||
|
|||
m := parseAuth(r.Header.Get("Authorization")) |
|||
if m["key"] != s.Key { |
|||
w.WriteHeader(http.StatusForbidden) |
|||
return |
|||
} |
|||
|
|||
token := m["token"] |
|||
session := s.manager.GetSession(token) |
|||
if session == nil { |
|||
w.WriteHeader(http.StatusUnauthorized) |
|||
return |
|||
} |
|||
|
|||
w.WriteHeader(http.StatusOK) |
|||
if fw, ok := w.(http.Flusher); ok { |
|||
fw.Flush() |
|||
} |
|||
|
|||
for { |
|||
select { |
|||
case data, ok := <-session.wchan: |
|||
if !ok { |
|||
s.manager.DelSession(token) |
|||
return // session is closed
|
|||
} |
|||
bw := bufio.NewWriter(w) |
|||
bw.WriteString(base64.StdEncoding.EncodeToString(data)) |
|||
bw.WriteString("\n") |
|||
if err := bw.Flush(); err != nil { |
|||
return |
|||
} |
|||
|
|||
if fw, ok := w.(http.Flusher); ok { |
|||
fw.Flush() |
|||
} |
|||
case <-time.After(time.Second * 25): |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (s *Server) upgrade(sess *session, r *http.Request) (net.Conn, error) { |
|||
conn := newConn(sess) |
|||
raddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr) |
|||
if err != nil { |
|||
raddr = &net.TCPAddr{} |
|||
} |
|||
conn.remoteAddr = raddr |
|||
|
|||
laddr, err := net.ResolveTCPAddr("tcp", s.Addr) |
|||
if err != nil { |
|||
laddr = &net.TCPAddr{} |
|||
} |
|||
conn.localAddr = laddr |
|||
|
|||
return conn, nil |
|||
} |
|||
|
|||
func parseAuth(auth string) map[string]string { |
|||
mkv := make(map[string]string) |
|||
|
|||
for _, s := range strings.Split(auth, ";") { |
|||
n := strings.Index(s, "=") |
|||
if n < 0 { |
|||
continue |
|||
} |
|||
mkv[strings.TrimSpace(s[:n])] = strings.TrimSpace(s[n+1:]) |
|||
} |
|||
|
|||
return mkv |
|||
} |
|||
@ -1,80 +0,0 @@ |
|||
package pht |
|||
|
|||
import ( |
|||
"crypto/rand" |
|||
"encoding/hex" |
|||
"sync" |
|||
) |
|||
|
|||
const ( |
|||
defaultRChanLen = 64 |
|||
defaultWChanLen = 64 |
|||
) |
|||
|
|||
type session struct { |
|||
rchan chan []byte |
|||
wchan chan []byte |
|||
closed chan interface{} |
|||
} |
|||
|
|||
func newSession(rlen, wlen int) *session { |
|||
if rlen <= 0 { |
|||
rlen = defaultRChanLen |
|||
} |
|||
if wlen <= 0 { |
|||
wlen = defaultWChanLen |
|||
} |
|||
|
|||
return &session{ |
|||
rchan: make(chan []byte, rlen), |
|||
wchan: make(chan []byte, wlen), |
|||
closed: make(chan interface{}), |
|||
} |
|||
} |
|||
|
|||
type sessionManager struct { |
|||
sessions map[string]*session |
|||
mux sync.Mutex |
|||
} |
|||
|
|||
func newSessionManager() *sessionManager { |
|||
return &sessionManager{ |
|||
sessions: make(map[string]*session), |
|||
mux: sync.Mutex{}, |
|||
} |
|||
} |
|||
|
|||
func (m *sessionManager) NewSession(rlen, wlen int) (token string, s *session, err error) { |
|||
var nonce [16]byte |
|||
if _, err = rand.Read(nonce[:]); err != nil { |
|||
return |
|||
} |
|||
token = hex.EncodeToString(nonce[:]) |
|||
s = newSession(rlen, wlen) |
|||
|
|||
m.mux.Lock() |
|||
defer m.mux.Unlock() |
|||
m.sessions[token] = s |
|||
|
|||
return |
|||
} |
|||
|
|||
func (m *sessionManager) SetSession(token string, session *session) { |
|||
m.mux.Lock() |
|||
defer m.mux.Unlock() |
|||
m.sessions[token] = session |
|||
} |
|||
|
|||
func (m *sessionManager) GetSession(token string) *session { |
|||
m.mux.Lock() |
|||
defer m.mux.Unlock() |
|||
|
|||
return m.sessions[token] |
|||
} |
|||
|
|||
func (m *sessionManager) DelSession(token string) { |
|||
m.mux.Lock() |
|||
defer m.mux.Unlock() |
|||
|
|||
delete(m.sessions, token) |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
#! /bin/bash |
|||
|
|||
bench() { |
|||
filename="/tmp/$1-$2.bench" |
|||
if test -e "${filename}"; |
|||
then |
|||
echo "Already exists ${filename}" |
|||
else |
|||
backup=`git rev-parse --abbrev-ref HEAD` |
|||
git checkout $1 |
|||
echo -n "Creating ${filename}... " |
|||
go test ./... -run=NONE -bench=$2 > "${filename}" -benchmem |
|||
echo "OK" |
|||
git checkout ${backup} |
|||
sleep 5 |
|||
fi |
|||
} |
|||
|
|||
|
|||
to=$1 |
|||
current=`git rev-parse --abbrev-ref HEAD` |
|||
|
|||
bench ${to} $2 |
|||
bench ${current} $2 |
|||
|
|||
benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench" |
|||
@ -0,0 +1,525 @@ |
|||
package compiler |
|||
|
|||
// TODO use constructor with all matchers, and to their structs private
|
|||
// TODO glue multiple Text nodes (like after QuoteMeta)
|
|||
|
|||
import ( |
|||
"fmt" |
|||
"reflect" |
|||
|
|||
"github.com/gobwas/glob/match" |
|||
"github.com/gobwas/glob/syntax/ast" |
|||
"github.com/gobwas/glob/util/runes" |
|||
) |
|||
|
|||
func optimizeMatcher(matcher match.Matcher) match.Matcher { |
|||
switch m := matcher.(type) { |
|||
|
|||
case match.Any: |
|||
if len(m.Separators) == 0 { |
|||
return match.NewSuper() |
|||
} |
|||
|
|||
case match.AnyOf: |
|||
if len(m.Matchers) == 1 { |
|||
return m.Matchers[0] |
|||
} |
|||
|
|||
return m |
|||
|
|||
case match.List: |
|||
if m.Not == false && len(m.List) == 1 { |
|||
return match.NewText(string(m.List)) |
|||
} |
|||
|
|||
return m |
|||
|
|||
case match.BTree: |
|||
m.Left = optimizeMatcher(m.Left) |
|||
m.Right = optimizeMatcher(m.Right) |
|||
|
|||
r, ok := m.Value.(match.Text) |
|||
if !ok { |
|||
return m |
|||
} |
|||
|
|||
var ( |
|||
leftNil = m.Left == nil |
|||
rightNil = m.Right == nil |
|||
) |
|||
if leftNil && rightNil { |
|||
return match.NewText(r.Str) |
|||
} |
|||
|
|||
_, leftSuper := m.Left.(match.Super) |
|||
lp, leftPrefix := m.Left.(match.Prefix) |
|||
la, leftAny := m.Left.(match.Any) |
|||
|
|||
_, rightSuper := m.Right.(match.Super) |
|||
rs, rightSuffix := m.Right.(match.Suffix) |
|||
ra, rightAny := m.Right.(match.Any) |
|||
|
|||
switch { |
|||
case leftSuper && rightSuper: |
|||
return match.NewContains(r.Str, false) |
|||
|
|||
case leftSuper && rightNil: |
|||
return match.NewSuffix(r.Str) |
|||
|
|||
case rightSuper && leftNil: |
|||
return match.NewPrefix(r.Str) |
|||
|
|||
case leftNil && rightSuffix: |
|||
return match.NewPrefixSuffix(r.Str, rs.Suffix) |
|||
|
|||
case rightNil && leftPrefix: |
|||
return match.NewPrefixSuffix(lp.Prefix, r.Str) |
|||
|
|||
case rightNil && leftAny: |
|||
return match.NewSuffixAny(r.Str, la.Separators) |
|||
|
|||
case leftNil && rightAny: |
|||
return match.NewPrefixAny(r.Str, ra.Separators) |
|||
} |
|||
|
|||
return m |
|||
} |
|||
|
|||
return matcher |
|||
} |
|||
|
|||
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) { |
|||
if len(matchers) == 0 { |
|||
return nil, fmt.Errorf("compile error: need at least one matcher") |
|||
} |
|||
if len(matchers) == 1 { |
|||
return matchers[0], nil |
|||
} |
|||
if m := glueMatchers(matchers); m != nil { |
|||
return m, nil |
|||
} |
|||
|
|||
idx := -1 |
|||
maxLen := -1 |
|||
var val match.Matcher |
|||
for i, matcher := range matchers { |
|||
if l := matcher.Len(); l != -1 && l >= maxLen { |
|||
maxLen = l |
|||
idx = i |
|||
val = matcher |
|||
} |
|||
} |
|||
|
|||
if val == nil { // not found matcher with static length
|
|||
r, err := compileMatchers(matchers[1:]) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return match.NewBTree(matchers[0], nil, r), nil |
|||
} |
|||
|
|||
left := matchers[:idx] |
|||
var right []match.Matcher |
|||
if len(matchers) > idx+1 { |
|||
right = matchers[idx+1:] |
|||
} |
|||
|
|||
var l, r match.Matcher |
|||
var err error |
|||
if len(left) > 0 { |
|||
l, err = compileMatchers(left) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
if len(right) > 0 { |
|||
r, err = compileMatchers(right) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
return match.NewBTree(val, l, r), nil |
|||
} |
|||
|
|||
func glueMatchers(matchers []match.Matcher) match.Matcher { |
|||
if m := glueMatchersAsEvery(matchers); m != nil { |
|||
return m |
|||
} |
|||
if m := glueMatchersAsRow(matchers); m != nil { |
|||
return m |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func glueMatchersAsRow(matchers []match.Matcher) match.Matcher { |
|||
if len(matchers) <= 1 { |
|||
return nil |
|||
} |
|||
|
|||
var ( |
|||
c []match.Matcher |
|||
l int |
|||
) |
|||
for _, matcher := range matchers { |
|||
if ml := matcher.Len(); ml == -1 { |
|||
return nil |
|||
} else { |
|||
c = append(c, matcher) |
|||
l += ml |
|||
} |
|||
} |
|||
return match.NewRow(l, c...) |
|||
} |
|||
|
|||
func glueMatchersAsEvery(matchers []match.Matcher) match.Matcher { |
|||
if len(matchers) <= 1 { |
|||
return nil |
|||
} |
|||
|
|||
var ( |
|||
hasAny bool |
|||
hasSuper bool |
|||
hasSingle bool |
|||
min int |
|||
separator []rune |
|||
) |
|||
|
|||
for i, matcher := range matchers { |
|||
var sep []rune |
|||
|
|||
switch m := matcher.(type) { |
|||
case match.Super: |
|||
sep = []rune{} |
|||
hasSuper = true |
|||
|
|||
case match.Any: |
|||
sep = m.Separators |
|||
hasAny = true |
|||
|
|||
case match.Single: |
|||
sep = m.Separators |
|||
hasSingle = true |
|||
min++ |
|||
|
|||
case match.List: |
|||
if !m.Not { |
|||
return nil |
|||
} |
|||
sep = m.List |
|||
hasSingle = true |
|||
min++ |
|||
|
|||
default: |
|||
return nil |
|||
} |
|||
|
|||
// initialize
|
|||
if i == 0 { |
|||
separator = sep |
|||
} |
|||
|
|||
if runes.Equal(sep, separator) { |
|||
continue |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
if hasSuper && !hasAny && !hasSingle { |
|||
return match.NewSuper() |
|||
} |
|||
|
|||
if hasAny && !hasSuper && !hasSingle { |
|||
return match.NewAny(separator) |
|||
} |
|||
|
|||
if (hasAny || hasSuper) && min > 0 && len(separator) == 0 { |
|||
return match.NewMin(min) |
|||
} |
|||
|
|||
every := match.NewEveryOf() |
|||
|
|||
if min > 0 { |
|||
every.Add(match.NewMin(min)) |
|||
|
|||
if !hasAny && !hasSuper { |
|||
every.Add(match.NewMax(min)) |
|||
} |
|||
} |
|||
|
|||
if len(separator) > 0 { |
|||
every.Add(match.NewContains(string(separator), true)) |
|||
} |
|||
|
|||
return every |
|||
} |
|||
|
|||
func minimizeMatchers(matchers []match.Matcher) []match.Matcher { |
|||
var done match.Matcher |
|||
var left, right, count int |
|||
|
|||
for l := 0; l < len(matchers); l++ { |
|||
for r := len(matchers); r > l; r-- { |
|||
if glued := glueMatchers(matchers[l:r]); glued != nil { |
|||
var swap bool |
|||
|
|||
if done == nil { |
|||
swap = true |
|||
} else { |
|||
cl, gl := done.Len(), glued.Len() |
|||
swap = cl > -1 && gl > -1 && gl > cl |
|||
swap = swap || count < r-l |
|||
} |
|||
|
|||
if swap { |
|||
done = glued |
|||
left = l |
|||
right = r |
|||
count = r - l |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
if done == nil { |
|||
return matchers |
|||
} |
|||
|
|||
next := append(append([]match.Matcher{}, matchers[:left]...), done) |
|||
if right < len(matchers) { |
|||
next = append(next, matchers[right:]...) |
|||
} |
|||
|
|||
if len(next) == len(matchers) { |
|||
return next |
|||
} |
|||
|
|||
return minimizeMatchers(next) |
|||
} |
|||
|
|||
// minimizeAnyOf tries to apply some heuristics to minimize number of nodes in given tree
|
|||
func minimizeTree(tree *ast.Node) *ast.Node { |
|||
switch tree.Kind { |
|||
case ast.KindAnyOf: |
|||
return minimizeTreeAnyOf(tree) |
|||
default: |
|||
return nil |
|||
} |
|||
} |
|||
|
|||
// minimizeAnyOf tries to find common children of given node of AnyOf pattern
|
|||
// it searches for common children from left and from right
|
|||
// if any common children are found – then it returns new optimized ast tree
|
|||
// else it returns nil
|
|||
func minimizeTreeAnyOf(tree *ast.Node) *ast.Node { |
|||
if !areOfSameKind(tree.Children, ast.KindPattern) { |
|||
return nil |
|||
} |
|||
|
|||
commonLeft, commonRight := commonChildren(tree.Children) |
|||
commonLeftCount, commonRightCount := len(commonLeft), len(commonRight) |
|||
if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts
|
|||
return nil |
|||
} |
|||
|
|||
var result []*ast.Node |
|||
if commonLeftCount > 0 { |
|||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...)) |
|||
} |
|||
|
|||
var anyOf []*ast.Node |
|||
for _, child := range tree.Children { |
|||
reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount] |
|||
var node *ast.Node |
|||
if len(reuse) == 0 { |
|||
// this pattern is completely reduced by commonLeft and commonRight patterns
|
|||
// so it become nothing
|
|||
node = ast.NewNode(ast.KindNothing, nil) |
|||
} else { |
|||
node = ast.NewNode(ast.KindPattern, nil, reuse...) |
|||
} |
|||
anyOf = appendIfUnique(anyOf, node) |
|||
} |
|||
switch { |
|||
case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing: |
|||
result = append(result, anyOf[0]) |
|||
case len(anyOf) > 1: |
|||
result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...)) |
|||
} |
|||
|
|||
if commonRightCount > 0 { |
|||
result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...)) |
|||
} |
|||
|
|||
return ast.NewNode(ast.KindPattern, nil, result...) |
|||
} |
|||
|
|||
func commonChildren(nodes []*ast.Node) (commonLeft, commonRight []*ast.Node) { |
|||
if len(nodes) <= 1 { |
|||
return |
|||
} |
|||
|
|||
// find node that has least number of children
|
|||
idx := leastChildren(nodes) |
|||
if idx == -1 { |
|||
return |
|||
} |
|||
tree := nodes[idx] |
|||
treeLength := len(tree.Children) |
|||
|
|||
// allocate max able size for rightCommon slice
|
|||
// to get ability insert elements in reverse order (from end to start)
|
|||
// without sorting
|
|||
commonRight = make([]*ast.Node, treeLength) |
|||
lastRight := treeLength // will use this to get results as commonRight[lastRight:]
|
|||
|
|||
var ( |
|||
breakLeft bool |
|||
breakRight bool |
|||
commonTotal int |
|||
) |
|||
for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 { |
|||
treeLeft := tree.Children[i] |
|||
treeRight := tree.Children[j] |
|||
|
|||
for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ { |
|||
// skip least children node
|
|||
if k == idx { |
|||
continue |
|||
} |
|||
|
|||
restLeft := nodes[k].Children[i] |
|||
restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength] |
|||
|
|||
breakLeft = breakLeft || !treeLeft.Equal(restLeft) |
|||
|
|||
// disable searching for right common parts, if left part is already overlapping
|
|||
breakRight = breakRight || (!breakLeft && j <= i) |
|||
breakRight = breakRight || !treeRight.Equal(restRight) |
|||
} |
|||
|
|||
if !breakLeft { |
|||
commonTotal++ |
|||
commonLeft = append(commonLeft, treeLeft) |
|||
} |
|||
if !breakRight { |
|||
commonTotal++ |
|||
lastRight = j |
|||
commonRight[j] = treeRight |
|||
} |
|||
} |
|||
|
|||
commonRight = commonRight[lastRight:] |
|||
|
|||
return |
|||
} |
|||
|
|||
func appendIfUnique(target []*ast.Node, val *ast.Node) []*ast.Node { |
|||
for _, n := range target { |
|||
if reflect.DeepEqual(n, val) { |
|||
return target |
|||
} |
|||
} |
|||
return append(target, val) |
|||
} |
|||
|
|||
func areOfSameKind(nodes []*ast.Node, kind ast.Kind) bool { |
|||
for _, n := range nodes { |
|||
if n.Kind != kind { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func leastChildren(nodes []*ast.Node) int { |
|||
min := -1 |
|||
idx := -1 |
|||
for i, n := range nodes { |
|||
if idx == -1 || (len(n.Children) < min) { |
|||
min = len(n.Children) |
|||
idx = i |
|||
} |
|||
} |
|||
return idx |
|||
} |
|||
|
|||
func compileTreeChildren(tree *ast.Node, sep []rune) ([]match.Matcher, error) { |
|||
var matchers []match.Matcher |
|||
for _, desc := range tree.Children { |
|||
m, err := compile(desc, sep) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
matchers = append(matchers, optimizeMatcher(m)) |
|||
} |
|||
return matchers, nil |
|||
} |
|||
|
|||
func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) { |
|||
switch tree.Kind { |
|||
case ast.KindAnyOf: |
|||
// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
|
|||
if n := minimizeTree(tree); n != nil { |
|||
return compile(n, sep) |
|||
} |
|||
matchers, err := compileTreeChildren(tree, sep) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return match.NewAnyOf(matchers...), nil |
|||
|
|||
case ast.KindPattern: |
|||
if len(tree.Children) == 0 { |
|||
return match.NewNothing(), nil |
|||
} |
|||
matchers, err := compileTreeChildren(tree, sep) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
m, err = compileMatchers(minimizeMatchers(matchers)) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
case ast.KindAny: |
|||
m = match.NewAny(sep) |
|||
|
|||
case ast.KindSuper: |
|||
m = match.NewSuper() |
|||
|
|||
case ast.KindSingle: |
|||
m = match.NewSingle(sep) |
|||
|
|||
case ast.KindNothing: |
|||
m = match.NewNothing() |
|||
|
|||
case ast.KindList: |
|||
l := tree.Value.(ast.List) |
|||
m = match.NewList([]rune(l.Chars), l.Not) |
|||
|
|||
case ast.KindRange: |
|||
r := tree.Value.(ast.Range) |
|||
m = match.NewRange(r.Lo, r.Hi, r.Not) |
|||
|
|||
case ast.KindText: |
|||
t := tree.Value.(ast.Text) |
|||
m = match.NewText(t.Text) |
|||
|
|||
default: |
|||
return nil, fmt.Errorf("could not compile tree: unknown node type") |
|||
} |
|||
|
|||
return optimizeMatcher(m), nil |
|||
} |
|||
|
|||
func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) { |
|||
m, err := compile(tree, sep) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return m, nil |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
package glob |
|||
|
|||
import ( |
|||
"github.com/gobwas/glob/compiler" |
|||
"github.com/gobwas/glob/syntax" |
|||
) |
|||
|
|||
// Glob represents compiled glob pattern.
|
|||
type Glob interface { |
|||
Match(string) bool |
|||
} |
|||
|
|||
// Compile creates Glob for given pattern and strings (if any present after pattern) as separators.
|
|||
// The pattern syntax is:
|
|||
//
|
|||
// pattern:
|
|||
// { term }
|
|||
//
|
|||
// term:
|
|||
// `*` matches any sequence of non-separator characters
|
|||
// `**` matches any sequence of characters
|
|||
// `?` matches any single non-separator character
|
|||
// `[` [ `!` ] { character-range } `]`
|
|||
// character class (must be non-empty)
|
|||
// `{` pattern-list `}`
|
|||
// pattern alternatives
|
|||
// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`)
|
|||
// `\` c matches character c
|
|||
//
|
|||
// character-range:
|
|||
// c matches character c (c != `\\`, `-`, `]`)
|
|||
// `\` c matches character c
|
|||
// lo `-` hi matches character c for lo <= c <= hi
|
|||
//
|
|||
// pattern-list:
|
|||
// pattern { `,` pattern }
|
|||
// comma-separated (without spaces) patterns
|
|||
//
|
|||
func Compile(pattern string, separators ...rune) (Glob, error) { |
|||
ast, err := syntax.Parse(pattern) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
matcher, err := compiler.Compile(ast, separators) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return matcher, nil |
|||
} |
|||
|
|||
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
|
|||
func MustCompile(pattern string, separators ...rune) Glob { |
|||
g, err := Compile(pattern, separators...) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
|
|||
return g |
|||
} |
|||
|
|||
// QuoteMeta returns a string that quotes all glob pattern meta characters
|
|||
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
|
|||
func QuoteMeta(s string) string { |
|||
b := make([]byte, 2*len(s)) |
|||
|
|||
// a byte loop is correct because all meta characters are ASCII
|
|||
j := 0 |
|||
for i := 0; i < len(s); i++ { |
|||
if syntax.Special(s[i]) { |
|||
b[j] = '\\' |
|||
j++ |
|||
} |
|||
b[j] = s[i] |
|||
j++ |
|||
} |
|||
|
|||
return string(b[0:j]) |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/gobwas/glob/util/strings" |
|||
) |
|||
|
|||
type Any struct { |
|||
Separators []rune |
|||
} |
|||
|
|||
func NewAny(s []rune) Any { |
|||
return Any{s} |
|||
} |
|||
|
|||
func (self Any) Match(s string) bool { |
|||
return strings.IndexAnyRunes(s, self.Separators) == -1 |
|||
} |
|||
|
|||
func (self Any) Index(s string) (int, []int) { |
|||
found := strings.IndexAnyRunes(s, self.Separators) |
|||
switch found { |
|||
case -1: |
|||
case 0: |
|||
return 0, segments0 |
|||
default: |
|||
s = s[:found] |
|||
} |
|||
|
|||
segments := acquireSegments(len(s)) |
|||
for i := range s { |
|||
segments = append(segments, i) |
|||
} |
|||
segments = append(segments, len(s)) |
|||
|
|||
return 0, segments |
|||
} |
|||
|
|||
func (self Any) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Any) String() string { |
|||
return fmt.Sprintf("<any:![%s]>", string(self.Separators)) |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
package match |
|||
|
|||
import "fmt" |
|||
|
|||
type AnyOf struct { |
|||
Matchers Matchers |
|||
} |
|||
|
|||
func NewAnyOf(m ...Matcher) AnyOf { |
|||
return AnyOf{Matchers(m)} |
|||
} |
|||
|
|||
func (self *AnyOf) Add(m Matcher) error { |
|||
self.Matchers = append(self.Matchers, m) |
|||
return nil |
|||
} |
|||
|
|||
func (self AnyOf) Match(s string) bool { |
|||
for _, m := range self.Matchers { |
|||
if m.Match(s) { |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
func (self AnyOf) Index(s string) (int, []int) { |
|||
index := -1 |
|||
|
|||
segments := acquireSegments(len(s)) |
|||
for _, m := range self.Matchers { |
|||
idx, seg := m.Index(s) |
|||
if idx == -1 { |
|||
continue |
|||
} |
|||
|
|||
if index == -1 || idx < index { |
|||
index = idx |
|||
segments = append(segments[:0], seg...) |
|||
continue |
|||
} |
|||
|
|||
if idx > index { |
|||
continue |
|||
} |
|||
|
|||
// here idx == index
|
|||
segments = appendMerge(segments, seg) |
|||
} |
|||
|
|||
if index == -1 { |
|||
releaseSegments(segments) |
|||
return -1, nil |
|||
} |
|||
|
|||
return index, segments |
|||
} |
|||
|
|||
func (self AnyOf) Len() (l int) { |
|||
l = -1 |
|||
for _, m := range self.Matchers { |
|||
ml := m.Len() |
|||
switch { |
|||
case l == -1: |
|||
l = ml |
|||
continue |
|||
|
|||
case ml == -1: |
|||
return -1 |
|||
|
|||
case l != ml: |
|||
return -1 |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (self AnyOf) String() string { |
|||
return fmt.Sprintf("<any_of:[%s]>", self.Matchers) |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type BTree struct { |
|||
Value Matcher |
|||
Left Matcher |
|||
Right Matcher |
|||
ValueLengthRunes int |
|||
LeftLengthRunes int |
|||
RightLengthRunes int |
|||
LengthRunes int |
|||
} |
|||
|
|||
func NewBTree(Value, Left, Right Matcher) (tree BTree) { |
|||
tree.Value = Value |
|||
tree.Left = Left |
|||
tree.Right = Right |
|||
|
|||
lenOk := true |
|||
if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 { |
|||
lenOk = false |
|||
} |
|||
|
|||
if Left != nil { |
|||
if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 { |
|||
lenOk = false |
|||
} |
|||
} |
|||
|
|||
if Right != nil { |
|||
if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 { |
|||
lenOk = false |
|||
} |
|||
} |
|||
|
|||
if lenOk { |
|||
tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes |
|||
} else { |
|||
tree.LengthRunes = -1 |
|||
} |
|||
|
|||
return tree |
|||
} |
|||
|
|||
func (self BTree) Len() int { |
|||
return self.LengthRunes |
|||
} |
|||
|
|||
// todo?
|
|||
func (self BTree) Index(s string) (index int, segments []int) { |
|||
//inputLen := len(s)
|
|||
//// try to cut unnecessary parts
|
|||
//// by knowledge of length of right and left part
|
|||
//offset, limit := self.offsetLimit(inputLen)
|
|||
//for offset < limit {
|
|||
// // search for matching part in substring
|
|||
// vi, segments := self.Value.Index(s[offset:limit])
|
|||
// if index == -1 {
|
|||
// return -1, nil
|
|||
// }
|
|||
// if self.Left == nil {
|
|||
// if index != offset {
|
|||
// return -1, nil
|
|||
// }
|
|||
// } else {
|
|||
// left := s[:offset+vi]
|
|||
// i := self.Left.IndexSuffix(left)
|
|||
// if i == -1 {
|
|||
// return -1, nil
|
|||
// }
|
|||
// index = i
|
|||
// }
|
|||
// if self.Right != nil {
|
|||
// for _, seg := range segments {
|
|||
// right := s[:offset+vi+seg]
|
|||
// }
|
|||
// }
|
|||
|
|||
// l := s[:offset+index]
|
|||
// var left bool
|
|||
// if self.Left != nil {
|
|||
// left = self.Left.Index(l)
|
|||
// } else {
|
|||
// left = l == ""
|
|||
// }
|
|||
//}
|
|||
|
|||
return -1, nil |
|||
} |
|||
|
|||
func (self BTree) Match(s string) bool { |
|||
inputLen := len(s) |
|||
// try to cut unnecessary parts
|
|||
// by knowledge of length of right and left part
|
|||
offset, limit := self.offsetLimit(inputLen) |
|||
|
|||
for offset < limit { |
|||
// search for matching part in substring
|
|||
index, segments := self.Value.Index(s[offset:limit]) |
|||
if index == -1 { |
|||
releaseSegments(segments) |
|||
return false |
|||
} |
|||
|
|||
l := s[:offset+index] |
|||
var left bool |
|||
if self.Left != nil { |
|||
left = self.Left.Match(l) |
|||
} else { |
|||
left = l == "" |
|||
} |
|||
|
|||
if left { |
|||
for i := len(segments) - 1; i >= 0; i-- { |
|||
length := segments[i] |
|||
|
|||
var right bool |
|||
var r string |
|||
// if there is no string for the right branch
|
|||
if inputLen <= offset+index+length { |
|||
r = "" |
|||
} else { |
|||
r = s[offset+index+length:] |
|||
} |
|||
|
|||
if self.Right != nil { |
|||
right = self.Right.Match(r) |
|||
} else { |
|||
right = r == "" |
|||
} |
|||
|
|||
if right { |
|||
releaseSegments(segments) |
|||
return true |
|||
} |
|||
} |
|||
} |
|||
|
|||
_, step := utf8.DecodeRuneInString(s[offset+index:]) |
|||
offset += index + step |
|||
|
|||
releaseSegments(segments) |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
func (self BTree) offsetLimit(inputLen int) (offset int, limit int) { |
|||
// self.Length, self.RLen and self.LLen are values meaning the length of runes for each part
|
|||
// here we manipulating byte length for better optimizations
|
|||
// but these checks still works, cause minLen of 1-rune string is 1 byte.
|
|||
if self.LengthRunes != -1 && self.LengthRunes > inputLen { |
|||
return 0, 0 |
|||
} |
|||
if self.LeftLengthRunes >= 0 { |
|||
offset = self.LeftLengthRunes |
|||
} |
|||
if self.RightLengthRunes >= 0 { |
|||
limit = inputLen - self.RightLengthRunes |
|||
} else { |
|||
limit = inputLen |
|||
} |
|||
return offset, limit |
|||
} |
|||
|
|||
func (self BTree) String() string { |
|||
const n string = "<nil>" |
|||
var l, r string |
|||
if self.Left == nil { |
|||
l = n |
|||
} else { |
|||
l = self.Left.String() |
|||
} |
|||
if self.Right == nil { |
|||
r = n |
|||
} else { |
|||
r = self.Right.String() |
|||
} |
|||
|
|||
return fmt.Sprintf("<btree:[%s<-%s->%s]>", l, self.Value, r) |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
type Contains struct { |
|||
Needle string |
|||
Not bool |
|||
} |
|||
|
|||
func NewContains(needle string, not bool) Contains { |
|||
return Contains{needle, not} |
|||
} |
|||
|
|||
func (self Contains) Match(s string) bool { |
|||
return strings.Contains(s, self.Needle) != self.Not |
|||
} |
|||
|
|||
func (self Contains) Index(s string) (int, []int) { |
|||
var offset int |
|||
|
|||
idx := strings.Index(s, self.Needle) |
|||
|
|||
if !self.Not { |
|||
if idx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
offset = idx + len(self.Needle) |
|||
if len(s) <= offset { |
|||
return 0, []int{offset} |
|||
} |
|||
s = s[offset:] |
|||
} else if idx != -1 { |
|||
s = s[:idx] |
|||
} |
|||
|
|||
segments := acquireSegments(len(s) + 1) |
|||
for i := range s { |
|||
segments = append(segments, offset+i) |
|||
} |
|||
|
|||
return 0, append(segments, offset+len(s)) |
|||
} |
|||
|
|||
func (self Contains) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Contains) String() string { |
|||
var not string |
|||
if self.Not { |
|||
not = "!" |
|||
} |
|||
return fmt.Sprintf("<contains:%s[%s]>", not, self.Needle) |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
) |
|||
|
|||
type EveryOf struct { |
|||
Matchers Matchers |
|||
} |
|||
|
|||
func NewEveryOf(m ...Matcher) EveryOf { |
|||
return EveryOf{Matchers(m)} |
|||
} |
|||
|
|||
func (self *EveryOf) Add(m Matcher) error { |
|||
self.Matchers = append(self.Matchers, m) |
|||
return nil |
|||
} |
|||
|
|||
func (self EveryOf) Len() (l int) { |
|||
for _, m := range self.Matchers { |
|||
if ml := m.Len(); l > 0 { |
|||
l += ml |
|||
} else { |
|||
return -1 |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (self EveryOf) Index(s string) (int, []int) { |
|||
var index int |
|||
var offset int |
|||
|
|||
// make `in` with cap as len(s),
|
|||
// cause it is the maximum size of output segments values
|
|||
next := acquireSegments(len(s)) |
|||
current := acquireSegments(len(s)) |
|||
|
|||
sub := s |
|||
for i, m := range self.Matchers { |
|||
idx, seg := m.Index(sub) |
|||
if idx == -1 { |
|||
releaseSegments(next) |
|||
releaseSegments(current) |
|||
return -1, nil |
|||
} |
|||
|
|||
if i == 0 { |
|||
// we use copy here instead of `current = seg`
|
|||
// cause seg is a slice from reusable buffer `in`
|
|||
// and it could be overwritten in next iteration
|
|||
current = append(current, seg...) |
|||
} else { |
|||
// clear the next
|
|||
next = next[:0] |
|||
|
|||
delta := index - (idx + offset) |
|||
for _, ex := range current { |
|||
for _, n := range seg { |
|||
if ex+delta == n { |
|||
next = append(next, n) |
|||
} |
|||
} |
|||
} |
|||
|
|||
if len(next) == 0 { |
|||
releaseSegments(next) |
|||
releaseSegments(current) |
|||
return -1, nil |
|||
} |
|||
|
|||
current = append(current[:0], next...) |
|||
} |
|||
|
|||
index = idx + offset |
|||
sub = s[index:] |
|||
offset += idx |
|||
} |
|||
|
|||
releaseSegments(next) |
|||
|
|||
return index, current |
|||
} |
|||
|
|||
func (self EveryOf) Match(s string) bool { |
|||
for _, m := range self.Matchers { |
|||
if !m.Match(s) { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
func (self EveryOf) String() string { |
|||
return fmt.Sprintf("<every_of:[%s]>", self.Matchers) |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/gobwas/glob/util/runes" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type List struct { |
|||
List []rune |
|||
Not bool |
|||
} |
|||
|
|||
func NewList(list []rune, not bool) List { |
|||
return List{list, not} |
|||
} |
|||
|
|||
func (self List) Match(s string) bool { |
|||
r, w := utf8.DecodeRuneInString(s) |
|||
if len(s) > w { |
|||
return false |
|||
} |
|||
|
|||
inList := runes.IndexRune(self.List, r) != -1 |
|||
return inList == !self.Not |
|||
} |
|||
|
|||
func (self List) Len() int { |
|||
return lenOne |
|||
} |
|||
|
|||
func (self List) Index(s string) (int, []int) { |
|||
for i, r := range s { |
|||
if self.Not == (runes.IndexRune(self.List, r) == -1) { |
|||
return i, segmentsByRuneLength[utf8.RuneLen(r)] |
|||
} |
|||
} |
|||
|
|||
return -1, nil |
|||
} |
|||
|
|||
func (self List) String() string { |
|||
var not string |
|||
if self.Not { |
|||
not = "!" |
|||
} |
|||
|
|||
return fmt.Sprintf("<list:%s[%s]>", not, string(self.List)) |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
package match |
|||
|
|||
// todo common table of rune's length
|
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
const lenOne = 1 |
|||
const lenZero = 0 |
|||
const lenNo = -1 |
|||
|
|||
type Matcher interface { |
|||
Match(string) bool |
|||
Index(string) (int, []int) |
|||
Len() int |
|||
String() string |
|||
} |
|||
|
|||
type Matchers []Matcher |
|||
|
|||
func (m Matchers) String() string { |
|||
var s []string |
|||
for _, matcher := range m { |
|||
s = append(s, fmt.Sprint(matcher)) |
|||
} |
|||
|
|||
return fmt.Sprintf("%s", strings.Join(s, ",")) |
|||
} |
|||
|
|||
// appendMerge merges and sorts given already SORTED and UNIQUE segments.
|
|||
func appendMerge(target, sub []int) []int { |
|||
lt, ls := len(target), len(sub) |
|||
out := make([]int, 0, lt+ls) |
|||
|
|||
for x, y := 0, 0; x < lt || y < ls; { |
|||
if x >= lt { |
|||
out = append(out, sub[y:]...) |
|||
break |
|||
} |
|||
|
|||
if y >= ls { |
|||
out = append(out, target[x:]...) |
|||
break |
|||
} |
|||
|
|||
xValue := target[x] |
|||
yValue := sub[y] |
|||
|
|||
switch { |
|||
|
|||
case xValue == yValue: |
|||
out = append(out, xValue) |
|||
x++ |
|||
y++ |
|||
|
|||
case xValue < yValue: |
|||
out = append(out, xValue) |
|||
x++ |
|||
|
|||
case yValue < xValue: |
|||
out = append(out, yValue) |
|||
y++ |
|||
|
|||
} |
|||
} |
|||
|
|||
target = append(target[:0], out...) |
|||
|
|||
return target |
|||
} |
|||
|
|||
func reverseSegments(input []int) { |
|||
l := len(input) |
|||
m := l / 2 |
|||
|
|||
for i := 0; i < m; i++ { |
|||
input[i], input[l-i-1] = input[l-i-1], input[i] |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type Max struct { |
|||
Limit int |
|||
} |
|||
|
|||
func NewMax(l int) Max { |
|||
return Max{l} |
|||
} |
|||
|
|||
func (self Max) Match(s string) bool { |
|||
var l int |
|||
for range s { |
|||
l += 1 |
|||
if l > self.Limit { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
func (self Max) Index(s string) (int, []int) { |
|||
segments := acquireSegments(self.Limit + 1) |
|||
segments = append(segments, 0) |
|||
var count int |
|||
for i, r := range s { |
|||
count++ |
|||
if count > self.Limit { |
|||
break |
|||
} |
|||
segments = append(segments, i+utf8.RuneLen(r)) |
|||
} |
|||
|
|||
return 0, segments |
|||
} |
|||
|
|||
func (self Max) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Max) String() string { |
|||
return fmt.Sprintf("<max:%d>", self.Limit) |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type Min struct { |
|||
Limit int |
|||
} |
|||
|
|||
func NewMin(l int) Min { |
|||
return Min{l} |
|||
} |
|||
|
|||
func (self Min) Match(s string) bool { |
|||
var l int |
|||
for range s { |
|||
l += 1 |
|||
if l >= self.Limit { |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
func (self Min) Index(s string) (int, []int) { |
|||
var count int |
|||
|
|||
c := len(s) - self.Limit + 1 |
|||
if c <= 0 { |
|||
return -1, nil |
|||
} |
|||
|
|||
segments := acquireSegments(c) |
|||
for i, r := range s { |
|||
count++ |
|||
if count >= self.Limit { |
|||
segments = append(segments, i+utf8.RuneLen(r)) |
|||
} |
|||
} |
|||
|
|||
if len(segments) == 0 { |
|||
return -1, nil |
|||
} |
|||
|
|||
return 0, segments |
|||
} |
|||
|
|||
func (self Min) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Min) String() string { |
|||
return fmt.Sprintf("<min:%d>", self.Limit) |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
) |
|||
|
|||
type Nothing struct{} |
|||
|
|||
func NewNothing() Nothing { |
|||
return Nothing{} |
|||
} |
|||
|
|||
func (self Nothing) Match(s string) bool { |
|||
return len(s) == 0 |
|||
} |
|||
|
|||
func (self Nothing) Index(s string) (int, []int) { |
|||
return 0, segments0 |
|||
} |
|||
|
|||
func (self Nothing) Len() int { |
|||
return lenZero |
|||
} |
|||
|
|||
func (self Nothing) String() string { |
|||
return fmt.Sprintf("<nothing>") |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type Prefix struct { |
|||
Prefix string |
|||
} |
|||
|
|||
func NewPrefix(p string) Prefix { |
|||
return Prefix{p} |
|||
} |
|||
|
|||
func (self Prefix) Index(s string) (int, []int) { |
|||
idx := strings.Index(s, self.Prefix) |
|||
if idx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
length := len(self.Prefix) |
|||
var sub string |
|||
if len(s) > idx+length { |
|||
sub = s[idx+length:] |
|||
} else { |
|||
sub = "" |
|||
} |
|||
|
|||
segments := acquireSegments(len(sub) + 1) |
|||
segments = append(segments, length) |
|||
for i, r := range sub { |
|||
segments = append(segments, length+i+utf8.RuneLen(r)) |
|||
} |
|||
|
|||
return idx, segments |
|||
} |
|||
|
|||
func (self Prefix) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Prefix) Match(s string) bool { |
|||
return strings.HasPrefix(s, self.Prefix) |
|||
} |
|||
|
|||
func (self Prefix) String() string { |
|||
return fmt.Sprintf("<prefix:%s>", self.Prefix) |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
"unicode/utf8" |
|||
|
|||
sutil "github.com/gobwas/glob/util/strings" |
|||
) |
|||
|
|||
type PrefixAny struct { |
|||
Prefix string |
|||
Separators []rune |
|||
} |
|||
|
|||
func NewPrefixAny(s string, sep []rune) PrefixAny { |
|||
return PrefixAny{s, sep} |
|||
} |
|||
|
|||
func (self PrefixAny) Index(s string) (int, []int) { |
|||
idx := strings.Index(s, self.Prefix) |
|||
if idx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
n := len(self.Prefix) |
|||
sub := s[idx+n:] |
|||
i := sutil.IndexAnyRunes(sub, self.Separators) |
|||
if i > -1 { |
|||
sub = sub[:i] |
|||
} |
|||
|
|||
seg := acquireSegments(len(sub) + 1) |
|||
seg = append(seg, n) |
|||
for i, r := range sub { |
|||
seg = append(seg, n+i+utf8.RuneLen(r)) |
|||
} |
|||
|
|||
return idx, seg |
|||
} |
|||
|
|||
func (self PrefixAny) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self PrefixAny) Match(s string) bool { |
|||
if !strings.HasPrefix(s, self.Prefix) { |
|||
return false |
|||
} |
|||
return sutil.IndexAnyRunes(s[len(self.Prefix):], self.Separators) == -1 |
|||
} |
|||
|
|||
func (self PrefixAny) String() string { |
|||
return fmt.Sprintf("<prefix_any:%s![%s]>", self.Prefix, string(self.Separators)) |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
type PrefixSuffix struct { |
|||
Prefix, Suffix string |
|||
} |
|||
|
|||
func NewPrefixSuffix(p, s string) PrefixSuffix { |
|||
return PrefixSuffix{p, s} |
|||
} |
|||
|
|||
func (self PrefixSuffix) Index(s string) (int, []int) { |
|||
prefixIdx := strings.Index(s, self.Prefix) |
|||
if prefixIdx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
suffixLen := len(self.Suffix) |
|||
if suffixLen <= 0 { |
|||
return prefixIdx, []int{len(s) - prefixIdx} |
|||
} |
|||
|
|||
if (len(s) - prefixIdx) <= 0 { |
|||
return -1, nil |
|||
} |
|||
|
|||
segments := acquireSegments(len(s) - prefixIdx) |
|||
for sub := s[prefixIdx:]; ; { |
|||
suffixIdx := strings.LastIndex(sub, self.Suffix) |
|||
if suffixIdx == -1 { |
|||
break |
|||
} |
|||
|
|||
segments = append(segments, suffixIdx+suffixLen) |
|||
sub = sub[:suffixIdx] |
|||
} |
|||
|
|||
if len(segments) == 0 { |
|||
releaseSegments(segments) |
|||
return -1, nil |
|||
} |
|||
|
|||
reverseSegments(segments) |
|||
|
|||
return prefixIdx, segments |
|||
} |
|||
|
|||
func (self PrefixSuffix) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self PrefixSuffix) Match(s string) bool { |
|||
return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix) |
|||
} |
|||
|
|||
func (self PrefixSuffix) String() string { |
|||
return fmt.Sprintf("<prefix_suffix:[%s,%s]>", self.Prefix, self.Suffix) |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type Range struct { |
|||
Lo, Hi rune |
|||
Not bool |
|||
} |
|||
|
|||
func NewRange(lo, hi rune, not bool) Range { |
|||
return Range{lo, hi, not} |
|||
} |
|||
|
|||
func (self Range) Len() int { |
|||
return lenOne |
|||
} |
|||
|
|||
func (self Range) Match(s string) bool { |
|||
r, w := utf8.DecodeRuneInString(s) |
|||
if len(s) > w { |
|||
return false |
|||
} |
|||
|
|||
inRange := r >= self.Lo && r <= self.Hi |
|||
|
|||
return inRange == !self.Not |
|||
} |
|||
|
|||
func (self Range) Index(s string) (int, []int) { |
|||
for i, r := range s { |
|||
if self.Not != (r >= self.Lo && r <= self.Hi) { |
|||
return i, segmentsByRuneLength[utf8.RuneLen(r)] |
|||
} |
|||
} |
|||
|
|||
return -1, nil |
|||
} |
|||
|
|||
func (self Range) String() string { |
|||
var not string |
|||
if self.Not { |
|||
not = "!" |
|||
} |
|||
return fmt.Sprintf("<range:%s[%s,%s]>", not, string(self.Lo), string(self.Hi)) |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
) |
|||
|
|||
type Row struct { |
|||
Matchers Matchers |
|||
RunesLength int |
|||
Segments []int |
|||
} |
|||
|
|||
func NewRow(len int, m ...Matcher) Row { |
|||
return Row{ |
|||
Matchers: Matchers(m), |
|||
RunesLength: len, |
|||
Segments: []int{len}, |
|||
} |
|||
} |
|||
|
|||
func (self Row) matchAll(s string) bool { |
|||
var idx int |
|||
for _, m := range self.Matchers { |
|||
length := m.Len() |
|||
|
|||
var next, i int |
|||
for next = range s[idx:] { |
|||
i++ |
|||
if i == length { |
|||
break |
|||
} |
|||
} |
|||
|
|||
if i < length || !m.Match(s[idx:idx+next+1]) { |
|||
return false |
|||
} |
|||
|
|||
idx += next + 1 |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
func (self Row) lenOk(s string) bool { |
|||
var i int |
|||
for range s { |
|||
i++ |
|||
if i > self.RunesLength { |
|||
return false |
|||
} |
|||
} |
|||
return self.RunesLength == i |
|||
} |
|||
|
|||
func (self Row) Match(s string) bool { |
|||
return self.lenOk(s) && self.matchAll(s) |
|||
} |
|||
|
|||
func (self Row) Len() (l int) { |
|||
return self.RunesLength |
|||
} |
|||
|
|||
func (self Row) Index(s string) (int, []int) { |
|||
for i := range s { |
|||
if len(s[i:]) < self.RunesLength { |
|||
break |
|||
} |
|||
if self.matchAll(s[i:]) { |
|||
return i, self.Segments |
|||
} |
|||
} |
|||
return -1, nil |
|||
} |
|||
|
|||
func (self Row) String() string { |
|||
return fmt.Sprintf("<row_%d:[%s]>", self.RunesLength, self.Matchers) |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"sync" |
|||
) |
|||
|
|||
type SomePool interface { |
|||
Get() []int |
|||
Put([]int) |
|||
} |
|||
|
|||
var segmentsPools [1024]sync.Pool |
|||
|
|||
func toPowerOfTwo(v int) int { |
|||
v-- |
|||
v |= v >> 1 |
|||
v |= v >> 2 |
|||
v |= v >> 4 |
|||
v |= v >> 8 |
|||
v |= v >> 16 |
|||
v++ |
|||
|
|||
return v |
|||
} |
|||
|
|||
const ( |
|||
cacheFrom = 16 |
|||
cacheToAndHigher = 1024 |
|||
cacheFromIndex = 15 |
|||
cacheToAndHigherIndex = 1023 |
|||
) |
|||
|
|||
var ( |
|||
segments0 = []int{0} |
|||
segments1 = []int{1} |
|||
segments2 = []int{2} |
|||
segments3 = []int{3} |
|||
segments4 = []int{4} |
|||
) |
|||
|
|||
var segmentsByRuneLength [5][]int = [5][]int{ |
|||
0: segments0, |
|||
1: segments1, |
|||
2: segments2, |
|||
3: segments3, |
|||
4: segments4, |
|||
} |
|||
|
|||
func init() { |
|||
for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 { |
|||
func(i int) { |
|||
segmentsPools[i-1] = sync.Pool{New: func() interface{} { |
|||
return make([]int, 0, i) |
|||
}} |
|||
}(i) |
|||
} |
|||
} |
|||
|
|||
func getTableIndex(c int) int { |
|||
p := toPowerOfTwo(c) |
|||
switch { |
|||
case p >= cacheToAndHigher: |
|||
return cacheToAndHigherIndex |
|||
case p <= cacheFrom: |
|||
return cacheFromIndex |
|||
default: |
|||
return p - 1 |
|||
} |
|||
} |
|||
|
|||
func acquireSegments(c int) []int { |
|||
// make []int with less capacity than cacheFrom
|
|||
// is faster than acquiring it from pool
|
|||
if c < cacheFrom { |
|||
return make([]int, 0, c) |
|||
} |
|||
|
|||
return segmentsPools[getTableIndex(c)].Get().([]int)[:0] |
|||
} |
|||
|
|||
func releaseSegments(s []int) { |
|||
c := cap(s) |
|||
|
|||
// make []int with less capacity than cacheFrom
|
|||
// is faster than acquiring it from pool
|
|||
if c < cacheFrom { |
|||
return |
|||
} |
|||
|
|||
segmentsPools[getTableIndex(c)].Put(s) |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/gobwas/glob/util/runes" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
// single represents ?
|
|||
type Single struct { |
|||
Separators []rune |
|||
} |
|||
|
|||
func NewSingle(s []rune) Single { |
|||
return Single{s} |
|||
} |
|||
|
|||
func (self Single) Match(s string) bool { |
|||
r, w := utf8.DecodeRuneInString(s) |
|||
if len(s) > w { |
|||
return false |
|||
} |
|||
|
|||
return runes.IndexRune(self.Separators, r) == -1 |
|||
} |
|||
|
|||
func (self Single) Len() int { |
|||
return lenOne |
|||
} |
|||
|
|||
func (self Single) Index(s string) (int, []int) { |
|||
for i, r := range s { |
|||
if runes.IndexRune(self.Separators, r) == -1 { |
|||
return i, segmentsByRuneLength[utf8.RuneLen(r)] |
|||
} |
|||
} |
|||
|
|||
return -1, nil |
|||
} |
|||
|
|||
func (self Single) String() string { |
|||
return fmt.Sprintf("<single:![%s]>", string(self.Separators)) |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
) |
|||
|
|||
type Suffix struct { |
|||
Suffix string |
|||
} |
|||
|
|||
func NewSuffix(s string) Suffix { |
|||
return Suffix{s} |
|||
} |
|||
|
|||
func (self Suffix) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Suffix) Match(s string) bool { |
|||
return strings.HasSuffix(s, self.Suffix) |
|||
} |
|||
|
|||
func (self Suffix) Index(s string) (int, []int) { |
|||
idx := strings.Index(s, self.Suffix) |
|||
if idx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
return 0, []int{idx + len(self.Suffix)} |
|||
} |
|||
|
|||
func (self Suffix) String() string { |
|||
return fmt.Sprintf("<suffix:%s>", self.Suffix) |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
|
|||
sutil "github.com/gobwas/glob/util/strings" |
|||
) |
|||
|
|||
type SuffixAny struct { |
|||
Suffix string |
|||
Separators []rune |
|||
} |
|||
|
|||
func NewSuffixAny(s string, sep []rune) SuffixAny { |
|||
return SuffixAny{s, sep} |
|||
} |
|||
|
|||
func (self SuffixAny) Index(s string) (int, []int) { |
|||
idx := strings.Index(s, self.Suffix) |
|||
if idx == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
i := sutil.LastIndexAnyRunes(s[:idx], self.Separators) + 1 |
|||
|
|||
return i, []int{idx + len(self.Suffix) - i} |
|||
} |
|||
|
|||
func (self SuffixAny) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self SuffixAny) Match(s string) bool { |
|||
if !strings.HasSuffix(s, self.Suffix) { |
|||
return false |
|||
} |
|||
return sutil.IndexAnyRunes(s[:len(s)-len(self.Suffix)], self.Separators) == -1 |
|||
} |
|||
|
|||
func (self SuffixAny) String() string { |
|||
return fmt.Sprintf("<suffix_any:![%s]%s>", string(self.Separators), self.Suffix) |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
) |
|||
|
|||
type Super struct{} |
|||
|
|||
func NewSuper() Super { |
|||
return Super{} |
|||
} |
|||
|
|||
func (self Super) Match(s string) bool { |
|||
return true |
|||
} |
|||
|
|||
func (self Super) Len() int { |
|||
return lenNo |
|||
} |
|||
|
|||
func (self Super) Index(s string) (int, []int) { |
|||
segments := acquireSegments(len(s) + 1) |
|||
for i := range s { |
|||
segments = append(segments, i) |
|||
} |
|||
segments = append(segments, len(s)) |
|||
|
|||
return 0, segments |
|||
} |
|||
|
|||
func (self Super) String() string { |
|||
return fmt.Sprintf("<super>") |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
package match |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strings" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
// raw represents raw string to match
|
|||
type Text struct { |
|||
Str string |
|||
RunesLength int |
|||
BytesLength int |
|||
Segments []int |
|||
} |
|||
|
|||
func NewText(s string) Text { |
|||
return Text{ |
|||
Str: s, |
|||
RunesLength: utf8.RuneCountInString(s), |
|||
BytesLength: len(s), |
|||
Segments: []int{len(s)}, |
|||
} |
|||
} |
|||
|
|||
func (self Text) Match(s string) bool { |
|||
return self.Str == s |
|||
} |
|||
|
|||
func (self Text) Len() int { |
|||
return self.RunesLength |
|||
} |
|||
|
|||
func (self Text) Index(s string) (int, []int) { |
|||
index := strings.Index(s, self.Str) |
|||
if index == -1 { |
|||
return -1, nil |
|||
} |
|||
|
|||
return index, self.Segments |
|||
} |
|||
|
|||
func (self Text) String() string { |
|||
return fmt.Sprintf("<text:`%v`>", self.Str) |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
# glob.[go](https://golang.org) |
|||
|
|||
[![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url] |
|||
|
|||
> Go Globbing Library. |
|||
|
|||
## Install |
|||
|
|||
```shell |
|||
go get github.com/gobwas/glob |
|||
``` |
|||
|
|||
## Example |
|||
|
|||
```go |
|||
|
|||
package main |
|||
|
|||
import "github.com/gobwas/glob" |
|||
|
|||
func main() { |
|||
var g glob.Glob |
|||
|
|||
// create simple glob |
|||
g = glob.MustCompile("*.github.com") |
|||
g.Match("api.github.com") // true |
|||
|
|||
// quote meta characters and then create simple glob |
|||
g = glob.MustCompile(glob.QuoteMeta("*.github.com")) |
|||
g.Match("*.github.com") // true |
|||
|
|||
// create new glob with set of delimiters as ["."] |
|||
g = glob.MustCompile("api.*.com", '.') |
|||
g.Match("api.github.com") // true |
|||
g.Match("api.gi.hub.com") // false |
|||
|
|||
// create new glob with set of delimiters as ["."] |
|||
// but now with super wildcard |
|||
g = glob.MustCompile("api.**.com", '.') |
|||
g.Match("api.github.com") // true |
|||
g.Match("api.gi.hub.com") // true |
|||
|
|||
// create glob with single symbol wildcard |
|||
g = glob.MustCompile("?at") |
|||
g.Match("cat") // true |
|||
g.Match("fat") // true |
|||
g.Match("at") // false |
|||
|
|||
// create glob with single symbol wildcard and delimiters ['f'] |
|||
g = glob.MustCompile("?at", 'f') |
|||
g.Match("cat") // true |
|||
g.Match("fat") // false |
|||
g.Match("at") // false |
|||
|
|||
// create glob with character-list matchers |
|||
g = glob.MustCompile("[abc]at") |
|||
g.Match("cat") // true |
|||
g.Match("bat") // true |
|||
g.Match("fat") // false |
|||
g.Match("at") // false |
|||
|
|||
// create glob with character-list matchers |
|||
g = glob.MustCompile("[!abc]at") |
|||
g.Match("cat") // false |
|||
g.Match("bat") // false |
|||
g.Match("fat") // true |
|||
g.Match("at") // false |
|||
|
|||
// create glob with character-range matchers |
|||
g = glob.MustCompile("[a-c]at") |
|||
g.Match("cat") // true |
|||
g.Match("bat") // true |
|||
g.Match("fat") // false |
|||
g.Match("at") // false |
|||
|
|||
// create glob with character-range matchers |
|||
g = glob.MustCompile("[!a-c]at") |
|||
g.Match("cat") // false |
|||
g.Match("bat") // false |
|||
g.Match("fat") // true |
|||
g.Match("at") // false |
|||
|
|||
// create glob with pattern-alternatives list |
|||
g = glob.MustCompile("{cat,bat,[fr]at}") |
|||
g.Match("cat") // true |
|||
g.Match("bat") // true |
|||
g.Match("fat") // true |
|||
g.Match("rat") // true |
|||
g.Match("at") // false |
|||
g.Match("zat") // false |
|||
} |
|||
|
|||
``` |
|||
|
|||
## Performance |
|||
|
|||
This library is created for compile-once patterns. This means, that compilation could take time, but |
|||
strings matching is done faster, than in case when always parsing template. |
|||
|
|||
If you will not use compiled `glob.Glob` object, and do `g := glob.MustCompile(pattern); g.Match(...)` every time, then your code will be much more slower. |
|||
|
|||
Run `go test -bench=.` from source root to see the benchmarks: |
|||
|
|||
Pattern | Fixture | Match | Speed (ns/op) |
|||
--------|---------|-------|-------------- |
|||
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 432 |
|||
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 199 |
|||
`https://*.google.*` | `https://account.google.com` | `true` | 96 |
|||
`https://*.google.*` | `https://google.com` | `false` | 66 |
|||
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 163 |
|||
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 197 |
|||
`{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 22 |
|||
`{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 24 |
|||
`abc*` | `abcdef` | `true` | 8.15 |
|||
`abc*` | `af` | `false` | 5.68 |
|||
`*def` | `abcdef` | `true` | 8.84 |
|||
`*def` | `af` | `false` | 5.74 |
|||
`ab*ef` | `abcdef` | `true` | 15.2 |
|||
`ab*ef` | `af` | `false` | 10.4 |
|||
|
|||
The same things with `regexp` package: |
|||
|
|||
Pattern | Fixture | Match | Speed (ns/op) |
|||
--------|---------|-------|-------------- |
|||
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 2553 |
|||
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 1383 |
|||
`^https:\/\/.*\.google\..*$` | `https://account.google.com` | `true` | 1205 |
|||
`^https:\/\/.*\.google\..*$` | `https://google.com` | `false` | 767 |
|||
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://yahoo.com` | `true` | 1435 |
|||
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://google.com` | `false` | 1674 |
|||
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `https://safe.gobwas.com` | `true` | 1039 |
|||
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `http://safe.gobwas.com` | `false` | 272 |
|||
`^abc.*$` | `abcdef` | `true` | 237 |
|||
`^abc.*$` | `af` | `false` | 100 |
|||
`^.*def$` | `abcdef` | `true` | 464 |
|||
`^.*def$` | `af` | `false` | 265 |
|||
`^ab.*ef$` | `abcdef` | `true` | 375 |
|||
`^ab.*ef$` | `af` | `false` | 145 |
|||
|
|||
[godoc-image]: https://godoc.org/github.com/gobwas/glob?status.svg |
|||
[godoc-url]: https://godoc.org/github.com/gobwas/glob |
|||
[travis-image]: https://travis-ci.org/gobwas/glob.svg?branch=master |
|||
[travis-url]: https://travis-ci.org/gobwas/glob |
|||
|
|||
## Syntax |
|||
|
|||
Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm), |
|||
except that `**` is aka super-asterisk, that do not sensitive for separators. |
|||
@ -0,0 +1,122 @@ |
|||
package ast |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
) |
|||
|
|||
type Node struct { |
|||
Parent *Node |
|||
Children []*Node |
|||
Value interface{} |
|||
Kind Kind |
|||
} |
|||
|
|||
func NewNode(k Kind, v interface{}, ch ...*Node) *Node { |
|||
n := &Node{ |
|||
Kind: k, |
|||
Value: v, |
|||
} |
|||
for _, c := range ch { |
|||
Insert(n, c) |
|||
} |
|||
return n |
|||
} |
|||
|
|||
func (a *Node) Equal(b *Node) bool { |
|||
if a.Kind != b.Kind { |
|||
return false |
|||
} |
|||
if a.Value != b.Value { |
|||
return false |
|||
} |
|||
if len(a.Children) != len(b.Children) { |
|||
return false |
|||
} |
|||
for i, c := range a.Children { |
|||
if !c.Equal(b.Children[i]) { |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func (a *Node) String() string { |
|||
var buf bytes.Buffer |
|||
buf.WriteString(a.Kind.String()) |
|||
if a.Value != nil { |
|||
buf.WriteString(" =") |
|||
buf.WriteString(fmt.Sprintf("%v", a.Value)) |
|||
} |
|||
if len(a.Children) > 0 { |
|||
buf.WriteString(" [") |
|||
for i, c := range a.Children { |
|||
if i > 0 { |
|||
buf.WriteString(", ") |
|||
} |
|||
buf.WriteString(c.String()) |
|||
} |
|||
buf.WriteString("]") |
|||
} |
|||
return buf.String() |
|||
} |
|||
|
|||
func Insert(parent *Node, children ...*Node) { |
|||
parent.Children = append(parent.Children, children...) |
|||
for _, ch := range children { |
|||
ch.Parent = parent |
|||
} |
|||
} |
|||
|
|||
type List struct { |
|||
Not bool |
|||
Chars string |
|||
} |
|||
|
|||
type Range struct { |
|||
Not bool |
|||
Lo, Hi rune |
|||
} |
|||
|
|||
type Text struct { |
|||
Text string |
|||
} |
|||
|
|||
type Kind int |
|||
|
|||
const ( |
|||
KindNothing Kind = iota |
|||
KindPattern |
|||
KindList |
|||
KindRange |
|||
KindText |
|||
KindAny |
|||
KindSuper |
|||
KindSingle |
|||
KindAnyOf |
|||
) |
|||
|
|||
func (k Kind) String() string { |
|||
switch k { |
|||
case KindNothing: |
|||
return "Nothing" |
|||
case KindPattern: |
|||
return "Pattern" |
|||
case KindList: |
|||
return "List" |
|||
case KindRange: |
|||
return "Range" |
|||
case KindText: |
|||
return "Text" |
|||
case KindAny: |
|||
return "Any" |
|||
case KindSuper: |
|||
return "Super" |
|||
case KindSingle: |
|||
return "Single" |
|||
case KindAnyOf: |
|||
return "AnyOf" |
|||
default: |
|||
return "" |
|||
} |
|||
} |
|||
@ -0,0 +1,157 @@ |
|||
package ast |
|||
|
|||
import ( |
|||
"errors" |
|||
"fmt" |
|||
"github.com/gobwas/glob/syntax/lexer" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
type Lexer interface { |
|||
Next() lexer.Token |
|||
} |
|||
|
|||
type parseFn func(*Node, Lexer) (parseFn, *Node, error) |
|||
|
|||
func Parse(lexer Lexer) (*Node, error) { |
|||
var parser parseFn |
|||
|
|||
root := NewNode(KindPattern, nil) |
|||
|
|||
var ( |
|||
tree *Node |
|||
err error |
|||
) |
|||
for parser, tree = parserMain, root; parser != nil; { |
|||
parser, tree, err = parser(tree, lexer) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
} |
|||
|
|||
return root, nil |
|||
} |
|||
|
|||
func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) { |
|||
for { |
|||
token := lex.Next() |
|||
switch token.Type { |
|||
case lexer.EOF: |
|||
return nil, tree, nil |
|||
|
|||
case lexer.Error: |
|||
return nil, tree, errors.New(token.Raw) |
|||
|
|||
case lexer.Text: |
|||
Insert(tree, NewNode(KindText, Text{token.Raw})) |
|||
return parserMain, tree, nil |
|||
|
|||
case lexer.Any: |
|||
Insert(tree, NewNode(KindAny, nil)) |
|||
return parserMain, tree, nil |
|||
|
|||
case lexer.Super: |
|||
Insert(tree, NewNode(KindSuper, nil)) |
|||
return parserMain, tree, nil |
|||
|
|||
case lexer.Single: |
|||
Insert(tree, NewNode(KindSingle, nil)) |
|||
return parserMain, tree, nil |
|||
|
|||
case lexer.RangeOpen: |
|||
return parserRange, tree, nil |
|||
|
|||
case lexer.TermsOpen: |
|||
a := NewNode(KindAnyOf, nil) |
|||
Insert(tree, a) |
|||
|
|||
p := NewNode(KindPattern, nil) |
|||
Insert(a, p) |
|||
|
|||
return parserMain, p, nil |
|||
|
|||
case lexer.Separator: |
|||
p := NewNode(KindPattern, nil) |
|||
Insert(tree.Parent, p) |
|||
|
|||
return parserMain, p, nil |
|||
|
|||
case lexer.TermsClose: |
|||
return parserMain, tree.Parent.Parent, nil |
|||
|
|||
default: |
|||
return nil, tree, fmt.Errorf("unexpected token: %s", token) |
|||
} |
|||
} |
|||
return nil, tree, fmt.Errorf("unknown error") |
|||
} |
|||
|
|||
func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) { |
|||
var ( |
|||
not bool |
|||
lo rune |
|||
hi rune |
|||
chars string |
|||
) |
|||
for { |
|||
token := lex.Next() |
|||
switch token.Type { |
|||
case lexer.EOF: |
|||
return nil, tree, errors.New("unexpected end") |
|||
|
|||
case lexer.Error: |
|||
return nil, tree, errors.New(token.Raw) |
|||
|
|||
case lexer.Not: |
|||
not = true |
|||
|
|||
case lexer.RangeLo: |
|||
r, w := utf8.DecodeRuneInString(token.Raw) |
|||
if len(token.Raw) > w { |
|||
return nil, tree, fmt.Errorf("unexpected length of lo character") |
|||
} |
|||
lo = r |
|||
|
|||
case lexer.RangeBetween: |
|||
//
|
|||
|
|||
case lexer.RangeHi: |
|||
r, w := utf8.DecodeRuneInString(token.Raw) |
|||
if len(token.Raw) > w { |
|||
return nil, tree, fmt.Errorf("unexpected length of lo character") |
|||
} |
|||
|
|||
hi = r |
|||
|
|||
if hi < lo { |
|||
return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo)) |
|||
} |
|||
|
|||
case lexer.Text: |
|||
chars = token.Raw |
|||
|
|||
case lexer.RangeClose: |
|||
isRange := lo != 0 && hi != 0 |
|||
isChars := chars != "" |
|||
|
|||
if isChars == isRange { |
|||
return nil, tree, fmt.Errorf("could not parse range") |
|||
} |
|||
|
|||
if isRange { |
|||
Insert(tree, NewNode(KindRange, Range{ |
|||
Lo: lo, |
|||
Hi: hi, |
|||
Not: not, |
|||
})) |
|||
} else { |
|||
Insert(tree, NewNode(KindList, List{ |
|||
Chars: chars, |
|||
Not: not, |
|||
})) |
|||
} |
|||
|
|||
return parserMain, tree, nil |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,273 @@ |
|||
package lexer |
|||
|
|||
import ( |
|||
"bytes" |
|||
"fmt" |
|||
"github.com/gobwas/glob/util/runes" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
const ( |
|||
char_any = '*' |
|||
char_comma = ',' |
|||
char_single = '?' |
|||
char_escape = '\\' |
|||
char_range_open = '[' |
|||
char_range_close = ']' |
|||
char_terms_open = '{' |
|||
char_terms_close = '}' |
|||
char_range_not = '!' |
|||
char_range_between = '-' |
|||
) |
|||
|
|||
var specials = []byte{ |
|||
char_any, |
|||
char_single, |
|||
char_escape, |
|||
char_range_open, |
|||
char_range_close, |
|||
char_terms_open, |
|||
char_terms_close, |
|||
} |
|||
|
|||
func Special(c byte) bool { |
|||
return bytes.IndexByte(specials, c) != -1 |
|||
} |
|||
|
|||
type tokens []Token |
|||
|
|||
func (i *tokens) shift() (ret Token) { |
|||
ret = (*i)[0] |
|||
copy(*i, (*i)[1:]) |
|||
*i = (*i)[:len(*i)-1] |
|||
return |
|||
} |
|||
|
|||
func (i *tokens) push(v Token) { |
|||
*i = append(*i, v) |
|||
} |
|||
|
|||
func (i *tokens) empty() bool { |
|||
return len(*i) == 0 |
|||
} |
|||
|
|||
var eof rune = 0 |
|||
|
|||
type lexer struct { |
|||
data string |
|||
pos int |
|||
err error |
|||
|
|||
tokens tokens |
|||
termsLevel int |
|||
|
|||
lastRune rune |
|||
lastRuneSize int |
|||
hasRune bool |
|||
} |
|||
|
|||
func NewLexer(source string) *lexer { |
|||
l := &lexer{ |
|||
data: source, |
|||
tokens: tokens(make([]Token, 0, 4)), |
|||
} |
|||
return l |
|||
} |
|||
|
|||
func (l *lexer) Next() Token { |
|||
if l.err != nil { |
|||
return Token{Error, l.err.Error()} |
|||
} |
|||
if !l.tokens.empty() { |
|||
return l.tokens.shift() |
|||
} |
|||
|
|||
l.fetchItem() |
|||
return l.Next() |
|||
} |
|||
|
|||
func (l *lexer) peek() (r rune, w int) { |
|||
if l.pos == len(l.data) { |
|||
return eof, 0 |
|||
} |
|||
|
|||
r, w = utf8.DecodeRuneInString(l.data[l.pos:]) |
|||
if r == utf8.RuneError { |
|||
l.errorf("could not read rune") |
|||
r = eof |
|||
w = 0 |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func (l *lexer) read() rune { |
|||
if l.hasRune { |
|||
l.hasRune = false |
|||
l.seek(l.lastRuneSize) |
|||
return l.lastRune |
|||
} |
|||
|
|||
r, s := l.peek() |
|||
l.seek(s) |
|||
|
|||
l.lastRune = r |
|||
l.lastRuneSize = s |
|||
|
|||
return r |
|||
} |
|||
|
|||
func (l *lexer) seek(w int) { |
|||
l.pos += w |
|||
} |
|||
|
|||
func (l *lexer) unread() { |
|||
if l.hasRune { |
|||
l.errorf("could not unread rune") |
|||
return |
|||
} |
|||
l.seek(-l.lastRuneSize) |
|||
l.hasRune = true |
|||
} |
|||
|
|||
func (l *lexer) errorf(f string, v ...interface{}) { |
|||
l.err = fmt.Errorf(f, v...) |
|||
} |
|||
|
|||
func (l *lexer) inTerms() bool { |
|||
return l.termsLevel > 0 |
|||
} |
|||
|
|||
func (l *lexer) termsEnter() { |
|||
l.termsLevel++ |
|||
} |
|||
|
|||
func (l *lexer) termsLeave() { |
|||
l.termsLevel-- |
|||
} |
|||
|
|||
var inTextBreakers = []rune{char_single, char_any, char_range_open, char_terms_open} |
|||
var inTermsBreakers = append(inTextBreakers, char_terms_close, char_comma) |
|||
|
|||
func (l *lexer) fetchItem() { |
|||
r := l.read() |
|||
switch { |
|||
case r == eof: |
|||
l.tokens.push(Token{EOF, ""}) |
|||
|
|||
case r == char_terms_open: |
|||
l.termsEnter() |
|||
l.tokens.push(Token{TermsOpen, string(r)}) |
|||
|
|||
case r == char_comma && l.inTerms(): |
|||
l.tokens.push(Token{Separator, string(r)}) |
|||
|
|||
case r == char_terms_close && l.inTerms(): |
|||
l.tokens.push(Token{TermsClose, string(r)}) |
|||
l.termsLeave() |
|||
|
|||
case r == char_range_open: |
|||
l.tokens.push(Token{RangeOpen, string(r)}) |
|||
l.fetchRange() |
|||
|
|||
case r == char_single: |
|||
l.tokens.push(Token{Single, string(r)}) |
|||
|
|||
case r == char_any: |
|||
if l.read() == char_any { |
|||
l.tokens.push(Token{Super, string(r) + string(r)}) |
|||
} else { |
|||
l.unread() |
|||
l.tokens.push(Token{Any, string(r)}) |
|||
} |
|||
|
|||
default: |
|||
l.unread() |
|||
|
|||
var breakers []rune |
|||
if l.inTerms() { |
|||
breakers = inTermsBreakers |
|||
} else { |
|||
breakers = inTextBreakers |
|||
} |
|||
l.fetchText(breakers) |
|||
} |
|||
} |
|||
|
|||
func (l *lexer) fetchRange() { |
|||
var wantHi bool |
|||
var wantClose bool |
|||
var seenNot bool |
|||
for { |
|||
r := l.read() |
|||
if r == eof { |
|||
l.errorf("unexpected end of input") |
|||
return |
|||
} |
|||
|
|||
if wantClose { |
|||
if r != char_range_close { |
|||
l.errorf("expected close range character") |
|||
} else { |
|||
l.tokens.push(Token{RangeClose, string(r)}) |
|||
} |
|||
return |
|||
} |
|||
|
|||
if wantHi { |
|||
l.tokens.push(Token{RangeHi, string(r)}) |
|||
wantClose = true |
|||
continue |
|||
} |
|||
|
|||
if !seenNot && r == char_range_not { |
|||
l.tokens.push(Token{Not, string(r)}) |
|||
seenNot = true |
|||
continue |
|||
} |
|||
|
|||
if n, w := l.peek(); n == char_range_between { |
|||
l.seek(w) |
|||
l.tokens.push(Token{RangeLo, string(r)}) |
|||
l.tokens.push(Token{RangeBetween, string(n)}) |
|||
wantHi = true |
|||
continue |
|||
} |
|||
|
|||
l.unread() // unread first peek and fetch as text
|
|||
l.fetchText([]rune{char_range_close}) |
|||
wantClose = true |
|||
} |
|||
} |
|||
|
|||
func (l *lexer) fetchText(breakers []rune) { |
|||
var data []rune |
|||
var escaped bool |
|||
|
|||
reading: |
|||
for { |
|||
r := l.read() |
|||
if r == eof { |
|||
break |
|||
} |
|||
|
|||
if !escaped { |
|||
if r == char_escape { |
|||
escaped = true |
|||
continue |
|||
} |
|||
|
|||
if runes.IndexRune(breakers, r) != -1 { |
|||
l.unread() |
|||
break reading |
|||
} |
|||
} |
|||
|
|||
escaped = false |
|||
data = append(data, r) |
|||
} |
|||
|
|||
if len(data) > 0 { |
|||
l.tokens.push(Token{Text, string(data)}) |
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
package lexer |
|||
|
|||
import "fmt" |
|||
|
|||
type TokenType int |
|||
|
|||
const ( |
|||
EOF TokenType = iota |
|||
Error |
|||
Text |
|||
Char |
|||
Any |
|||
Super |
|||
Single |
|||
Not |
|||
Separator |
|||
RangeOpen |
|||
RangeClose |
|||
RangeLo |
|||
RangeHi |
|||
RangeBetween |
|||
TermsOpen |
|||
TermsClose |
|||
) |
|||
|
|||
func (tt TokenType) String() string { |
|||
switch tt { |
|||
case EOF: |
|||
return "eof" |
|||
|
|||
case Error: |
|||
return "error" |
|||
|
|||
case Text: |
|||
return "text" |
|||
|
|||
case Char: |
|||
return "char" |
|||
|
|||
case Any: |
|||
return "any" |
|||
|
|||
case Super: |
|||
return "super" |
|||
|
|||
case Single: |
|||
return "single" |
|||
|
|||
case Not: |
|||
return "not" |
|||
|
|||
case Separator: |
|||
return "separator" |
|||
|
|||
case RangeOpen: |
|||
return "range_open" |
|||
|
|||
case RangeClose: |
|||
return "range_close" |
|||
|
|||
case RangeLo: |
|||
return "range_lo" |
|||
|
|||
case RangeHi: |
|||
return "range_hi" |
|||
|
|||
case RangeBetween: |
|||
return "range_between" |
|||
|
|||
case TermsOpen: |
|||
return "terms_open" |
|||
|
|||
case TermsClose: |
|||
return "terms_close" |
|||
|
|||
default: |
|||
return "undef" |
|||
} |
|||
} |
|||
|
|||
type Token struct { |
|||
Type TokenType |
|||
Raw string |
|||
} |
|||
|
|||
func (t Token) String() string { |
|||
return fmt.Sprintf("%v<%q>", t.Type, t.Raw) |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
package syntax |
|||
|
|||
import ( |
|||
"github.com/gobwas/glob/syntax/ast" |
|||
"github.com/gobwas/glob/syntax/lexer" |
|||
) |
|||
|
|||
func Parse(s string) (*ast.Node, error) { |
|||
return ast.Parse(lexer.NewLexer(s)) |
|||
} |
|||
|
|||
func Special(b byte) bool { |
|||
return lexer.Special(b) |
|||
} |
|||
@ -0,0 +1,154 @@ |
|||
package runes |
|||
|
|||
func Index(s, needle []rune) int { |
|||
ls, ln := len(s), len(needle) |
|||
|
|||
switch { |
|||
case ln == 0: |
|||
return 0 |
|||
case ln == 1: |
|||
return IndexRune(s, needle[0]) |
|||
case ln == ls: |
|||
if Equal(s, needle) { |
|||
return 0 |
|||
} |
|||
return -1 |
|||
case ln > ls: |
|||
return -1 |
|||
} |
|||
|
|||
head: |
|||
for i := 0; i < ls && ls-i >= ln; i++ { |
|||
for y := 0; y < ln; y++ { |
|||
if s[i+y] != needle[y] { |
|||
continue head |
|||
} |
|||
} |
|||
|
|||
return i |
|||
} |
|||
|
|||
return -1 |
|||
} |
|||
|
|||
func LastIndex(s, needle []rune) int { |
|||
ls, ln := len(s), len(needle) |
|||
|
|||
switch { |
|||
case ln == 0: |
|||
if ls == 0 { |
|||
return 0 |
|||
} |
|||
return ls |
|||
case ln == 1: |
|||
return IndexLastRune(s, needle[0]) |
|||
case ln == ls: |
|||
if Equal(s, needle) { |
|||
return 0 |
|||
} |
|||
return -1 |
|||
case ln > ls: |
|||
return -1 |
|||
} |
|||
|
|||
head: |
|||
for i := ls - 1; i >= 0 && i >= ln; i-- { |
|||
for y := ln - 1; y >= 0; y-- { |
|||
if s[i-(ln-y-1)] != needle[y] { |
|||
continue head |
|||
} |
|||
} |
|||
|
|||
return i - ln + 1 |
|||
} |
|||
|
|||
return -1 |
|||
} |
|||
|
|||
// IndexAny returns the index of the first instance of any Unicode code point
|
|||
// from chars in s, or -1 if no Unicode code point from chars is present in s.
|
|||
func IndexAny(s, chars []rune) int { |
|||
if len(chars) > 0 { |
|||
for i, c := range s { |
|||
for _, m := range chars { |
|||
if c == m { |
|||
return i |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return -1 |
|||
} |
|||
|
|||
func Contains(s, needle []rune) bool { |
|||
return Index(s, needle) >= 0 |
|||
} |
|||
|
|||
func Max(s []rune) (max rune) { |
|||
for _, r := range s { |
|||
if r > max { |
|||
max = r |
|||
} |
|||
} |
|||
|
|||
return |
|||
} |
|||
|
|||
func Min(s []rune) rune { |
|||
min := rune(-1) |
|||
for _, r := range s { |
|||
if min == -1 { |
|||
min = r |
|||
continue |
|||
} |
|||
|
|||
if r < min { |
|||
min = r |
|||
} |
|||
} |
|||
|
|||
return min |
|||
} |
|||
|
|||
func IndexRune(s []rune, r rune) int { |
|||
for i, c := range s { |
|||
if c == r { |
|||
return i |
|||
} |
|||
} |
|||
return -1 |
|||
} |
|||
|
|||
func IndexLastRune(s []rune, r rune) int { |
|||
for i := len(s) - 1; i >= 0; i-- { |
|||
if s[i] == r { |
|||
return i |
|||
} |
|||
} |
|||
|
|||
return -1 |
|||
} |
|||
|
|||
func Equal(a, b []rune) bool { |
|||
if len(a) == len(b) { |
|||
for i := 0; i < len(a); i++ { |
|||
if a[i] != b[i] { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
return true |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
// HasPrefix tests whether the string s begins with prefix.
|
|||
func HasPrefix(s, prefix []rune) bool { |
|||
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix) |
|||
} |
|||
|
|||
// HasSuffix tests whether the string s ends with suffix.
|
|||
func HasSuffix(s, suffix []rune) bool { |
|||
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix) |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
package strings |
|||
|
|||
import ( |
|||
"strings" |
|||
"unicode/utf8" |
|||
) |
|||
|
|||
func IndexAnyRunes(s string, rs []rune) int { |
|||
for _, r := range rs { |
|||
if i := strings.IndexRune(s, r); i != -1 { |
|||
return i |
|||
} |
|||
} |
|||
|
|||
return -1 |
|||
} |
|||
|
|||
func LastIndexAnyRunes(s string, rs []rune) int { |
|||
for _, r := range rs { |
|||
i := -1 |
|||
if 0 <= r && r < utf8.RuneSelf { |
|||
i = strings.LastIndexByte(s, byte(r)) |
|||
} else { |
|||
sub := s |
|||
for len(sub) > 0 { |
|||
j := strings.IndexRune(s, r) |
|||
if j == -1 { |
|||
break |
|||
} |
|||
i = j |
|||
sub = sub[i+1:] |
|||
} |
|||
} |
|||
if i != -1 { |
|||
return i |
|||
} |
|||
} |
|||
return -1 |
|||
} |
|||
@ -1,191 +0,0 @@ |
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, and |
|||
distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by the copyright |
|||
owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all other entities |
|||
that control, are controlled by, or are under common control with that entity. |
|||
For the purposes of this definition, "control" means (i) the power, direct or |
|||
indirect, to cause the direction or management of such entity, whether by |
|||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity exercising |
|||
permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, including |
|||
but not limited to software source code, documentation source, and configuration |
|||
files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical transformation or |
|||
translation of a Source form, including but not limited to compiled object code, |
|||
generated documentation, and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or Object form, made |
|||
available under the License, as indicated by a copyright notice that is included |
|||
in or attached to the work (an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object form, that |
|||
is based on (or derived from) the Work and for which the editorial revisions, |
|||
annotations, elaborations, or other modifications represent, as a whole, an |
|||
original work of authorship. For the purposes of this License, Derivative Works |
|||
shall not include works that remain separable from, or merely link (or bind by |
|||
name) to the interfaces of, the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including the original version |
|||
of the Work and any modifications or additions to that Work or Derivative Works |
|||
thereof, that is intentionally submitted to Licensor for inclusion in the Work |
|||
by the copyright owner or by an individual or Legal Entity authorized to submit |
|||
on behalf of the copyright owner. For the purposes of this definition, |
|||
"submitted" means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, and |
|||
issue tracking systems that are managed by, or on behalf of, the Licensor for |
|||
the purpose of discussing and improving the Work, but excluding communication |
|||
that is conspicuously marked or otherwise designated in writing by the copyright |
|||
owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf |
|||
of whom a Contribution has been received by Licensor and subsequently |
|||
incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. |
|||
|
|||
Subject to the terms and conditions of this License, each Contributor hereby |
|||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, |
|||
irrevocable copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the Work and such |
|||
Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. |
|||
|
|||
Subject to the terms and conditions of this License, each Contributor hereby |
|||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, |
|||
irrevocable (except as stated in this section) patent license to make, have |
|||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where |
|||
such license applies only to those patent claims licensable by such Contributor |
|||
that are necessarily infringed by their Contribution(s) alone or by combination |
|||
of their Contribution(s) with the Work to which such Contribution(s) was |
|||
submitted. If You institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a |
|||
Contribution incorporated within the Work constitutes direct or contributory |
|||
patent infringement, then any patent licenses granted to You under this License |
|||
for that Work shall terminate as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. |
|||
|
|||
You may reproduce and distribute copies of the Work or Derivative Works thereof |
|||
in any medium, with or without modifications, and in Source or Object form, |
|||
provided that You meet the following conditions: |
|||
|
|||
You must give any other recipients of the Work or Derivative Works a copy of |
|||
this License; and |
|||
You must cause any modified files to carry prominent notices stating that You |
|||
changed the files; and |
|||
You must retain, in the Source form of any Derivative Works that You distribute, |
|||
all copyright, patent, trademark, and attribution notices from the Source form |
|||
of the Work, excluding those notices that do not pertain to any part of the |
|||
Derivative Works; and |
|||
If the Work includes a "NOTICE" text file as part of its distribution, then any |
|||
Derivative Works that You distribute must include a readable copy of the |
|||
attribution notices contained within such NOTICE file, excluding those notices |
|||
that do not pertain to any part of the Derivative Works, in at least one of the |
|||
following places: within a NOTICE text file distributed as part of the |
|||
Derivative Works; within the Source form or documentation, if provided along |
|||
with the Derivative Works; or, within a display generated by the Derivative |
|||
Works, if and wherever such third-party notices normally appear. The contents of |
|||
the NOTICE file are for informational purposes only and do not modify the |
|||
License. You may add Your own attribution notices within Derivative Works that |
|||
You distribute, alongside or as an addendum to the NOTICE text from the Work, |
|||
provided that such additional attribution notices cannot be construed as |
|||
modifying the License. |
|||
You may add Your own copyright statement to Your modifications and may provide |
|||
additional or different license terms and conditions for use, reproduction, or |
|||
distribution of Your modifications, or for any such Derivative Works as a whole, |
|||
provided Your use, reproduction, and distribution of the Work otherwise complies |
|||
with the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. |
|||
|
|||
Unless You explicitly state otherwise, any Contribution intentionally submitted |
|||
for inclusion in the Work by You to the Licensor shall be under the terms and |
|||
conditions of this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify the terms of |
|||
any separate license agreement you may have executed with Licensor regarding |
|||
such Contributions. |
|||
|
|||
6. Trademarks. |
|||
|
|||
This License does not grant permission to use the trade names, trademarks, |
|||
service marks, or product names of the Licensor, except as required for |
|||
reasonable and customary use in describing the origin of the Work and |
|||
reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. |
|||
|
|||
Unless required by applicable law or agreed to in writing, Licensor provides the |
|||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, |
|||
including, without limitation, any warranties or conditions of TITLE, |
|||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are |
|||
solely responsible for determining the appropriateness of using or |
|||
redistributing the Work and assume any risks associated with Your exercise of |
|||
permissions under this License. |
|||
|
|||
8. Limitation of Liability. |
|||
|
|||
In no event and under no legal theory, whether in tort (including negligence), |
|||
contract, or otherwise, unless required by applicable law (such as deliberate |
|||
and grossly negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, incidental, |
|||
or consequential damages of any character arising as a result of this License or |
|||
out of the use or inability to use the Work (including but not limited to |
|||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or |
|||
any and all other commercial damages or losses), even if such Contributor has |
|||
been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. |
|||
|
|||
While redistributing the Work or Derivative Works thereof, You may choose to |
|||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or |
|||
other liability obligations and/or rights consistent with this License. However, |
|||
in accepting such obligations, You may act only on Your own behalf and on Your |
|||
sole responsibility, not on behalf of any other Contributor, and only if You |
|||
agree to indemnify, defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason of your |
|||
accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work |
|||
|
|||
To apply the Apache License to your work, attach the following boilerplate |
|||
notice, with the fields enclosed by brackets "[]" replaced with your own |
|||
identifying information. (Don't include the brackets!) The text should be |
|||
enclosed in the appropriate comment syntax for the file format. We also |
|||
recommend that a file or class name and description of purpose be included on |
|||
the same "printed page" as the copyright notice for easier identification within |
|||
third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
@ -1,44 +0,0 @@ |
|||
glog |
|||
==== |
|||
|
|||
Leveled execution logs for Go. |
|||
|
|||
This is an efficient pure Go implementation of leveled logs in the |
|||
manner of the open source C++ package |
|||
https://github.com/google/glog |
|||
|
|||
By binding methods to booleans it is possible to use the log package |
|||
without paying the expense of evaluating the arguments to the log. |
|||
Through the -vmodule flag, the package also provides fine-grained |
|||
control over logging at the file level. |
|||
|
|||
The comment from glog.go introduces the ideas: |
|||
|
|||
Package glog implements logging analogous to the Google-internal |
|||
C++ INFO/ERROR/V setup. It provides functions Info, Warning, |
|||
Error, Fatal, plus formatting variants such as Infof. It |
|||
also provides V-style logging controlled by the -v and |
|||
-vmodule=file=2 flags. |
|||
|
|||
Basic examples: |
|||
|
|||
glog.Info("Prepare to repel boarders") |
|||
|
|||
glog.Fatalf("Initialization failed: %s", err) |
|||
|
|||
See the documentation for the V function for an explanation |
|||
of these examples: |
|||
|
|||
if glog.V(2) { |
|||
glog.Info("Starting transaction...") |
|||
} |
|||
|
|||
glog.V(2).Infoln("Processed", nItems, "elements") |
|||
|
|||
|
|||
The repository contains an open source version of the log package |
|||
used inside Google. The master copy of the source lives inside |
|||
Google, not here. The code in this repo is for export only and is not itself |
|||
under development. Feature requests will be ignored. |
|||
|
|||
Send bug reports to [email protected]. |
|||
File diff suppressed because it is too large
@ -1,124 +0,0 @@ |
|||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
|||
//
|
|||
// Copyright 2013 Google Inc. All Rights Reserved.
|
|||
//
|
|||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
// you may not use this file except in compliance with the License.
|
|||
// You may obtain a copy of the License at
|
|||
//
|
|||
// http://www.apache.org/licenses/LICENSE-2.0
|
|||
//
|
|||
// Unless required by applicable law or agreed to in writing, software
|
|||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
// See the License for the specific language governing permissions and
|
|||
// limitations under the License.
|
|||
|
|||
// File I/O for logs.
|
|||
|
|||
package glog |
|||
|
|||
import ( |
|||
"errors" |
|||
"flag" |
|||
"fmt" |
|||
"os" |
|||
"os/user" |
|||
"path/filepath" |
|||
"strings" |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
// MaxSize is the maximum size of a log file in bytes.
|
|||
var MaxSize uint64 = 1024 * 1024 * 1800 |
|||
|
|||
// logDirs lists the candidate directories for new log files.
|
|||
var logDirs []string |
|||
|
|||
// If non-empty, overrides the choice of directory in which to write logs.
|
|||
// See createLogDirs for the full list of possible destinations.
|
|||
var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") |
|||
|
|||
func createLogDirs() { |
|||
if *logDir != "" { |
|||
logDirs = append(logDirs, *logDir) |
|||
} |
|||
logDirs = append(logDirs, os.TempDir()) |
|||
} |
|||
|
|||
var ( |
|||
pid = os.Getpid() |
|||
program = filepath.Base(os.Args[0]) |
|||
host = "unknownhost" |
|||
userName = "unknownuser" |
|||
) |
|||
|
|||
func init() { |
|||
h, err := os.Hostname() |
|||
if err == nil { |
|||
host = shortHostname(h) |
|||
} |
|||
|
|||
current, err := user.Current() |
|||
if err == nil { |
|||
userName = current.Username |
|||
} |
|||
|
|||
// Sanitize userName since it may contain filepath separators on Windows.
|
|||
userName = strings.Replace(userName, `\`, "_", -1) |
|||
} |
|||
|
|||
// shortHostname returns its argument, truncating at the first period.
|
|||
// For instance, given "www.google.com" it returns "www".
|
|||
func shortHostname(hostname string) string { |
|||
if i := strings.Index(hostname, "."); i >= 0 { |
|||
return hostname[:i] |
|||
} |
|||
return hostname |
|||
} |
|||
|
|||
// logName returns a new log file name containing tag, with start time t, and
|
|||
// the name for the symlink for tag.
|
|||
func logName(tag string, t time.Time) (name, link string) { |
|||
name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", |
|||
program, |
|||
host, |
|||
userName, |
|||
tag, |
|||
t.Year(), |
|||
t.Month(), |
|||
t.Day(), |
|||
t.Hour(), |
|||
t.Minute(), |
|||
t.Second(), |
|||
pid) |
|||
return name, program + "." + tag |
|||
} |
|||
|
|||
var onceLogDirs sync.Once |
|||
|
|||
// create creates a new log file and returns the file and its filename, which
|
|||
// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
|
|||
// successfully, create also attempts to update the symlink for that tag, ignoring
|
|||
// errors.
|
|||
func create(tag string, t time.Time) (f *os.File, filename string, err error) { |
|||
onceLogDirs.Do(createLogDirs) |
|||
if len(logDirs) == 0 { |
|||
return nil, "", errors.New("log: no log dirs") |
|||
} |
|||
name, link := logName(tag, t) |
|||
var lastErr error |
|||
for _, dir := range logDirs { |
|||
fname := filepath.Join(dir, name) |
|||
f, err := os.Create(fname) |
|||
if err == nil { |
|||
symlink := filepath.Join(dir, link) |
|||
os.Remove(symlink) // ignore err
|
|||
os.Symlink(name, symlink) // ignore err
|
|||
return f, fname, nil |
|||
} |
|||
lastErr = err |
|||
} |
|||
return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) |
|||
} |
|||
Loading…
Reference in new issue