mirror of
https://github.com/golang/go
synced 2024-11-12 04:00:23 -07:00
crypto: housekeeping
Rename _Block to block, don't bother making it compute count. Add benchmarks. R=agl, agl CC=golang-dev https://golang.org/cl/6243053
This commit is contained in:
parent
14ad411407
commit
992a11b88b
@ -186,15 +186,14 @@ import (
|
||||
}
|
||||
{{end}}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
a := dig.s[0]
|
||||
b := dig.s[1]
|
||||
c := dig.s[2]
|
||||
d := dig.s[3]
|
||||
n := 0
|
||||
var X *[16]uint32
|
||||
var xbuf [16]uint32
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
aa, bb, cc, dd := a, b, c, d
|
||||
|
||||
// This is a constant condition - it is not evaluated on each iteration.
|
||||
@ -288,14 +287,12 @@ func _Block(dig *digest, p []byte) int {
|
||||
c += cc
|
||||
d += dd
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.s[0] = a
|
||||
dig.s[1] = b
|
||||
dig.s[2] = c
|
||||
dig.s[3] = d
|
||||
return n
|
||||
}
|
||||
`
|
||||
|
@ -21,26 +21,26 @@ const Size = 16
|
||||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x67452301
|
||||
_Init1 = 0xEFCDAB89
|
||||
_Init2 = 0x98BADCFE
|
||||
_Init3 = 0x10325476
|
||||
chunk = 64
|
||||
init0 = 0x67452301
|
||||
init1 = 0xEFCDAB89
|
||||
init2 = 0x98BADCFE
|
||||
init3 = 0x10325476
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
s [4]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.s[0] = _Init0
|
||||
d.s[1] = _Init1
|
||||
d.s[2] = _Init2
|
||||
d.s[3] = _Init3
|
||||
d.s[0] = init0
|
||||
d.s[1] = init1
|
||||
d.s[2] = init2
|
||||
d.s[3] = init3
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
@ -61,21 +61,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:chunk])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
@ -5,15 +5,14 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
a := dig.s[0]
|
||||
b := dig.s[1]
|
||||
c := dig.s[2]
|
||||
d := dig.s[3]
|
||||
n := 0
|
||||
var X *[16]uint32
|
||||
var xbuf [16]uint32
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
aa, bb, cc, dd := a, b, c, d
|
||||
|
||||
// This is a constant condition - it is not evaluated on each iteration.
|
||||
@ -237,13 +236,11 @@ func _Block(dig *digest, p []byte) int {
|
||||
c += cc
|
||||
d += dd
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.s[0] = a
|
||||
dig.s[1] = b
|
||||
dig.s[2] = c
|
||||
dig.s[3] = d
|
||||
return n
|
||||
}
|
||||
|
@ -21,28 +21,28 @@ const Size = 20
|
||||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x67452301
|
||||
_Init1 = 0xEFCDAB89
|
||||
_Init2 = 0x98BADCFE
|
||||
_Init3 = 0x10325476
|
||||
_Init4 = 0xC3D2E1F0
|
||||
chunk = 64
|
||||
init0 = 0x67452301
|
||||
init1 = 0xEFCDAB89
|
||||
init2 = 0x98BADCFE
|
||||
init3 = 0x10325476
|
||||
init4 = 0xC3D2E1F0
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [5]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
@ -63,21 +63,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
@ -79,3 +79,28 @@ func ExampleNew() {
|
||||
fmt.Printf("% x", h.Sum(nil))
|
||||
// Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
|
||||
}
|
||||
|
||||
var bench = sha1.New()
|
||||
var buf = makeBuf()
|
||||
|
||||
func makeBuf() []byte {
|
||||
b := make([]byte, 8<<10)
|
||||
for i := range b {
|
||||
b[i] = byte(i)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func BenchmarkHash1K(b *testing.B) {
|
||||
b.SetBytes(1024)
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf[:1024])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHash8K(b *testing.B) {
|
||||
b.SetBytes(int64(len(buf)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf)
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,11 @@ const (
|
||||
_K3 = 0xCA62C1D6
|
||||
)
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
var w [80]uint32
|
||||
|
||||
n := 0
|
||||
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
// Can interlace the computation of w with the
|
||||
// rounds below if needed for speed.
|
||||
for i := 0; i < 16; i++ {
|
||||
@ -72,10 +71,8 @@ func _Block(dig *digest, p []byte) int {
|
||||
h3 += d
|
||||
h4 += e
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
|
||||
return n
|
||||
}
|
||||
|
@ -26,29 +26,29 @@ const Size224 = 28
|
||||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x6A09E667
|
||||
_Init1 = 0xBB67AE85
|
||||
_Init2 = 0x3C6EF372
|
||||
_Init3 = 0xA54FF53A
|
||||
_Init4 = 0x510E527F
|
||||
_Init5 = 0x9B05688C
|
||||
_Init6 = 0x1F83D9AB
|
||||
_Init7 = 0x5BE0CD19
|
||||
_Init0_224 = 0xC1059ED8
|
||||
_Init1_224 = 0x367CD507
|
||||
_Init2_224 = 0x3070DD17
|
||||
_Init3_224 = 0xF70E5939
|
||||
_Init4_224 = 0xFFC00B31
|
||||
_Init5_224 = 0x68581511
|
||||
_Init6_224 = 0x64F98FA7
|
||||
_Init7_224 = 0xBEFA4FA4
|
||||
chunk = 64
|
||||
init0 = 0x6A09E667
|
||||
init1 = 0xBB67AE85
|
||||
init2 = 0x3C6EF372
|
||||
init3 = 0xA54FF53A
|
||||
init4 = 0x510E527F
|
||||
init5 = 0x9B05688C
|
||||
init6 = 0x1F83D9AB
|
||||
init7 = 0x5BE0CD19
|
||||
init0_224 = 0xC1059ED8
|
||||
init1_224 = 0x367CD507
|
||||
init2_224 = 0x3070DD17
|
||||
init3_224 = 0xF70E5939
|
||||
init4_224 = 0xFFC00B31
|
||||
init5_224 = 0x68581511
|
||||
init6_224 = 0x64F98FA7
|
||||
init7_224 = 0xBEFA4FA4
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [8]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
is224 bool // mark if this digest is SHA-224
|
||||
@ -56,23 +56,23 @@ type digest struct {
|
||||
|
||||
func (d *digest) Reset() {
|
||||
if !d.is224 {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[5] = _Init5
|
||||
d.h[6] = _Init6
|
||||
d.h[7] = _Init7
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.h[5] = init5
|
||||
d.h[6] = init6
|
||||
d.h[7] = init7
|
||||
} else {
|
||||
d.h[0] = _Init0_224
|
||||
d.h[1] = _Init1_224
|
||||
d.h[2] = _Init2_224
|
||||
d.h[3] = _Init3_224
|
||||
d.h[4] = _Init4_224
|
||||
d.h[5] = _Init5_224
|
||||
d.h[6] = _Init6_224
|
||||
d.h[7] = _Init7_224
|
||||
d.h[0] = init0_224
|
||||
d.h[1] = init1_224
|
||||
d.h[2] = init2_224
|
||||
d.h[3] = init3_224
|
||||
d.h[4] = init4_224
|
||||
d.h[5] = init5_224
|
||||
d.h[6] = init6_224
|
||||
d.h[7] = init7_224
|
||||
}
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bench = New()
|
||||
var buf = makeBuf()
|
||||
|
||||
func makeBuf() []byte {
|
||||
b := make([]byte, 8<<10)
|
||||
for i := range b {
|
||||
b[i] = byte(i)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func BenchmarkHash1K(b *testing.B) {
|
||||
b.SetBytes(1024)
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf[:1024])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHash8K(b *testing.B) {
|
||||
b.SetBytes(int64(len(buf)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf)
|
||||
}
|
||||
}
|
||||
|
@ -75,11 +75,10 @@ var _K = []uint32{
|
||||
0xc67178f2,
|
||||
}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
var w [64]uint32
|
||||
n := 0
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
// Can interlace the computation of w with the
|
||||
// rounds below if needed for speed.
|
||||
for i := 0; i < 16; i++ {
|
||||
@ -120,10 +119,8 @@ func _Block(dig *digest, p []byte) int {
|
||||
h6 += g
|
||||
h7 += h
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
return n
|
||||
}
|
||||
|
@ -26,29 +26,29 @@ const Size384 = 48
|
||||
const BlockSize = 128
|
||||
|
||||
const (
|
||||
_Chunk = 128
|
||||
_Init0 = 0x6a09e667f3bcc908
|
||||
_Init1 = 0xbb67ae8584caa73b
|
||||
_Init2 = 0x3c6ef372fe94f82b
|
||||
_Init3 = 0xa54ff53a5f1d36f1
|
||||
_Init4 = 0x510e527fade682d1
|
||||
_Init5 = 0x9b05688c2b3e6c1f
|
||||
_Init6 = 0x1f83d9abfb41bd6b
|
||||
_Init7 = 0x5be0cd19137e2179
|
||||
_Init0_384 = 0xcbbb9d5dc1059ed8
|
||||
_Init1_384 = 0x629a292a367cd507
|
||||
_Init2_384 = 0x9159015a3070dd17
|
||||
_Init3_384 = 0x152fecd8f70e5939
|
||||
_Init4_384 = 0x67332667ffc00b31
|
||||
_Init5_384 = 0x8eb44a8768581511
|
||||
_Init6_384 = 0xdb0c2e0d64f98fa7
|
||||
_Init7_384 = 0x47b5481dbefa4fa4
|
||||
chunk = 128
|
||||
init0 = 0x6a09e667f3bcc908
|
||||
init1 = 0xbb67ae8584caa73b
|
||||
init2 = 0x3c6ef372fe94f82b
|
||||
init3 = 0xa54ff53a5f1d36f1
|
||||
init4 = 0x510e527fade682d1
|
||||
init5 = 0x9b05688c2b3e6c1f
|
||||
init6 = 0x1f83d9abfb41bd6b
|
||||
init7 = 0x5be0cd19137e2179
|
||||
init0_384 = 0xcbbb9d5dc1059ed8
|
||||
init1_384 = 0x629a292a367cd507
|
||||
init2_384 = 0x9159015a3070dd17
|
||||
init3_384 = 0x152fecd8f70e5939
|
||||
init4_384 = 0x67332667ffc00b31
|
||||
init5_384 = 0x8eb44a8768581511
|
||||
init6_384 = 0xdb0c2e0d64f98fa7
|
||||
init7_384 = 0x47b5481dbefa4fa4
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [8]uint64
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
is384 bool // mark if this digest is SHA-384
|
||||
@ -56,23 +56,23 @@ type digest struct {
|
||||
|
||||
func (d *digest) Reset() {
|
||||
if !d.is384 {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[5] = _Init5
|
||||
d.h[6] = _Init6
|
||||
d.h[7] = _Init7
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.h[5] = init5
|
||||
d.h[6] = init6
|
||||
d.h[7] = init7
|
||||
} else {
|
||||
d.h[0] = _Init0_384
|
||||
d.h[1] = _Init1_384
|
||||
d.h[2] = _Init2_384
|
||||
d.h[3] = _Init3_384
|
||||
d.h[4] = _Init4_384
|
||||
d.h[5] = _Init5_384
|
||||
d.h[6] = _Init6_384
|
||||
d.h[7] = _Init7_384
|
||||
d.h[0] = init0_384
|
||||
d.h[1] = init1_384
|
||||
d.h[2] = init2_384
|
||||
d.h[3] = init3_384
|
||||
d.h[4] = init4_384
|
||||
d.h[5] = init5_384
|
||||
d.h[6] = init6_384
|
||||
d.h[7] = init7_384
|
||||
}
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
||||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var bench = New()
|
||||
var buf = makeBuf()
|
||||
|
||||
func makeBuf() []byte {
|
||||
b := make([]byte, 8<<10)
|
||||
for i := range b {
|
||||
b[i] = byte(i)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func BenchmarkHash1K(b *testing.B) {
|
||||
b.SetBytes(1024)
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf[:1024])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHash8K(b *testing.B) {
|
||||
b.SetBytes(int64(len(buf)))
|
||||
for i := 0; i < b.N; i++ {
|
||||
bench.Write(buf)
|
||||
}
|
||||
}
|
||||
|
@ -91,11 +91,10 @@ var _K = []uint64{
|
||||
0x6c44198c4a475817,
|
||||
}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
var w [80]uint64
|
||||
n := 0
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
for i := 0; i < 16; i++ {
|
||||
j := i * 8
|
||||
w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 |
|
||||
@ -135,10 +134,8 @@ func _Block(dig *digest, p []byte) int {
|
||||
h6 += g
|
||||
h7 += h
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
return n
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user