//go:build amd64 || arm64
// +build amd64 arm64

package sm4

import (
	"crypto/cipher"

	"golang.org/x/sys/cpu"
	"xdx.jelly/xgcl/internal/subtle"
)

type sm4CipherAsm struct {
	sm4Cipher
}

var supportsAES = cpu.X86.HasAES || cpu.ARM64.HasSM4
var supportsGFMUL = cpu.X86.HasPCLMULQDQ || cpu.ARM64.HasPMULL

func newCipher(key []byte) (cipher.Block, error) {
	if !supportsAES {
		return newCipherGeneric(key)
	}
	c := sm4CipherAsm{sm4Cipher{}}

	expandKeyAsm(key, c.encKey[:], c.decKey[:])
	if supportsAES && supportsGFMUL {
		return &sm4CipherGCM{c}, nil
	}
	return &c, nil
}

func (c *sm4CipherAsm) BlockSize() int { return BlockSize }

func (c *sm4CipherAsm) Encrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("gcl/sm/sm4/Encrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("gcl/sm/sm4/Encrypt: output not full block")
	}
	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
		panic("gcl/sm/sm4/Encrypt: invalid buffer overlap")
	}
	encryptBlockAsm(c.encKey[:], dst, src)
}

func (c *sm4CipherAsm) Decrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("gcl/sm/sm4/Encrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("gcl/sm/sm4/Encrypt: output not full block")
	}
	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
		panic("gcl/sm/sm4/Encrypt: invalid buffer overlap")
	}

	decryptBlockAsm(c.decKey[:], dst, src)
}

// expandKey is used by BenchmarkExpand to ensure that the asm implementation
// of key expansion is used for the benchmark when it is available.
func expandKey(key []byte, enc, dec []uint32) {
	if supportsAES {
		expandKeyAsm(key, enc, dec)
	} else {
		expandKeyGo(key, enc, dec)
	}
}
