accounts:smartcard wallet without the dependency on libpcsclite (#19273)
* accounts, core, internal, node: Add support for smartcard wallets * accounts, internal: Changes in response to review * vendor: pull in missing go-echd library * accounts/scwallet, console: user friendly card opening * accounts/scwallet: ordered wallets, tighter events, derivation logs * accounts, console: frendly card errors, support pin unblock * accounts/scwallet: fix crypto API change * accounts/scwallet: rebase and update * Fix some linter issues * Remove the direct dependency on libpcsclite Instead, use a go library that communicates with pcscd over a socket. Also update the changes introduced by @gravityblast since this PR's inception * Temporary fix to the ADBU status call * fix wallet status update This is a temporary fix, better checks need to be performed once the whole process has been validated. * Fix key derivation * Add some documentation * Update a comment to reflect the workings of the updated system * Vendor keycard-go/derivationpath * Formatting fixes * Add instructions on how to install the card * Achieve full transaction signature+sending * PK derivation has to be supported by the card * Fix linter issues * Upgrade to keycard app v2.1.1 * Set gballet as codeowner of the smartcard wallet dir * fix unnecessary condition linter warning * refuse to overwrite the master key of a previously initialized card * refresh the account list when initializing the card * Update the card preparation instructions based on review feedback * 'sanitize' JSON input Co-Authored-By: gballet <gballet@gmail.com> * Apply suggestions from code review Co-Authored-By: gballet <gballet@gmail.com> * fix a serialization error * more review feedback * More review feedback * Can now specify the number of empty accounts to derive * Fix rebase error: include norm package * Update bip-39 ref and remove ebfe/scard from vendor * Add missing dependency
This commit is contained in:
		
						commit
						1fc3e44ffe
					
				
							
								
								
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -2,6 +2,7 @@ | ||||
| # Each line is a file pattern followed by one or more owners. | ||||
| 
 | ||||
| accounts/usbwallet              @karalabe | ||||
| accounts/scwallet               @gballet | ||||
| accounts/abi                    @gballet | ||||
| consensus                       @karalabe | ||||
| core/                           @karalabe @holiman | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| package accounts | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| @ -133,3 +134,19 @@ func (path DerivationPath) String() string { | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON turns a derivation path into its json-serialized string
 | ||||
| func (path DerivationPath) MarshalJSON() ([]byte, error) { | ||||
| 	return json.Marshal(path.String()) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalJSON a json-serialized string back into a derivation path
 | ||||
| func (path *DerivationPath) UnmarshalJSON(b []byte) error { | ||||
| 	var dp string | ||||
| 	var err error | ||||
| 	if err = json.Unmarshal(b, &dp); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*path, err = ParseDerivationPath(dp) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
							
								
								
									
										69
									
								
								accounts/scwallet/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								accounts/scwallet/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| # Using the smartcard wallet | ||||
| 
 | ||||
| ## Requirements | ||||
| 
 | ||||
|   * A USB smartcard reader | ||||
|   * A keycard that supports the status app | ||||
|   * PCSCD version 4.3 running on your system **Only version 4.3 is currently supported** | ||||
| 
 | ||||
| ## Preparing the smartcard | ||||
| 
 | ||||
|   **WARNING: FOILLOWING THESE INSTRUCTIONS WILL DESTROY THE MASTER KEY ON YOUR CARD. ONLY PROCEED IF NO FUNDS ARE ASSOCIATED WITH THESE ACCOUNTS** | ||||
| 
 | ||||
|   You can use status' [keycard-cli](https://github.com/status-im/keycard-cli) and you should get version 2.1.1 of their [smartcard application](https://github.com/status-im/status-keycard/releases/download/2.1.1/keycard_v2.1.1.cap) | ||||
| 
 | ||||
|   You also need to make sure that the PCSC daemon is running on your system. | ||||
| 
 | ||||
|   Then, you can install the application to the card by typing: | ||||
| 
 | ||||
|   ``` | ||||
|   keycard install -a keycard_v2.1.cap | ||||
|   ``` | ||||
| 
 | ||||
|   Then you can initialize the application by typing: | ||||
| 
 | ||||
|   ``` | ||||
|   keycard init | ||||
|   ``` | ||||
| 
 | ||||
|   Then the card needs to be paired: | ||||
| 
 | ||||
|   ``` | ||||
|   keycard pair | ||||
|   ``` | ||||
| 
 | ||||
|   Finally, you need to have the card generate a new master key: | ||||
| 
 | ||||
|   ``` | ||||
|   keycard shell <<END | ||||
|   keycard-select | ||||
|   keycard-set-pairing PAIRING_KEY PAIRING_INDEX | ||||
|   keycard-open-secure-channel | ||||
|   keycard-verify-pin CARD_PIN | ||||
|   keycard-generate-key | ||||
|   END | ||||
|   ``` | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
|   1. Start `geth` with the `console` command | ||||
|   2. Check the card's URL by checking `personal.listWallets`: | ||||
| 
 | ||||
| ``` | ||||
|   listWallets: [{ | ||||
|       status: "Online, can derive public keys", | ||||
|       url: "pcsc://a4d73015" | ||||
|   }] | ||||
| ``` | ||||
| 
 | ||||
|   3. Open the wallet, you will be prompted for your pairing password, then PIN: | ||||
| 
 | ||||
| ``` | ||||
| personal.openWallet("pcsc://a4d73015") | ||||
| ``` | ||||
| 
 | ||||
|   4. Check that creation was successful by typing e.g. `personal`. Then use it like a regular wallet. | ||||
| 
 | ||||
| ## Known issues | ||||
| 
 | ||||
|   * Starting geth with a valid card seems to make firefox crash. | ||||
							
								
								
									
										87
									
								
								accounts/scwallet/apdu.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								accounts/scwallet/apdu.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package scwallet | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| // commandAPDU represents an application data unit sent to a smartcard.
 | ||||
| type commandAPDU struct { | ||||
| 	Cla, Ins, P1, P2 uint8  // Class, Instruction, Parameter 1, Parameter 2
 | ||||
| 	Data             []byte // Command data
 | ||||
| 	Le               uint8  // Command data length
 | ||||
| } | ||||
| 
 | ||||
| // serialize serializes a command APDU.
 | ||||
| func (ca commandAPDU) serialize() ([]byte, error) { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 
 | ||||
| 	if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(ca.Data) > 0 { | ||||
| 		if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return buf.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| // responseAPDU represents an application data unit received from a smart card.
 | ||||
| type responseAPDU struct { | ||||
| 	Data     []byte // response data
 | ||||
| 	Sw1, Sw2 uint8  // status words 1 and 2
 | ||||
| } | ||||
| 
 | ||||
| // deserialize deserializes a response APDU.
 | ||||
| func (ra *responseAPDU) deserialize(data []byte) error { | ||||
| 	if len(data) < 2 { | ||||
| 		return fmt.Errorf("can not deserialize data: payload too short (%d < 2)", len(data)) | ||||
| 	} | ||||
| 
 | ||||
| 	ra.Data = make([]byte, len(data)-2) | ||||
| 
 | ||||
| 	buf := bytes.NewReader(data) | ||||
| 	if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										302
									
								
								accounts/scwallet/hub.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								accounts/scwallet/hub.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,302 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| // This package implements support for smartcard-based hardware wallets such as
 | ||||
| // the one written by Status: https://github.com/status-im/hardware-wallet
 | ||||
| //
 | ||||
| // This implementation of smartcard wallets have a different interaction process
 | ||||
| // to other types of hardware wallet. The process works like this:
 | ||||
| //
 | ||||
| // 1. (First use with a given client) Establish a pairing between hardware
 | ||||
| //    wallet and client. This requires a secret value called a 'pairing password'.
 | ||||
| //    You can pair with an unpaired wallet with `personal.openWallet(URI, pairing password)`.
 | ||||
| // 2. (First use only) Initialize the wallet, which generates a keypair, stores
 | ||||
| //    it on the wallet, and returns it so the user can back it up. You can
 | ||||
| //    initialize a wallet with `personal.initializeWallet(URI)`.
 | ||||
| // 3. Connect to the wallet using the pairing information established in step 1.
 | ||||
| //    You can connect to a paired wallet with `personal.openWallet(URI, PIN)`.
 | ||||
| // 4. Interact with the wallet as normal.
 | ||||
| 
 | ||||
| package scwallet | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	pcsc "github.com/gballet/go-libpcsclite" | ||||
| ) | ||||
| 
 | ||||
| // Scheme is the URI prefix for smartcard wallets.
 | ||||
| const Scheme = "pcsc" | ||||
| 
 | ||||
| // refreshCycle is the maximum time between wallet refreshes (if USB hotplug
 | ||||
| // notifications don't work).
 | ||||
| const refreshCycle = time.Second | ||||
| 
 | ||||
| // refreshThrottling is the minimum time between wallet refreshes to avoid thrashing.
 | ||||
| const refreshThrottling = 500 * time.Millisecond | ||||
| 
 | ||||
| // smartcardPairing contains information about a smart card we have paired with
 | ||||
| // or might pair with the hub.
 | ||||
| type smartcardPairing struct { | ||||
| 	PublicKey    []byte                                     `json:"publicKey"` | ||||
| 	PairingIndex uint8                                      `json:"pairingIndex"` | ||||
| 	PairingKey   []byte                                     `json:"pairingKey"` | ||||
| 	Accounts     map[common.Address]accounts.DerivationPath `json:"accounts"` | ||||
| } | ||||
| 
 | ||||
| // Hub is a accounts.Backend that can find and handle generic PC/SC hardware wallets.
 | ||||
| type Hub struct { | ||||
| 	scheme string // Protocol scheme prefixing account and wallet URLs.
 | ||||
| 
 | ||||
| 	context  *pcsc.Client | ||||
| 	datadir  string | ||||
| 	pairings map[string]smartcardPairing | ||||
| 
 | ||||
| 	refreshed   time.Time               // Time instance when the list of wallets was last refreshed
 | ||||
| 	wallets     map[string]*Wallet      // Mapping from reader names to wallet instances
 | ||||
| 	updateFeed  event.Feed              // Event feed to notify wallet additions/removals
 | ||||
| 	updateScope event.SubscriptionScope // Subscription scope tracking current live listeners
 | ||||
| 	updating    bool                    // Whether the event notification loop is running
 | ||||
| 
 | ||||
| 	quit chan chan error | ||||
| 
 | ||||
| 	stateLock sync.RWMutex // Protects the internals of the hub from racey access
 | ||||
| } | ||||
| 
 | ||||
| func (hub *Hub) readPairings() error { | ||||
| 	hub.pairings = make(map[string]smartcardPairing) | ||||
| 	pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json")) | ||||
| 	if err != nil { | ||||
| 		if os.IsNotExist(err) { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	pairingData, err := ioutil.ReadAll(pairingFile) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	var pairings []smartcardPairing | ||||
| 	if err := json.Unmarshal(pairingData, &pairings); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	for _, pairing := range pairings { | ||||
| 		hub.pairings[string(pairing.PublicKey)] = pairing | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (hub *Hub) writePairings() error { | ||||
| 	pairingFile, err := os.OpenFile(filepath.Join(hub.datadir, "smartcards.json"), os.O_RDWR|os.O_CREATE, 0755) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer pairingFile.Close() | ||||
| 
 | ||||
| 	pairings := make([]smartcardPairing, 0, len(hub.pairings)) | ||||
| 	for _, pairing := range hub.pairings { | ||||
| 		pairings = append(pairings, pairing) | ||||
| 	} | ||||
| 
 | ||||
| 	pairingData, err := json.Marshal(pairings) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := pairingFile.Write(pairingData); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing { | ||||
| 	if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok { | ||||
| 		return &pairing | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error { | ||||
| 	if pairing == nil { | ||||
| 		delete(hub.pairings, string(wallet.PublicKey)) | ||||
| 	} else { | ||||
| 		hub.pairings[string(wallet.PublicKey)] = *pairing | ||||
| 	} | ||||
| 	return hub.writePairings() | ||||
| } | ||||
| 
 | ||||
| // NewHub creates a new hardware wallet manager for smartcards.
 | ||||
| func NewHub(scheme string, datadir string) (*Hub, error) { | ||||
| 	context, err := pcsc.EstablishContext(pcsc.ScopeSystem) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	hub := &Hub{ | ||||
| 		scheme:  scheme, | ||||
| 		context: context, | ||||
| 		datadir: datadir, | ||||
| 		wallets: make(map[string]*Wallet), | ||||
| 		quit:    make(chan chan error), | ||||
| 	} | ||||
| 	if err := hub.readPairings(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	hub.refreshWallets() | ||||
| 	return hub, nil | ||||
| } | ||||
| 
 | ||||
| // Wallets implements accounts.Backend, returning all the currently tracked smart
 | ||||
| // cards that appear to be hardware wallets.
 | ||||
| func (hub *Hub) Wallets() []accounts.Wallet { | ||||
| 	// Make sure the list of wallets is up to date
 | ||||
| 	hub.refreshWallets() | ||||
| 
 | ||||
| 	hub.stateLock.RLock() | ||||
| 	defer hub.stateLock.RUnlock() | ||||
| 
 | ||||
| 	cpy := make([]accounts.Wallet, 0, len(hub.wallets)) | ||||
| 	for _, wallet := range hub.wallets { | ||||
| 		cpy = append(cpy, wallet) | ||||
| 	} | ||||
| 	sort.Sort(accounts.WalletsByURL(cpy)) | ||||
| 	return cpy | ||||
| } | ||||
| 
 | ||||
| // refreshWallets scans the devices attached to the machine and updates the
 | ||||
| // list of wallets based on the found devices.
 | ||||
| func (hub *Hub) refreshWallets() { | ||||
| 	// Don't scan the USB like crazy it the user fetches wallets in a loop
 | ||||
| 	hub.stateLock.RLock() | ||||
| 	elapsed := time.Since(hub.refreshed) | ||||
| 	hub.stateLock.RUnlock() | ||||
| 
 | ||||
| 	if elapsed < refreshThrottling { | ||||
| 		return | ||||
| 	} | ||||
| 	// Retrieve all the smart card reader to check for cards
 | ||||
| 	readers, err := hub.context.ListReaders() | ||||
| 	if err != nil { | ||||
| 		// This is a perverted hack, the scard library returns an error if no card
 | ||||
| 		// readers are present instead of simply returning an empty list. We don't
 | ||||
| 		// want to fill the user's log with errors, so filter those out.
 | ||||
| 		if err.Error() != "scard: Cannot find a smart card reader." { | ||||
| 			log.Error("Failed to enumerate smart card readers", "err", err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	// Transform the current list of wallets into the new one
 | ||||
| 	hub.stateLock.Lock() | ||||
| 
 | ||||
| 	events := []accounts.WalletEvent{} | ||||
| 	seen := make(map[string]struct{}) | ||||
| 
 | ||||
| 	for _, reader := range readers { | ||||
| 		// Mark the reader as present
 | ||||
| 		seen[reader] = struct{}{} | ||||
| 
 | ||||
| 		// If we alreay know about this card, skip to the next reader, otherwise clean up
 | ||||
| 		if wallet, ok := hub.wallets[reader]; ok { | ||||
| 			if err := wallet.ping(); err == nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			wallet.Close() | ||||
| 			events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) | ||||
| 			delete(hub.wallets, reader) | ||||
| 		} | ||||
| 		// New card detected, try to connect to it
 | ||||
| 		card, err := hub.context.Connect(reader, pcsc.ShareShared, pcsc.ProtocolAny) | ||||
| 		if err != nil { | ||||
| 			log.Debug("Failed to open smart card", "reader", reader, "err", err) | ||||
| 			continue | ||||
| 		} | ||||
| 		wallet := NewWallet(hub, card) | ||||
| 		if err = wallet.connect(); err != nil { | ||||
| 			log.Debug("Failed to connect to smart card", "reader", reader, "err", err) | ||||
| 			card.Disconnect(pcsc.LeaveCard) | ||||
| 			continue | ||||
| 		} | ||||
| 		// Card connected, start tracking in amongs the wallets
 | ||||
| 		hub.wallets[reader] = wallet | ||||
| 		events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived}) | ||||
| 	} | ||||
| 	// Remove any wallets no longer present
 | ||||
| 	for reader, wallet := range hub.wallets { | ||||
| 		if _, ok := seen[reader]; !ok { | ||||
| 			wallet.Close() | ||||
| 			events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped}) | ||||
| 			delete(hub.wallets, reader) | ||||
| 		} | ||||
| 	} | ||||
| 	hub.refreshed = time.Now() | ||||
| 	hub.stateLock.Unlock() | ||||
| 
 | ||||
| 	for _, event := range events { | ||||
| 		hub.updateFeed.Send(event) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Subscribe implements accounts.Backend, creating an async subscription to
 | ||||
| // receive notifications on the addition or removal of smart card wallets.
 | ||||
| func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription { | ||||
| 	// We need the mutex to reliably start/stop the update loop
 | ||||
| 	hub.stateLock.Lock() | ||||
| 	defer hub.stateLock.Unlock() | ||||
| 
 | ||||
| 	// Subscribe the caller and track the subscriber count
 | ||||
| 	sub := hub.updateScope.Track(hub.updateFeed.Subscribe(sink)) | ||||
| 
 | ||||
| 	// Subscribers require an active notification loop, start it
 | ||||
| 	if !hub.updating { | ||||
| 		hub.updating = true | ||||
| 		go hub.updater() | ||||
| 	} | ||||
| 	return sub | ||||
| } | ||||
| 
 | ||||
| // updater is responsible for maintaining an up-to-date list of wallets managed
 | ||||
| // by the smart card hub, and for firing wallet addition/removal events.
 | ||||
| func (hub *Hub) updater() { | ||||
| 	for { | ||||
| 		// TODO: Wait for a USB hotplug event (not supported yet) or a refresh timeout
 | ||||
| 		// <-hub.changes
 | ||||
| 		time.Sleep(refreshCycle) | ||||
| 
 | ||||
| 		// Run the wallet refresher
 | ||||
| 		hub.refreshWallets() | ||||
| 
 | ||||
| 		// If all our subscribers left, stop the updater
 | ||||
| 		hub.stateLock.Lock() | ||||
| 		if hub.updateScope.Count() == 0 { | ||||
| 			hub.updating = false | ||||
| 			hub.stateLock.Unlock() | ||||
| 			return | ||||
| 		} | ||||
| 		hub.stateLock.Unlock() | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										346
									
								
								accounts/scwallet/securechannel.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										346
									
								
								accounts/scwallet/securechannel.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,346 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package scwallet | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/aes" | ||||
| 	"crypto/cipher" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| 	pcsc "github.com/gballet/go-libpcsclite" | ||||
| 	"github.com/wsddn/go-ecdh" | ||||
| 	"golang.org/x/crypto/pbkdf2" | ||||
| 	"golang.org/x/text/unicode/norm" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	maxPayloadSize  = 223 | ||||
| 	pairP1FirstStep = 0 | ||||
| 	pairP1LastStep  = 1 | ||||
| 
 | ||||
| 	scSecretLength = 32 | ||||
| 	scBlockSize    = 16 | ||||
| 
 | ||||
| 	insOpenSecureChannel    = 0x10 | ||||
| 	insMutuallyAuthenticate = 0x11 | ||||
| 	insPair                 = 0x12 | ||||
| 	insUnpair               = 0x13 | ||||
| 
 | ||||
| 	pairingSalt = "Keycard Pairing Password Salt" | ||||
| ) | ||||
| 
 | ||||
| // SecureChannelSession enables secure communication with a hardware wallet.
 | ||||
| type SecureChannelSession struct { | ||||
| 	card          *pcsc.Card // A handle to the smartcard for communication
 | ||||
| 	secret        []byte     // A shared secret generated from our ECDSA keys
 | ||||
| 	publicKey     []byte     // Our own ephemeral public key
 | ||||
| 	PairingKey    []byte     // A permanent shared secret for a pairing, if present
 | ||||
| 	sessionEncKey []byte     // The current session encryption key
 | ||||
| 	sessionMacKey []byte     // The current session MAC key
 | ||||
| 	iv            []byte     // The current IV
 | ||||
| 	PairingIndex  uint8      // The pairing index
 | ||||
| } | ||||
| 
 | ||||
| // NewSecureChannelSession creates a new secure channel for the given card and public key.
 | ||||
| func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSession, error) { | ||||
| 	// Generate an ECDSA keypair for ourselves
 | ||||
| 	gen := ecdh.NewEllipticECDH(crypto.S256()) | ||||
| 	private, public, err := gen.GenerateKey(rand.Reader) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	cardPublic, ok := gen.Unmarshal(keyData) | ||||
| 	if !ok { | ||||
| 		return nil, fmt.Errorf("Could not unmarshal public key from card") | ||||
| 	} | ||||
| 
 | ||||
| 	secret, err := gen.GenerateSharedSecret(private, cardPublic) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &SecureChannelSession{ | ||||
| 		card:      card, | ||||
| 		secret:    secret, | ||||
| 		publicKey: gen.Marshal(public), | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // Pair establishes a new pairing with the smartcard.
 | ||||
| func (s *SecureChannelSession) Pair(pairingPassword []byte) error { | ||||
| 	secretHash := pbkdf2.Key(norm.NFKD.Bytes(pairingPassword), norm.NFKD.Bytes([]byte(pairingSalt)), 50000, 32, sha256.New) | ||||
| 
 | ||||
| 	challenge := make([]byte, 32) | ||||
| 	if _, err := rand.Read(challenge); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	response, err := s.pair(pairP1FirstStep, challenge) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	md := sha256.New() | ||||
| 	md.Write(secretHash[:]) | ||||
| 	md.Write(challenge) | ||||
| 
 | ||||
| 	expectedCryptogram := md.Sum(nil) | ||||
| 	cardCryptogram := response.Data[:32] | ||||
| 	cardChallenge := response.Data[32:64] | ||||
| 
 | ||||
| 	if !bytes.Equal(expectedCryptogram, cardCryptogram) { | ||||
| 		return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram) | ||||
| 	} | ||||
| 
 | ||||
| 	md.Reset() | ||||
| 	md.Write(secretHash[:]) | ||||
| 	md.Write(cardChallenge) | ||||
| 	response, err = s.pair(pairP1LastStep, md.Sum(nil)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	md.Reset() | ||||
| 	md.Write(secretHash[:]) | ||||
| 	md.Write(response.Data[1:]) | ||||
| 	s.PairingKey = md.Sum(nil) | ||||
| 	s.PairingIndex = response.Data[0] | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Unpair disestablishes an existing pairing.
 | ||||
| func (s *SecureChannelSession) Unpair() error { | ||||
| 	if s.PairingKey == nil { | ||||
| 		return fmt.Errorf("Cannot unpair: not paired") | ||||
| 	} | ||||
| 
 | ||||
| 	_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{}) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.PairingKey = nil | ||||
| 	// Close channel
 | ||||
| 	s.iv = nil | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Open initializes the secure channel.
 | ||||
| func (s *SecureChannelSession) Open() error { | ||||
| 	if s.iv != nil { | ||||
| 		return fmt.Errorf("Session already opened") | ||||
| 	} | ||||
| 
 | ||||
| 	response, err := s.open() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate the encryption/mac key by hashing our shared secret,
 | ||||
| 	// pairing key, and the first bytes returned from the Open APDU.
 | ||||
| 	md := sha512.New() | ||||
| 	md.Write(s.secret) | ||||
| 	md.Write(s.PairingKey) | ||||
| 	md.Write(response.Data[:scSecretLength]) | ||||
| 	keyData := md.Sum(nil) | ||||
| 	s.sessionEncKey = keyData[:scSecretLength] | ||||
| 	s.sessionMacKey = keyData[scSecretLength : scSecretLength*2] | ||||
| 
 | ||||
| 	// The IV is the last bytes returned from the Open APDU.
 | ||||
| 	s.iv = response.Data[scSecretLength:] | ||||
| 
 | ||||
| 	return s.mutuallyAuthenticate() | ||||
| } | ||||
| 
 | ||||
| // mutuallyAuthenticate is an internal method to authenticate both ends of the
 | ||||
| // connection.
 | ||||
| func (s *SecureChannelSession) mutuallyAuthenticate() error { | ||||
| 	data := make([]byte, scSecretLength) | ||||
| 	if _, err := rand.Read(data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	response, err := s.transmitEncrypted(claSCWallet, insMutuallyAuthenticate, 0, 0, data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if response.Sw1 != 0x90 || response.Sw2 != 0x00 { | ||||
| 		return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(response.Data) != scSecretLength { | ||||
| 		return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // open is an internal method that sends an open APDU.
 | ||||
| func (s *SecureChannelSession) open() (*responseAPDU, error) { | ||||
| 	return transmit(s.card, &commandAPDU{ | ||||
| 		Cla:  claSCWallet, | ||||
| 		Ins:  insOpenSecureChannel, | ||||
| 		P1:   s.PairingIndex, | ||||
| 		P2:   0, | ||||
| 		Data: s.publicKey, | ||||
| 		Le:   0, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // pair is an internal method that sends a pair APDU.
 | ||||
| func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error) { | ||||
| 	return transmit(s.card, &commandAPDU{ | ||||
| 		Cla:  claSCWallet, | ||||
| 		Ins:  insPair, | ||||
| 		P1:   p1, | ||||
| 		P2:   0, | ||||
| 		Data: data, | ||||
| 		Le:   0, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| // transmitEncrypted sends an encrypted message, and decrypts and returns the response.
 | ||||
| func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) { | ||||
| 	if s.iv == nil { | ||||
| 		return nil, fmt.Errorf("Channel not open") | ||||
| 	} | ||||
| 
 | ||||
| 	data, err := s.encryptAPDU(data) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)} | ||||
| 	if err = s.updateIV(meta[:], data); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	fulldata := make([]byte, len(s.iv)+len(data)) | ||||
| 	copy(fulldata, s.iv) | ||||
| 	copy(fulldata[len(s.iv):], data) | ||||
| 
 | ||||
| 	response, err := transmit(s.card, &commandAPDU{ | ||||
| 		Cla:  cla, | ||||
| 		Ins:  ins, | ||||
| 		P1:   p1, | ||||
| 		P2:   p2, | ||||
| 		Data: fulldata, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	rmeta := [16]byte{byte(len(response.Data))} | ||||
| 	rmac := response.Data[:len(s.iv)] | ||||
| 	rdata := response.Data[len(s.iv):] | ||||
| 	plainData, err := s.decryptAPDU(rdata) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err = s.updateIV(rmeta[:], rdata); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if !bytes.Equal(s.iv, rmac) { | ||||
| 		return nil, fmt.Errorf("Invalid MAC in response") | ||||
| 	} | ||||
| 
 | ||||
| 	rapdu := &responseAPDU{} | ||||
| 	rapdu.deserialize(plainData) | ||||
| 
 | ||||
| 	if rapdu.Sw1 != sw1Ok { | ||||
| 		return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2) | ||||
| 	} | ||||
| 
 | ||||
| 	return rapdu, nil | ||||
| } | ||||
| 
 | ||||
| // encryptAPDU is an internal method that serializes and encrypts an APDU.
 | ||||
| func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) { | ||||
| 	if len(data) > maxPayloadSize { | ||||
| 		return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize) | ||||
| 	} | ||||
| 	data = pad(data, 0x80) | ||||
| 
 | ||||
| 	ret := make([]byte, len(data)) | ||||
| 
 | ||||
| 	a, err := aes.NewCipher(s.sessionEncKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	crypter := cipher.NewCBCEncrypter(a, s.iv) | ||||
| 	crypter.CryptBlocks(ret, data) | ||||
| 	return ret, nil | ||||
| } | ||||
| 
 | ||||
| // pad applies message padding to a 16 byte boundary.
 | ||||
| func pad(data []byte, terminator byte) []byte { | ||||
| 	padded := make([]byte, (len(data)/16+1)*16) | ||||
| 	copy(padded, data) | ||||
| 	padded[len(data)] = terminator | ||||
| 	return padded | ||||
| } | ||||
| 
 | ||||
| // decryptAPDU is an internal method that decrypts and deserializes an APDU.
 | ||||
| func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) { | ||||
| 	a, err := aes.NewCipher(s.sessionEncKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	ret := make([]byte, len(data)) | ||||
| 
 | ||||
| 	crypter := cipher.NewCBCDecrypter(a, s.iv) | ||||
| 	crypter.CryptBlocks(ret, data) | ||||
| 	return unpad(ret, 0x80) | ||||
| } | ||||
| 
 | ||||
| // unpad strips padding from a message.
 | ||||
| func unpad(data []byte, terminator byte) ([]byte, error) { | ||||
| 	for i := 1; i <= 16; i++ { | ||||
| 		switch data[len(data)-i] { | ||||
| 		case 0: | ||||
| 			continue | ||||
| 		case terminator: | ||||
| 			return data[:len(data)-i], nil | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i]) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("Expected end of padding, got 0") | ||||
| } | ||||
| 
 | ||||
| // updateIV is an internal method that updates the initialization vector after
 | ||||
| // each message exchanged.
 | ||||
| func (s *SecureChannelSession) updateIV(meta, data []byte) error { | ||||
| 	data = pad(data, 0) | ||||
| 	a, err := aes.NewCipher(s.sessionMacKey) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	crypter := cipher.NewCBCEncrypter(a, make([]byte, 16)) | ||||
| 	crypter.CryptBlocks(meta, meta) | ||||
| 	crypter.CryptBlocks(data, data) | ||||
| 	// The first 16 bytes of the last block is the MAC
 | ||||
| 	s.iv = data[len(data)-32 : len(data)-16] | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										1074
									
								
								accounts/scwallet/wallet.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1074
									
								
								accounts/scwallet/wallet.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										31
									
								
								accounts/sort.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								accounts/sort.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | ||||
| // Copyright 2018 The go-ethereum Authors
 | ||||
| // This file is part of the go-ethereum library.
 | ||||
| //
 | ||||
| // The go-ethereum library is free software: you can redistribute it and/or modify
 | ||||
| // it under the terms of the GNU Lesser General Public License as published by
 | ||||
| // the Free Software Foundation, either version 3 of the License, or
 | ||||
| // (at your option) any later version.
 | ||||
| //
 | ||||
| // The go-ethereum library is distributed in the hope that it will be useful,
 | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | ||||
| // GNU Lesser General Public License for more details.
 | ||||
| //
 | ||||
| // You should have received a copy of the GNU Lesser General Public License
 | ||||
| // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| package accounts | ||||
| 
 | ||||
| // AccountsByURL implements sort.Interface for []Account based on the URL field.
 | ||||
| type AccountsByURL []Account | ||||
| 
 | ||||
| func (a AccountsByURL) Len() int           { return len(a) } | ||||
| func (a AccountsByURL) Swap(i, j int)      { a[i], a[j] = a[j], a[i] } | ||||
| func (a AccountsByURL) Less(i, j int) bool { return a[i].URL.Cmp(a[j].URL) < 0 } | ||||
| 
 | ||||
| // WalletsByURL implements sort.Interface for []Wallet based on the URL field.
 | ||||
| type WalletsByURL []Wallet | ||||
| 
 | ||||
| func (w WalletsByURL) Len() int           { return len(w) } | ||||
| func (w WalletsByURL) Swap(i, j int)      { w[i], w[j] = w[j], w[i] } | ||||
| func (w WalletsByURL) Less(i, j int) bool { return w[i].URL().Cmp(w[j].URL()) < 0 } | ||||
| @ -23,6 +23,7 @@ import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ethereum/go-ethereum/accounts/scwallet" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/usbwallet" | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| @ -104,19 +105,73 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) { | ||||
| 	if err == nil { | ||||
| 		return val | ||||
| 	} | ||||
| 	// Wallet open failed, report error unless it's a PIN entry
 | ||||
| 	if strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) { | ||||
| 
 | ||||
| 	// Wallet open failed, report error unless it's a PIN or PUK entry
 | ||||
| 	switch { | ||||
| 	case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()): | ||||
| 		val, err = b.readPinAndReopenWallet(call) | ||||
| 		if err == nil { | ||||
| 			return val | ||||
| 		} | ||||
| 	} | ||||
| 	// Check if the user needs to input a passphrase
 | ||||
| 	if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPassphraseNeeded.Error()) { | ||||
| 		throwJSException(err.Error()) | ||||
| 	} | ||||
| 	val, err = b.readPassphraseAndReopenWallet(call) | ||||
| 	if err != nil { | ||||
| 		val, err = b.readPassphraseAndReopenWallet(call) | ||||
| 		if err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 	case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()): | ||||
| 		// PUK input requested, fetch from the user and call open again
 | ||||
| 		if input, err := b.prompter.PromptPassword("Please enter the pairing password: "); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} else { | ||||
| 			passwd, _ = otto.ToValue(input) | ||||
| 		} | ||||
| 		if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { | ||||
| 			if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) { | ||||
| 				throwJSException(err.Error()) | ||||
| 			} else { | ||||
| 				// PIN input requested, fetch from the user and call open again
 | ||||
| 				if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { | ||||
| 					throwJSException(err.Error()) | ||||
| 				} else { | ||||
| 					passwd, _ = otto.ToValue(input) | ||||
| 				} | ||||
| 				if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { | ||||
| 					throwJSException(err.Error()) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()): | ||||
| 		// PIN unblock requested, fetch PUK and new PIN from the user
 | ||||
| 		var pukpin string | ||||
| 		if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} else { | ||||
| 			pukpin = input | ||||
| 		} | ||||
| 		if input, err := b.prompter.PromptPassword("Please enter new PIN: "); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} else { | ||||
| 			pukpin += input | ||||
| 		} | ||||
| 		passwd, _ = otto.ToValue(pukpin) | ||||
| 		if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 	case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()): | ||||
| 		// PIN input requested, fetch from the user and call open again
 | ||||
| 		if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} else { | ||||
| 			passwd, _ = otto.ToValue(input) | ||||
| 		} | ||||
| 		if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { | ||||
| 			throwJSException(err.Error()) | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		// Unknown error occurred, drop to the user
 | ||||
| 		throwJSException(err.Error()) | ||||
| 	} | ||||
| 	return val | ||||
|  | ||||
| @ -28,6 +28,7 @@ import ( | ||||
| 	"github.com/davecgh/go-spew/spew" | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/keystore" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/scwallet" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/common/hexutil" | ||||
| 	"github.com/ethereum/go-ethereum/common/math" | ||||
| @ -44,6 +45,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"github.com/ethereum/go-ethereum/rpc" | ||||
| 	"github.com/syndtr/goleveldb/leveldb" | ||||
| 	"github.com/tyler-smith/go-bip39" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -471,6 +473,48 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen | ||||
| 	return s.SendTransaction(ctx, args, passwd) | ||||
| } | ||||
| 
 | ||||
| // InitializeWallet initializes a new wallet at the provided URL, by generating and returning a new private key.
 | ||||
| func (s *PrivateAccountAPI) InitializeWallet(ctx context.Context, url string) (string, error) { | ||||
| 	wallet, err := s.am.Wallet(url) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	entropy, err := bip39.NewEntropy(256) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	mnemonic, err := bip39.NewMnemonic(entropy) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	seed := bip39.NewSeed(mnemonic, "") | ||||
| 
 | ||||
| 	switch wallet := wallet.(type) { | ||||
| 	case *scwallet.Wallet: | ||||
| 		return mnemonic, wallet.Initialize(seed) | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("Specified wallet does not support initialization") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Unpair deletes a pairing between wallet and geth.
 | ||||
| func (s *PrivateAccountAPI) Unpair(ctx context.Context, url string, pin string) error { | ||||
| 	wallet, err := s.am.Wallet(url) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	switch wallet := wallet.(type) { | ||||
| 	case *scwallet.Wallet: | ||||
| 		return wallet.Unpair([]byte(pin)) | ||||
| 	default: | ||||
| 		return fmt.Errorf("Specified wallet does not support pairing") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // PublicBlockChainAPI provides an API to access the Ethereum blockchain.
 | ||||
| // It offers only methods that operate on public data that is freely available to anyone.
 | ||||
| type PublicBlockChainAPI struct { | ||||
|  | ||||
| @ -613,6 +613,16 @@ web3._extend({ | ||||
| 			params: 2, | ||||
| 			inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] | ||||
| 		}), | ||||
| 		new web3._extend.Method({ | ||||
| 			name: 'unpair', | ||||
| 			call: 'personal_unpair', | ||||
| 			params: 2 | ||||
| 		}), | ||||
| 		new web3._extend.Method({ | ||||
| 			name: 'initializeWallet', | ||||
| 			call: 'personal_initializeWallet', | ||||
| 			params: 1 | ||||
| 		}) | ||||
| 	], | ||||
| 	properties: [ | ||||
| 		new web3._extend.Property({ | ||||
|  | ||||
| @ -29,6 +29,7 @@ import ( | ||||
| 	"github.com/ethereum/go-ethereum/accounts" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/external" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/keystore" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/scwallet" | ||||
| 	"github.com/ethereum/go-ethereum/accounts/usbwallet" | ||||
| 	"github.com/ethereum/go-ethereum/common" | ||||
| 	"github.com/ethereum/go-ethereum/crypto" | ||||
| @ -504,6 +505,12 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) { | ||||
| 				backends = append(backends, trezorhub) | ||||
| 			} | ||||
| 		} | ||||
| 		// Start a smart card hub
 | ||||
| 		if schub, err := scwallet.NewHub(scwallet.Scheme, keydir); err != nil { | ||||
| 			log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) | ||||
| 		} else { | ||||
| 			backends = append(backends, schub) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed}, backends...), ephemeral, nil | ||||
|  | ||||
							
								
								
									
										29
									
								
								vendor/github.com/gballet/go-libpcsclite/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/gballet/go-libpcsclite/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| BSD 3-Clause License | ||||
| 
 | ||||
| Copyright (c) 2019, Guillaume Ballet | ||||
| All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
| 
 | ||||
| * Redistributions of source code must retain the above copyright notice, this | ||||
|   list of conditions and the following disclaimer. | ||||
| 
 | ||||
| * Redistributions in binary form must reproduce the above copyright notice, | ||||
|   this list of conditions and the following disclaimer in the documentation | ||||
|   and/or other materials provided with the distribution. | ||||
| 
 | ||||
| * Neither the name of the copyright holder nor the names of its | ||||
|   contributors may be used to endorse or promote products derived from | ||||
|   this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										53
									
								
								vendor/github.com/gballet/go-libpcsclite/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								vendor/github.com/gballet/go-libpcsclite/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| # go-libpcsclite | ||||
| 
 | ||||
| A golang implementation of the [libpcpsclite](http://github.com/LudovicRousseau/PCSC) client. It connects to the `pcscd` daemon over sockets. | ||||
| 
 | ||||
| ## Purpose | ||||
| 
 | ||||
| The goal is for major open source projects to distribute a single binary that doesn't depend on `libpcsclite`. It provides an extra function `CheckPCSCDaemon` that will tell the user if `pcscd` is running. | ||||
| 
 | ||||
| ## Building | ||||
| 
 | ||||
| TODO | ||||
| 
 | ||||
| ## Example | ||||
| 
 | ||||
| TODO | ||||
| 
 | ||||
| ## TODO | ||||
| 
 | ||||
|   - [ ] Finish this README | ||||
|   - [ ] Lock context | ||||
|   - [ ] implement missing functions | ||||
| 
 | ||||
| ## License | ||||
| 
 | ||||
| BSD 3-Clause License | ||||
| 
 | ||||
| Copyright (c) 2019, Guillaume Ballet | ||||
| All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
| 
 | ||||
| * Redistributions of source code must retain the above copyright notice, this | ||||
|   list of conditions and the following disclaimer. | ||||
| 
 | ||||
| * Redistributions in binary form must reproduce the above copyright notice, | ||||
|   this list of conditions and the following disclaimer in the documentation | ||||
|   and/or other materials provided with the distribution. | ||||
| 
 | ||||
| * Neither the name of the copyright holder nor the names of its | ||||
|   contributors may be used to endorse or promote products derived from | ||||
|   this software without specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										99
									
								
								vendor/github.com/gballet/go-libpcsclite/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								vendor/github.com/gballet/go-libpcsclite/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| // BSD 3-Clause License
 | ||||
| //
 | ||||
| // Copyright (c) 2019, Guillaume Ballet
 | ||||
| // All rights reserved.
 | ||||
| //
 | ||||
| // Redistribution and use in source and binary forms, with or without
 | ||||
| // modification, are permitted provided that the following conditions are met:
 | ||||
| //
 | ||||
| // * Redistributions of source code must retain the above copyright notice, this
 | ||||
| //   list of conditions and the following disclaimer.
 | ||||
| //
 | ||||
| // * Redistributions in binary form must reproduce the above copyright notice,
 | ||||
| //   this list of conditions and the following disclaimer in the documentation
 | ||||
| //   and/or other materials provided with the distribution.
 | ||||
| //
 | ||||
| // * Neither the name of the copyright holder nor the names of its
 | ||||
| //   contributors may be used to endorse or promote products derived from
 | ||||
| //   this software without specific prior written permission.
 | ||||
| //
 | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | ||||
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | ||||
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | ||||
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 | ||||
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | ||||
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | ||||
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | ||||
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | ||||
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| package pcsc | ||||
| 
 | ||||
| const ( | ||||
| 	SCardSuccess                   = 0x00000000 /* No error was encountered. */ | ||||
| 
 | ||||
| 	AutoAllocate  = -1     /* see SCardFreeMemory() */ | ||||
| 	ScopeUser     = 0x0000 /* Scope in user space */ | ||||
| 	ScopeTerminal = 0x0001 /* Scope in terminal */ | ||||
| 	ScopeSystem   = 0x0002 /* Scope in system */ | ||||
| 	ScopeGlobal   = 0x0003 /* Scope is global */ | ||||
| 
 | ||||
| 	ProtocolUndefined = 0x0000                    /* protocol not set */ | ||||
| 	ProtocolUnSet     = ProtocolUndefined         /* backward compat */ | ||||
| 	ProtocolT0        = 0x0001                    /* T=0 active protocol. */ | ||||
| 	ProtocolT1        = 0x0002                    /* T=1 active protocol. */ | ||||
| 	ProtocolRaw       = 0x0004                    /* Raw active protocol. */ | ||||
| 	ProtocolT15       = 0x0008                    /* T=15 protocol. */ | ||||
| 	ProtocolAny       = (ProtocolT0 | ProtocolT1) /* IFD determines prot. */ | ||||
| 
 | ||||
| 	ShareExclusive = 0x0001 /* Exclusive mode only */ | ||||
| 	ShareShared    = 0x0002 /* Shared mode only */ | ||||
| 	ShareDirect    = 0x0003 /* Raw mode only */ | ||||
| 
 | ||||
| 	LeaveCard   = 0x0000 /* Do nothing on close */ | ||||
| 	ResetCard   = 0x0001 /* Reset on close */ | ||||
| 	UnpowerCard = 0x0002 /* Power down on close */ | ||||
| 	EjectCard   = 0x0003 /* Eject on close */ | ||||
| 
 | ||||
| 	SCardUnknown    = 0x0001 /* Unknown state */ | ||||
| 	SCardAbsent     = 0x0002 /* Card is absent */ | ||||
| 	SCardPresent    = 0x0004 /* Card is present */ | ||||
| 	SCardSwallowed  = 0x0008 /* Card not powered */ | ||||
| 	SCardPowever    = 0x0010 /* Card is powered */ | ||||
| 	SCardNegotiable = 0x0020 /* Ready for PTS */ | ||||
| 	SCardSpecific   = 0x0040 /* PTS has been set */ | ||||
| 
 | ||||
| 	PCSCDSockName = "/run/pcscd/pcscd.comm" | ||||
| ) | ||||
| 
 | ||||
| // List of commands to send to the daemon
 | ||||
| const ( | ||||
| 	_                                   = iota | ||||
| 	SCardEstablishContext               /* used by SCardEstablishContext() */ | ||||
| 	SCardReleaseContext                 /* used by SCardReleaseContext() */ | ||||
| 	SCardListReaders                    /* used by SCardListReaders() */ | ||||
| 	SCardConnect                        /* used by SCardConnect() */ | ||||
| 	SCardReConnect                      /* used by SCardReconnect() */ | ||||
| 	SCardDisConnect                     /* used by SCardDisconnect() */ | ||||
| 	SCardBeginTransaction               /* used by SCardBeginTransaction() */ | ||||
| 	SCardEndTransaction                 /* used by SCardEndTransaction() */ | ||||
| 	SCardTransmit                       /* used by SCardTransmit() */ | ||||
| 	SCardControl                        /* used by SCardControl() */ | ||||
| 	SCardStatus                         /* used by SCardStatus() */ | ||||
| 	SCardGetStatusChange                /* not used */ | ||||
| 	SCardCancel                         /* used by SCardCancel() */ | ||||
| 	SCardCancelTransaction              /* not used */ | ||||
| 	SCardGetAttrib                      /* used by SCardGetAttrib() */ | ||||
| 	SCardSetAttrib                      /* used by SCardSetAttrib() */ | ||||
| 	CommandVersion                      /* get the client/server protocol version */ | ||||
| 	CommandGetReaderState               /* get the readers state */ | ||||
| 	CommandWaitReaderStateChange        /* wait for a reader state change */ | ||||
| 	CommandStopWaitingReaderStateChange /* stop waiting for a reader state change */ | ||||
| ) | ||||
| 
 | ||||
| // Protocol information
 | ||||
| const ( | ||||
| 	ProtocolVersionMajor = 4 /* IPC major */ | ||||
| 	ProtocolVersionMinor = 3 /* IPC minor */ | ||||
| ) | ||||
							
								
								
									
										1
									
								
								vendor/github.com/gballet/go-libpcsclite/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/gballet/go-libpcsclite/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| module github.com/gballet/go-libpcsclite | ||||
							
								
								
									
										78
									
								
								vendor/github.com/gballet/go-libpcsclite/msg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								vendor/github.com/gballet/go-libpcsclite/msg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| // BSD 3-Clause License
 | ||||
| //
 | ||||
| // Copyright (c) 2019, Guillaume Ballet
 | ||||
| // All rights reserved.
 | ||||
| //
 | ||||
| // Redistribution and use in source and binary forms, with or without
 | ||||
| // modification, are permitted provided that the following conditions are met:
 | ||||
| //
 | ||||
| // * Redistributions of source code must retain the above copyright notice, this
 | ||||
| //   list of conditions and the following disclaimer.
 | ||||
| //
 | ||||
| // * Redistributions in binary form must reproduce the above copyright notice,
 | ||||
| //   this list of conditions and the following disclaimer in the documentation
 | ||||
| //   and/or other materials provided with the distribution.
 | ||||
| //
 | ||||
| // * Neither the name of the copyright holder nor the names of its
 | ||||
| //   contributors may be used to endorse or promote products derived from
 | ||||
| //   this software without specific prior written permission.
 | ||||
| //
 | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | ||||
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | ||||
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | ||||
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 | ||||
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | ||||
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | ||||
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | ||||
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | ||||
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| package pcsc | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"net" | ||||
| ) | ||||
| 
 | ||||
| /** | ||||
|  * @brief Wrapper for the MessageSend() function. | ||||
|  * | ||||
|  * Called by clients to send messages to the server. | ||||
|  * The parameters \p command and \p data are set in the \c sharedSegmentMsg | ||||
|  * struct in order to be sent. | ||||
|  * | ||||
|  * @param[in] command Command to be sent. | ||||
|  * @param[in] dwClientID Client socket handle. | ||||
|  * @param[in] size Size of the message (\p data). | ||||
|  * @param[in] data_void Data to be sent. | ||||
|  * | ||||
|  * @return Same error codes as MessageSend(). | ||||
|  */ | ||||
| func messageSendWithHeader(command uint32, conn net.Conn, data []byte) error { | ||||
| 	/* Translate header into bytes */ | ||||
| 	msgData := make([]byte, 8+len(data)) | ||||
| 	binary.LittleEndian.PutUint32(msgData[4:], command) | ||||
| 	binary.LittleEndian.PutUint32(msgData, uint32(len(data))) | ||||
| 
 | ||||
| 	/* Copy payload */ | ||||
| 	copy(msgData[8:], data) | ||||
| 
 | ||||
| 	_, err := conn.Write(msgData) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // ClientSetupSession prepares a communication channel for the client to talk to the server.
 | ||||
| // This is called by the application to create a socket for local IPC with the
 | ||||
| // server. The socket is associated to the file \c PCSCLITE_CSOCK_NAME.
 | ||||
| /* | ||||
|  * @param[out] pdwClientID Client Connection ID. | ||||
|  * | ||||
|  * @retval 0 Success. | ||||
|  * @retval -1 Can not create the socket. | ||||
|  * @retval -1 The socket can not open a connection. | ||||
|  * @retval -1 Can not set the socket to non-blocking. | ||||
|  */ | ||||
| func clientSetupSession() (net.Conn, error) { | ||||
| 	return net.Dial("unix", PCSCDSockName) | ||||
| } | ||||
							
								
								
									
										389
									
								
								vendor/github.com/gballet/go-libpcsclite/winscard.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								vendor/github.com/gballet/go-libpcsclite/winscard.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,389 @@ | ||||
| // BSD 3-Clause License
 | ||||
| //
 | ||||
| // Copyright (c) 2019, Guillaume Ballet
 | ||||
| // All rights reserved.
 | ||||
| //
 | ||||
| // Redistribution and use in source and binary forms, with or without
 | ||||
| // modification, are permitted provided that the following conditions are met:
 | ||||
| //
 | ||||
| // * Redistributions of source code must retain the above copyright notice, this
 | ||||
| //   list of conditions and the following disclaimer.
 | ||||
| //
 | ||||
| // * Redistributions in binary form must reproduce the above copyright notice,
 | ||||
| //   this list of conditions and the following disclaimer in the documentation
 | ||||
| //   and/or other materials provided with the distribution.
 | ||||
| //
 | ||||
| // * Neither the name of the copyright holder nor the names of its
 | ||||
| //   contributors may be used to endorse or promote products derived from
 | ||||
| //   this software without specific prior written permission.
 | ||||
| //
 | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | ||||
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | ||||
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | ||||
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 | ||||
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | ||||
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 | ||||
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 | ||||
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 | ||||
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| package pcsc | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| // Client contains all the information needed to establish
 | ||||
| // and maintain a connection to the deamon/card.
 | ||||
| type Client struct { | ||||
| 	conn net.Conn | ||||
| 
 | ||||
| 	minor uint32 | ||||
| 	major uint32 | ||||
| 
 | ||||
| 	ctx uint32 | ||||
| 
 | ||||
| 	mutex sync.Mutex | ||||
| 
 | ||||
| 	readerStateDescriptors [MaxReaderStateDescriptors]ReaderState | ||||
| } | ||||
| 
 | ||||
| // EstablishContext asks the PCSC daemon to create a context
 | ||||
| // handle for further communication with connected cards and
 | ||||
| // readers.
 | ||||
| func EstablishContext(scope uint32) (*Client, error) { | ||||
| 	client := &Client{} | ||||
| 
 | ||||
| 	conn, err := clientSetupSession() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	client.conn = conn | ||||
| 
 | ||||
| 	/* Exchange version information */ | ||||
| 	payload := make([]byte, 12) | ||||
| 	binary.LittleEndian.PutUint32(payload, ProtocolVersionMajor) | ||||
| 	binary.LittleEndian.PutUint32(payload[4:], ProtocolVersionMinor) | ||||
| 	binary.LittleEndian.PutUint32(payload[8:], SCardSuccess) | ||||
| 	err = messageSendWithHeader(CommandVersion, conn, payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	response := make([]byte, 12) | ||||
| 	n, err := conn.Read(response) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if n != len(response) { | ||||
| 		return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n) | ||||
| 	} | ||||
| 	code := binary.LittleEndian.Uint32(response[8:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return nil, fmt.Errorf("invalid response code: expected %d, got %d", SCardSuccess, code) | ||||
| 	} | ||||
| 	client.major = binary.LittleEndian.Uint32(response) | ||||
| 	client.minor = binary.LittleEndian.Uint32(response[4:]) | ||||
| 	if client.major != ProtocolVersionMajor || client.minor != ProtocolVersionMinor { | ||||
| 		return nil, fmt.Errorf("invalid version found: expected %d.%d, got %d.%d", ProtocolVersionMajor, ProtocolVersionMinor, client.major, client.minor) | ||||
| 	} | ||||
| 
 | ||||
| 	/* Establish the context proper */ | ||||
| 	binary.LittleEndian.PutUint32(payload, scope) | ||||
| 	binary.LittleEndian.PutUint32(payload[4:], 0) | ||||
| 	binary.LittleEndian.PutUint32(payload[8:], SCardSuccess) | ||||
| 	err = messageSendWithHeader(SCardEstablishContext, conn, payload) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	response = make([]byte, 12) | ||||
| 	n, err = conn.Read(response) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if n != len(response) { | ||||
| 		return nil, fmt.Errorf("invalid response length: expected %d, got %d", len(response), n) | ||||
| 	} | ||||
| 	code = binary.LittleEndian.Uint32(response[8:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return nil, fmt.Errorf("invalid response code: expected %d, got %d", SCardSuccess, code) | ||||
| 	} | ||||
| 	client.ctx = binary.LittleEndian.Uint32(response[4:]) | ||||
| 
 | ||||
| 	return client, nil | ||||
| } | ||||
| 
 | ||||
| // ReleaseContext tells the daemon that the client will no longer
 | ||||
| // need the context.
 | ||||
| func (client *Client) ReleaseContext() error { | ||||
| 	client.mutex.Lock() | ||||
| 	defer client.mutex.Unlock() | ||||
| 
 | ||||
| 	data := [8]byte{} | ||||
| 	binary.LittleEndian.PutUint32(data[:], client.ctx) | ||||
| 	binary.LittleEndian.PutUint32(data[4:], SCardSuccess) | ||||
| 	err := messageSendWithHeader(SCardReleaseContext, client.conn, data[:]) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	total := 0 | ||||
| 	for total < len(data) { | ||||
| 		n, err := client.conn.Read(data[total:]) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		total += n | ||||
| 	} | ||||
| 	code := binary.LittleEndian.Uint32(data[4:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return fmt.Errorf("invalid return code: %x", code) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Constants related to the reader state structure
 | ||||
| const ( | ||||
| 	ReaderStateNameLength       = 128 | ||||
| 	ReaderStateMaxAtrSizeLength = 33 | ||||
| 	// NOTE: ATR is 32-byte aligned in the C version, which means it's
 | ||||
| 	// actually 36 byte long and not 33.
 | ||||
| 	ReaderStateDescriptorLength = ReaderStateNameLength + ReaderStateMaxAtrSizeLength + 5*4 + 3 | ||||
| 
 | ||||
| 	MaxReaderStateDescriptors = 16 | ||||
| ) | ||||
| 
 | ||||
| // ReaderState represent the state of a single reader, as reported
 | ||||
| // by the PCSC daemon.
 | ||||
| type ReaderState struct { | ||||
| 	Name          string /* reader name */ | ||||
| 	eventCounter  uint32 /* number of card events */ | ||||
| 	readerState   uint32 /* SCARD_* bit field */ | ||||
| 	readerSharing uint32 /* PCSCLITE_SHARING_* sharing status */ | ||||
| 
 | ||||
| 	cardAtr       [ReaderStateMaxAtrSizeLength]byte /* ATR */ | ||||
| 	cardAtrLength uint32                            /* ATR length */ | ||||
| 	cardProtocol  uint32                            /* SCARD_PROTOCOL_* value */ | ||||
| } | ||||
| 
 | ||||
| func getReaderState(data []byte) (ReaderState, error) { | ||||
| 	ret := ReaderState{} | ||||
| 	if len(data) < ReaderStateDescriptorLength { | ||||
| 		return ret, fmt.Errorf("could not unmarshall data of length %d < %d", len(data), ReaderStateDescriptorLength) | ||||
| 	} | ||||
| 
 | ||||
| 	ret.Name = string(data[:ReaderStateNameLength]) | ||||
| 	ret.eventCounter = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.eventCounter):]) | ||||
| 	ret.readerState = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerState):]) | ||||
| 	ret.readerSharing = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.readerSharing):]) | ||||
| 	copy(ret.cardAtr[:], data[unsafe.Offsetof(ret.cardAtr):unsafe.Offsetof(ret.cardAtr)+ReaderStateMaxAtrSizeLength]) | ||||
| 	ret.cardAtrLength = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardAtrLength):]) | ||||
| 	ret.cardProtocol = binary.LittleEndian.Uint32(data[unsafe.Offsetof(ret.cardProtocol):]) | ||||
| 
 | ||||
| 	return ret, nil | ||||
| } | ||||
| 
 | ||||
| // ListReaders gets the list of readers from the daemon
 | ||||
| func (client *Client) ListReaders() ([]string, error) { | ||||
| 	client.mutex.Lock() | ||||
| 	defer client.mutex.Unlock() | ||||
| 
 | ||||
| 	err := messageSendWithHeader(CommandGetReaderState, client.conn, []byte{}) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	response := make([]byte, ReaderStateDescriptorLength*MaxReaderStateDescriptors) | ||||
| 	total := 0 | ||||
| 	for total < len(response) { | ||||
| 		n, err := client.conn.Read(response[total:]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		total += n | ||||
| 	} | ||||
| 
 | ||||
| 	var names []string | ||||
| 	for i := range client.readerStateDescriptors { | ||||
| 		desc, err := getReaderState(response[i*ReaderStateDescriptorLength:]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		client.readerStateDescriptors[i] = desc | ||||
| 		if desc.Name[0] == 0 { | ||||
| 			break | ||||
| 		} | ||||
| 		names = append(names, desc.Name) | ||||
| 	} | ||||
| 
 | ||||
| 	return names, nil | ||||
| } | ||||
| 
 | ||||
| // Offsets into the Connect request/response packet
 | ||||
| const ( | ||||
| 	SCardConnectReaderNameOffset        = 4 | ||||
| 	SCardConnectShareModeOffset         = SCardConnectReaderNameOffset + ReaderStateNameLength | ||||
| 	SCardConnectPreferredProtocolOffset = SCardConnectShareModeOffset + 4 | ||||
| 	SCardConnectReturnValueOffset       = SCardConnectPreferredProtocolOffset + 12 | ||||
| ) | ||||
| 
 | ||||
| // Card represents the connection to a card
 | ||||
| type Card struct { | ||||
| 	handle      uint32 | ||||
| 	activeProto uint32 | ||||
| 	client      *Client | ||||
| } | ||||
| 
 | ||||
| // Connect asks the daemon to connect to the card
 | ||||
| func (client *Client) Connect(name string, shareMode uint32, preferredProtocol uint32) (*Card, error) { | ||||
| 	client.mutex.Lock() | ||||
| 	defer client.mutex.Unlock() | ||||
| 
 | ||||
| 	request := make([]byte, ReaderStateNameLength+4*6) | ||||
| 	binary.LittleEndian.PutUint32(request, client.ctx) | ||||
| 	copy(request[SCardConnectReaderNameOffset:], []byte(name)) | ||||
| 	binary.LittleEndian.PutUint32(request[SCardConnectShareModeOffset:], shareMode) | ||||
| 	binary.LittleEndian.PutUint32(request[SCardConnectPreferredProtocolOffset:], preferredProtocol) | ||||
| 	binary.LittleEndian.PutUint32(request[SCardConnectReturnValueOffset:], SCardSuccess) | ||||
| 
 | ||||
| 	err := messageSendWithHeader(SCardConnect, client.conn, request) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	response := make([]byte, ReaderStateNameLength+4*6) | ||||
| 	total := 0 | ||||
| 	for total < len(response) { | ||||
| 		n, err := client.conn.Read(response[total:]) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		fmt.Println("total, n", total, n, response) | ||||
| 		total += n | ||||
| 	} | ||||
| 	code := binary.LittleEndian.Uint32(response[148:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return nil, fmt.Errorf("invalid return code: %x", code) | ||||
| 	} | ||||
| 	handle := binary.LittleEndian.Uint32(response[140:]) | ||||
| 	active := binary.LittleEndian.Uint32(response[SCardConnectPreferredProtocolOffset:]) | ||||
| 
 | ||||
| 	return &Card{handle: handle, activeProto: active, client: client}, nil | ||||
| } | ||||
| 
 | ||||
| /** | ||||
| * @brief contained in \ref SCARD_TRANSMIT Messages. | ||||
| * | ||||
| * These data are passed throw the field \c sharedSegmentMsg.data. | ||||
|  */ | ||||
| type transmit struct { | ||||
| 	hCard             uint32 | ||||
| 	ioSendPciProtocol uint32 | ||||
| 	ioSendPciLength   uint32 | ||||
| 	cbSendLength      uint32 | ||||
| 	ioRecvPciProtocol uint32 | ||||
| 	ioRecvPciLength   uint32 | ||||
| 	pcbRecvLength     uint32 | ||||
| 	rv                uint32 | ||||
| } | ||||
| 
 | ||||
| // SCardIoRequest contains the info needed for performing an IO request
 | ||||
| type SCardIoRequest struct { | ||||
| 	proto  uint32 | ||||
| 	length uint32 | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	TransmitRequestLength = 32 | ||||
| ) | ||||
| 
 | ||||
| // Transmit sends request data to a card and returns the response
 | ||||
| func (card *Card) Transmit(adpu []byte) ([]byte, *SCardIoRequest, error) { | ||||
| 	card.client.mutex.Lock() | ||||
| 	defer card.client.mutex.Unlock() | ||||
| 
 | ||||
| 	request := [TransmitRequestLength]byte{} | ||||
| 	binary.LittleEndian.PutUint32(request[:], card.handle) | ||||
| 	binary.LittleEndian.PutUint32(request[4:] /*card.activeProto*/, 2) | ||||
| 	binary.LittleEndian.PutUint32(request[8:], 8) | ||||
| 	binary.LittleEndian.PutUint32(request[12:], uint32(len(adpu))) | ||||
| 	binary.LittleEndian.PutUint32(request[16:], 0) | ||||
| 	binary.LittleEndian.PutUint32(request[20:], 0) | ||||
| 	binary.LittleEndian.PutUint32(request[24:], 0x10000) | ||||
| 	binary.LittleEndian.PutUint32(request[28:], SCardSuccess) | ||||
| 	err := messageSendWithHeader(SCardTransmit, card.client.conn, request[:]) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	// Add the ADPU payload after the transmit descriptor
 | ||||
| 	n, err := card.client.conn.Write(adpu) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 	if n != len(adpu) { | ||||
| 		return nil, nil, fmt.Errorf("Invalid number of bytes written: expected %d, got %d", len(adpu), n) | ||||
| 	} | ||||
| 	response := [TransmitRequestLength]byte{} | ||||
| 	total := 0 | ||||
| 	for total < len(response) { | ||||
| 		n, err = card.client.conn.Read(response[total:]) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		total += n | ||||
| 	} | ||||
| 
 | ||||
| 	code := binary.LittleEndian.Uint32(response[28:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return nil, nil, fmt.Errorf("invalid return code: %x", code) | ||||
| 	} | ||||
| 
 | ||||
| 	// Recover the response data
 | ||||
| 	recvProto := binary.LittleEndian.Uint32(response[16:]) | ||||
| 	recvLength := binary.LittleEndian.Uint32(response[20:]) | ||||
| 	recv := &SCardIoRequest{proto: recvProto, length: recvLength} | ||||
| 	recvLength = binary.LittleEndian.Uint32(response[24:]) | ||||
| 	recvData := make([]byte, recvLength) | ||||
| 	total = 0 | ||||
| 	for uint32(total) < recvLength { | ||||
| 		n, err := card.client.conn.Read(recvData[total:]) | ||||
| 		if err != nil { | ||||
| 			return nil, nil, err | ||||
| 		} | ||||
| 		total += n | ||||
| 	} | ||||
| 
 | ||||
| 	return recvData, recv, nil | ||||
| } | ||||
| 
 | ||||
| // Disconnect tells the PCSC daemon that the client is no longer
 | ||||
| // interested in communicating with the card.
 | ||||
| func (card *Card) Disconnect(disposition uint32) error { | ||||
| 	card.client.mutex.Lock() | ||||
| 	defer card.client.mutex.Unlock() | ||||
| 
 | ||||
| 	data := [12]byte{} | ||||
| 	binary.LittleEndian.PutUint32(data[:], card.handle) | ||||
| 	binary.LittleEndian.PutUint32(data[4:], disposition) | ||||
| 	binary.LittleEndian.PutUint32(data[8:], SCardSuccess) | ||||
| 	err := messageSendWithHeader(SCardDisConnect, card.client.conn, data[:]) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	total := 0 | ||||
| 	for total < len(data) { | ||||
| 		n, err := card.client.conn.Read(data[total:]) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		total += n | ||||
| 	} | ||||
| 	code := binary.LittleEndian.Uint32(data[8:]) | ||||
| 	if code != SCardSuccess { | ||||
| 		return fmt.Errorf("invalid return code: %x", code) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										356
									
								
								vendor/github.com/status-im/keycard-go/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								vendor/github.com/status-im/keycard-go/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,356 @@ | ||||
| Mozilla Public License Version 2.0 | ||||
| ================================== | ||||
| 
 | ||||
| ### 1. Definitions | ||||
| 
 | ||||
| **1.1. “Contributor”**   | ||||
|     means each individual or legal entity that creates, contributes to | ||||
|     the creation of, or owns Covered Software. | ||||
| 
 | ||||
| **1.2. “Contributor Version”**   | ||||
|     means the combination of the Contributions of others (if any) used | ||||
|     by a Contributor and that particular Contributor's Contribution. | ||||
| 
 | ||||
| **1.3. “Contribution”**   | ||||
|     means Covered Software of a particular Contributor. | ||||
| 
 | ||||
| **1.4. “Covered Software”**   | ||||
|     means Source Code Form to which the initial Contributor has attached | ||||
|     the notice in Exhibit A, the Executable Form of such Source Code | ||||
|     Form, and Modifications of such Source Code Form, in each case | ||||
|     including portions thereof. | ||||
| 
 | ||||
| **1.5. “Incompatible With Secondary Licenses”**   | ||||
|     means | ||||
| 
 | ||||
| * **(a)** that the initial Contributor has attached the notice described | ||||
|     in Exhibit B to the Covered Software; or | ||||
| * **(b)** that the Covered Software was made available under the terms of | ||||
|     version 1.1 or earlier of the License, but not also under the | ||||
|     terms of a Secondary License. | ||||
| 
 | ||||
| **1.6. “Executable Form”**   | ||||
|     means any form of the work other than Source Code Form. | ||||
| 
 | ||||
| **1.7. “Larger Work”**   | ||||
|     means a work that combines Covered Software with other material, in  | ||||
|     a separate file or files, that is not Covered Software. | ||||
| 
 | ||||
| **1.8. “License”**   | ||||
|     means this document. | ||||
| 
 | ||||
| **1.9. “Licensable”**   | ||||
|     means having the right to grant, to the maximum extent possible, | ||||
|     whether at the time of the initial grant or subsequently, any and | ||||
|     all of the rights conveyed by this License. | ||||
| 
 | ||||
| **1.10. “Modifications”**   | ||||
|     means any of the following: | ||||
| 
 | ||||
| * **(a)** any file in Source Code Form that results from an addition to, | ||||
|     deletion from, or modification of the contents of Covered | ||||
|     Software; or | ||||
| * **(b)** any new file in Source Code Form that contains any Covered | ||||
|     Software. | ||||
| 
 | ||||
| **1.11. “Patent Claims” of a Contributor**   | ||||
|     means any patent claim(s), including without limitation, method, | ||||
|     process, and apparatus claims, in any patent Licensable by such | ||||
|     Contributor that would be infringed, but for the grant of the | ||||
|     License, by the making, using, selling, offering for sale, having | ||||
|     made, import, or transfer of either its Contributions or its | ||||
|     Contributor Version. | ||||
| 
 | ||||
| **1.12. “Secondary License”**   | ||||
|     means either the GNU General Public License, Version 2.0, the GNU | ||||
|     Lesser General Public License, Version 2.1, the GNU Affero General | ||||
|     Public License, Version 3.0, or any later versions of those | ||||
|     licenses. | ||||
| 
 | ||||
| **1.13. “Source Code Form”**   | ||||
|     means the form of the work preferred for making modifications. | ||||
| 
 | ||||
| **1.14. “You” (or “Your”)**   | ||||
|     means an individual or a legal entity exercising rights under this | ||||
|     License. For legal entities, “You” includes any entity that | ||||
|     controls, is controlled by, or is under common control with You. For | ||||
|     purposes of this definition, “control” means **(a)** the power, direct | ||||
|     or indirect, to cause the direction or management of such entity, | ||||
|     whether by contract or otherwise, or **(b)** ownership of more than | ||||
|     fifty percent (50%) of the outstanding shares or beneficial | ||||
|     ownership of such entity. | ||||
| 
 | ||||
| 
 | ||||
| ### 2. License Grants and Conditions | ||||
| 
 | ||||
| #### 2.1. Grants | ||||
| 
 | ||||
| Each Contributor hereby grants You a world-wide, royalty-free, | ||||
| non-exclusive license: | ||||
| 
 | ||||
| * **(a)** under intellectual property rights (other than patent or trademark) | ||||
|     Licensable by such Contributor to use, reproduce, make available, | ||||
|     modify, display, perform, distribute, and otherwise exploit its | ||||
|     Contributions, either on an unmodified basis, with Modifications, or | ||||
|     as part of a Larger Work; and | ||||
| * **(b)** under Patent Claims of such Contributor to make, use, sell, offer | ||||
|     for sale, have made, import, and otherwise transfer either its | ||||
|     Contributions or its Contributor Version. | ||||
| 
 | ||||
| #### 2.2. Effective Date | ||||
| 
 | ||||
| The licenses granted in Section 2.1 with respect to any Contribution | ||||
| become effective for each Contribution on the date the Contributor first | ||||
| distributes such Contribution. | ||||
| 
 | ||||
| #### 2.3. Limitations on Grant Scope | ||||
| 
 | ||||
| The licenses granted in this Section 2 are the only rights granted under | ||||
| this License. No additional rights or licenses will be implied from the | ||||
| distribution or licensing of Covered Software under this License. | ||||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||||
| Contributor: | ||||
| 
 | ||||
| * **(a)** for any code that a Contributor has removed from Covered Software; | ||||
|     or | ||||
| * **(b)** for infringements caused by: **(i)** Your and any other third party's | ||||
|     modifications of Covered Software, or **(ii)** the combination of its | ||||
|     Contributions with other software (except as part of its Contributor | ||||
|     Version); or | ||||
| * **(c)** under Patent Claims infringed by Covered Software in the absence of | ||||
|     its Contributions. | ||||
| 
 | ||||
| This License does not grant any rights in the trademarks, service marks, | ||||
| or logos of any Contributor (except as may be necessary to comply with | ||||
| the notice requirements in Section 3.4). | ||||
| 
 | ||||
| #### 2.4. Subsequent Licenses | ||||
| 
 | ||||
| No Contributor makes additional grants as a result of Your choice to | ||||
| distribute the Covered Software under a subsequent version of this | ||||
| License (see Section 10.2) or under the terms of a Secondary License (if | ||||
| permitted under the terms of Section 3.3). | ||||
| 
 | ||||
| #### 2.5. Representation | ||||
| 
 | ||||
| Each Contributor represents that the Contributor believes its | ||||
| Contributions are its original creation(s) or it has sufficient rights | ||||
| to grant the rights to its Contributions conveyed by this License. | ||||
| 
 | ||||
| #### 2.6. Fair Use | ||||
| 
 | ||||
| This License is not intended to limit any rights You have under | ||||
| applicable copyright doctrines of fair use, fair dealing, or other | ||||
| equivalents. | ||||
| 
 | ||||
| #### 2.7. Conditions | ||||
| 
 | ||||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||||
| in Section 2.1. | ||||
| 
 | ||||
| 
 | ||||
| ### 3. Responsibilities | ||||
| 
 | ||||
| #### 3.1. Distribution of Source Form | ||||
| 
 | ||||
| All distribution of Covered Software in Source Code Form, including any | ||||
| Modifications that You create or to which You contribute, must be under | ||||
| the terms of this License. You must inform recipients that the Source | ||||
| Code Form of the Covered Software is governed by the terms of this | ||||
| License, and how they can obtain a copy of this License. You may not | ||||
| attempt to alter or restrict the recipients' rights in the Source Code | ||||
| Form. | ||||
| 
 | ||||
| #### 3.2. Distribution of Executable Form | ||||
| 
 | ||||
| If You distribute Covered Software in Executable Form then: | ||||
| 
 | ||||
| * **(a)** such Covered Software must also be made available in Source Code | ||||
|     Form, as described in Section 3.1, and You must inform recipients of | ||||
|     the Executable Form how they can obtain a copy of such Source Code | ||||
|     Form by reasonable means in a timely manner, at a charge no more | ||||
|     than the cost of distribution to the recipient; and | ||||
| 
 | ||||
| * **(b)** You may distribute such Executable Form under the terms of this | ||||
|     License, or sublicense it under different terms, provided that the | ||||
|     license for the Executable Form does not attempt to limit or alter | ||||
|     the recipients' rights in the Source Code Form under this License. | ||||
| 
 | ||||
| #### 3.3. Distribution of a Larger Work | ||||
| 
 | ||||
| You may create and distribute a Larger Work under terms of Your choice, | ||||
| provided that You also comply with the requirements of this License for | ||||
| the Covered Software. If the Larger Work is a combination of Covered | ||||
| Software with a work governed by one or more Secondary Licenses, and the | ||||
| Covered Software is not Incompatible With Secondary Licenses, this | ||||
| License permits You to additionally distribute such Covered Software | ||||
| under the terms of such Secondary License(s), so that the recipient of | ||||
| the Larger Work may, at their option, further distribute the Covered | ||||
| Software under the terms of either this License or such Secondary | ||||
| License(s). | ||||
| 
 | ||||
| #### 3.4. Notices | ||||
| 
 | ||||
| You may not remove or alter the substance of any license notices | ||||
| (including copyright notices, patent notices, disclaimers of warranty, | ||||
| or limitations of liability) contained within the Source Code Form of | ||||
| the Covered Software, except that You may alter any license notices to | ||||
| the extent required to remedy known factual inaccuracies. | ||||
| 
 | ||||
| #### 3.5. Application of Additional Terms | ||||
| 
 | ||||
| You may choose to offer, and to charge a fee for, warranty, support, | ||||
| indemnity or liability obligations to one or more recipients of Covered | ||||
| Software. However, You may do so only on Your own behalf, and not on | ||||
| behalf of any Contributor. You must make it absolutely clear that any | ||||
| such warranty, support, indemnity, or liability obligation is offered by | ||||
| You alone, and You hereby agree to indemnify every Contributor for any | ||||
| liability incurred by such Contributor as a result of warranty, support, | ||||
| indemnity or liability terms You offer. You may include additional | ||||
| disclaimers of warranty and limitations of liability specific to any | ||||
| jurisdiction. | ||||
| 
 | ||||
| 
 | ||||
| ### 4. Inability to Comply Due to Statute or Regulation | ||||
| 
 | ||||
| If it is impossible for You to comply with any of the terms of this | ||||
| License with respect to some or all of the Covered Software due to | ||||
| statute, judicial order, or regulation then You must: **(a)** comply with | ||||
| the terms of this License to the maximum extent possible; and **(b)** | ||||
| describe the limitations and the code they affect. Such description must | ||||
| be placed in a text file included with all distributions of the Covered | ||||
| Software under this License. Except to the extent prohibited by statute | ||||
| or regulation, such description must be sufficiently detailed for a | ||||
| recipient of ordinary skill to be able to understand it. | ||||
| 
 | ||||
| 
 | ||||
| ### 5. Termination | ||||
| 
 | ||||
| **5.1.** The rights granted under this License will terminate automatically | ||||
| if You fail to comply with any of its terms. However, if You become | ||||
| compliant, then the rights granted under this License from a particular | ||||
| Contributor are reinstated **(a)** provisionally, unless and until such | ||||
| Contributor explicitly and finally terminates Your grants, and **(b)** on an | ||||
| ongoing basis, if such Contributor fails to notify You of the | ||||
| non-compliance by some reasonable means prior to 60 days after You have | ||||
| come back into compliance. Moreover, Your grants from a particular | ||||
| Contributor are reinstated on an ongoing basis if such Contributor | ||||
| notifies You of the non-compliance by some reasonable means, this is the | ||||
| first time You have received notice of non-compliance with this License | ||||
| from such Contributor, and You become compliant prior to 30 days after | ||||
| Your receipt of the notice. | ||||
| 
 | ||||
| **5.2.** If You initiate litigation against any entity by asserting a patent | ||||
| infringement claim (excluding declaratory judgment actions, | ||||
| counter-claims, and cross-claims) alleging that a Contributor Version | ||||
| directly or indirectly infringes any patent, then the rights granted to | ||||
| You by any and all Contributors for the Covered Software under Section | ||||
| 2.1 of this License shall terminate. | ||||
| 
 | ||||
| **5.3.** In the event of termination under Sections 5.1 or 5.2 above, all | ||||
| end user license agreements (excluding distributors and resellers) which | ||||
| have been validly granted by You or Your distributors under this License | ||||
| prior to termination shall survive termination. | ||||
| 
 | ||||
| 
 | ||||
| ### 6. Disclaimer of Warranty | ||||
| 
 | ||||
| > Covered Software is provided under this License on an “as is” | ||||
| > basis, without warranty of any kind, either expressed, implied, or | ||||
| > statutory, including, without limitation, warranties that the | ||||
| > Covered Software is free of defects, merchantable, fit for a | ||||
| > particular purpose or non-infringing. The entire risk as to the | ||||
| > quality and performance of the Covered Software is with You. | ||||
| > Should any Covered Software prove defective in any respect, You | ||||
| > (not any Contributor) assume the cost of any necessary servicing, | ||||
| > repair, or correction. This disclaimer of warranty constitutes an | ||||
| > essential part of this License. No use of any Covered Software is | ||||
| > authorized under this License except under this disclaimer. | ||||
| 
 | ||||
| ### 7. Limitation of Liability | ||||
| 
 | ||||
| > Under no circumstances and under no legal theory, whether tort | ||||
| > (including negligence), contract, or otherwise, shall any | ||||
| > Contributor, or anyone who distributes Covered Software as | ||||
| > permitted above, be liable to You for any direct, indirect, | ||||
| > special, incidental, or consequential damages of any character | ||||
| > including, without limitation, damages for lost profits, loss of | ||||
| > goodwill, work stoppage, computer failure or malfunction, or any | ||||
| > and all other commercial damages or losses, even if such party | ||||
| > shall have been informed of the possibility of such damages. This | ||||
| > limitation of liability shall not apply to liability for death or | ||||
| > personal injury resulting from such party's negligence to the | ||||
| > extent applicable law prohibits such limitation. Some | ||||
| > jurisdictions do not allow the exclusion or limitation of | ||||
| > incidental or consequential damages, so this exclusion and | ||||
| > limitation may not apply to You. | ||||
| 
 | ||||
| 
 | ||||
| ### 8. Litigation | ||||
| 
 | ||||
| Any litigation relating to this License may be brought only in the | ||||
| courts of a jurisdiction where the defendant maintains its principal | ||||
| place of business and such litigation shall be governed by laws of that | ||||
| jurisdiction, without reference to its conflict-of-law provisions. | ||||
| Nothing in this Section shall prevent a party's ability to bring | ||||
| cross-claims or counter-claims. | ||||
| 
 | ||||
| 
 | ||||
| ### 9. Miscellaneous | ||||
| 
 | ||||
| This License represents the complete agreement concerning the subject | ||||
| matter hereof. If any provision of this License is held to be | ||||
| unenforceable, such provision shall be reformed only to the extent | ||||
| necessary to make it enforceable. Any law or regulation which provides | ||||
| that the language of a contract shall be construed against the drafter | ||||
| shall not be used to construe this License against a Contributor. | ||||
| 
 | ||||
| 
 | ||||
| ### 10. Versions of the License | ||||
| 
 | ||||
| #### 10.1. New Versions | ||||
| 
 | ||||
| Mozilla Foundation is the license steward. Except as provided in Section | ||||
| 10.3, no one other than the license steward has the right to modify or | ||||
| publish new versions of this License. Each version will be given a | ||||
| distinguishing version number. | ||||
| 
 | ||||
| #### 10.2. Effect of New Versions | ||||
| 
 | ||||
| You may distribute the Covered Software under the terms of the version | ||||
| of the License under which You originally received the Covered Software, | ||||
| or under the terms of any subsequent version published by the license | ||||
| steward. | ||||
| 
 | ||||
| #### 10.3. Modified Versions | ||||
| 
 | ||||
| If you create software not governed by this License, and you want to | ||||
| create a new license for such software, you may create and use a | ||||
| modified version of this License if you rename the license and remove | ||||
| any references to the name of the license steward (except to note that | ||||
| such modified license differs from this License). | ||||
| 
 | ||||
| #### 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | ||||
| 
 | ||||
| If You choose to distribute Source Code Form that is Incompatible With | ||||
| Secondary Licenses under the terms of this version of the License, the | ||||
| notice described in Exhibit B of this License must be attached. | ||||
| 
 | ||||
| ## Exhibit A - Source Code Form License Notice | ||||
| 
 | ||||
|     This Source Code Form is subject to the terms of the Mozilla Public | ||||
|     License, v. 2.0. If a copy of the MPL was not distributed with this | ||||
|     file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
| 
 | ||||
| If it is not possible or desirable to put the notice in a particular | ||||
| file, then You may include the notice in a location (such as a LICENSE | ||||
| file in a relevant directory) where a recipient would be likely to look | ||||
| for such a notice. | ||||
| 
 | ||||
| You may add additional accurate notices of copyright ownership. | ||||
| 
 | ||||
| ## Exhibit B - “Incompatible With Secondary Licenses” Notice | ||||
| 
 | ||||
|     This Source Code Form is "Incompatible With Secondary Licenses", as | ||||
|     defined by the Mozilla Public License, v. 2.0. | ||||
| 
 | ||||
							
								
								
									
										214
									
								
								vendor/github.com/status-im/keycard-go/derivationpath/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								vendor/github.com/status-im/keycard-go/derivationpath/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,214 @@ | ||||
| package derivationpath | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| type StartingPoint int | ||||
| 
 | ||||
| const ( | ||||
| 	tokenMaster    = 0x6D // char m
 | ||||
| 	tokenSeparator = 0x2F // char /
 | ||||
| 	tokenHardened  = 0x27 // char '
 | ||||
| 	tokenDot       = 0x2E // char .
 | ||||
| 
 | ||||
| 	hardenedStart = 0x80000000 // 2^31
 | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	StartingPointMaster StartingPoint = iota + 1 | ||||
| 	StartingPointCurrent | ||||
| 	StartingPointParent | ||||
| ) | ||||
| 
 | ||||
| type parseFunc = func() error | ||||
| 
 | ||||
| type decoder struct { | ||||
| 	r                    *strings.Reader | ||||
| 	f                    parseFunc | ||||
| 	pos                  int | ||||
| 	path                 []uint32 | ||||
| 	start                StartingPoint | ||||
| 	currentToken         string | ||||
| 	currentTokenHardened bool | ||||
| } | ||||
| 
 | ||||
| func newDecoder(path string) *decoder { | ||||
| 	d := &decoder{ | ||||
| 		r: strings.NewReader(path), | ||||
| 	} | ||||
| 
 | ||||
| 	d.reset() | ||||
| 
 | ||||
| 	return d | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) reset() { | ||||
| 	d.r.Seek(0, io.SeekStart) | ||||
| 	d.pos = 0 | ||||
| 	d.start = StartingPointCurrent | ||||
| 	d.f = d.parseStart | ||||
| 	d.path = make([]uint32, 0) | ||||
| 	d.resetCurrentToken() | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) resetCurrentToken() { | ||||
| 	d.currentToken = "" | ||||
| 	d.currentTokenHardened = false | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) parse() (StartingPoint, []uint32, error) { | ||||
| 	for { | ||||
| 		err := d.f() | ||||
| 		if err != nil { | ||||
| 			if err == io.EOF { | ||||
| 				err = nil | ||||
| 			} else { | ||||
| 				err = fmt.Errorf("at position %d, %s", d.pos, err.Error()) | ||||
| 			} | ||||
| 
 | ||||
| 			return d.start, d.path, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return d.start, d.path, nil | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) readByte() (byte, error) { | ||||
| 	b, err := d.r.ReadByte() | ||||
| 	if err != nil { | ||||
| 		return b, err | ||||
| 	} | ||||
| 
 | ||||
| 	d.pos++ | ||||
| 
 | ||||
| 	return b, nil | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) unreadByte() error { | ||||
| 	err := d.r.UnreadByte() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	d.pos-- | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) parseStart() error { | ||||
| 	b, err := d.readByte() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if b == tokenMaster { | ||||
| 		d.start = StartingPointMaster | ||||
| 		d.f = d.parseSeparator | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if b == tokenDot { | ||||
| 		b2, err := d.readByte() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if b2 == tokenDot { | ||||
| 			d.f = d.parseSeparator | ||||
| 			d.start = StartingPointParent | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		d.f = d.parseSeparator | ||||
| 		d.start = StartingPointCurrent | ||||
| 		return d.unreadByte() | ||||
| 	} | ||||
| 
 | ||||
| 	d.f = d.parseSegment | ||||
| 
 | ||||
| 	return d.unreadByte() | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) saveSegment() error { | ||||
| 	if len(d.currentToken) > 0 { | ||||
| 		i, err := strconv.ParseUint(d.currentToken, 10, 32) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		if i >= hardenedStart { | ||||
| 			d.pos -= len(d.currentToken) - 1 | ||||
| 			return fmt.Errorf("index must be lower than 2^31, got %d", i) | ||||
| 		} | ||||
| 
 | ||||
| 		if d.currentTokenHardened { | ||||
| 			i += hardenedStart | ||||
| 		} | ||||
| 
 | ||||
| 		d.path = append(d.path, uint32(i)) | ||||
| 	} | ||||
| 
 | ||||
| 	d.f = d.parseSegment | ||||
| 	d.resetCurrentToken() | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) parseSeparator() error { | ||||
| 	b, err := d.readByte() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if b == tokenSeparator { | ||||
| 		return d.saveSegment() | ||||
| 	} | ||||
| 
 | ||||
| 	return fmt.Errorf("expected %s, got %s", string(tokenSeparator), string(b)) | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) parseSegment() error { | ||||
| 	b, err := d.readByte() | ||||
| 	if err == io.EOF { | ||||
| 		if len(d.currentToken) == 0 { | ||||
| 			return fmt.Errorf("expected number, got EOF") | ||||
| 		} | ||||
| 
 | ||||
| 		if newErr := d.saveSegment(); newErr != nil { | ||||
| 			return newErr | ||||
| 		} | ||||
| 
 | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if len(d.currentToken) > 0 && b == tokenSeparator { | ||||
| 		return d.saveSegment() | ||||
| 	} | ||||
| 
 | ||||
| 	if len(d.currentToken) > 0 && b == tokenHardened { | ||||
| 		d.currentTokenHardened = true | ||||
| 		d.f = d.parseSeparator | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	if b < 0x30 || b > 0x39 { | ||||
| 		return fmt.Errorf("expected number, got %s", string(b)) | ||||
| 	} | ||||
| 
 | ||||
| 	d.currentToken = fmt.Sprintf("%s%s", d.currentToken, string(b)) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func Decode(str string) (StartingPoint, []uint32, error) { | ||||
| 	d := newDecoder(str) | ||||
| 	return d.parse() | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/github.com/status-im/keycard-go/derivationpath/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/github.com/status-im/keycard-go/derivationpath/encoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| package derivationpath | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func Encode(rawPath []uint32) string { | ||||
| 	segments := []string{string(tokenMaster)} | ||||
| 
 | ||||
| 	for _, i := range rawPath { | ||||
| 		suffix := "" | ||||
| 
 | ||||
| 		if i >= hardenedStart { | ||||
| 			i = i - hardenedStart | ||||
| 			suffix = string(tokenHardened) | ||||
| 		} | ||||
| 
 | ||||
| 		segments = append(segments, fmt.Sprintf("%d%s", i, suffix)) | ||||
| 	} | ||||
| 
 | ||||
| 	return strings.Join(segments, string(tokenSeparator)) | ||||
| } | ||||
| 
 | ||||
| func EncodeFromBytes(data []byte) (string, error) { | ||||
| 	buf := bytes.NewBuffer(data) | ||||
| 	rawPath := make([]uint32, buf.Len()/4) | ||||
| 	err := binary.Read(buf, binary.BigEndian, &rawPath) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	return Encode(rawPath), nil | ||||
| } | ||||
							
								
								
									
										15
									
								
								vendor/github.com/tyler-smith/go-bip39/Gopkg.lock
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/tyler-smith/go-bip39/Gopkg.lock
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | ||||
| 
 | ||||
| 
 | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/crypto" | ||||
|   packages = ["pbkdf2"] | ||||
|   revision = "a49355c7e3f8fe157a85be2f77e6e269a0f89602" | ||||
| 
 | ||||
| [solve-meta] | ||||
|   analyzer-name = "dep" | ||||
|   analyzer-version = 1 | ||||
|   inputs-digest = "d7f1a7207c39125afcb9ca2365832cb83458edfc17f2f7e8d28fd56f19436856" | ||||
|   solver-name = "gps-cdcl" | ||||
|   solver-version = 1 | ||||
							
								
								
									
										26
									
								
								vendor/github.com/tyler-smith/go-bip39/Gopkg.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/tyler-smith/go-bip39/Gopkg.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| 
 | ||||
| # Gopkg.toml example | ||||
| # | ||||
| # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | ||||
| # for detailed Gopkg.toml documentation. | ||||
| # | ||||
| # required = ["github.com/user/thing/cmd/thing"] | ||||
| # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | ||||
| # | ||||
| # [[constraint]] | ||||
| #   name = "github.com/user/project" | ||||
| #   version = "1.0.0" | ||||
| # | ||||
| # [[constraint]] | ||||
| #   name = "github.com/user/project2" | ||||
| #   branch = "dev" | ||||
| #   source = "github.com/myfork/project2" | ||||
| # | ||||
| # [[override]] | ||||
| #  name = "github.com/x/y" | ||||
| #  version = "2.4.0" | ||||
| 
 | ||||
| 
 | ||||
| [[constraint]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/crypto" | ||||
							
								
								
									
										21
									
								
								vendor/github.com/tyler-smith/go-bip39/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/tyler-smith/go-bip39/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
| 
 | ||||
| Copyright (c) 2014-2018 Tyler Smith and contributors | ||||
| 
 | ||||
| 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. | ||||
							
								
								
									
										11
									
								
								vendor/github.com/tyler-smith/go-bip39/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/tyler-smith/go-bip39/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| .DEFAULT_GOAL := help | ||||
| 
 | ||||
| tests: ## Run tests with coverage
 | ||||
| 	go test -v -cover ./... | ||||
| 
 | ||||
| profile_tests: ## Run tests and output coverage profiling
 | ||||
| 	go test -v -coverprofile=coverage.out . | ||||
| 	go tool cover -html=coverage.out | ||||
| 
 | ||||
| help: | ||||
| 	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' | ||||
							
								
								
									
										45
									
								
								vendor/github.com/tyler-smith/go-bip39/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/tyler-smith/go-bip39/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| # go-bip39 | ||||
| [](https://travis-ci.org/tyler-smith/go-bip39) | ||||
| [](https://github.com/tyler-smith/go-bip39/blob/master/LICENSE) | ||||
| [](http://godoc.org/github.com/tyler-smith/go-bip39) | ||||
| [](https://goreportcard.com/report/github.com/tyler-smith/go-bip39) | ||||
| [](https://github.com/tyler-smith/go-bip39/issues) | ||||
| 
 | ||||
| 
 | ||||
| A golang implementation of the BIP0039 spec for mnemonic seeds | ||||
| 
 | ||||
| ## Example | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|   "github.com/tyler-smith/go-bip39" | ||||
|   "github.com/tyler-smith/go-bip32" | ||||
|   "fmt" | ||||
| ) | ||||
| 
 | ||||
| func main(){ | ||||
|   // Generate a mnemonic for memorization or user-friendly seeds | ||||
|   entropy, _ := bip39.NewEntropy(256) | ||||
|   mnemonic, _ := bip39.NewMnemonic(entropy) | ||||
| 
 | ||||
|   // Generate a Bip32 HD wallet for the mnemonic and a user supplied password | ||||
|   seed := bip39.NewSeed(mnemonic, "Secret Passphrase") | ||||
| 
 | ||||
|   masterKey, _ := bip32.NewMasterKey(seed) | ||||
|   publicKey := masterKey.PublicKey() | ||||
| 
 | ||||
|   // Display mnemonic and keys | ||||
|   fmt.Println("Mnemonic: ", mnemonic) | ||||
|   fmt.Println("Master private key: ", masterKey) | ||||
|   fmt.Println("Master public key: ", publicKey) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Credits | ||||
| 
 | ||||
| Wordlists are from the [bip39 spec](https://github.com/bitcoin/bips/tree/master/bip-0039). | ||||
| 
 | ||||
| Test vectors are from the standard Python BIP0039 implementation from the | ||||
| Trezor team: [https://github.com/trezor/python-mnemonic](https://github.com/trezor/python-mnemonic) | ||||
							
								
								
									
										377
									
								
								vendor/github.com/tyler-smith/go-bip39/bip39.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								vendor/github.com/tyler-smith/go-bip39/bip39.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,377 @@ | ||||
| // Package bip39 is the Golang implementation of the BIP39 spec.
 | ||||
| //
 | ||||
| // The official BIP39 spec can be found at
 | ||||
| // https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
 | ||||
| package bip39 | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/sha512" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/big" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/tyler-smith/go-bip39/wordlists" | ||||
| 	"golang.org/x/crypto/pbkdf2" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// Some bitwise operands for working with big.Ints
 | ||||
| 	last11BitsMask  = big.NewInt(2047) | ||||
| 	shift11BitsMask = big.NewInt(2048) | ||||
| 	bigOne          = big.NewInt(1) | ||||
| 	bigTwo          = big.NewInt(2) | ||||
| 
 | ||||
| 	// used to isolate the checksum bits from the entropy+checksum byte array
 | ||||
| 	wordLengthChecksumMasksMapping = map[int]*big.Int{ | ||||
| 		12: big.NewInt(15), | ||||
| 		15: big.NewInt(31), | ||||
| 		18: big.NewInt(63), | ||||
| 		21: big.NewInt(127), | ||||
| 		24: big.NewInt(255), | ||||
| 	} | ||||
| 	// used to use only the desired x of 8 available checksum bits.
 | ||||
| 	// 256 bit (word length 24) requires all 8 bits of the checksum,
 | ||||
| 	// and thus no shifting is needed for it (we would get a divByZero crash if we did)
 | ||||
| 	wordLengthChecksumShiftMapping = map[int]*big.Int{ | ||||
| 		12: big.NewInt(16), | ||||
| 		15: big.NewInt(8), | ||||
| 		18: big.NewInt(4), | ||||
| 		21: big.NewInt(2), | ||||
| 	} | ||||
| 
 | ||||
| 	// wordList is the set of words to use
 | ||||
| 	wordList []string | ||||
| 
 | ||||
| 	// wordMap is a reverse lookup map for wordList
 | ||||
| 	wordMap map[string]int | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	// ErrInvalidMnemonic is returned when trying to use a malformed mnemonic.
 | ||||
| 	ErrInvalidMnemonic = errors.New("Invalid mnenomic") | ||||
| 
 | ||||
| 	// ErrEntropyLengthInvalid is returned when trying to use an entropy set with
 | ||||
| 	// an invalid size.
 | ||||
| 	ErrEntropyLengthInvalid = errors.New("Entropy length must be [128, 256] and a multiple of 32") | ||||
| 
 | ||||
| 	// ErrValidatedSeedLengthMismatch is returned when a validated seed is not the
 | ||||
| 	// same size as the given seed. This should never happen is present only as a
 | ||||
| 	// sanity assertion.
 | ||||
| 	ErrValidatedSeedLengthMismatch = errors.New("Seed length does not match validated seed length") | ||||
| 
 | ||||
| 	// ErrChecksumIncorrect is returned when entropy has the incorrect checksum.
 | ||||
| 	ErrChecksumIncorrect = errors.New("Checksum incorrect") | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	SetWordList(wordlists.English) | ||||
| } | ||||
| 
 | ||||
| // SetWordList sets the list of words to use for mnemonics. Currently the list
 | ||||
| // that is set is used package-wide.
 | ||||
| func SetWordList(list []string) { | ||||
| 	wordList = list | ||||
| 	wordMap = map[string]int{} | ||||
| 	for i, v := range wordList { | ||||
| 		wordMap[v] = i | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetWordList gets the list of words to use for mnemonics.
 | ||||
| func GetWordList() []string { | ||||
| 	return wordList | ||||
| } | ||||
| 
 | ||||
| // GetWordIndex gets word index in wordMap.
 | ||||
| func GetWordIndex(word string) (int, bool) { | ||||
| 	idx, ok := wordMap[word] | ||||
| 	return idx, ok | ||||
| } | ||||
| 
 | ||||
| // NewEntropy will create random entropy bytes
 | ||||
| // so long as the requested size bitSize is an appropriate size.
 | ||||
| //
 | ||||
| // bitSize has to be a multiple 32 and be within the inclusive range of {128, 256}
 | ||||
| func NewEntropy(bitSize int) ([]byte, error) { | ||||
| 	err := validateEntropyBitSize(bitSize) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	entropy := make([]byte, bitSize/8) | ||||
| 	_, err = rand.Read(entropy) | ||||
| 	return entropy, err | ||||
| } | ||||
| 
 | ||||
| // EntropyFromMnemonic takes a mnemonic generated by this library,
 | ||||
| // and returns the input entropy used to generate the given mnemonic.
 | ||||
| // An error is returned if the given mnemonic is invalid.
 | ||||
| func EntropyFromMnemonic(mnemonic string) ([]byte, error) { | ||||
| 	mnemonicSlice, isValid := splitMnemonicWords(mnemonic) | ||||
| 	if !isValid { | ||||
| 		return nil, ErrInvalidMnemonic | ||||
| 	} | ||||
| 
 | ||||
| 	// Decode the words into a big.Int.
 | ||||
| 	b := big.NewInt(0) | ||||
| 	for _, v := range mnemonicSlice { | ||||
| 		index, found := wordMap[v] | ||||
| 		if found == false { | ||||
| 			return nil, fmt.Errorf("word `%v` not found in reverse map", v) | ||||
| 		} | ||||
| 		var wordBytes [2]byte | ||||
| 		binary.BigEndian.PutUint16(wordBytes[:], uint16(index)) | ||||
| 		b = b.Mul(b, shift11BitsMask) | ||||
| 		b = b.Or(b, big.NewInt(0).SetBytes(wordBytes[:])) | ||||
| 	} | ||||
| 
 | ||||
| 	// Build and add the checksum to the big.Int.
 | ||||
| 	checksum := big.NewInt(0) | ||||
| 	checksumMask := wordLengthChecksumMasksMapping[len(mnemonicSlice)] | ||||
| 	checksum = checksum.And(b, checksumMask) | ||||
| 
 | ||||
| 	b.Div(b, big.NewInt(0).Add(checksumMask, bigOne)) | ||||
| 
 | ||||
| 	// The entropy is the underlying bytes of the big.Int. Any upper bytes of
 | ||||
| 	// all 0's are not returned so we pad the beginning of the slice with empty
 | ||||
| 	// bytes if necessary.
 | ||||
| 	entropy := b.Bytes() | ||||
| 	entropy = padByteSlice(entropy, len(mnemonicSlice)/3*4) | ||||
| 
 | ||||
| 	// Generate the checksum and compare with the one we got from the mneomnic.
 | ||||
| 	entropyChecksumBytes := computeChecksum(entropy) | ||||
| 	entropyChecksum := big.NewInt(int64(entropyChecksumBytes[0])) | ||||
| 	if l := len(mnemonicSlice); l != 24 { | ||||
| 		checksumShift := wordLengthChecksumShiftMapping[l] | ||||
| 		entropyChecksum.Div(entropyChecksum, checksumShift) | ||||
| 	} | ||||
| 
 | ||||
| 	if checksum.Cmp(entropyChecksum) != 0 { | ||||
| 		return nil, ErrChecksumIncorrect | ||||
| 	} | ||||
| 
 | ||||
| 	return entropy, nil | ||||
| } | ||||
| 
 | ||||
| // NewMnemonic will return a string consisting of the mnemonic words for
 | ||||
| // the given entropy.
 | ||||
| // If the provide entropy is invalid, an error will be returned.
 | ||||
| func NewMnemonic(entropy []byte) (string, error) { | ||||
| 	// Compute some lengths for convenience.
 | ||||
| 	entropyBitLength := len(entropy) * 8 | ||||
| 	checksumBitLength := entropyBitLength / 32 | ||||
| 	sentenceLength := (entropyBitLength + checksumBitLength) / 11 | ||||
| 
 | ||||
| 	// Validate that the requested size is supported.
 | ||||
| 	err := validateEntropyBitSize(entropyBitLength) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	// Add checksum to entropy.
 | ||||
| 	entropy = addChecksum(entropy) | ||||
| 
 | ||||
| 	// Break entropy up into sentenceLength chunks of 11 bits.
 | ||||
| 	// For each word AND mask the rightmost 11 bits and find the word at that index.
 | ||||
| 	// Then bitshift entropy 11 bits right and repeat.
 | ||||
| 	// Add to the last empty slot so we can work with LSBs instead of MSB.
 | ||||
| 
 | ||||
| 	// Entropy as an int so we can bitmask without worrying about bytes slices.
 | ||||
| 	entropyInt := new(big.Int).SetBytes(entropy) | ||||
| 
 | ||||
| 	// Slice to hold words in.
 | ||||
| 	words := make([]string, sentenceLength) | ||||
| 
 | ||||
| 	// Throw away big.Int for AND masking.
 | ||||
| 	word := big.NewInt(0) | ||||
| 
 | ||||
| 	for i := sentenceLength - 1; i >= 0; i-- { | ||||
| 		// Get 11 right most bits and bitshift 11 to the right for next time.
 | ||||
| 		word.And(entropyInt, last11BitsMask) | ||||
| 		entropyInt.Div(entropyInt, shift11BitsMask) | ||||
| 
 | ||||
| 		// Get the bytes representing the 11 bits as a 2 byte slice.
 | ||||
| 		wordBytes := padByteSlice(word.Bytes(), 2) | ||||
| 
 | ||||
| 		// Convert bytes to an index and add that word to the list.
 | ||||
| 		words[i] = wordList[binary.BigEndian.Uint16(wordBytes)] | ||||
| 	} | ||||
| 
 | ||||
| 	return strings.Join(words, " "), nil | ||||
| } | ||||
| 
 | ||||
| // MnemonicToByteArray takes a mnemonic string and turns it into a byte array
 | ||||
| // suitable for creating another mnemonic.
 | ||||
| // An error is returned if the mnemonic is invalid.
 | ||||
| func MnemonicToByteArray(mnemonic string, raw ...bool) ([]byte, error) { | ||||
| 	var ( | ||||
| 		mnemonicSlice    = strings.Split(mnemonic, " ") | ||||
| 		entropyBitSize   = len(mnemonicSlice) * 11 | ||||
| 		checksumBitSize  = entropyBitSize % 32 | ||||
| 		fullByteSize     = (entropyBitSize-checksumBitSize)/8 + 1 | ||||
| 		checksumByteSize = fullByteSize - (fullByteSize % 4) | ||||
| 	) | ||||
| 
 | ||||
| 	// Pre validate that the mnemonic is well formed and only contains words that
 | ||||
| 	// are present in the word list.
 | ||||
| 	if !IsMnemonicValid(mnemonic) { | ||||
| 		return nil, ErrInvalidMnemonic | ||||
| 	} | ||||
| 
 | ||||
| 	// Convert word indices to a big.Int representing the entropy.
 | ||||
| 	checksummedEntropy := big.NewInt(0) | ||||
| 	modulo := big.NewInt(2048) | ||||
| 	for _, v := range mnemonicSlice { | ||||
| 		index := big.NewInt(int64(wordMap[v])) | ||||
| 		checksummedEntropy.Mul(checksummedEntropy, modulo) | ||||
| 		checksummedEntropy.Add(checksummedEntropy, index) | ||||
| 	} | ||||
| 
 | ||||
| 	// Calculate the unchecksummed entropy so we can validate that the checksum is
 | ||||
| 	// correct.
 | ||||
| 	checksumModulo := big.NewInt(0).Exp(bigTwo, big.NewInt(int64(checksumBitSize)), nil) | ||||
| 	rawEntropy := big.NewInt(0).Div(checksummedEntropy, checksumModulo) | ||||
| 
 | ||||
| 	// Convert big.Ints to byte padded byte slices.
 | ||||
| 	rawEntropyBytes := padByteSlice(rawEntropy.Bytes(), checksumByteSize) | ||||
| 	checksummedEntropyBytes := padByteSlice(checksummedEntropy.Bytes(), fullByteSize) | ||||
| 
 | ||||
| 	// Validate that the checksum is correct.
 | ||||
| 	newChecksummedEntropyBytes := padByteSlice(addChecksum(rawEntropyBytes), fullByteSize) | ||||
| 	if !compareByteSlices(checksummedEntropyBytes, newChecksummedEntropyBytes) { | ||||
| 		return nil, ErrChecksumIncorrect | ||||
| 	} | ||||
| 
 | ||||
| 	if len(raw) > 0 && raw[0] { | ||||
| 		return rawEntropyBytes, nil | ||||
| 	} | ||||
| 
 | ||||
| 	return checksummedEntropyBytes, nil | ||||
| } | ||||
| 
 | ||||
| // NewSeedWithErrorChecking creates a hashed seed output given the mnemonic string and a password.
 | ||||
| // An error is returned if the mnemonic is not convertible to a byte array.
 | ||||
| func NewSeedWithErrorChecking(mnemonic string, password string) ([]byte, error) { | ||||
| 	_, err := MnemonicToByteArray(mnemonic) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return NewSeed(mnemonic, password), nil | ||||
| } | ||||
| 
 | ||||
| // NewSeed creates a hashed seed output given a provided string and password.
 | ||||
| // No checking is performed to validate that the string provided is a valid mnemonic.
 | ||||
| func NewSeed(mnemonic string, password string) []byte { | ||||
| 	return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New) | ||||
| } | ||||
| 
 | ||||
| // IsMnemonicValid attempts to verify that the provided mnemonic is valid.
 | ||||
| // Validity is determined by both the number of words being appropriate,
 | ||||
| // and that all the words in the mnemonic are present in the word list.
 | ||||
| func IsMnemonicValid(mnemonic string) bool { | ||||
| 	// Create a list of all the words in the mnemonic sentence
 | ||||
| 	words := strings.Fields(mnemonic) | ||||
| 
 | ||||
| 	// Get word count
 | ||||
| 	wordCount := len(words) | ||||
| 
 | ||||
| 	// The number of words should be 12, 15, 18, 21 or 24
 | ||||
| 	if wordCount%3 != 0 || wordCount < 12 || wordCount > 24 { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if all words belong in the wordlist
 | ||||
| 	for _, word := range words { | ||||
| 		if _, ok := wordMap[word]; !ok { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Appends to data the first (len(data) / 32)bits of the result of sha256(data)
 | ||||
| // Currently only supports data up to 32 bytes
 | ||||
| func addChecksum(data []byte) []byte { | ||||
| 	// Get first byte of sha256
 | ||||
| 	hash := computeChecksum(data) | ||||
| 	firstChecksumByte := hash[0] | ||||
| 
 | ||||
| 	// len() is in bytes so we divide by 4
 | ||||
| 	checksumBitLength := uint(len(data) / 4) | ||||
| 
 | ||||
| 	// For each bit of check sum we want we shift the data one the left
 | ||||
| 	// and then set the (new) right most bit equal to checksum bit at that index
 | ||||
| 	// staring from the left
 | ||||
| 	dataBigInt := new(big.Int).SetBytes(data) | ||||
| 	for i := uint(0); i < checksumBitLength; i++ { | ||||
| 		// Bitshift 1 left
 | ||||
| 		dataBigInt.Mul(dataBigInt, bigTwo) | ||||
| 
 | ||||
| 		// Set rightmost bit if leftmost checksum bit is set
 | ||||
| 		if uint8(firstChecksumByte&(1<<(7-i))) > 0 { | ||||
| 			dataBigInt.Or(dataBigInt, bigOne) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return dataBigInt.Bytes() | ||||
| } | ||||
| 
 | ||||
| func computeChecksum(data []byte) []byte { | ||||
| 	hasher := sha256.New() | ||||
| 	hasher.Write(data) | ||||
| 	return hasher.Sum(nil) | ||||
| } | ||||
| 
 | ||||
| // validateEntropyBitSize ensures that entropy is the correct size for being a
 | ||||
| // mnemonic.
 | ||||
| func validateEntropyBitSize(bitSize int) error { | ||||
| 	if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 { | ||||
| 		return ErrEntropyLengthInvalid | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // padByteSlice returns a byte slice of the given size with contents of the
 | ||||
| // given slice left padded and any empty spaces filled with 0's.
 | ||||
| func padByteSlice(slice []byte, length int) []byte { | ||||
| 	offset := length - len(slice) | ||||
| 	if offset <= 0 { | ||||
| 		return slice | ||||
| 	} | ||||
| 	newSlice := make([]byte, length) | ||||
| 	copy(newSlice[offset:], slice) | ||||
| 	return newSlice | ||||
| } | ||||
| 
 | ||||
| // compareByteSlices returns true of the byte slices have equal contents and
 | ||||
| // returns false otherwise.
 | ||||
| func compareByteSlices(a, b []byte) bool { | ||||
| 	if len(a) != len(b) { | ||||
| 		return false | ||||
| 	} | ||||
| 	for i := range a { | ||||
| 		if a[i] != b[i] { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func splitMnemonicWords(mnemonic string) ([]string, bool) { | ||||
| 	// Create a list of all the words in the mnemonic sentence
 | ||||
| 	words := strings.Fields(mnemonic) | ||||
| 
 | ||||
| 	// Get num of words
 | ||||
| 	numOfWords := len(words) | ||||
| 
 | ||||
| 	// The number of words should be 12, 15, 18, 21 or 24
 | ||||
| 	if numOfWords%3 != 0 || numOfWords < 12 || numOfWords > 24 { | ||||
| 		return nil, false | ||||
| 	} | ||||
| 	return words, true | ||||
| } | ||||
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/chinese_simplified.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/chinese_simplified.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/chinese_traditional.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/chinese_traditional.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/english.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/english.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/french.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/french.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/italian.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/italian.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/japanese.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/japanese.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/korean.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2071
									
								
								vendor/github.com/tyler-smith/go-bip39/wordlists/korean.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										24
									
								
								vendor/github.com/wsddn/go-ecdh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/wsddn/go-ecdh/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| Copyright (c) 2014, tang0th | ||||
| All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|     * Redistributions of source code must retain the above copyright | ||||
|       notice, this list of conditions and the following disclaimer. | ||||
|     * Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|     * Neither the name of tang0th nor the names of its contributors may be | ||||
|       used to endorse or promote products derived from this software without | ||||
|       specific prior written permission. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY | ||||
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | ||||
							
								
								
									
										19
									
								
								vendor/github.com/wsddn/go-ecdh/Readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/wsddn/go-ecdh/Readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| # ECDH | ||||
| 
 | ||||
| [](https://travis-ci.org/wsddn/go-ecdh) | ||||
| 
 | ||||
| This is a go implementation of elliptical curve diffie-hellman key exchange method. | ||||
| It supports the NIST curves (and any curves using the `elliptic.Curve` go interface) | ||||
| as well as djb's curve25519.  | ||||
| 
 | ||||
| The library handles generating of keys, generating a shared secret, and the | ||||
| (un)marshalling of the elliptical curve keys into slices of bytes. | ||||
| 
 | ||||
| ## Warning and Disclaimer | ||||
| I am not a cryptographer, this was written as part of a personal project to learn about cryptographic systems and protocols. No claims as to the security of this library are made, I would not advise using it for anything that requires any level of security. Pull requests or issues about security flaws are however still welcome. | ||||
| 
 | ||||
| ## Compatibility | ||||
| Works with go 1.2 onwards. | ||||
| 
 | ||||
| ## TODO | ||||
|  * Improve documentation | ||||
							
								
								
									
										62
									
								
								vendor/github.com/wsddn/go-ecdh/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/wsddn/go-ecdh/curve25519.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| package ecdh | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"io" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/curve25519" | ||||
| ) | ||||
| 
 | ||||
| type curve25519ECDH struct { | ||||
| 	ECDH | ||||
| } | ||||
| 
 | ||||
| // NewCurve25519ECDH creates a new ECDH instance that uses djb's curve25519
 | ||||
| // elliptical curve.
 | ||||
| func NewCurve25519ECDH() ECDH { | ||||
| 	return &curve25519ECDH{} | ||||
| } | ||||
| 
 | ||||
| func (e *curve25519ECDH) GenerateKey(rand io.Reader) (crypto.PrivateKey, crypto.PublicKey, error) { | ||||
| 	var pub, priv [32]byte | ||||
| 	var err error | ||||
| 
 | ||||
| 	_, err = io.ReadFull(rand, priv[:]) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	priv[0] &= 248 | ||||
| 	priv[31] &= 127 | ||||
| 	priv[31] |= 64 | ||||
| 
 | ||||
| 	curve25519.ScalarBaseMult(&pub, &priv) | ||||
| 
 | ||||
| 	return &priv, &pub, nil | ||||
| } | ||||
| 
 | ||||
| func (e *curve25519ECDH) Marshal(p crypto.PublicKey) []byte { | ||||
| 	pub := p.(*[32]byte) | ||||
| 	return pub[:] | ||||
| } | ||||
| 
 | ||||
| func (e *curve25519ECDH) Unmarshal(data []byte) (crypto.PublicKey, bool) { | ||||
| 	var pub [32]byte | ||||
| 	if len(data) != 32 { | ||||
| 		return nil, false | ||||
| 	} | ||||
| 
 | ||||
| 	copy(pub[:], data) | ||||
| 	return &pub, true | ||||
| } | ||||
| 
 | ||||
| func (e *curve25519ECDH) GenerateSharedSecret(privKey crypto.PrivateKey, pubKey crypto.PublicKey) ([]byte, error) { | ||||
| 	var priv, pub, secret *[32]byte | ||||
| 
 | ||||
| 	priv = privKey.(*[32]byte) | ||||
| 	pub = pubKey.(*[32]byte) | ||||
| 	secret = new([32]byte) | ||||
| 
 | ||||
| 	curve25519.ScalarMult(secret, priv, pub) | ||||
| 	return secret[:], nil | ||||
| } | ||||
							
								
								
									
										14
									
								
								vendor/github.com/wsddn/go-ecdh/ecdh.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/wsddn/go-ecdh/ecdh.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| package ecdh | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| // The main interface for ECDH key exchange.
 | ||||
| type ECDH interface { | ||||
| 	GenerateKey(io.Reader) (crypto.PrivateKey, crypto.PublicKey, error) | ||||
| 	Marshal(crypto.PublicKey) []byte | ||||
| 	Unmarshal([]byte) (crypto.PublicKey, bool) | ||||
| 	GenerateSharedSecret(crypto.PrivateKey, crypto.PublicKey) ([]byte, error) | ||||
| } | ||||
							
								
								
									
										87
									
								
								vendor/github.com/wsddn/go-ecdh/elliptic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/wsddn/go-ecdh/elliptic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | ||||
| package ecdh | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/elliptic" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| ) | ||||
| 
 | ||||
| type ellipticECDH struct { | ||||
| 	ECDH | ||||
| 	curve elliptic.Curve | ||||
| } | ||||
| 
 | ||||
| type ellipticPublicKey struct { | ||||
| 	elliptic.Curve | ||||
| 	X, Y *big.Int | ||||
| } | ||||
| 
 | ||||
| type ellipticPrivateKey struct { | ||||
| 	D []byte | ||||
| } | ||||
| 
 | ||||
| // NewEllipticECDH creates a new instance of ECDH with the given elliptic.Curve curve
 | ||||
| // to use as the elliptical curve for elliptical curve diffie-hellman.
 | ||||
| func NewEllipticECDH(curve elliptic.Curve) ECDH { | ||||
| 	return &ellipticECDH{ | ||||
| 		curve: curve, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (e *ellipticECDH) GenerateKey(rand io.Reader) (crypto.PrivateKey, crypto.PublicKey, error) { | ||||
| 	var d []byte | ||||
| 	var x, y *big.Int | ||||
| 	var priv *ellipticPrivateKey | ||||
| 	var pub *ellipticPublicKey | ||||
| 	var err error | ||||
| 
 | ||||
| 	d, x, y, err = elliptic.GenerateKey(e.curve, rand) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	priv = &ellipticPrivateKey{ | ||||
| 		D: d, | ||||
| 	} | ||||
| 	pub = &ellipticPublicKey{ | ||||
| 		Curve: e.curve, | ||||
| 		X: x, | ||||
| 		Y: y, | ||||
| 	} | ||||
| 	 | ||||
| 	return priv, pub, nil | ||||
| } | ||||
| 
 | ||||
| func (e *ellipticECDH) Marshal(p crypto.PublicKey) []byte { | ||||
| 	pub := p.(*ellipticPublicKey) | ||||
| 	return elliptic.Marshal(e.curve, pub.X, pub.Y) | ||||
| } | ||||
| 
 | ||||
| func (e *ellipticECDH) Unmarshal(data []byte) (crypto.PublicKey, bool) { | ||||
| 	var key *ellipticPublicKey | ||||
| 	var x, y *big.Int | ||||
| 
 | ||||
| 	x, y = elliptic.Unmarshal(e.curve, data) | ||||
| 	if x == nil || y == nil { | ||||
| 		return key, false | ||||
| 	} | ||||
| 	key = &ellipticPublicKey{ | ||||
| 		Curve: e.curve, | ||||
| 		X:     x, | ||||
| 		Y:     y, | ||||
| 	} | ||||
| 	return key, true | ||||
| } | ||||
| 
 | ||||
| // GenerateSharedSecret takes in a public key and a private key
 | ||||
| // and generates a shared secret.
 | ||||
| //
 | ||||
| // RFC5903 Section 9 states we should only return x.
 | ||||
| func (e *ellipticECDH) GenerateSharedSecret(privKey crypto.PrivateKey, pubKey crypto.PublicKey) ([]byte, error) { | ||||
| 	priv := privKey.(*ellipticPrivateKey) | ||||
| 	pub := pubKey.(*ellipticPublicKey) | ||||
| 
 | ||||
| 	x, _ := e.curve.ScalarMult(pub.X, pub.Y, priv.D) | ||||
| 	return x.Bytes(), nil | ||||
| } | ||||
							
								
								
									
										512
									
								
								vendor/golang.org/x/text/unicode/norm/composition.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										512
									
								
								vendor/golang.org/x/text/unicode/norm/composition.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,512 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import "unicode/utf8" | ||||
| 
 | ||||
| const ( | ||||
| 	maxNonStarters = 30 | ||||
| 	// The maximum number of characters needed for a buffer is
 | ||||
| 	// maxNonStarters + 1 for the starter + 1 for the GCJ
 | ||||
| 	maxBufferSize    = maxNonStarters + 2 | ||||
| 	maxNFCExpansion  = 3  // NFC(0x1D160)
 | ||||
| 	maxNFKCExpansion = 18 // NFKC(0xFDFA)
 | ||||
| 
 | ||||
| 	maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
 | ||||
| ) | ||||
| 
 | ||||
| // ssState is used for reporting the segment state after inserting a rune.
 | ||||
| // It is returned by streamSafe.next.
 | ||||
| type ssState int | ||||
| 
 | ||||
| const ( | ||||
| 	// Indicates a rune was successfully added to the segment.
 | ||||
| 	ssSuccess ssState = iota | ||||
| 	// Indicates a rune starts a new segment and should not be added.
 | ||||
| 	ssStarter | ||||
| 	// Indicates a rune caused a segment overflow and a CGJ should be inserted.
 | ||||
| 	ssOverflow | ||||
| ) | ||||
| 
 | ||||
| // streamSafe implements the policy of when a CGJ should be inserted.
 | ||||
| type streamSafe uint8 | ||||
| 
 | ||||
| // first inserts the first rune of a segment. It is a faster version of next if
 | ||||
| // it is known p represents the first rune in a segment.
 | ||||
| func (ss *streamSafe) first(p Properties) { | ||||
| 	*ss = streamSafe(p.nTrailingNonStarters()) | ||||
| } | ||||
| 
 | ||||
| // insert returns a ssState value to indicate whether a rune represented by p
 | ||||
| // can be inserted.
 | ||||
| func (ss *streamSafe) next(p Properties) ssState { | ||||
| 	if *ss > maxNonStarters { | ||||
| 		panic("streamSafe was not reset") | ||||
| 	} | ||||
| 	n := p.nLeadingNonStarters() | ||||
| 	if *ss += streamSafe(n); *ss > maxNonStarters { | ||||
| 		*ss = 0 | ||||
| 		return ssOverflow | ||||
| 	} | ||||
| 	// The Stream-Safe Text Processing prescribes that the counting can stop
 | ||||
| 	// as soon as a starter is encountered. However, there are some starters,
 | ||||
| 	// like Jamo V and T, that can combine with other runes, leaving their
 | ||||
| 	// successive non-starters appended to the previous, possibly causing an
 | ||||
| 	// overflow. We will therefore consider any rune with a non-zero nLead to
 | ||||
| 	// be a non-starter. Note that it always hold that if nLead > 0 then
 | ||||
| 	// nLead == nTrail.
 | ||||
| 	if n == 0 { | ||||
| 		*ss = streamSafe(p.nTrailingNonStarters()) | ||||
| 		return ssStarter | ||||
| 	} | ||||
| 	return ssSuccess | ||||
| } | ||||
| 
 | ||||
| // backwards is used for checking for overflow and segment starts
 | ||||
| // when traversing a string backwards. Users do not need to call first
 | ||||
| // for the first rune. The state of the streamSafe retains the count of
 | ||||
| // the non-starters loaded.
 | ||||
| func (ss *streamSafe) backwards(p Properties) ssState { | ||||
| 	if *ss > maxNonStarters { | ||||
| 		panic("streamSafe was not reset") | ||||
| 	} | ||||
| 	c := *ss + streamSafe(p.nTrailingNonStarters()) | ||||
| 	if c > maxNonStarters { | ||||
| 		return ssOverflow | ||||
| 	} | ||||
| 	*ss = c | ||||
| 	if p.nLeadingNonStarters() == 0 { | ||||
| 		return ssStarter | ||||
| 	} | ||||
| 	return ssSuccess | ||||
| } | ||||
| 
 | ||||
| func (ss streamSafe) isMax() bool { | ||||
| 	return ss == maxNonStarters | ||||
| } | ||||
| 
 | ||||
| // GraphemeJoiner is inserted after maxNonStarters non-starter runes.
 | ||||
| const GraphemeJoiner = "\u034F" | ||||
| 
 | ||||
| // reorderBuffer is used to normalize a single segment.  Characters inserted with
 | ||||
| // insert are decomposed and reordered based on CCC. The compose method can
 | ||||
| // be used to recombine characters.  Note that the byte buffer does not hold
 | ||||
| // the UTF-8 characters in order.  Only the rune array is maintained in sorted
 | ||||
| // order. flush writes the resulting segment to a byte array.
 | ||||
| type reorderBuffer struct { | ||||
| 	rune  [maxBufferSize]Properties // Per character info.
 | ||||
| 	byte  [maxByteBufferSize]byte   // UTF-8 buffer. Referenced by runeInfo.pos.
 | ||||
| 	nbyte uint8                     // Number or bytes.
 | ||||
| 	ss    streamSafe                // For limiting length of non-starter sequence.
 | ||||
| 	nrune int                       // Number of runeInfos.
 | ||||
| 	f     formInfo | ||||
| 
 | ||||
| 	src      input | ||||
| 	nsrc     int | ||||
| 	tmpBytes input | ||||
| 
 | ||||
| 	out    []byte | ||||
| 	flushF func(*reorderBuffer) bool | ||||
| } | ||||
| 
 | ||||
| func (rb *reorderBuffer) init(f Form, src []byte) { | ||||
| 	rb.f = *formTable[f] | ||||
| 	rb.src.setBytes(src) | ||||
| 	rb.nsrc = len(src) | ||||
| 	rb.ss = 0 | ||||
| } | ||||
| 
 | ||||
| func (rb *reorderBuffer) initString(f Form, src string) { | ||||
| 	rb.f = *formTable[f] | ||||
| 	rb.src.setString(src) | ||||
| 	rb.nsrc = len(src) | ||||
| 	rb.ss = 0 | ||||
| } | ||||
| 
 | ||||
| func (rb *reorderBuffer) setFlusher(out []byte, f func(*reorderBuffer) bool) { | ||||
| 	rb.out = out | ||||
| 	rb.flushF = f | ||||
| } | ||||
| 
 | ||||
| // reset discards all characters from the buffer.
 | ||||
| func (rb *reorderBuffer) reset() { | ||||
| 	rb.nrune = 0 | ||||
| 	rb.nbyte = 0 | ||||
| } | ||||
| 
 | ||||
| func (rb *reorderBuffer) doFlush() bool { | ||||
| 	if rb.f.composing { | ||||
| 		rb.compose() | ||||
| 	} | ||||
| 	res := rb.flushF(rb) | ||||
| 	rb.reset() | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| // appendFlush appends the normalized segment to rb.out.
 | ||||
| func appendFlush(rb *reorderBuffer) bool { | ||||
| 	for i := 0; i < rb.nrune; i++ { | ||||
| 		start := rb.rune[i].pos | ||||
| 		end := start + rb.rune[i].size | ||||
| 		rb.out = append(rb.out, rb.byte[start:end]...) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // flush appends the normalized segment to out and resets rb.
 | ||||
| func (rb *reorderBuffer) flush(out []byte) []byte { | ||||
| 	for i := 0; i < rb.nrune; i++ { | ||||
| 		start := rb.rune[i].pos | ||||
| 		end := start + rb.rune[i].size | ||||
| 		out = append(out, rb.byte[start:end]...) | ||||
| 	} | ||||
| 	rb.reset() | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // flushCopy copies the normalized segment to buf and resets rb.
 | ||||
| // It returns the number of bytes written to buf.
 | ||||
| func (rb *reorderBuffer) flushCopy(buf []byte) int { | ||||
| 	p := 0 | ||||
| 	for i := 0; i < rb.nrune; i++ { | ||||
| 		runep := rb.rune[i] | ||||
| 		p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size]) | ||||
| 	} | ||||
| 	rb.reset() | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| // insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
 | ||||
| // It returns false if the buffer is not large enough to hold the rune.
 | ||||
| // It is used internally by insert and insertString only.
 | ||||
| func (rb *reorderBuffer) insertOrdered(info Properties) { | ||||
| 	n := rb.nrune | ||||
| 	b := rb.rune[:] | ||||
| 	cc := info.ccc | ||||
| 	if cc > 0 { | ||||
| 		// Find insertion position + move elements to make room.
 | ||||
| 		for ; n > 0; n-- { | ||||
| 			if b[n-1].ccc <= cc { | ||||
| 				break | ||||
| 			} | ||||
| 			b[n] = b[n-1] | ||||
| 		} | ||||
| 	} | ||||
| 	rb.nrune += 1 | ||||
| 	pos := uint8(rb.nbyte) | ||||
| 	rb.nbyte += utf8.UTFMax | ||||
| 	info.pos = pos | ||||
| 	b[n] = info | ||||
| } | ||||
| 
 | ||||
| // insertErr is an error code returned by insert. Using this type instead
 | ||||
| // of error improves performance up to 20% for many of the benchmarks.
 | ||||
| type insertErr int | ||||
| 
 | ||||
| const ( | ||||
| 	iSuccess insertErr = -iota | ||||
| 	iShortDst | ||||
| 	iShortSrc | ||||
| ) | ||||
| 
 | ||||
| // insertFlush inserts the given rune in the buffer ordered by CCC.
 | ||||
| // If a decomposition with multiple segments are encountered, they leading
 | ||||
| // ones are flushed.
 | ||||
| // It returns a non-zero error code if the rune was not inserted.
 | ||||
| func (rb *reorderBuffer) insertFlush(src input, i int, info Properties) insertErr { | ||||
| 	if rune := src.hangul(i); rune != 0 { | ||||
| 		rb.decomposeHangul(rune) | ||||
| 		return iSuccess | ||||
| 	} | ||||
| 	if info.hasDecomposition() { | ||||
| 		return rb.insertDecomposed(info.Decomposition()) | ||||
| 	} | ||||
| 	rb.insertSingle(src, i, info) | ||||
| 	return iSuccess | ||||
| } | ||||
| 
 | ||||
| // insertUnsafe inserts the given rune in the buffer ordered by CCC.
 | ||||
| // It is assumed there is sufficient space to hold the runes. It is the
 | ||||
| // responsibility of the caller to ensure this. This can be done by checking
 | ||||
| // the state returned by the streamSafe type.
 | ||||
| func (rb *reorderBuffer) insertUnsafe(src input, i int, info Properties) { | ||||
| 	if rune := src.hangul(i); rune != 0 { | ||||
| 		rb.decomposeHangul(rune) | ||||
| 	} | ||||
| 	if info.hasDecomposition() { | ||||
| 		// TODO: inline.
 | ||||
| 		rb.insertDecomposed(info.Decomposition()) | ||||
| 	} else { | ||||
| 		rb.insertSingle(src, i, info) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // insertDecomposed inserts an entry in to the reorderBuffer for each rune
 | ||||
| // in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
 | ||||
| // It flushes the buffer on each new segment start.
 | ||||
| func (rb *reorderBuffer) insertDecomposed(dcomp []byte) insertErr { | ||||
| 	rb.tmpBytes.setBytes(dcomp) | ||||
| 	// As the streamSafe accounting already handles the counting for modifiers,
 | ||||
| 	// we don't have to call next. However, we do need to keep the accounting
 | ||||
| 	// intact when flushing the buffer.
 | ||||
| 	for i := 0; i < len(dcomp); { | ||||
| 		info := rb.f.info(rb.tmpBytes, i) | ||||
| 		if info.BoundaryBefore() && rb.nrune > 0 && !rb.doFlush() { | ||||
| 			return iShortDst | ||||
| 		} | ||||
| 		i += copy(rb.byte[rb.nbyte:], dcomp[i:i+int(info.size)]) | ||||
| 		rb.insertOrdered(info) | ||||
| 	} | ||||
| 	return iSuccess | ||||
| } | ||||
| 
 | ||||
| // insertSingle inserts an entry in the reorderBuffer for the rune at
 | ||||
| // position i. info is the runeInfo for the rune at position i.
 | ||||
| func (rb *reorderBuffer) insertSingle(src input, i int, info Properties) { | ||||
| 	src.copySlice(rb.byte[rb.nbyte:], i, i+int(info.size)) | ||||
| 	rb.insertOrdered(info) | ||||
| } | ||||
| 
 | ||||
| // insertCGJ inserts a Combining Grapheme Joiner (0x034f) into rb.
 | ||||
| func (rb *reorderBuffer) insertCGJ() { | ||||
| 	rb.insertSingle(input{str: GraphemeJoiner}, 0, Properties{size: uint8(len(GraphemeJoiner))}) | ||||
| } | ||||
| 
 | ||||
| // appendRune inserts a rune at the end of the buffer. It is used for Hangul.
 | ||||
| func (rb *reorderBuffer) appendRune(r rune) { | ||||
| 	bn := rb.nbyte | ||||
| 	sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) | ||||
| 	rb.nbyte += utf8.UTFMax | ||||
| 	rb.rune[rb.nrune] = Properties{pos: bn, size: uint8(sz)} | ||||
| 	rb.nrune++ | ||||
| } | ||||
| 
 | ||||
| // assignRune sets a rune at position pos. It is used for Hangul and recomposition.
 | ||||
| func (rb *reorderBuffer) assignRune(pos int, r rune) { | ||||
| 	bn := rb.rune[pos].pos | ||||
| 	sz := utf8.EncodeRune(rb.byte[bn:], rune(r)) | ||||
| 	rb.rune[pos] = Properties{pos: bn, size: uint8(sz)} | ||||
| } | ||||
| 
 | ||||
| // runeAt returns the rune at position n. It is used for Hangul and recomposition.
 | ||||
| func (rb *reorderBuffer) runeAt(n int) rune { | ||||
| 	inf := rb.rune[n] | ||||
| 	r, _ := utf8.DecodeRune(rb.byte[inf.pos : inf.pos+inf.size]) | ||||
| 	return r | ||||
| } | ||||
| 
 | ||||
| // bytesAt returns the UTF-8 encoding of the rune at position n.
 | ||||
| // It is used for Hangul and recomposition.
 | ||||
| func (rb *reorderBuffer) bytesAt(n int) []byte { | ||||
| 	inf := rb.rune[n] | ||||
| 	return rb.byte[inf.pos : int(inf.pos)+int(inf.size)] | ||||
| } | ||||
| 
 | ||||
| // For Hangul we combine algorithmically, instead of using tables.
 | ||||
| const ( | ||||
| 	hangulBase  = 0xAC00 // UTF-8(hangulBase) -> EA B0 80
 | ||||
| 	hangulBase0 = 0xEA | ||||
| 	hangulBase1 = 0xB0 | ||||
| 	hangulBase2 = 0x80 | ||||
| 
 | ||||
| 	hangulEnd  = hangulBase + jamoLVTCount // UTF-8(0xD7A4) -> ED 9E A4
 | ||||
| 	hangulEnd0 = 0xED | ||||
| 	hangulEnd1 = 0x9E | ||||
| 	hangulEnd2 = 0xA4 | ||||
| 
 | ||||
| 	jamoLBase  = 0x1100 // UTF-8(jamoLBase) -> E1 84 00
 | ||||
| 	jamoLBase0 = 0xE1 | ||||
| 	jamoLBase1 = 0x84 | ||||
| 	jamoLEnd   = 0x1113 | ||||
| 	jamoVBase  = 0x1161 | ||||
| 	jamoVEnd   = 0x1176 | ||||
| 	jamoTBase  = 0x11A7 | ||||
| 	jamoTEnd   = 0x11C3 | ||||
| 
 | ||||
| 	jamoTCount   = 28 | ||||
| 	jamoVCount   = 21 | ||||
| 	jamoVTCount  = 21 * 28 | ||||
| 	jamoLVTCount = 19 * 21 * 28 | ||||
| ) | ||||
| 
 | ||||
| const hangulUTF8Size = 3 | ||||
| 
 | ||||
| func isHangul(b []byte) bool { | ||||
| 	if len(b) < hangulUTF8Size { | ||||
| 		return false | ||||
| 	} | ||||
| 	b0 := b[0] | ||||
| 	if b0 < hangulBase0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	b1 := b[1] | ||||
| 	switch { | ||||
| 	case b0 == hangulBase0: | ||||
| 		return b1 >= hangulBase1 | ||||
| 	case b0 < hangulEnd0: | ||||
| 		return true | ||||
| 	case b0 > hangulEnd0: | ||||
| 		return false | ||||
| 	case b1 < hangulEnd1: | ||||
| 		return true | ||||
| 	} | ||||
| 	return b1 == hangulEnd1 && b[2] < hangulEnd2 | ||||
| } | ||||
| 
 | ||||
| func isHangulString(b string) bool { | ||||
| 	if len(b) < hangulUTF8Size { | ||||
| 		return false | ||||
| 	} | ||||
| 	b0 := b[0] | ||||
| 	if b0 < hangulBase0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	b1 := b[1] | ||||
| 	switch { | ||||
| 	case b0 == hangulBase0: | ||||
| 		return b1 >= hangulBase1 | ||||
| 	case b0 < hangulEnd0: | ||||
| 		return true | ||||
| 	case b0 > hangulEnd0: | ||||
| 		return false | ||||
| 	case b1 < hangulEnd1: | ||||
| 		return true | ||||
| 	} | ||||
| 	return b1 == hangulEnd1 && b[2] < hangulEnd2 | ||||
| } | ||||
| 
 | ||||
| // Caller must ensure len(b) >= 2.
 | ||||
| func isJamoVT(b []byte) bool { | ||||
| 	// True if (rune & 0xff00) == jamoLBase
 | ||||
| 	return b[0] == jamoLBase0 && (b[1]&0xFC) == jamoLBase1 | ||||
| } | ||||
| 
 | ||||
| func isHangulWithoutJamoT(b []byte) bool { | ||||
| 	c, _ := utf8.DecodeRune(b) | ||||
| 	c -= hangulBase | ||||
| 	return c < jamoLVTCount && c%jamoTCount == 0 | ||||
| } | ||||
| 
 | ||||
| // decomposeHangul writes the decomposed Hangul to buf and returns the number
 | ||||
| // of bytes written.  len(buf) should be at least 9.
 | ||||
| func decomposeHangul(buf []byte, r rune) int { | ||||
| 	const JamoUTF8Len = 3 | ||||
| 	r -= hangulBase | ||||
| 	x := r % jamoTCount | ||||
| 	r /= jamoTCount | ||||
| 	utf8.EncodeRune(buf, jamoLBase+r/jamoVCount) | ||||
| 	utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount) | ||||
| 	if x != 0 { | ||||
| 		utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x) | ||||
| 		return 3 * JamoUTF8Len | ||||
| 	} | ||||
| 	return 2 * JamoUTF8Len | ||||
| } | ||||
| 
 | ||||
| // decomposeHangul algorithmically decomposes a Hangul rune into
 | ||||
| // its Jamo components.
 | ||||
| // See https://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
 | ||||
| func (rb *reorderBuffer) decomposeHangul(r rune) { | ||||
| 	r -= hangulBase | ||||
| 	x := r % jamoTCount | ||||
| 	r /= jamoTCount | ||||
| 	rb.appendRune(jamoLBase + r/jamoVCount) | ||||
| 	rb.appendRune(jamoVBase + r%jamoVCount) | ||||
| 	if x != 0 { | ||||
| 		rb.appendRune(jamoTBase + x) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // combineHangul algorithmically combines Jamo character components into Hangul.
 | ||||
| // See https://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
 | ||||
| func (rb *reorderBuffer) combineHangul(s, i, k int) { | ||||
| 	b := rb.rune[:] | ||||
| 	bn := rb.nrune | ||||
| 	for ; i < bn; i++ { | ||||
| 		cccB := b[k-1].ccc | ||||
| 		cccC := b[i].ccc | ||||
| 		if cccB == 0 { | ||||
| 			s = k - 1 | ||||
| 		} | ||||
| 		if s != k-1 && cccB >= cccC { | ||||
| 			// b[i] is blocked by greater-equal cccX below it
 | ||||
| 			b[k] = b[i] | ||||
| 			k++ | ||||
| 		} else { | ||||
| 			l := rb.runeAt(s) // also used to compare to hangulBase
 | ||||
| 			v := rb.runeAt(i) // also used to compare to jamoT
 | ||||
| 			switch { | ||||
| 			case jamoLBase <= l && l < jamoLEnd && | ||||
| 				jamoVBase <= v && v < jamoVEnd: | ||||
| 				// 11xx plus 116x to LV
 | ||||
| 				rb.assignRune(s, hangulBase+ | ||||
| 					(l-jamoLBase)*jamoVTCount+(v-jamoVBase)*jamoTCount) | ||||
| 			case hangulBase <= l && l < hangulEnd && | ||||
| 				jamoTBase < v && v < jamoTEnd && | ||||
| 				((l-hangulBase)%jamoTCount) == 0: | ||||
| 				// ACxx plus 11Ax to LVT
 | ||||
| 				rb.assignRune(s, l+v-jamoTBase) | ||||
| 			default: | ||||
| 				b[k] = b[i] | ||||
| 				k++ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	rb.nrune = k | ||||
| } | ||||
| 
 | ||||
| // compose recombines the runes in the buffer.
 | ||||
| // It should only be used to recompose a single segment, as it will not
 | ||||
| // handle alternations between Hangul and non-Hangul characters correctly.
 | ||||
| func (rb *reorderBuffer) compose() { | ||||
| 	// Lazily load the map used by the combine func below, but do
 | ||||
| 	// it outside of the loop.
 | ||||
| 	recompMapOnce.Do(buildRecompMap) | ||||
| 
 | ||||
| 	// UAX #15, section X5 , including Corrigendum #5
 | ||||
| 	// "In any character sequence beginning with starter S, a character C is
 | ||||
| 	//  blocked from S if and only if there is some character B between S
 | ||||
| 	//  and C, and either B is a starter or it has the same or higher
 | ||||
| 	//  combining class as C."
 | ||||
| 	bn := rb.nrune | ||||
| 	if bn == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 	k := 1 | ||||
| 	b := rb.rune[:] | ||||
| 	for s, i := 0, 1; i < bn; i++ { | ||||
| 		if isJamoVT(rb.bytesAt(i)) { | ||||
| 			// Redo from start in Hangul mode. Necessary to support
 | ||||
| 			// U+320E..U+321E in NFKC mode.
 | ||||
| 			rb.combineHangul(s, i, k) | ||||
| 			return | ||||
| 		} | ||||
| 		ii := b[i] | ||||
| 		// We can only use combineForward as a filter if we later
 | ||||
| 		// get the info for the combined character. This is more
 | ||||
| 		// expensive than using the filter. Using combinesBackward()
 | ||||
| 		// is safe.
 | ||||
| 		if ii.combinesBackward() { | ||||
| 			cccB := b[k-1].ccc | ||||
| 			cccC := ii.ccc | ||||
| 			blocked := false // b[i] blocked by starter or greater or equal CCC?
 | ||||
| 			if cccB == 0 { | ||||
| 				s = k - 1 | ||||
| 			} else { | ||||
| 				blocked = s != k-1 && cccB >= cccC | ||||
| 			} | ||||
| 			if !blocked { | ||||
| 				combined := combine(rb.runeAt(s), rb.runeAt(i)) | ||||
| 				if combined != 0 { | ||||
| 					rb.assignRune(s, combined) | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		b[k] = b[i] | ||||
| 		k++ | ||||
| 	} | ||||
| 	rb.nrune = k | ||||
| } | ||||
							
								
								
									
										278
									
								
								vendor/golang.org/x/text/unicode/norm/forminfo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/golang.org/x/text/unicode/norm/forminfo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,278 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import "encoding/binary" | ||||
| 
 | ||||
| // This file contains Form-specific logic and wrappers for data in tables.go.
 | ||||
| 
 | ||||
| // Rune info is stored in a separate trie per composing form. A composing form
 | ||||
| // and its corresponding decomposing form share the same trie.  Each trie maps
 | ||||
| // a rune to a uint16. The values take two forms.  For v >= 0x8000:
 | ||||
| //   bits
 | ||||
| //   15:    1 (inverse of NFD_QC bit of qcInfo)
 | ||||
| //   13..7: qcInfo (see below). isYesD is always true (no decompostion).
 | ||||
| //    6..0: ccc (compressed CCC value).
 | ||||
| // For v < 0x8000, the respective rune has a decomposition and v is an index
 | ||||
| // into a byte array of UTF-8 decomposition sequences and additional info and
 | ||||
| // has the form:
 | ||||
| //    <header> <decomp_byte>* [<tccc> [<lccc>]]
 | ||||
| // The header contains the number of bytes in the decomposition (excluding this
 | ||||
| // length byte). The two most significant bits of this length byte correspond
 | ||||
| // to bit 5 and 4 of qcInfo (see below).  The byte sequence itself starts at v+1.
 | ||||
| // The byte sequence is followed by a trailing and leading CCC if the values
 | ||||
| // for these are not zero.  The value of v determines which ccc are appended
 | ||||
| // to the sequences.  For v < firstCCC, there are none, for v >= firstCCC,
 | ||||
| // the sequence is followed by a trailing ccc, and for v >= firstLeadingCC
 | ||||
| // there is an additional leading ccc. The value of tccc itself is the
 | ||||
| // trailing CCC shifted left 2 bits. The two least-significant bits of tccc
 | ||||
| // are the number of trailing non-starters.
 | ||||
| 
 | ||||
| const ( | ||||
| 	qcInfoMask      = 0x3F // to clear all but the relevant bits in a qcInfo
 | ||||
| 	headerLenMask   = 0x3F // extract the length value from the header byte
 | ||||
| 	headerFlagsMask = 0xC0 // extract the qcInfo bits from the header byte
 | ||||
| ) | ||||
| 
 | ||||
| // Properties provides access to normalization properties of a rune.
 | ||||
| type Properties struct { | ||||
| 	pos   uint8  // start position in reorderBuffer; used in composition.go
 | ||||
| 	size  uint8  // length of UTF-8 encoding of this rune
 | ||||
| 	ccc   uint8  // leading canonical combining class (ccc if not decomposition)
 | ||||
| 	tccc  uint8  // trailing canonical combining class (ccc if not decomposition)
 | ||||
| 	nLead uint8  // number of leading non-starters.
 | ||||
| 	flags qcInfo // quick check flags
 | ||||
| 	index uint16 | ||||
| } | ||||
| 
 | ||||
| // functions dispatchable per form
 | ||||
| type lookupFunc func(b input, i int) Properties | ||||
| 
 | ||||
| // formInfo holds Form-specific functions and tables.
 | ||||
| type formInfo struct { | ||||
| 	form                     Form | ||||
| 	composing, compatibility bool // form type
 | ||||
| 	info                     lookupFunc | ||||
| 	nextMain                 iterFunc | ||||
| } | ||||
| 
 | ||||
| var formTable = []*formInfo{{ | ||||
| 	form:          NFC, | ||||
| 	composing:     true, | ||||
| 	compatibility: false, | ||||
| 	info:          lookupInfoNFC, | ||||
| 	nextMain:      nextComposed, | ||||
| }, { | ||||
| 	form:          NFD, | ||||
| 	composing:     false, | ||||
| 	compatibility: false, | ||||
| 	info:          lookupInfoNFC, | ||||
| 	nextMain:      nextDecomposed, | ||||
| }, { | ||||
| 	form:          NFKC, | ||||
| 	composing:     true, | ||||
| 	compatibility: true, | ||||
| 	info:          lookupInfoNFKC, | ||||
| 	nextMain:      nextComposed, | ||||
| }, { | ||||
| 	form:          NFKD, | ||||
| 	composing:     false, | ||||
| 	compatibility: true, | ||||
| 	info:          lookupInfoNFKC, | ||||
| 	nextMain:      nextDecomposed, | ||||
| }} | ||||
| 
 | ||||
| // We do not distinguish between boundaries for NFC, NFD, etc. to avoid
 | ||||
| // unexpected behavior for the user.  For example, in NFD, there is a boundary
 | ||||
| // after 'a'.  However, 'a' might combine with modifiers, so from the application's
 | ||||
| // perspective it is not a good boundary. We will therefore always use the
 | ||||
| // boundaries for the combining variants.
 | ||||
| 
 | ||||
| // BoundaryBefore returns true if this rune starts a new segment and
 | ||||
| // cannot combine with any rune on the left.
 | ||||
| func (p Properties) BoundaryBefore() bool { | ||||
| 	if p.ccc == 0 && !p.combinesBackward() { | ||||
| 		return true | ||||
| 	} | ||||
| 	// We assume that the CCC of the first character in a decomposition
 | ||||
| 	// is always non-zero if different from info.ccc and that we can return
 | ||||
| 	// false at this point. This is verified by maketables.
 | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // BoundaryAfter returns true if runes cannot combine with or otherwise
 | ||||
| // interact with this or previous runes.
 | ||||
| func (p Properties) BoundaryAfter() bool { | ||||
| 	// TODO: loosen these conditions.
 | ||||
| 	return p.isInert() | ||||
| } | ||||
| 
 | ||||
| // We pack quick check data in 4 bits:
 | ||||
| //   5:    Combines forward  (0 == false, 1 == true)
 | ||||
| //   4..3: NFC_QC Yes(00), No (10), or Maybe (11)
 | ||||
| //   2:    NFD_QC Yes (0) or No (1). No also means there is a decomposition.
 | ||||
| //   1..0: Number of trailing non-starters.
 | ||||
| //
 | ||||
| // When all 4 bits are zero, the character is inert, meaning it is never
 | ||||
| // influenced by normalization.
 | ||||
| type qcInfo uint8 | ||||
| 
 | ||||
| func (p Properties) isYesC() bool { return p.flags&0x10 == 0 } | ||||
| func (p Properties) isYesD() bool { return p.flags&0x4 == 0 } | ||||
| 
 | ||||
| func (p Properties) combinesForward() bool  { return p.flags&0x20 != 0 } | ||||
| func (p Properties) combinesBackward() bool { return p.flags&0x8 != 0 } // == isMaybe
 | ||||
| func (p Properties) hasDecomposition() bool { return p.flags&0x4 != 0 } // == isNoD
 | ||||
| 
 | ||||
| func (p Properties) isInert() bool { | ||||
| 	return p.flags&qcInfoMask == 0 && p.ccc == 0 | ||||
| } | ||||
| 
 | ||||
| func (p Properties) multiSegment() bool { | ||||
| 	return p.index >= firstMulti && p.index < endMulti | ||||
| } | ||||
| 
 | ||||
| func (p Properties) nLeadingNonStarters() uint8 { | ||||
| 	return p.nLead | ||||
| } | ||||
| 
 | ||||
| func (p Properties) nTrailingNonStarters() uint8 { | ||||
| 	return uint8(p.flags & 0x03) | ||||
| } | ||||
| 
 | ||||
| // Decomposition returns the decomposition for the underlying rune
 | ||||
| // or nil if there is none.
 | ||||
| func (p Properties) Decomposition() []byte { | ||||
| 	// TODO: create the decomposition for Hangul?
 | ||||
| 	if p.index == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	i := p.index | ||||
| 	n := decomps[i] & headerLenMask | ||||
| 	i++ | ||||
| 	return decomps[i : i+uint16(n)] | ||||
| } | ||||
| 
 | ||||
| // Size returns the length of UTF-8 encoding of the rune.
 | ||||
| func (p Properties) Size() int { | ||||
| 	return int(p.size) | ||||
| } | ||||
| 
 | ||||
| // CCC returns the canonical combining class of the underlying rune.
 | ||||
| func (p Properties) CCC() uint8 { | ||||
| 	if p.index >= firstCCCZeroExcept { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return ccc[p.ccc] | ||||
| } | ||||
| 
 | ||||
| // LeadCCC returns the CCC of the first rune in the decomposition.
 | ||||
| // If there is no decomposition, LeadCCC equals CCC.
 | ||||
| func (p Properties) LeadCCC() uint8 { | ||||
| 	return ccc[p.ccc] | ||||
| } | ||||
| 
 | ||||
| // TrailCCC returns the CCC of the last rune in the decomposition.
 | ||||
| // If there is no decomposition, TrailCCC equals CCC.
 | ||||
| func (p Properties) TrailCCC() uint8 { | ||||
| 	return ccc[p.tccc] | ||||
| } | ||||
| 
 | ||||
| func buildRecompMap() { | ||||
| 	recompMap = make(map[uint32]rune, len(recompMapPacked)/8) | ||||
| 	var buf [8]byte | ||||
| 	for i := 0; i < len(recompMapPacked); i += 8 { | ||||
| 		copy(buf[:], recompMapPacked[i:i+8]) | ||||
| 		key := binary.BigEndian.Uint32(buf[:4]) | ||||
| 		val := binary.BigEndian.Uint32(buf[4:]) | ||||
| 		recompMap[key] = rune(val) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Recomposition
 | ||||
| // We use 32-bit keys instead of 64-bit for the two codepoint keys.
 | ||||
| // This clips off the bits of three entries, but we know this will not
 | ||||
| // result in a collision. In the unlikely event that changes to
 | ||||
| // UnicodeData.txt introduce collisions, the compiler will catch it.
 | ||||
| // Note that the recomposition map for NFC and NFKC are identical.
 | ||||
| 
 | ||||
| // combine returns the combined rune or 0 if it doesn't exist.
 | ||||
| //
 | ||||
| // The caller is responsible for calling
 | ||||
| // recompMapOnce.Do(buildRecompMap) sometime before this is called.
 | ||||
| func combine(a, b rune) rune { | ||||
| 	key := uint32(uint16(a))<<16 + uint32(uint16(b)) | ||||
| 	if recompMap == nil { | ||||
| 		panic("caller error") // see func comment
 | ||||
| 	} | ||||
| 	return recompMap[key] | ||||
| } | ||||
| 
 | ||||
| func lookupInfoNFC(b input, i int) Properties { | ||||
| 	v, sz := b.charinfoNFC(i) | ||||
| 	return compInfo(v, sz) | ||||
| } | ||||
| 
 | ||||
| func lookupInfoNFKC(b input, i int) Properties { | ||||
| 	v, sz := b.charinfoNFKC(i) | ||||
| 	return compInfo(v, sz) | ||||
| } | ||||
| 
 | ||||
| // Properties returns properties for the first rune in s.
 | ||||
| func (f Form) Properties(s []byte) Properties { | ||||
| 	if f == NFC || f == NFD { | ||||
| 		return compInfo(nfcData.lookup(s)) | ||||
| 	} | ||||
| 	return compInfo(nfkcData.lookup(s)) | ||||
| } | ||||
| 
 | ||||
| // PropertiesString returns properties for the first rune in s.
 | ||||
| func (f Form) PropertiesString(s string) Properties { | ||||
| 	if f == NFC || f == NFD { | ||||
| 		return compInfo(nfcData.lookupString(s)) | ||||
| 	} | ||||
| 	return compInfo(nfkcData.lookupString(s)) | ||||
| } | ||||
| 
 | ||||
| // compInfo converts the information contained in v and sz
 | ||||
| // to a Properties.  See the comment at the top of the file
 | ||||
| // for more information on the format.
 | ||||
| func compInfo(v uint16, sz int) Properties { | ||||
| 	if v == 0 { | ||||
| 		return Properties{size: uint8(sz)} | ||||
| 	} else if v >= 0x8000 { | ||||
| 		p := Properties{ | ||||
| 			size:  uint8(sz), | ||||
| 			ccc:   uint8(v), | ||||
| 			tccc:  uint8(v), | ||||
| 			flags: qcInfo(v >> 8), | ||||
| 		} | ||||
| 		if p.ccc > 0 || p.combinesBackward() { | ||||
| 			p.nLead = uint8(p.flags & 0x3) | ||||
| 		} | ||||
| 		return p | ||||
| 	} | ||||
| 	// has decomposition
 | ||||
| 	h := decomps[v] | ||||
| 	f := (qcInfo(h&headerFlagsMask) >> 2) | 0x4 | ||||
| 	p := Properties{size: uint8(sz), flags: f, index: v} | ||||
| 	if v >= firstCCC { | ||||
| 		v += uint16(h&headerLenMask) + 1 | ||||
| 		c := decomps[v] | ||||
| 		p.tccc = c >> 2 | ||||
| 		p.flags |= qcInfo(c & 0x3) | ||||
| 		if v >= firstLeadingCCC { | ||||
| 			p.nLead = c & 0x3 | ||||
| 			if v >= firstStarterWithNLead { | ||||
| 				// We were tricked. Remove the decomposition.
 | ||||
| 				p.flags &= 0x03 | ||||
| 				p.index = 0 | ||||
| 				return p | ||||
| 			} | ||||
| 			p.ccc = decomps[v+1] | ||||
| 		} | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
							
								
								
									
										109
									
								
								vendor/golang.org/x/text/unicode/norm/input.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/golang.org/x/text/unicode/norm/input.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import "unicode/utf8" | ||||
| 
 | ||||
| type input struct { | ||||
| 	str   string | ||||
| 	bytes []byte | ||||
| } | ||||
| 
 | ||||
| func inputBytes(str []byte) input { | ||||
| 	return input{bytes: str} | ||||
| } | ||||
| 
 | ||||
| func inputString(str string) input { | ||||
| 	return input{str: str} | ||||
| } | ||||
| 
 | ||||
| func (in *input) setBytes(str []byte) { | ||||
| 	in.str = "" | ||||
| 	in.bytes = str | ||||
| } | ||||
| 
 | ||||
| func (in *input) setString(str string) { | ||||
| 	in.str = str | ||||
| 	in.bytes = nil | ||||
| } | ||||
| 
 | ||||
| func (in *input) _byte(p int) byte { | ||||
| 	if in.bytes == nil { | ||||
| 		return in.str[p] | ||||
| 	} | ||||
| 	return in.bytes[p] | ||||
| } | ||||
| 
 | ||||
| func (in *input) skipASCII(p, max int) int { | ||||
| 	if in.bytes == nil { | ||||
| 		for ; p < max && in.str[p] < utf8.RuneSelf; p++ { | ||||
| 		} | ||||
| 	} else { | ||||
| 		for ; p < max && in.bytes[p] < utf8.RuneSelf; p++ { | ||||
| 		} | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| func (in *input) skipContinuationBytes(p int) int { | ||||
| 	if in.bytes == nil { | ||||
| 		for ; p < len(in.str) && !utf8.RuneStart(in.str[p]); p++ { | ||||
| 		} | ||||
| 	} else { | ||||
| 		for ; p < len(in.bytes) && !utf8.RuneStart(in.bytes[p]); p++ { | ||||
| 		} | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| func (in *input) appendSlice(buf []byte, b, e int) []byte { | ||||
| 	if in.bytes != nil { | ||||
| 		return append(buf, in.bytes[b:e]...) | ||||
| 	} | ||||
| 	for i := b; i < e; i++ { | ||||
| 		buf = append(buf, in.str[i]) | ||||
| 	} | ||||
| 	return buf | ||||
| } | ||||
| 
 | ||||
| func (in *input) copySlice(buf []byte, b, e int) int { | ||||
| 	if in.bytes == nil { | ||||
| 		return copy(buf, in.str[b:e]) | ||||
| 	} | ||||
| 	return copy(buf, in.bytes[b:e]) | ||||
| } | ||||
| 
 | ||||
| func (in *input) charinfoNFC(p int) (uint16, int) { | ||||
| 	if in.bytes == nil { | ||||
| 		return nfcData.lookupString(in.str[p:]) | ||||
| 	} | ||||
| 	return nfcData.lookup(in.bytes[p:]) | ||||
| } | ||||
| 
 | ||||
| func (in *input) charinfoNFKC(p int) (uint16, int) { | ||||
| 	if in.bytes == nil { | ||||
| 		return nfkcData.lookupString(in.str[p:]) | ||||
| 	} | ||||
| 	return nfkcData.lookup(in.bytes[p:]) | ||||
| } | ||||
| 
 | ||||
| func (in *input) hangul(p int) (r rune) { | ||||
| 	var size int | ||||
| 	if in.bytes == nil { | ||||
| 		if !isHangulString(in.str[p:]) { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		r, size = utf8.DecodeRuneInString(in.str[p:]) | ||||
| 	} else { | ||||
| 		if !isHangul(in.bytes[p:]) { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		r, size = utf8.DecodeRune(in.bytes[p:]) | ||||
| 	} | ||||
| 	if size != hangulUTF8Size { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	return r | ||||
| } | ||||
							
								
								
									
										458
									
								
								vendor/golang.org/x/text/unicode/norm/iter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										458
									
								
								vendor/golang.org/x/text/unicode/norm/iter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,458 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
| 
 | ||||
| // MaxSegmentSize is the maximum size of a byte buffer needed to consider any
 | ||||
| // sequence of starter and non-starter runes for the purpose of normalization.
 | ||||
| const MaxSegmentSize = maxByteBufferSize | ||||
| 
 | ||||
| // An Iter iterates over a string or byte slice, while normalizing it
 | ||||
| // to a given Form.
 | ||||
| type Iter struct { | ||||
| 	rb     reorderBuffer | ||||
| 	buf    [maxByteBufferSize]byte | ||||
| 	info   Properties // first character saved from previous iteration
 | ||||
| 	next   iterFunc   // implementation of next depends on form
 | ||||
| 	asciiF iterFunc | ||||
| 
 | ||||
| 	p        int    // current position in input source
 | ||||
| 	multiSeg []byte // remainder of multi-segment decomposition
 | ||||
| } | ||||
| 
 | ||||
| type iterFunc func(*Iter) []byte | ||||
| 
 | ||||
| // Init initializes i to iterate over src after normalizing it to Form f.
 | ||||
| func (i *Iter) Init(f Form, src []byte) { | ||||
| 	i.p = 0 | ||||
| 	if len(src) == 0 { | ||||
| 		i.setDone() | ||||
| 		i.rb.nsrc = 0 | ||||
| 		return | ||||
| 	} | ||||
| 	i.multiSeg = nil | ||||
| 	i.rb.init(f, src) | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	i.asciiF = nextASCIIBytes | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.rb.ss.first(i.info) | ||||
| } | ||||
| 
 | ||||
| // InitString initializes i to iterate over src after normalizing it to Form f.
 | ||||
| func (i *Iter) InitString(f Form, src string) { | ||||
| 	i.p = 0 | ||||
| 	if len(src) == 0 { | ||||
| 		i.setDone() | ||||
| 		i.rb.nsrc = 0 | ||||
| 		return | ||||
| 	} | ||||
| 	i.multiSeg = nil | ||||
| 	i.rb.initString(f, src) | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	i.asciiF = nextASCIIString | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.rb.ss.first(i.info) | ||||
| } | ||||
| 
 | ||||
| // Seek sets the segment to be returned by the next call to Next to start
 | ||||
| // at position p.  It is the responsibility of the caller to set p to the
 | ||||
| // start of a segment.
 | ||||
| func (i *Iter) Seek(offset int64, whence int) (int64, error) { | ||||
| 	var abs int64 | ||||
| 	switch whence { | ||||
| 	case 0: | ||||
| 		abs = offset | ||||
| 	case 1: | ||||
| 		abs = int64(i.p) + offset | ||||
| 	case 2: | ||||
| 		abs = int64(i.rb.nsrc) + offset | ||||
| 	default: | ||||
| 		return 0, fmt.Errorf("norm: invalid whence") | ||||
| 	} | ||||
| 	if abs < 0 { | ||||
| 		return 0, fmt.Errorf("norm: negative position") | ||||
| 	} | ||||
| 	if int(abs) >= i.rb.nsrc { | ||||
| 		i.setDone() | ||||
| 		return int64(i.p), nil | ||||
| 	} | ||||
| 	i.p = int(abs) | ||||
| 	i.multiSeg = nil | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.rb.ss.first(i.info) | ||||
| 	return abs, nil | ||||
| } | ||||
| 
 | ||||
| // returnSlice returns a slice of the underlying input type as a byte slice.
 | ||||
| // If the underlying is of type []byte, it will simply return a slice.
 | ||||
| // If the underlying is of type string, it will copy the slice to the buffer
 | ||||
| // and return that.
 | ||||
| func (i *Iter) returnSlice(a, b int) []byte { | ||||
| 	if i.rb.src.bytes == nil { | ||||
| 		return i.buf[:copy(i.buf[:], i.rb.src.str[a:b])] | ||||
| 	} | ||||
| 	return i.rb.src.bytes[a:b] | ||||
| } | ||||
| 
 | ||||
| // Pos returns the byte position at which the next call to Next will commence processing.
 | ||||
| func (i *Iter) Pos() int { | ||||
| 	return i.p | ||||
| } | ||||
| 
 | ||||
| func (i *Iter) setDone() { | ||||
| 	i.next = nextDone | ||||
| 	i.p = i.rb.nsrc | ||||
| } | ||||
| 
 | ||||
| // Done returns true if there is no more input to process.
 | ||||
| func (i *Iter) Done() bool { | ||||
| 	return i.p >= i.rb.nsrc | ||||
| } | ||||
| 
 | ||||
| // Next returns f(i.input[i.Pos():n]), where n is a boundary of i.input.
 | ||||
| // For any input a and b for which f(a) == f(b), subsequent calls
 | ||||
| // to Next will return the same segments.
 | ||||
| // Modifying runes are grouped together with the preceding starter, if such a starter exists.
 | ||||
| // Although not guaranteed, n will typically be the smallest possible n.
 | ||||
| func (i *Iter) Next() []byte { | ||||
| 	return i.next(i) | ||||
| } | ||||
| 
 | ||||
| func nextASCIIBytes(i *Iter) []byte { | ||||
| 	p := i.p + 1 | ||||
| 	if p >= i.rb.nsrc { | ||||
| 		p0 := i.p | ||||
| 		i.setDone() | ||||
| 		return i.rb.src.bytes[p0:p] | ||||
| 	} | ||||
| 	if i.rb.src.bytes[p] < utf8.RuneSelf { | ||||
| 		p0 := i.p | ||||
| 		i.p = p | ||||
| 		return i.rb.src.bytes[p0:p] | ||||
| 	} | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	return i.next(i) | ||||
| } | ||||
| 
 | ||||
| func nextASCIIString(i *Iter) []byte { | ||||
| 	p := i.p + 1 | ||||
| 	if p >= i.rb.nsrc { | ||||
| 		i.buf[0] = i.rb.src.str[i.p] | ||||
| 		i.setDone() | ||||
| 		return i.buf[:1] | ||||
| 	} | ||||
| 	if i.rb.src.str[p] < utf8.RuneSelf { | ||||
| 		i.buf[0] = i.rb.src.str[i.p] | ||||
| 		i.p = p | ||||
| 		return i.buf[:1] | ||||
| 	} | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	return i.next(i) | ||||
| } | ||||
| 
 | ||||
| func nextHangul(i *Iter) []byte { | ||||
| 	p := i.p | ||||
| 	next := p + hangulUTF8Size | ||||
| 	if next >= i.rb.nsrc { | ||||
| 		i.setDone() | ||||
| 	} else if i.rb.src.hangul(next) == 0 { | ||||
| 		i.rb.ss.next(i.info) | ||||
| 		i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 		i.next = i.rb.f.nextMain | ||||
| 		return i.next(i) | ||||
| 	} | ||||
| 	i.p = next | ||||
| 	return i.buf[:decomposeHangul(i.buf[:], i.rb.src.hangul(p))] | ||||
| } | ||||
| 
 | ||||
| func nextDone(i *Iter) []byte { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // nextMulti is used for iterating over multi-segment decompositions
 | ||||
| // for decomposing normal forms.
 | ||||
| func nextMulti(i *Iter) []byte { | ||||
| 	j := 0 | ||||
| 	d := i.multiSeg | ||||
| 	// skip first rune
 | ||||
| 	for j = 1; j < len(d) && !utf8.RuneStart(d[j]); j++ { | ||||
| 	} | ||||
| 	for j < len(d) { | ||||
| 		info := i.rb.f.info(input{bytes: d}, j) | ||||
| 		if info.BoundaryBefore() { | ||||
| 			i.multiSeg = d[j:] | ||||
| 			return d[:j] | ||||
| 		} | ||||
| 		j += int(info.size) | ||||
| 	} | ||||
| 	// treat last segment as normal decomposition
 | ||||
| 	i.next = i.rb.f.nextMain | ||||
| 	return i.next(i) | ||||
| } | ||||
| 
 | ||||
| // nextMultiNorm is used for iterating over multi-segment decompositions
 | ||||
| // for composing normal forms.
 | ||||
| func nextMultiNorm(i *Iter) []byte { | ||||
| 	j := 0 | ||||
| 	d := i.multiSeg | ||||
| 	for j < len(d) { | ||||
| 		info := i.rb.f.info(input{bytes: d}, j) | ||||
| 		if info.BoundaryBefore() { | ||||
| 			i.rb.compose() | ||||
| 			seg := i.buf[:i.rb.flushCopy(i.buf[:])] | ||||
| 			i.rb.insertUnsafe(input{bytes: d}, j, info) | ||||
| 			i.multiSeg = d[j+int(info.size):] | ||||
| 			return seg | ||||
| 		} | ||||
| 		i.rb.insertUnsafe(input{bytes: d}, j, info) | ||||
| 		j += int(info.size) | ||||
| 	} | ||||
| 	i.multiSeg = nil | ||||
| 	i.next = nextComposed | ||||
| 	return doNormComposed(i) | ||||
| } | ||||
| 
 | ||||
| // nextDecomposed is the implementation of Next for forms NFD and NFKD.
 | ||||
| func nextDecomposed(i *Iter) (next []byte) { | ||||
| 	outp := 0 | ||||
| 	inCopyStart, outCopyStart := i.p, 0 | ||||
| 	for { | ||||
| 		if sz := int(i.info.size); sz <= 1 { | ||||
| 			i.rb.ss = 0 | ||||
| 			p := i.p | ||||
| 			i.p++ // ASCII or illegal byte.  Either way, advance by 1.
 | ||||
| 			if i.p >= i.rb.nsrc { | ||||
| 				i.setDone() | ||||
| 				return i.returnSlice(p, i.p) | ||||
| 			} else if i.rb.src._byte(i.p) < utf8.RuneSelf { | ||||
| 				i.next = i.asciiF | ||||
| 				return i.returnSlice(p, i.p) | ||||
| 			} | ||||
| 			outp++ | ||||
| 		} else if d := i.info.Decomposition(); d != nil { | ||||
| 			// Note: If leading CCC != 0, then len(d) == 2 and last is also non-zero.
 | ||||
| 			// Case 1: there is a leftover to copy.  In this case the decomposition
 | ||||
| 			// must begin with a modifier and should always be appended.
 | ||||
| 			// Case 2: no leftover. Simply return d if followed by a ccc == 0 value.
 | ||||
| 			p := outp + len(d) | ||||
| 			if outp > 0 { | ||||
| 				i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) | ||||
| 				// TODO: this condition should not be possible, but we leave it
 | ||||
| 				// in for defensive purposes.
 | ||||
| 				if p > len(i.buf) { | ||||
| 					return i.buf[:outp] | ||||
| 				} | ||||
| 			} else if i.info.multiSegment() { | ||||
| 				// outp must be 0 as multi-segment decompositions always
 | ||||
| 				// start a new segment.
 | ||||
| 				if i.multiSeg == nil { | ||||
| 					i.multiSeg = d | ||||
| 					i.next = nextMulti | ||||
| 					return nextMulti(i) | ||||
| 				} | ||||
| 				// We are in the last segment.  Treat as normal decomposition.
 | ||||
| 				d = i.multiSeg | ||||
| 				i.multiSeg = nil | ||||
| 				p = len(d) | ||||
| 			} | ||||
| 			prevCC := i.info.tccc | ||||
| 			if i.p += sz; i.p >= i.rb.nsrc { | ||||
| 				i.setDone() | ||||
| 				i.info = Properties{} // Force BoundaryBefore to succeed.
 | ||||
| 			} else { | ||||
| 				i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 			} | ||||
| 			switch i.rb.ss.next(i.info) { | ||||
| 			case ssOverflow: | ||||
| 				i.next = nextCGJDecompose | ||||
| 				fallthrough | ||||
| 			case ssStarter: | ||||
| 				if outp > 0 { | ||||
| 					copy(i.buf[outp:], d) | ||||
| 					return i.buf[:p] | ||||
| 				} | ||||
| 				return d | ||||
| 			} | ||||
| 			copy(i.buf[outp:], d) | ||||
| 			outp = p | ||||
| 			inCopyStart, outCopyStart = i.p, outp | ||||
| 			if i.info.ccc < prevCC { | ||||
| 				goto doNorm | ||||
| 			} | ||||
| 			continue | ||||
| 		} else if r := i.rb.src.hangul(i.p); r != 0 { | ||||
| 			outp = decomposeHangul(i.buf[:], r) | ||||
| 			i.p += hangulUTF8Size | ||||
| 			inCopyStart, outCopyStart = i.p, outp | ||||
| 			if i.p >= i.rb.nsrc { | ||||
| 				i.setDone() | ||||
| 				break | ||||
| 			} else if i.rb.src.hangul(i.p) != 0 { | ||||
| 				i.next = nextHangul | ||||
| 				return i.buf[:outp] | ||||
| 			} | ||||
| 		} else { | ||||
| 			p := outp + sz | ||||
| 			if p > len(i.buf) { | ||||
| 				break | ||||
| 			} | ||||
| 			outp = p | ||||
| 			i.p += sz | ||||
| 		} | ||||
| 		if i.p >= i.rb.nsrc { | ||||
| 			i.setDone() | ||||
| 			break | ||||
| 		} | ||||
| 		prevCC := i.info.tccc | ||||
| 		i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 		if v := i.rb.ss.next(i.info); v == ssStarter { | ||||
| 			break | ||||
| 		} else if v == ssOverflow { | ||||
| 			i.next = nextCGJDecompose | ||||
| 			break | ||||
| 		} | ||||
| 		if i.info.ccc < prevCC { | ||||
| 			goto doNorm | ||||
| 		} | ||||
| 	} | ||||
| 	if outCopyStart == 0 { | ||||
| 		return i.returnSlice(inCopyStart, i.p) | ||||
| 	} else if inCopyStart < i.p { | ||||
| 		i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) | ||||
| 	} | ||||
| 	return i.buf[:outp] | ||||
| doNorm: | ||||
| 	// Insert what we have decomposed so far in the reorderBuffer.
 | ||||
| 	// As we will only reorder, there will always be enough room.
 | ||||
| 	i.rb.src.copySlice(i.buf[outCopyStart:], inCopyStart, i.p) | ||||
| 	i.rb.insertDecomposed(i.buf[0:outp]) | ||||
| 	return doNormDecomposed(i) | ||||
| } | ||||
| 
 | ||||
| func doNormDecomposed(i *Iter) []byte { | ||||
| 	for { | ||||
| 		i.rb.insertUnsafe(i.rb.src, i.p, i.info) | ||||
| 		if i.p += int(i.info.size); i.p >= i.rb.nsrc { | ||||
| 			i.setDone() | ||||
| 			break | ||||
| 		} | ||||
| 		i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 		if i.info.ccc == 0 { | ||||
| 			break | ||||
| 		} | ||||
| 		if s := i.rb.ss.next(i.info); s == ssOverflow { | ||||
| 			i.next = nextCGJDecompose | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	// new segment or too many combining characters: exit normalization
 | ||||
| 	return i.buf[:i.rb.flushCopy(i.buf[:])] | ||||
| } | ||||
| 
 | ||||
| func nextCGJDecompose(i *Iter) []byte { | ||||
| 	i.rb.ss = 0 | ||||
| 	i.rb.insertCGJ() | ||||
| 	i.next = nextDecomposed | ||||
| 	i.rb.ss.first(i.info) | ||||
| 	buf := doNormDecomposed(i) | ||||
| 	return buf | ||||
| } | ||||
| 
 | ||||
| // nextComposed is the implementation of Next for forms NFC and NFKC.
 | ||||
| func nextComposed(i *Iter) []byte { | ||||
| 	outp, startp := 0, i.p | ||||
| 	var prevCC uint8 | ||||
| 	for { | ||||
| 		if !i.info.isYesC() { | ||||
| 			goto doNorm | ||||
| 		} | ||||
| 		prevCC = i.info.tccc | ||||
| 		sz := int(i.info.size) | ||||
| 		if sz == 0 { | ||||
| 			sz = 1 // illegal rune: copy byte-by-byte
 | ||||
| 		} | ||||
| 		p := outp + sz | ||||
| 		if p > len(i.buf) { | ||||
| 			break | ||||
| 		} | ||||
| 		outp = p | ||||
| 		i.p += sz | ||||
| 		if i.p >= i.rb.nsrc { | ||||
| 			i.setDone() | ||||
| 			break | ||||
| 		} else if i.rb.src._byte(i.p) < utf8.RuneSelf { | ||||
| 			i.rb.ss = 0 | ||||
| 			i.next = i.asciiF | ||||
| 			break | ||||
| 		} | ||||
| 		i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 		if v := i.rb.ss.next(i.info); v == ssStarter { | ||||
| 			break | ||||
| 		} else if v == ssOverflow { | ||||
| 			i.next = nextCGJCompose | ||||
| 			break | ||||
| 		} | ||||
| 		if i.info.ccc < prevCC { | ||||
| 			goto doNorm | ||||
| 		} | ||||
| 	} | ||||
| 	return i.returnSlice(startp, i.p) | ||||
| doNorm: | ||||
| 	// reset to start position
 | ||||
| 	i.p = startp | ||||
| 	i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 	i.rb.ss.first(i.info) | ||||
| 	if i.info.multiSegment() { | ||||
| 		d := i.info.Decomposition() | ||||
| 		info := i.rb.f.info(input{bytes: d}, 0) | ||||
| 		i.rb.insertUnsafe(input{bytes: d}, 0, info) | ||||
| 		i.multiSeg = d[int(info.size):] | ||||
| 		i.next = nextMultiNorm | ||||
| 		return nextMultiNorm(i) | ||||
| 	} | ||||
| 	i.rb.ss.first(i.info) | ||||
| 	i.rb.insertUnsafe(i.rb.src, i.p, i.info) | ||||
| 	return doNormComposed(i) | ||||
| } | ||||
| 
 | ||||
| func doNormComposed(i *Iter) []byte { | ||||
| 	// First rune should already be inserted.
 | ||||
| 	for { | ||||
| 		if i.p += int(i.info.size); i.p >= i.rb.nsrc { | ||||
| 			i.setDone() | ||||
| 			break | ||||
| 		} | ||||
| 		i.info = i.rb.f.info(i.rb.src, i.p) | ||||
| 		if s := i.rb.ss.next(i.info); s == ssStarter { | ||||
| 			break | ||||
| 		} else if s == ssOverflow { | ||||
| 			i.next = nextCGJCompose | ||||
| 			break | ||||
| 		} | ||||
| 		i.rb.insertUnsafe(i.rb.src, i.p, i.info) | ||||
| 	} | ||||
| 	i.rb.compose() | ||||
| 	seg := i.buf[:i.rb.flushCopy(i.buf[:])] | ||||
| 	return seg | ||||
| } | ||||
| 
 | ||||
| func nextCGJCompose(i *Iter) []byte { | ||||
| 	i.rb.ss = 0 // instead of first
 | ||||
| 	i.rb.insertCGJ() | ||||
| 	i.next = nextComposed | ||||
| 	// Note that we treat any rune with nLeadingNonStarters > 0 as a non-starter,
 | ||||
| 	// even if they are not. This is particularly dubious for U+FF9E and UFF9A.
 | ||||
| 	// If we ever change that, insert a check here.
 | ||||
| 	i.rb.ss.first(i.info) | ||||
| 	i.rb.insertUnsafe(i.rb.src, i.p, i.info) | ||||
| 	return doNormComposed(i) | ||||
| } | ||||
							
								
								
									
										986
									
								
								vendor/golang.org/x/text/unicode/norm/maketables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										986
									
								
								vendor/golang.org/x/text/unicode/norm/maketables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,986 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // +build ignore
 | ||||
| 
 | ||||
| // Normalization table generator.
 | ||||
| // Data read from the web.
 | ||||
| // See forminfo.go for a description of the trie values associated with each rune.
 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/binary" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"golang.org/x/text/internal/gen" | ||||
| 	"golang.org/x/text/internal/triegen" | ||||
| 	"golang.org/x/text/internal/ucd" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	gen.Init() | ||||
| 	loadUnicodeData() | ||||
| 	compactCCC() | ||||
| 	loadCompositionExclusions() | ||||
| 	completeCharFields(FCanonical) | ||||
| 	completeCharFields(FCompatibility) | ||||
| 	computeNonStarterCounts() | ||||
| 	verifyComputed() | ||||
| 	printChars() | ||||
| 	testDerived() | ||||
| 	printTestdata() | ||||
| 	makeTables() | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	tablelist = flag.String("tables", | ||||
| 		"all", | ||||
| 		"comma-separated list of which tables to generate; "+ | ||||
| 			"can be 'decomp', 'recomp', 'info' and 'all'") | ||||
| 	test = flag.Bool("test", | ||||
| 		false, | ||||
| 		"test existing tables against DerivedNormalizationProps and generate test data for regression testing") | ||||
| 	verbose = flag.Bool("verbose", | ||||
| 		false, | ||||
| 		"write data to stdout as it is parsed") | ||||
| ) | ||||
| 
 | ||||
| const MaxChar = 0x10FFFF // anything above this shouldn't exist
 | ||||
| 
 | ||||
| // Quick Check properties of runes allow us to quickly
 | ||||
| // determine whether a rune may occur in a normal form.
 | ||||
| // For a given normal form, a rune may be guaranteed to occur
 | ||||
| // verbatim (QC=Yes), may or may not combine with another
 | ||||
| // rune (QC=Maybe), or may not occur (QC=No).
 | ||||
| type QCResult int | ||||
| 
 | ||||
| const ( | ||||
| 	QCUnknown QCResult = iota | ||||
| 	QCYes | ||||
| 	QCNo | ||||
| 	QCMaybe | ||||
| ) | ||||
| 
 | ||||
| func (r QCResult) String() string { | ||||
| 	switch r { | ||||
| 	case QCYes: | ||||
| 		return "Yes" | ||||
| 	case QCNo: | ||||
| 		return "No" | ||||
| 	case QCMaybe: | ||||
| 		return "Maybe" | ||||
| 	} | ||||
| 	return "***UNKNOWN***" | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	FCanonical     = iota // NFC or NFD
 | ||||
| 	FCompatibility        // NFKC or NFKD
 | ||||
| 	FNumberOfFormTypes | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	MComposed   = iota // NFC or NFKC
 | ||||
| 	MDecomposed        // NFD or NFKD
 | ||||
| 	MNumberOfModes | ||||
| ) | ||||
| 
 | ||||
| // This contains only the properties we're interested in.
 | ||||
| type Char struct { | ||||
| 	name          string | ||||
| 	codePoint     rune  // if zero, this index is not a valid code point.
 | ||||
| 	ccc           uint8 // canonical combining class
 | ||||
| 	origCCC       uint8 | ||||
| 	excludeInComp bool // from CompositionExclusions.txt
 | ||||
| 	compatDecomp  bool // it has a compatibility expansion
 | ||||
| 
 | ||||
| 	nTrailingNonStarters uint8 | ||||
| 	nLeadingNonStarters  uint8 // must be equal to trailing if non-zero
 | ||||
| 
 | ||||
| 	forms [FNumberOfFormTypes]FormInfo // For FCanonical and FCompatibility
 | ||||
| 
 | ||||
| 	state State | ||||
| } | ||||
| 
 | ||||
| var chars = make([]Char, MaxChar+1) | ||||
| var cccMap = make(map[uint8]uint8) | ||||
| 
 | ||||
| func (c Char) String() string { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 
 | ||||
| 	fmt.Fprintf(buf, "%U [%s]:\n", c.codePoint, c.name) | ||||
| 	fmt.Fprintf(buf, "  ccc: %v\n", c.ccc) | ||||
| 	fmt.Fprintf(buf, "  excludeInComp: %v\n", c.excludeInComp) | ||||
| 	fmt.Fprintf(buf, "  compatDecomp: %v\n", c.compatDecomp) | ||||
| 	fmt.Fprintf(buf, "  state: %v\n", c.state) | ||||
| 	fmt.Fprintf(buf, "  NFC:\n") | ||||
| 	fmt.Fprint(buf, c.forms[FCanonical]) | ||||
| 	fmt.Fprintf(buf, "  NFKC:\n") | ||||
| 	fmt.Fprint(buf, c.forms[FCompatibility]) | ||||
| 
 | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| // In UnicodeData.txt, some ranges are marked like this:
 | ||||
| //	3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
 | ||||
| //	4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
 | ||||
| // parseCharacter keeps a state variable indicating the weirdness.
 | ||||
| type State int | ||||
| 
 | ||||
| const ( | ||||
| 	SNormal State = iota // known to be zero for the type
 | ||||
| 	SFirst | ||||
| 	SLast | ||||
| 	SMissing | ||||
| ) | ||||
| 
 | ||||
| var lastChar = rune('\u0000') | ||||
| 
 | ||||
| func (c Char) isValid() bool { | ||||
| 	return c.codePoint != 0 && c.state != SMissing | ||||
| } | ||||
| 
 | ||||
| type FormInfo struct { | ||||
| 	quickCheck [MNumberOfModes]QCResult // index: MComposed or MDecomposed
 | ||||
| 	verified   [MNumberOfModes]bool     // index: MComposed or MDecomposed
 | ||||
| 
 | ||||
| 	combinesForward  bool // May combine with rune on the right
 | ||||
| 	combinesBackward bool // May combine with rune on the left
 | ||||
| 	isOneWay         bool // Never appears in result
 | ||||
| 	inDecomp         bool // Some decompositions result in this char.
 | ||||
| 	decomp           Decomposition | ||||
| 	expandedDecomp   Decomposition | ||||
| } | ||||
| 
 | ||||
| func (f FormInfo) String() string { | ||||
| 	buf := bytes.NewBuffer(make([]byte, 0)) | ||||
| 
 | ||||
| 	fmt.Fprintf(buf, "    quickCheck[C]: %v\n", f.quickCheck[MComposed]) | ||||
| 	fmt.Fprintf(buf, "    quickCheck[D]: %v\n", f.quickCheck[MDecomposed]) | ||||
| 	fmt.Fprintf(buf, "    cmbForward: %v\n", f.combinesForward) | ||||
| 	fmt.Fprintf(buf, "    cmbBackward: %v\n", f.combinesBackward) | ||||
| 	fmt.Fprintf(buf, "    isOneWay: %v\n", f.isOneWay) | ||||
| 	fmt.Fprintf(buf, "    inDecomp: %v\n", f.inDecomp) | ||||
| 	fmt.Fprintf(buf, "    decomposition: %X\n", f.decomp) | ||||
| 	fmt.Fprintf(buf, "    expandedDecomp: %X\n", f.expandedDecomp) | ||||
| 
 | ||||
| 	return buf.String() | ||||
| } | ||||
| 
 | ||||
| type Decomposition []rune | ||||
| 
 | ||||
| func parseDecomposition(s string, skipfirst bool) (a []rune, err error) { | ||||
| 	decomp := strings.Split(s, " ") | ||||
| 	if len(decomp) > 0 && skipfirst { | ||||
| 		decomp = decomp[1:] | ||||
| 	} | ||||
| 	for _, d := range decomp { | ||||
| 		point, err := strconv.ParseUint(d, 16, 64) | ||||
| 		if err != nil { | ||||
| 			return a, err | ||||
| 		} | ||||
| 		a = append(a, rune(point)) | ||||
| 	} | ||||
| 	return a, nil | ||||
| } | ||||
| 
 | ||||
| func loadUnicodeData() { | ||||
| 	f := gen.OpenUCDFile("UnicodeData.txt") | ||||
| 	defer f.Close() | ||||
| 	p := ucd.New(f) | ||||
| 	for p.Next() { | ||||
| 		r := p.Rune(ucd.CodePoint) | ||||
| 		char := &chars[r] | ||||
| 
 | ||||
| 		char.ccc = uint8(p.Uint(ucd.CanonicalCombiningClass)) | ||||
| 		decmap := p.String(ucd.DecompMapping) | ||||
| 
 | ||||
| 		exp, err := parseDecomposition(decmap, false) | ||||
| 		isCompat := false | ||||
| 		if err != nil { | ||||
| 			if len(decmap) > 0 { | ||||
| 				exp, err = parseDecomposition(decmap, true) | ||||
| 				if err != nil { | ||||
| 					log.Fatalf(`%U: bad decomp |%v|: "%s"`, r, decmap, err) | ||||
| 				} | ||||
| 				isCompat = true | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		char.name = p.String(ucd.Name) | ||||
| 		char.codePoint = r | ||||
| 		char.forms[FCompatibility].decomp = exp | ||||
| 		if !isCompat { | ||||
| 			char.forms[FCanonical].decomp = exp | ||||
| 		} else { | ||||
| 			char.compatDecomp = true | ||||
| 		} | ||||
| 		if len(decmap) > 0 { | ||||
| 			char.forms[FCompatibility].decomp = exp | ||||
| 		} | ||||
| 	} | ||||
| 	if err := p.Err(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // compactCCC converts the sparse set of CCC values to a continguous one,
 | ||||
| // reducing the number of bits needed from 8 to 6.
 | ||||
| func compactCCC() { | ||||
| 	m := make(map[uint8]uint8) | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 		m[c.ccc] = 0 | ||||
| 	} | ||||
| 	cccs := []int{} | ||||
| 	for v, _ := range m { | ||||
| 		cccs = append(cccs, int(v)) | ||||
| 	} | ||||
| 	sort.Ints(cccs) | ||||
| 	for i, c := range cccs { | ||||
| 		cccMap[uint8(i)] = uint8(c) | ||||
| 		m[uint8(c)] = uint8(i) | ||||
| 	} | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 		c.origCCC = c.ccc | ||||
| 		c.ccc = m[c.ccc] | ||||
| 	} | ||||
| 	if len(m) >= 1<<6 { | ||||
| 		log.Fatalf("too many difference CCC values: %d >= 64", len(m)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // CompositionExclusions.txt has form:
 | ||||
| // 0958    # ...
 | ||||
| // See https://unicode.org/reports/tr44/ for full explanation
 | ||||
| func loadCompositionExclusions() { | ||||
| 	f := gen.OpenUCDFile("CompositionExclusions.txt") | ||||
| 	defer f.Close() | ||||
| 	p := ucd.New(f) | ||||
| 	for p.Next() { | ||||
| 		c := &chars[p.Rune(0)] | ||||
| 		if c.excludeInComp { | ||||
| 			log.Fatalf("%U: Duplicate entry in exclusions.", c.codePoint) | ||||
| 		} | ||||
| 		c.excludeInComp = true | ||||
| 	} | ||||
| 	if e := p.Err(); e != nil { | ||||
| 		log.Fatal(e) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // hasCompatDecomp returns true if any of the recursive
 | ||||
| // decompositions contains a compatibility expansion.
 | ||||
| // In this case, the character may not occur in NFK*.
 | ||||
| func hasCompatDecomp(r rune) bool { | ||||
| 	c := &chars[r] | ||||
| 	if c.compatDecomp { | ||||
| 		return true | ||||
| 	} | ||||
| 	for _, d := range c.forms[FCompatibility].decomp { | ||||
| 		if hasCompatDecomp(d) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // Hangul related constants.
 | ||||
| const ( | ||||
| 	HangulBase = 0xAC00 | ||||
| 	HangulEnd  = 0xD7A4 // hangulBase + Jamo combinations (19 * 21 * 28)
 | ||||
| 
 | ||||
| 	JamoLBase = 0x1100 | ||||
| 	JamoLEnd  = 0x1113 | ||||
| 	JamoVBase = 0x1161 | ||||
| 	JamoVEnd  = 0x1176 | ||||
| 	JamoTBase = 0x11A8 | ||||
| 	JamoTEnd  = 0x11C3 | ||||
| 
 | ||||
| 	JamoLVTCount = 19 * 21 * 28 | ||||
| 	JamoTCount   = 28 | ||||
| ) | ||||
| 
 | ||||
| func isHangul(r rune) bool { | ||||
| 	return HangulBase <= r && r < HangulEnd | ||||
| } | ||||
| 
 | ||||
| func isHangulWithoutJamoT(r rune) bool { | ||||
| 	if !isHangul(r) { | ||||
| 		return false | ||||
| 	} | ||||
| 	r -= HangulBase | ||||
| 	return r < JamoLVTCount && r%JamoTCount == 0 | ||||
| } | ||||
| 
 | ||||
| func ccc(r rune) uint8 { | ||||
| 	return chars[r].ccc | ||||
| } | ||||
| 
 | ||||
| // Insert a rune in a buffer, ordered by Canonical Combining Class.
 | ||||
| func insertOrdered(b Decomposition, r rune) Decomposition { | ||||
| 	n := len(b) | ||||
| 	b = append(b, 0) | ||||
| 	cc := ccc(r) | ||||
| 	if cc > 0 { | ||||
| 		// Use bubble sort.
 | ||||
| 		for ; n > 0; n-- { | ||||
| 			if ccc(b[n-1]) <= cc { | ||||
| 				break | ||||
| 			} | ||||
| 			b[n] = b[n-1] | ||||
| 		} | ||||
| 	} | ||||
| 	b[n] = r | ||||
| 	return b | ||||
| } | ||||
| 
 | ||||
| // Recursively decompose.
 | ||||
| func decomposeRecursive(form int, r rune, d Decomposition) Decomposition { | ||||
| 	dcomp := chars[r].forms[form].decomp | ||||
| 	if len(dcomp) == 0 { | ||||
| 		return insertOrdered(d, r) | ||||
| 	} | ||||
| 	for _, c := range dcomp { | ||||
| 		d = decomposeRecursive(form, c, d) | ||||
| 	} | ||||
| 	return d | ||||
| } | ||||
| 
 | ||||
| func completeCharFields(form int) { | ||||
| 	// Phase 0: pre-expand decomposition.
 | ||||
| 	for i := range chars { | ||||
| 		f := &chars[i].forms[form] | ||||
| 		if len(f.decomp) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		exp := make(Decomposition, 0) | ||||
| 		for _, c := range f.decomp { | ||||
| 			exp = decomposeRecursive(form, c, exp) | ||||
| 		} | ||||
| 		f.expandedDecomp = exp | ||||
| 	} | ||||
| 
 | ||||
| 	// Phase 1: composition exclusion, mark decomposition.
 | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 		f := &c.forms[form] | ||||
| 
 | ||||
| 		// Marks script-specific exclusions and version restricted.
 | ||||
| 		f.isOneWay = c.excludeInComp | ||||
| 
 | ||||
| 		// Singletons
 | ||||
| 		f.isOneWay = f.isOneWay || len(f.decomp) == 1 | ||||
| 
 | ||||
| 		// Non-starter decompositions
 | ||||
| 		if len(f.decomp) > 1 { | ||||
| 			chk := c.ccc != 0 || chars[f.decomp[0]].ccc != 0 | ||||
| 			f.isOneWay = f.isOneWay || chk | ||||
| 		} | ||||
| 
 | ||||
| 		// Runes that decompose into more than two runes.
 | ||||
| 		f.isOneWay = f.isOneWay || len(f.decomp) > 2 | ||||
| 
 | ||||
| 		if form == FCompatibility { | ||||
| 			f.isOneWay = f.isOneWay || hasCompatDecomp(c.codePoint) | ||||
| 		} | ||||
| 
 | ||||
| 		for _, r := range f.decomp { | ||||
| 			chars[r].forms[form].inDecomp = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Phase 2: forward and backward combining.
 | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 		f := &c.forms[form] | ||||
| 
 | ||||
| 		if !f.isOneWay && len(f.decomp) == 2 { | ||||
| 			f0 := &chars[f.decomp[0]].forms[form] | ||||
| 			f1 := &chars[f.decomp[1]].forms[form] | ||||
| 			if !f0.isOneWay { | ||||
| 				f0.combinesForward = true | ||||
| 			} | ||||
| 			if !f1.isOneWay { | ||||
| 				f1.combinesBackward = true | ||||
| 			} | ||||
| 		} | ||||
| 		if isHangulWithoutJamoT(rune(i)) { | ||||
| 			f.combinesForward = true | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Phase 3: quick check values.
 | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 		f := &c.forms[form] | ||||
| 
 | ||||
| 		switch { | ||||
| 		case len(f.decomp) > 0: | ||||
| 			f.quickCheck[MDecomposed] = QCNo | ||||
| 		case isHangul(rune(i)): | ||||
| 			f.quickCheck[MDecomposed] = QCNo | ||||
| 		default: | ||||
| 			f.quickCheck[MDecomposed] = QCYes | ||||
| 		} | ||||
| 		switch { | ||||
| 		case f.isOneWay: | ||||
| 			f.quickCheck[MComposed] = QCNo | ||||
| 		case (i & 0xffff00) == JamoLBase: | ||||
| 			f.quickCheck[MComposed] = QCYes | ||||
| 			if JamoLBase <= i && i < JamoLEnd { | ||||
| 				f.combinesForward = true | ||||
| 			} | ||||
| 			if JamoVBase <= i && i < JamoVEnd { | ||||
| 				f.quickCheck[MComposed] = QCMaybe | ||||
| 				f.combinesBackward = true | ||||
| 				f.combinesForward = true | ||||
| 			} | ||||
| 			if JamoTBase <= i && i < JamoTEnd { | ||||
| 				f.quickCheck[MComposed] = QCMaybe | ||||
| 				f.combinesBackward = true | ||||
| 			} | ||||
| 		case !f.combinesBackward: | ||||
| 			f.quickCheck[MComposed] = QCYes | ||||
| 		default: | ||||
| 			f.quickCheck[MComposed] = QCMaybe | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func computeNonStarterCounts() { | ||||
| 	// Phase 4: leading and trailing non-starter count
 | ||||
| 	for i := range chars { | ||||
| 		c := &chars[i] | ||||
| 
 | ||||
| 		runes := []rune{rune(i)} | ||||
| 		// We always use FCompatibility so that the CGJ insertion points do not
 | ||||
| 		// change for repeated normalizations with different forms.
 | ||||
| 		if exp := c.forms[FCompatibility].expandedDecomp; len(exp) > 0 { | ||||
| 			runes = exp | ||||
| 		} | ||||
| 		// We consider runes that combine backwards to be non-starters for the
 | ||||
| 		// purpose of Stream-Safe Text Processing.
 | ||||
| 		for _, r := range runes { | ||||
| 			if cr := &chars[r]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { | ||||
| 				break | ||||
| 			} | ||||
| 			c.nLeadingNonStarters++ | ||||
| 		} | ||||
| 		for i := len(runes) - 1; i >= 0; i-- { | ||||
| 			if cr := &chars[runes[i]]; cr.ccc == 0 && !cr.forms[FCompatibility].combinesBackward { | ||||
| 				break | ||||
| 			} | ||||
| 			c.nTrailingNonStarters++ | ||||
| 		} | ||||
| 		if c.nTrailingNonStarters > 3 { | ||||
| 			log.Fatalf("%U: Decomposition with more than 3 (%d) trailing modifiers (%U)", i, c.nTrailingNonStarters, runes) | ||||
| 		} | ||||
| 
 | ||||
| 		if isHangul(rune(i)) { | ||||
| 			c.nTrailingNonStarters = 2 | ||||
| 			if isHangulWithoutJamoT(rune(i)) { | ||||
| 				c.nTrailingNonStarters = 1 | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if l, t := c.nLeadingNonStarters, c.nTrailingNonStarters; l > 0 && l != t { | ||||
| 			log.Fatalf("%U: number of leading and trailing non-starters should be equal (%d vs %d)", i, l, t) | ||||
| 		} | ||||
| 		if t := c.nTrailingNonStarters; t > 3 { | ||||
| 			log.Fatalf("%U: number of trailing non-starters is %d > 3", t) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func printBytes(w io.Writer, b []byte, name string) { | ||||
| 	fmt.Fprintf(w, "// %s: %d bytes\n", name, len(b)) | ||||
| 	fmt.Fprintf(w, "var %s = [...]byte {", name) | ||||
| 	for i, c := range b { | ||||
| 		switch { | ||||
| 		case i%64 == 0: | ||||
| 			fmt.Fprintf(w, "\n// Bytes %x - %x\n", i, i+63) | ||||
| 		case i%8 == 0: | ||||
| 			fmt.Fprintf(w, "\n") | ||||
| 		} | ||||
| 		fmt.Fprintf(w, "0x%.2X, ", c) | ||||
| 	} | ||||
| 	fmt.Fprint(w, "\n}\n\n") | ||||
| } | ||||
| 
 | ||||
| // See forminfo.go for format.
 | ||||
| func makeEntry(f *FormInfo, c *Char) uint16 { | ||||
| 	e := uint16(0) | ||||
| 	if r := c.codePoint; HangulBase <= r && r < HangulEnd { | ||||
| 		e |= 0x40 | ||||
| 	} | ||||
| 	if f.combinesForward { | ||||
| 		e |= 0x20 | ||||
| 	} | ||||
| 	if f.quickCheck[MDecomposed] == QCNo { | ||||
| 		e |= 0x4 | ||||
| 	} | ||||
| 	switch f.quickCheck[MComposed] { | ||||
| 	case QCYes: | ||||
| 	case QCNo: | ||||
| 		e |= 0x10 | ||||
| 	case QCMaybe: | ||||
| 		e |= 0x18 | ||||
| 	default: | ||||
| 		log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed]) | ||||
| 	} | ||||
| 	e |= uint16(c.nTrailingNonStarters) | ||||
| 	return e | ||||
| } | ||||
| 
 | ||||
| // decompSet keeps track of unique decompositions, grouped by whether
 | ||||
| // the decomposition is followed by a trailing and/or leading CCC.
 | ||||
| type decompSet [7]map[string]bool | ||||
| 
 | ||||
| const ( | ||||
| 	normalDecomp = iota | ||||
| 	firstMulti | ||||
| 	firstCCC | ||||
| 	endMulti | ||||
| 	firstLeadingCCC | ||||
| 	firstCCCZeroExcept | ||||
| 	firstStarterWithNLead | ||||
| 	lastDecomp | ||||
| ) | ||||
| 
 | ||||
| var cname = []string{"firstMulti", "firstCCC", "endMulti", "firstLeadingCCC", "firstCCCZeroExcept", "firstStarterWithNLead", "lastDecomp"} | ||||
| 
 | ||||
| func makeDecompSet() decompSet { | ||||
| 	m := decompSet{} | ||||
| 	for i := range m { | ||||
| 		m[i] = make(map[string]bool) | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
| func (m *decompSet) insert(key int, s string) { | ||||
| 	m[key][s] = true | ||||
| } | ||||
| 
 | ||||
| func printCharInfoTables(w io.Writer) int { | ||||
| 	mkstr := func(r rune, f *FormInfo) (int, string) { | ||||
| 		d := f.expandedDecomp | ||||
| 		s := string([]rune(d)) | ||||
| 		if max := 1 << 6; len(s) >= max { | ||||
| 			const msg = "%U: too many bytes in decomposition: %d >= %d" | ||||
| 			log.Fatalf(msg, r, len(s), max) | ||||
| 		} | ||||
| 		head := uint8(len(s)) | ||||
| 		if f.quickCheck[MComposed] != QCYes { | ||||
| 			head |= 0x40 | ||||
| 		} | ||||
| 		if f.combinesForward { | ||||
| 			head |= 0x80 | ||||
| 		} | ||||
| 		s = string([]byte{head}) + s | ||||
| 
 | ||||
| 		lccc := ccc(d[0]) | ||||
| 		tccc := ccc(d[len(d)-1]) | ||||
| 		cc := ccc(r) | ||||
| 		if cc != 0 && lccc == 0 && tccc == 0 { | ||||
| 			log.Fatalf("%U: trailing and leading ccc are 0 for non-zero ccc %d", r, cc) | ||||
| 		} | ||||
| 		if tccc < lccc && lccc != 0 { | ||||
| 			const msg = "%U: lccc (%d) must be <= tcc (%d)" | ||||
| 			log.Fatalf(msg, r, lccc, tccc) | ||||
| 		} | ||||
| 		index := normalDecomp | ||||
| 		nTrail := chars[r].nTrailingNonStarters | ||||
| 		nLead := chars[r].nLeadingNonStarters | ||||
| 		if tccc > 0 || lccc > 0 || nTrail > 0 { | ||||
| 			tccc <<= 2 | ||||
| 			tccc |= nTrail | ||||
| 			s += string([]byte{tccc}) | ||||
| 			index = endMulti | ||||
| 			for _, r := range d[1:] { | ||||
| 				if ccc(r) == 0 { | ||||
| 					index = firstCCC | ||||
| 				} | ||||
| 			} | ||||
| 			if lccc > 0 || nLead > 0 { | ||||
| 				s += string([]byte{lccc}) | ||||
| 				if index == firstCCC { | ||||
| 					log.Fatalf("%U: multi-segment decomposition not supported for decompositions with leading CCC != 0", r) | ||||
| 				} | ||||
| 				index = firstLeadingCCC | ||||
| 			} | ||||
| 			if cc != lccc { | ||||
| 				if cc != 0 { | ||||
| 					log.Fatalf("%U: for lccc != ccc, expected ccc to be 0; was %d", r, cc) | ||||
| 				} | ||||
| 				index = firstCCCZeroExcept | ||||
| 			} | ||||
| 		} else if len(d) > 1 { | ||||
| 			index = firstMulti | ||||
| 		} | ||||
| 		return index, s | ||||
| 	} | ||||
| 
 | ||||
| 	decompSet := makeDecompSet() | ||||
| 	const nLeadStr = "\x00\x01" // 0-byte length and tccc with nTrail.
 | ||||
| 	decompSet.insert(firstStarterWithNLead, nLeadStr) | ||||
| 
 | ||||
| 	// Store the uniqued decompositions in a byte buffer,
 | ||||
| 	// preceded by their byte length.
 | ||||
| 	for _, c := range chars { | ||||
| 		for _, f := range c.forms { | ||||
| 			if len(f.expandedDecomp) == 0 { | ||||
| 				continue | ||||
| 			} | ||||
| 			if f.combinesBackward { | ||||
| 				log.Fatalf("%U: combinesBackward and decompose", c.codePoint) | ||||
| 			} | ||||
| 			index, s := mkstr(c.codePoint, &f) | ||||
| 			decompSet.insert(index, s) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	decompositions := bytes.NewBuffer(make([]byte, 0, 10000)) | ||||
| 	size := 0 | ||||
| 	positionMap := make(map[string]uint16) | ||||
| 	decompositions.WriteString("\000") | ||||
| 	fmt.Fprintln(w, "const (") | ||||
| 	for i, m := range decompSet { | ||||
| 		sa := []string{} | ||||
| 		for s := range m { | ||||
| 			sa = append(sa, s) | ||||
| 		} | ||||
| 		sort.Strings(sa) | ||||
| 		for _, s := range sa { | ||||
| 			p := decompositions.Len() | ||||
| 			decompositions.WriteString(s) | ||||
| 			positionMap[s] = uint16(p) | ||||
| 		} | ||||
| 		if cname[i] != "" { | ||||
| 			fmt.Fprintf(w, "%s = 0x%X\n", cname[i], decompositions.Len()) | ||||
| 		} | ||||
| 	} | ||||
| 	fmt.Fprintln(w, "maxDecomp = 0x8000") | ||||
| 	fmt.Fprintln(w, ")") | ||||
| 	b := decompositions.Bytes() | ||||
| 	printBytes(w, b, "decomps") | ||||
| 	size += len(b) | ||||
| 
 | ||||
| 	varnames := []string{"nfc", "nfkc"} | ||||
| 	for i := 0; i < FNumberOfFormTypes; i++ { | ||||
| 		trie := triegen.NewTrie(varnames[i]) | ||||
| 
 | ||||
| 		for r, c := range chars { | ||||
| 			f := c.forms[i] | ||||
| 			d := f.expandedDecomp | ||||
| 			if len(d) != 0 { | ||||
| 				_, key := mkstr(c.codePoint, &f) | ||||
| 				trie.Insert(rune(r), uint64(positionMap[key])) | ||||
| 				if c.ccc != ccc(d[0]) { | ||||
| 					// We assume the lead ccc of a decomposition !=0 in this case.
 | ||||
| 					if ccc(d[0]) == 0 { | ||||
| 						log.Fatalf("Expected leading CCC to be non-zero; ccc is %d", c.ccc) | ||||
| 					} | ||||
| 				} | ||||
| 			} else if c.nLeadingNonStarters > 0 && len(f.expandedDecomp) == 0 && c.ccc == 0 && !f.combinesBackward { | ||||
| 				// Handle cases where it can't be detected that the nLead should be equal
 | ||||
| 				// to nTrail.
 | ||||
| 				trie.Insert(c.codePoint, uint64(positionMap[nLeadStr])) | ||||
| 			} else if v := makeEntry(&f, &c)<<8 | uint16(c.ccc); v != 0 { | ||||
| 				trie.Insert(c.codePoint, uint64(0x8000|v)) | ||||
| 			} | ||||
| 		} | ||||
| 		sz, err := trie.Gen(w, triegen.Compact(&normCompacter{name: varnames[i]})) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		size += sz | ||||
| 	} | ||||
| 	return size | ||||
| } | ||||
| 
 | ||||
| func contains(sa []string, s string) bool { | ||||
| 	for _, a := range sa { | ||||
| 		if a == s { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func makeTables() { | ||||
| 	w := &bytes.Buffer{} | ||||
| 
 | ||||
| 	size := 0 | ||||
| 	if *tablelist == "" { | ||||
| 		return | ||||
| 	} | ||||
| 	list := strings.Split(*tablelist, ",") | ||||
| 	if *tablelist == "all" { | ||||
| 		list = []string{"recomp", "info"} | ||||
| 	} | ||||
| 
 | ||||
| 	// Compute maximum decomposition size.
 | ||||
| 	max := 0 | ||||
| 	for _, c := range chars { | ||||
| 		if n := len(string(c.forms[FCompatibility].expandedDecomp)); n > max { | ||||
| 			max = n | ||||
| 		} | ||||
| 	} | ||||
| 	fmt.Fprintln(w, `import "sync"`) | ||||
| 	fmt.Fprintln(w) | ||||
| 
 | ||||
| 	fmt.Fprintln(w, "const (") | ||||
| 	fmt.Fprintln(w, "\t// Version is the Unicode edition from which the tables are derived.") | ||||
| 	fmt.Fprintf(w, "\tVersion = %q\n", gen.UnicodeVersion()) | ||||
| 	fmt.Fprintln(w) | ||||
| 	fmt.Fprintln(w, "\t// MaxTransformChunkSize indicates the maximum number of bytes that Transform") | ||||
| 	fmt.Fprintln(w, "\t// may need to write atomically for any Form. Making a destination buffer at") | ||||
| 	fmt.Fprintln(w, "\t// least this size ensures that Transform can always make progress and that") | ||||
| 	fmt.Fprintln(w, "\t// the user does not need to grow the buffer on an ErrShortDst.") | ||||
| 	fmt.Fprintf(w, "\tMaxTransformChunkSize = %d+maxNonStarters*4\n", len(string(0x034F))+max) | ||||
| 	fmt.Fprintln(w, ")\n") | ||||
| 
 | ||||
| 	// Print the CCC remap table.
 | ||||
| 	size += len(cccMap) | ||||
| 	fmt.Fprintf(w, "var ccc = [%d]uint8{", len(cccMap)) | ||||
| 	for i := 0; i < len(cccMap); i++ { | ||||
| 		if i%8 == 0 { | ||||
| 			fmt.Fprintln(w) | ||||
| 		} | ||||
| 		fmt.Fprintf(w, "%3d, ", cccMap[uint8(i)]) | ||||
| 	} | ||||
| 	fmt.Fprintln(w, "\n}\n") | ||||
| 
 | ||||
| 	if contains(list, "info") { | ||||
| 		size += printCharInfoTables(w) | ||||
| 	} | ||||
| 
 | ||||
| 	if contains(list, "recomp") { | ||||
| 		// Note that we use 32 bit keys, instead of 64 bit.
 | ||||
| 		// This clips the bits of three entries, but we know
 | ||||
| 		// this won't cause a collision. The compiler will catch
 | ||||
| 		// any changes made to UnicodeData.txt that introduces
 | ||||
| 		// a collision.
 | ||||
| 		// Note that the recomposition map for NFC and NFKC
 | ||||
| 		// are identical.
 | ||||
| 
 | ||||
| 		// Recomposition map
 | ||||
| 		nrentries := 0 | ||||
| 		for _, c := range chars { | ||||
| 			f := c.forms[FCanonical] | ||||
| 			if !f.isOneWay && len(f.decomp) > 0 { | ||||
| 				nrentries++ | ||||
| 			} | ||||
| 		} | ||||
| 		sz := nrentries * 8 | ||||
| 		size += sz | ||||
| 		fmt.Fprintf(w, "// recompMap: %d bytes (entries only)\n", sz) | ||||
| 		fmt.Fprintln(w, "var recompMap map[uint32]rune") | ||||
| 		fmt.Fprintln(w, "var recompMapOnce sync.Once\n") | ||||
| 		fmt.Fprintln(w, `const recompMapPacked = "" +`) | ||||
| 		var buf [8]byte | ||||
| 		for i, c := range chars { | ||||
| 			f := c.forms[FCanonical] | ||||
| 			d := f.decomp | ||||
| 			if !f.isOneWay && len(d) > 0 { | ||||
| 				key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1])) | ||||
| 				binary.BigEndian.PutUint32(buf[:4], key) | ||||
| 				binary.BigEndian.PutUint32(buf[4:], uint32(i)) | ||||
| 				fmt.Fprintf(w, "\t\t%q + // 0x%.8X: 0x%.8X\n", string(buf[:]), key, uint32(i)) | ||||
| 			} | ||||
| 		} | ||||
| 		// hack so we don't have to special case the trailing plus sign
 | ||||
| 		fmt.Fprintf(w, `	""`) | ||||
| 		fmt.Fprintln(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fmt.Fprintf(w, "// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size) | ||||
| 	gen.WriteVersionedGoFile("tables.go", "norm", w.Bytes()) | ||||
| } | ||||
| 
 | ||||
| func printChars() { | ||||
| 	if *verbose { | ||||
| 		for _, c := range chars { | ||||
| 			if !c.isValid() || c.state == SMissing { | ||||
| 				continue | ||||
| 			} | ||||
| 			fmt.Println(c) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // verifyComputed does various consistency tests.
 | ||||
| func verifyComputed() { | ||||
| 	for i, c := range chars { | ||||
| 		for _, f := range c.forms { | ||||
| 			isNo := (f.quickCheck[MDecomposed] == QCNo) | ||||
| 			if (len(f.decomp) > 0) != isNo && !isHangul(rune(i)) { | ||||
| 				log.Fatalf("%U: NF*D QC must be No if rune decomposes", i) | ||||
| 			} | ||||
| 
 | ||||
| 			isMaybe := f.quickCheck[MComposed] == QCMaybe | ||||
| 			if f.combinesBackward != isMaybe { | ||||
| 				log.Fatalf("%U: NF*C QC must be Maybe if combinesBackward", i) | ||||
| 			} | ||||
| 			if len(f.decomp) > 0 && f.combinesForward && isMaybe { | ||||
| 				log.Fatalf("%U: NF*C QC must be Yes or No if combinesForward and decomposes", i) | ||||
| 			} | ||||
| 
 | ||||
| 			if len(f.expandedDecomp) != 0 { | ||||
| 				continue | ||||
| 			} | ||||
| 			if a, b := c.nLeadingNonStarters > 0, (c.ccc > 0 || f.combinesBackward); a != b { | ||||
| 				// We accept these runes to be treated differently (it only affects
 | ||||
| 				// segment breaking in iteration, most likely on improper use), but
 | ||||
| 				// reconsider if more characters are added.
 | ||||
| 				// U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;;;;
 | ||||
| 				// U+FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;;;;
 | ||||
| 				// U+3133 HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
 | ||||
| 				// U+318E HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
 | ||||
| 				// U+FFA3 HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
 | ||||
| 				// U+FFDC HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
 | ||||
| 				if i != 0xFF9E && i != 0xFF9F && !(0x3133 <= i && i <= 0x318E) && !(0xFFA3 <= i && i <= 0xFFDC) { | ||||
| 					log.Fatalf("%U: nLead was %v; want %v", i, a, b) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		nfc := c.forms[FCanonical] | ||||
| 		nfkc := c.forms[FCompatibility] | ||||
| 		if nfc.combinesBackward != nfkc.combinesBackward { | ||||
| 			log.Fatalf("%U: Cannot combine combinesBackward\n", c.codePoint) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Use values in DerivedNormalizationProps.txt to compare against the
 | ||||
| // values we computed.
 | ||||
| // DerivedNormalizationProps.txt has form:
 | ||||
| // 00C0..00C5    ; NFD_QC; N # ...
 | ||||
| // 0374          ; NFD_QC; N # ...
 | ||||
| // See https://unicode.org/reports/tr44/ for full explanation
 | ||||
| func testDerived() { | ||||
| 	f := gen.OpenUCDFile("DerivedNormalizationProps.txt") | ||||
| 	defer f.Close() | ||||
| 	p := ucd.New(f) | ||||
| 	for p.Next() { | ||||
| 		r := p.Rune(0) | ||||
| 		c := &chars[r] | ||||
| 
 | ||||
| 		var ftype, mode int | ||||
| 		qt := p.String(1) | ||||
| 		switch qt { | ||||
| 		case "NFC_QC": | ||||
| 			ftype, mode = FCanonical, MComposed | ||||
| 		case "NFD_QC": | ||||
| 			ftype, mode = FCanonical, MDecomposed | ||||
| 		case "NFKC_QC": | ||||
| 			ftype, mode = FCompatibility, MComposed | ||||
| 		case "NFKD_QC": | ||||
| 			ftype, mode = FCompatibility, MDecomposed | ||||
| 		default: | ||||
| 			continue | ||||
| 		} | ||||
| 		var qr QCResult | ||||
| 		switch p.String(2) { | ||||
| 		case "Y": | ||||
| 			qr = QCYes | ||||
| 		case "N": | ||||
| 			qr = QCNo | ||||
| 		case "M": | ||||
| 			qr = QCMaybe | ||||
| 		default: | ||||
| 			log.Fatalf(`Unexpected quick check value "%s"`, p.String(2)) | ||||
| 		} | ||||
| 		if got := c.forms[ftype].quickCheck[mode]; got != qr { | ||||
| 			log.Printf("%U: FAILED %s (was %v need %v)\n", r, qt, got, qr) | ||||
| 		} | ||||
| 		c.forms[ftype].verified[mode] = true | ||||
| 	} | ||||
| 	if err := p.Err(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	// Any unspecified value must be QCYes. Verify this.
 | ||||
| 	for i, c := range chars { | ||||
| 		for j, fd := range c.forms { | ||||
| 			for k, qr := range fd.quickCheck { | ||||
| 				if !fd.verified[k] && qr != QCYes { | ||||
| 					m := "%U: FAIL F:%d M:%d (was %v need Yes) %s\n" | ||||
| 					log.Printf(m, i, j, k, qr, c.name) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var testHeader = `const ( | ||||
| 	Yes = iota | ||||
| 	No | ||||
| 	Maybe | ||||
| ) | ||||
| 
 | ||||
| type formData struct { | ||||
| 	qc              uint8 | ||||
| 	combinesForward bool | ||||
| 	decomposition   string | ||||
| } | ||||
| 
 | ||||
| type runeData struct { | ||||
| 	r      rune | ||||
| 	ccc    uint8 | ||||
| 	nLead  uint8 | ||||
| 	nTrail uint8 | ||||
| 	f      [2]formData // 0: canonical; 1: compatibility
 | ||||
| } | ||||
| 
 | ||||
| func f(qc uint8, cf bool, dec string) [2]formData { | ||||
| 	return [2]formData{{qc, cf, dec}, {qc, cf, dec}} | ||||
| } | ||||
| 
 | ||||
| func g(qc, qck uint8, cf, cfk bool, d, dk string) [2]formData { | ||||
| 	return [2]formData{{qc, cf, d}, {qck, cfk, dk}} | ||||
| } | ||||
| 
 | ||||
| var testData = []runeData{ | ||||
| ` | ||||
| 
 | ||||
| func printTestdata() { | ||||
| 	type lastInfo struct { | ||||
| 		ccc    uint8 | ||||
| 		nLead  uint8 | ||||
| 		nTrail uint8 | ||||
| 		f      string | ||||
| 	} | ||||
| 
 | ||||
| 	last := lastInfo{} | ||||
| 	w := &bytes.Buffer{} | ||||
| 	fmt.Fprintf(w, testHeader) | ||||
| 	for r, c := range chars { | ||||
| 		f := c.forms[FCanonical] | ||||
| 		qc, cf, d := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) | ||||
| 		f = c.forms[FCompatibility] | ||||
| 		qck, cfk, dk := f.quickCheck[MComposed], f.combinesForward, string(f.expandedDecomp) | ||||
| 		s := "" | ||||
| 		if d == dk && qc == qck && cf == cfk { | ||||
| 			s = fmt.Sprintf("f(%s, %v, %q)", qc, cf, d) | ||||
| 		} else { | ||||
| 			s = fmt.Sprintf("g(%s, %s, %v, %v, %q, %q)", qc, qck, cf, cfk, d, dk) | ||||
| 		} | ||||
| 		current := lastInfo{c.ccc, c.nLeadingNonStarters, c.nTrailingNonStarters, s} | ||||
| 		if last != current { | ||||
| 			fmt.Fprintf(w, "\t{0x%x, %d, %d, %d, %s},\n", r, c.origCCC, c.nLeadingNonStarters, c.nTrailingNonStarters, s) | ||||
| 			last = current | ||||
| 		} | ||||
| 	} | ||||
| 	fmt.Fprintln(w, "}") | ||||
| 	gen.WriteVersionedGoFile("data_test.go", "norm", w.Bytes()) | ||||
| } | ||||
							
								
								
									
										609
									
								
								vendor/golang.org/x/text/unicode/norm/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										609
									
								
								vendor/golang.org/x/text/unicode/norm/normalize.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,609 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // Note: the file data_test.go that is generated should not be checked in.
 | ||||
| //go:generate go run maketables.go triegen.go
 | ||||
| //go:generate go test -tags test
 | ||||
| 
 | ||||
| // Package norm contains types and functions for normalizing Unicode strings.
 | ||||
| package norm // import "golang.org/x/text/unicode/norm"
 | ||||
| 
 | ||||
| import ( | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"golang.org/x/text/transform" | ||||
| ) | ||||
| 
 | ||||
| // A Form denotes a canonical representation of Unicode code points.
 | ||||
| // The Unicode-defined normalization and equivalence forms are:
 | ||||
| //
 | ||||
| //   NFC   Unicode Normalization Form C
 | ||||
| //   NFD   Unicode Normalization Form D
 | ||||
| //   NFKC  Unicode Normalization Form KC
 | ||||
| //   NFKD  Unicode Normalization Form KD
 | ||||
| //
 | ||||
| // For a Form f, this documentation uses the notation f(x) to mean
 | ||||
| // the bytes or string x converted to the given form.
 | ||||
| // A position n in x is called a boundary if conversion to the form can
 | ||||
| // proceed independently on both sides:
 | ||||
| //   f(x) == append(f(x[0:n]), f(x[n:])...)
 | ||||
| //
 | ||||
| // References: https://unicode.org/reports/tr15/ and
 | ||||
| // https://unicode.org/notes/tn5/.
 | ||||
| type Form int | ||||
| 
 | ||||
| const ( | ||||
| 	NFC Form = iota | ||||
| 	NFD | ||||
| 	NFKC | ||||
| 	NFKD | ||||
| ) | ||||
| 
 | ||||
| // Bytes returns f(b). May return b if f(b) = b.
 | ||||
| func (f Form) Bytes(b []byte) []byte { | ||||
| 	src := inputBytes(b) | ||||
| 	ft := formTable[f] | ||||
| 	n, ok := ft.quickSpan(src, 0, len(b), true) | ||||
| 	if ok { | ||||
| 		return b | ||||
| 	} | ||||
| 	out := make([]byte, n, len(b)) | ||||
| 	copy(out, b[0:n]) | ||||
| 	rb := reorderBuffer{f: *ft, src: src, nsrc: len(b), out: out, flushF: appendFlush} | ||||
| 	return doAppendInner(&rb, n) | ||||
| } | ||||
| 
 | ||||
| // String returns f(s).
 | ||||
| func (f Form) String(s string) string { | ||||
| 	src := inputString(s) | ||||
| 	ft := formTable[f] | ||||
| 	n, ok := ft.quickSpan(src, 0, len(s), true) | ||||
| 	if ok { | ||||
| 		return s | ||||
| 	} | ||||
| 	out := make([]byte, n, len(s)) | ||||
| 	copy(out, s[0:n]) | ||||
| 	rb := reorderBuffer{f: *ft, src: src, nsrc: len(s), out: out, flushF: appendFlush} | ||||
| 	return string(doAppendInner(&rb, n)) | ||||
| } | ||||
| 
 | ||||
| // IsNormal returns true if b == f(b).
 | ||||
| func (f Form) IsNormal(b []byte) bool { | ||||
| 	src := inputBytes(b) | ||||
| 	ft := formTable[f] | ||||
| 	bp, ok := ft.quickSpan(src, 0, len(b), true) | ||||
| 	if ok { | ||||
| 		return true | ||||
| 	} | ||||
| 	rb := reorderBuffer{f: *ft, src: src, nsrc: len(b)} | ||||
| 	rb.setFlusher(nil, cmpNormalBytes) | ||||
| 	for bp < len(b) { | ||||
| 		rb.out = b[bp:] | ||||
| 		if bp = decomposeSegment(&rb, bp, true); bp < 0 { | ||||
| 			return false | ||||
| 		} | ||||
| 		bp, _ = rb.f.quickSpan(rb.src, bp, len(b), true) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func cmpNormalBytes(rb *reorderBuffer) bool { | ||||
| 	b := rb.out | ||||
| 	for i := 0; i < rb.nrune; i++ { | ||||
| 		info := rb.rune[i] | ||||
| 		if int(info.size) > len(b) { | ||||
| 			return false | ||||
| 		} | ||||
| 		p := info.pos | ||||
| 		pe := p + info.size | ||||
| 		for ; p < pe; p++ { | ||||
| 			if b[0] != rb.byte[p] { | ||||
| 				return false | ||||
| 			} | ||||
| 			b = b[1:] | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // IsNormalString returns true if s == f(s).
 | ||||
| func (f Form) IsNormalString(s string) bool { | ||||
| 	src := inputString(s) | ||||
| 	ft := formTable[f] | ||||
| 	bp, ok := ft.quickSpan(src, 0, len(s), true) | ||||
| 	if ok { | ||||
| 		return true | ||||
| 	} | ||||
| 	rb := reorderBuffer{f: *ft, src: src, nsrc: len(s)} | ||||
| 	rb.setFlusher(nil, func(rb *reorderBuffer) bool { | ||||
| 		for i := 0; i < rb.nrune; i++ { | ||||
| 			info := rb.rune[i] | ||||
| 			if bp+int(info.size) > len(s) { | ||||
| 				return false | ||||
| 			} | ||||
| 			p := info.pos | ||||
| 			pe := p + info.size | ||||
| 			for ; p < pe; p++ { | ||||
| 				if s[bp] != rb.byte[p] { | ||||
| 					return false | ||||
| 				} | ||||
| 				bp++ | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	}) | ||||
| 	for bp < len(s) { | ||||
| 		if bp = decomposeSegment(&rb, bp, true); bp < 0 { | ||||
| 			return false | ||||
| 		} | ||||
| 		bp, _ = rb.f.quickSpan(rb.src, bp, len(s), true) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // patchTail fixes a case where a rune may be incorrectly normalized
 | ||||
| // if it is followed by illegal continuation bytes. It returns the
 | ||||
| // patched buffer and whether the decomposition is still in progress.
 | ||||
| func patchTail(rb *reorderBuffer) bool { | ||||
| 	info, p := lastRuneStart(&rb.f, rb.out) | ||||
| 	if p == -1 || info.size == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	end := p + int(info.size) | ||||
| 	extra := len(rb.out) - end | ||||
| 	if extra > 0 { | ||||
| 		// Potentially allocating memory. However, this only
 | ||||
| 		// happens with ill-formed UTF-8.
 | ||||
| 		x := make([]byte, 0) | ||||
| 		x = append(x, rb.out[len(rb.out)-extra:]...) | ||||
| 		rb.out = rb.out[:end] | ||||
| 		decomposeToLastBoundary(rb) | ||||
| 		rb.doFlush() | ||||
| 		rb.out = append(rb.out, x...) | ||||
| 		return false | ||||
| 	} | ||||
| 	buf := rb.out[p:] | ||||
| 	rb.out = rb.out[:p] | ||||
| 	decomposeToLastBoundary(rb) | ||||
| 	if s := rb.ss.next(info); s == ssStarter { | ||||
| 		rb.doFlush() | ||||
| 		rb.ss.first(info) | ||||
| 	} else if s == ssOverflow { | ||||
| 		rb.doFlush() | ||||
| 		rb.insertCGJ() | ||||
| 		rb.ss = 0 | ||||
| 	} | ||||
| 	rb.insertUnsafe(inputBytes(buf), 0, info) | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func appendQuick(rb *reorderBuffer, i int) int { | ||||
| 	if rb.nsrc == i { | ||||
| 		return i | ||||
| 	} | ||||
| 	end, _ := rb.f.quickSpan(rb.src, i, rb.nsrc, true) | ||||
| 	rb.out = rb.src.appendSlice(rb.out, i, end) | ||||
| 	return end | ||||
| } | ||||
| 
 | ||||
| // Append returns f(append(out, b...)).
 | ||||
| // The buffer out must be nil, empty, or equal to f(out).
 | ||||
| func (f Form) Append(out []byte, src ...byte) []byte { | ||||
| 	return f.doAppend(out, inputBytes(src), len(src)) | ||||
| } | ||||
| 
 | ||||
| func (f Form) doAppend(out []byte, src input, n int) []byte { | ||||
| 	if n == 0 { | ||||
| 		return out | ||||
| 	} | ||||
| 	ft := formTable[f] | ||||
| 	// Attempt to do a quickSpan first so we can avoid initializing the reorderBuffer.
 | ||||
| 	if len(out) == 0 { | ||||
| 		p, _ := ft.quickSpan(src, 0, n, true) | ||||
| 		out = src.appendSlice(out, 0, p) | ||||
| 		if p == n { | ||||
| 			return out | ||||
| 		} | ||||
| 		rb := reorderBuffer{f: *ft, src: src, nsrc: n, out: out, flushF: appendFlush} | ||||
| 		return doAppendInner(&rb, p) | ||||
| 	} | ||||
| 	rb := reorderBuffer{f: *ft, src: src, nsrc: n} | ||||
| 	return doAppend(&rb, out, 0) | ||||
| } | ||||
| 
 | ||||
| func doAppend(rb *reorderBuffer, out []byte, p int) []byte { | ||||
| 	rb.setFlusher(out, appendFlush) | ||||
| 	src, n := rb.src, rb.nsrc | ||||
| 	doMerge := len(out) > 0 | ||||
| 	if q := src.skipContinuationBytes(p); q > p { | ||||
| 		// Move leading non-starters to destination.
 | ||||
| 		rb.out = src.appendSlice(rb.out, p, q) | ||||
| 		p = q | ||||
| 		doMerge = patchTail(rb) | ||||
| 	} | ||||
| 	fd := &rb.f | ||||
| 	if doMerge { | ||||
| 		var info Properties | ||||
| 		if p < n { | ||||
| 			info = fd.info(src, p) | ||||
| 			if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 { | ||||
| 				if p == 0 { | ||||
| 					decomposeToLastBoundary(rb) | ||||
| 				} | ||||
| 				p = decomposeSegment(rb, p, true) | ||||
| 			} | ||||
| 		} | ||||
| 		if info.size == 0 { | ||||
| 			rb.doFlush() | ||||
| 			// Append incomplete UTF-8 encoding.
 | ||||
| 			return src.appendSlice(rb.out, p, n) | ||||
| 		} | ||||
| 		if rb.nrune > 0 { | ||||
| 			return doAppendInner(rb, p) | ||||
| 		} | ||||
| 	} | ||||
| 	p = appendQuick(rb, p) | ||||
| 	return doAppendInner(rb, p) | ||||
| } | ||||
| 
 | ||||
| func doAppendInner(rb *reorderBuffer, p int) []byte { | ||||
| 	for n := rb.nsrc; p < n; { | ||||
| 		p = decomposeSegment(rb, p, true) | ||||
| 		p = appendQuick(rb, p) | ||||
| 	} | ||||
| 	return rb.out | ||||
| } | ||||
| 
 | ||||
| // AppendString returns f(append(out, []byte(s))).
 | ||||
| // The buffer out must be nil, empty, or equal to f(out).
 | ||||
| func (f Form) AppendString(out []byte, src string) []byte { | ||||
| 	return f.doAppend(out, inputString(src), len(src)) | ||||
| } | ||||
| 
 | ||||
| // QuickSpan returns a boundary n such that b[0:n] == f(b[0:n]).
 | ||||
| // It is not guaranteed to return the largest such n.
 | ||||
| func (f Form) QuickSpan(b []byte) int { | ||||
| 	n, _ := formTable[f].quickSpan(inputBytes(b), 0, len(b), true) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| // Span implements transform.SpanningTransformer. It returns a boundary n such
 | ||||
| // that b[0:n] == f(b[0:n]). It is not guaranteed to return the largest such n.
 | ||||
| func (f Form) Span(b []byte, atEOF bool) (n int, err error) { | ||||
| 	n, ok := formTable[f].quickSpan(inputBytes(b), 0, len(b), atEOF) | ||||
| 	if n < len(b) { | ||||
| 		if !ok { | ||||
| 			err = transform.ErrEndOfSpan | ||||
| 		} else { | ||||
| 			err = transform.ErrShortSrc | ||||
| 		} | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| // SpanString returns a boundary n such that s[0:n] == f(s[0:n]).
 | ||||
| // It is not guaranteed to return the largest such n.
 | ||||
| func (f Form) SpanString(s string, atEOF bool) (n int, err error) { | ||||
| 	n, ok := formTable[f].quickSpan(inputString(s), 0, len(s), atEOF) | ||||
| 	if n < len(s) { | ||||
| 		if !ok { | ||||
| 			err = transform.ErrEndOfSpan | ||||
| 		} else { | ||||
| 			err = transform.ErrShortSrc | ||||
| 		} | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| // quickSpan returns a boundary n such that src[0:n] == f(src[0:n]) and
 | ||||
| // whether any non-normalized parts were found. If atEOF is false, n will
 | ||||
| // not point past the last segment if this segment might be become
 | ||||
| // non-normalized by appending other runes.
 | ||||
| func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool) { | ||||
| 	var lastCC uint8 | ||||
| 	ss := streamSafe(0) | ||||
| 	lastSegStart := i | ||||
| 	for n = end; i < n; { | ||||
| 		if j := src.skipASCII(i, n); i != j { | ||||
| 			i = j | ||||
| 			lastSegStart = i - 1 | ||||
| 			lastCC = 0 | ||||
| 			ss = 0 | ||||
| 			continue | ||||
| 		} | ||||
| 		info := f.info(src, i) | ||||
| 		if info.size == 0 { | ||||
| 			if atEOF { | ||||
| 				// include incomplete runes
 | ||||
| 				return n, true | ||||
| 			} | ||||
| 			return lastSegStart, true | ||||
| 		} | ||||
| 		// This block needs to be before the next, because it is possible to
 | ||||
| 		// have an overflow for runes that are starters (e.g. with U+FF9E).
 | ||||
| 		switch ss.next(info) { | ||||
| 		case ssStarter: | ||||
| 			lastSegStart = i | ||||
| 		case ssOverflow: | ||||
| 			return lastSegStart, false | ||||
| 		case ssSuccess: | ||||
| 			if lastCC > info.ccc { | ||||
| 				return lastSegStart, false | ||||
| 			} | ||||
| 		} | ||||
| 		if f.composing { | ||||
| 			if !info.isYesC() { | ||||
| 				break | ||||
| 			} | ||||
| 		} else { | ||||
| 			if !info.isYesD() { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		lastCC = info.ccc | ||||
| 		i += int(info.size) | ||||
| 	} | ||||
| 	if i == n { | ||||
| 		if !atEOF { | ||||
| 			n = lastSegStart | ||||
| 		} | ||||
| 		return n, true | ||||
| 	} | ||||
| 	return lastSegStart, false | ||||
| } | ||||
| 
 | ||||
| // QuickSpanString returns a boundary n such that s[0:n] == f(s[0:n]).
 | ||||
| // It is not guaranteed to return the largest such n.
 | ||||
| func (f Form) QuickSpanString(s string) int { | ||||
| 	n, _ := formTable[f].quickSpan(inputString(s), 0, len(s), true) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| // FirstBoundary returns the position i of the first boundary in b
 | ||||
| // or -1 if b contains no boundary.
 | ||||
| func (f Form) FirstBoundary(b []byte) int { | ||||
| 	return f.firstBoundary(inputBytes(b), len(b)) | ||||
| } | ||||
| 
 | ||||
| func (f Form) firstBoundary(src input, nsrc int) int { | ||||
| 	i := src.skipContinuationBytes(0) | ||||
| 	if i >= nsrc { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	fd := formTable[f] | ||||
| 	ss := streamSafe(0) | ||||
| 	// We should call ss.first here, but we can't as the first rune is
 | ||||
| 	// skipped already. This means FirstBoundary can't really determine
 | ||||
| 	// CGJ insertion points correctly. Luckily it doesn't have to.
 | ||||
| 	for { | ||||
| 		info := fd.info(src, i) | ||||
| 		if info.size == 0 { | ||||
| 			return -1 | ||||
| 		} | ||||
| 		if s := ss.next(info); s != ssSuccess { | ||||
| 			return i | ||||
| 		} | ||||
| 		i += int(info.size) | ||||
| 		if i >= nsrc { | ||||
| 			if !info.BoundaryAfter() && !ss.isMax() { | ||||
| 				return -1 | ||||
| 			} | ||||
| 			return nsrc | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // FirstBoundaryInString returns the position i of the first boundary in s
 | ||||
| // or -1 if s contains no boundary.
 | ||||
| func (f Form) FirstBoundaryInString(s string) int { | ||||
| 	return f.firstBoundary(inputString(s), len(s)) | ||||
| } | ||||
| 
 | ||||
| // NextBoundary reports the index of the boundary between the first and next
 | ||||
| // segment in b or -1 if atEOF is false and there are not enough bytes to
 | ||||
| // determine this boundary.
 | ||||
| func (f Form) NextBoundary(b []byte, atEOF bool) int { | ||||
| 	return f.nextBoundary(inputBytes(b), len(b), atEOF) | ||||
| } | ||||
| 
 | ||||
| // NextBoundaryInString reports the index of the boundary between the first and
 | ||||
| // next segment in b or -1 if atEOF is false and there are not enough bytes to
 | ||||
| // determine this boundary.
 | ||||
| func (f Form) NextBoundaryInString(s string, atEOF bool) int { | ||||
| 	return f.nextBoundary(inputString(s), len(s), atEOF) | ||||
| } | ||||
| 
 | ||||
| func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int { | ||||
| 	if nsrc == 0 { | ||||
| 		if atEOF { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		return -1 | ||||
| 	} | ||||
| 	fd := formTable[f] | ||||
| 	info := fd.info(src, 0) | ||||
| 	if info.size == 0 { | ||||
| 		if atEOF { | ||||
| 			return 1 | ||||
| 		} | ||||
| 		return -1 | ||||
| 	} | ||||
| 	ss := streamSafe(0) | ||||
| 	ss.first(info) | ||||
| 
 | ||||
| 	for i := int(info.size); i < nsrc; i += int(info.size) { | ||||
| 		info = fd.info(src, i) | ||||
| 		if info.size == 0 { | ||||
| 			if atEOF { | ||||
| 				return i | ||||
| 			} | ||||
| 			return -1 | ||||
| 		} | ||||
| 		// TODO: Using streamSafe to determine the boundary isn't the same as
 | ||||
| 		// using BoundaryBefore. Determine which should be used.
 | ||||
| 		if s := ss.next(info); s != ssSuccess { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	if !atEOF && !info.BoundaryAfter() && !ss.isMax() { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return nsrc | ||||
| } | ||||
| 
 | ||||
| // LastBoundary returns the position i of the last boundary in b
 | ||||
| // or -1 if b contains no boundary.
 | ||||
| func (f Form) LastBoundary(b []byte) int { | ||||
| 	return lastBoundary(formTable[f], b) | ||||
| } | ||||
| 
 | ||||
| func lastBoundary(fd *formInfo, b []byte) int { | ||||
| 	i := len(b) | ||||
| 	info, p := lastRuneStart(fd, b) | ||||
| 	if p == -1 { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	if info.size == 0 { // ends with incomplete rune
 | ||||
| 		if p == 0 { // starts with incomplete rune
 | ||||
| 			return -1 | ||||
| 		} | ||||
| 		i = p | ||||
| 		info, p = lastRuneStart(fd, b[:i]) | ||||
| 		if p == -1 { // incomplete UTF-8 encoding or non-starter bytes without a starter
 | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	if p+int(info.size) != i { // trailing non-starter bytes: illegal UTF-8
 | ||||
| 		return i | ||||
| 	} | ||||
| 	if info.BoundaryAfter() { | ||||
| 		return i | ||||
| 	} | ||||
| 	ss := streamSafe(0) | ||||
| 	v := ss.backwards(info) | ||||
| 	for i = p; i >= 0 && v != ssStarter; i = p { | ||||
| 		info, p = lastRuneStart(fd, b[:i]) | ||||
| 		if v = ss.backwards(info); v == ssOverflow { | ||||
| 			break | ||||
| 		} | ||||
| 		if p+int(info.size) != i { | ||||
| 			if p == -1 { // no boundary found
 | ||||
| 				return -1 | ||||
| 			} | ||||
| 			return i // boundary after an illegal UTF-8 encoding
 | ||||
| 		} | ||||
| 	} | ||||
| 	return i | ||||
| } | ||||
| 
 | ||||
| // decomposeSegment scans the first segment in src into rb. It inserts 0x034f
 | ||||
| // (Grapheme Joiner) when it encounters a sequence of more than 30 non-starters
 | ||||
| // and returns the number of bytes consumed from src or iShortDst or iShortSrc.
 | ||||
| func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int { | ||||
| 	// Force one character to be consumed.
 | ||||
| 	info := rb.f.info(rb.src, sp) | ||||
| 	if info.size == 0 { | ||||
| 		return 0 | ||||
| 	} | ||||
| 	if s := rb.ss.next(info); s == ssStarter { | ||||
| 		// TODO: this could be removed if we don't support merging.
 | ||||
| 		if rb.nrune > 0 { | ||||
| 			goto end | ||||
| 		} | ||||
| 	} else if s == ssOverflow { | ||||
| 		rb.insertCGJ() | ||||
| 		goto end | ||||
| 	} | ||||
| 	if err := rb.insertFlush(rb.src, sp, info); err != iSuccess { | ||||
| 		return int(err) | ||||
| 	} | ||||
| 	for { | ||||
| 		sp += int(info.size) | ||||
| 		if sp >= rb.nsrc { | ||||
| 			if !atEOF && !info.BoundaryAfter() { | ||||
| 				return int(iShortSrc) | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		info = rb.f.info(rb.src, sp) | ||||
| 		if info.size == 0 { | ||||
| 			if !atEOF { | ||||
| 				return int(iShortSrc) | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		if s := rb.ss.next(info); s == ssStarter { | ||||
| 			break | ||||
| 		} else if s == ssOverflow { | ||||
| 			rb.insertCGJ() | ||||
| 			break | ||||
| 		} | ||||
| 		if err := rb.insertFlush(rb.src, sp, info); err != iSuccess { | ||||
| 			return int(err) | ||||
| 		} | ||||
| 	} | ||||
| end: | ||||
| 	if !rb.doFlush() { | ||||
| 		return int(iShortDst) | ||||
| 	} | ||||
| 	return sp | ||||
| } | ||||
| 
 | ||||
| // lastRuneStart returns the runeInfo and position of the last
 | ||||
| // rune in buf or the zero runeInfo and -1 if no rune was found.
 | ||||
| func lastRuneStart(fd *formInfo, buf []byte) (Properties, int) { | ||||
| 	p := len(buf) - 1 | ||||
| 	for ; p >= 0 && !utf8.RuneStart(buf[p]); p-- { | ||||
| 	} | ||||
| 	if p < 0 { | ||||
| 		return Properties{}, -1 | ||||
| 	} | ||||
| 	return fd.info(inputBytes(buf), p), p | ||||
| } | ||||
| 
 | ||||
| // decomposeToLastBoundary finds an open segment at the end of the buffer
 | ||||
| // and scans it into rb. Returns the buffer minus the last segment.
 | ||||
| func decomposeToLastBoundary(rb *reorderBuffer) { | ||||
| 	fd := &rb.f | ||||
| 	info, i := lastRuneStart(fd, rb.out) | ||||
| 	if int(info.size) != len(rb.out)-i { | ||||
| 		// illegal trailing continuation bytes
 | ||||
| 		return | ||||
| 	} | ||||
| 	if info.BoundaryAfter() { | ||||
| 		return | ||||
| 	} | ||||
| 	var add [maxNonStarters + 1]Properties // stores runeInfo in reverse order
 | ||||
| 	padd := 0 | ||||
| 	ss := streamSafe(0) | ||||
| 	p := len(rb.out) | ||||
| 	for { | ||||
| 		add[padd] = info | ||||
| 		v := ss.backwards(info) | ||||
| 		if v == ssOverflow { | ||||
| 			// Note that if we have an overflow, it the string we are appending to
 | ||||
| 			// is not correctly normalized. In this case the behavior is undefined.
 | ||||
| 			break | ||||
| 		} | ||||
| 		padd++ | ||||
| 		p -= int(info.size) | ||||
| 		if v == ssStarter || p < 0 { | ||||
| 			break | ||||
| 		} | ||||
| 		info, i = lastRuneStart(fd, rb.out[:p]) | ||||
| 		if int(info.size) != p-i { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	rb.ss = ss | ||||
| 	// Copy bytes for insertion as we may need to overwrite rb.out.
 | ||||
| 	var buf [maxBufferSize * utf8.UTFMax]byte | ||||
| 	cp := buf[:copy(buf[:], rb.out[p:])] | ||||
| 	rb.out = rb.out[:p] | ||||
| 	for padd--; padd >= 0; padd-- { | ||||
| 		info = add[padd] | ||||
| 		rb.insertUnsafe(inputBytes(cp), 0, info) | ||||
| 		cp = cp[info.size:] | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										125
									
								
								vendor/golang.org/x/text/unicode/norm/readwriter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/golang.org/x/text/unicode/norm/readwriter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,125 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import "io" | ||||
| 
 | ||||
| type normWriter struct { | ||||
| 	rb  reorderBuffer | ||||
| 	w   io.Writer | ||||
| 	buf []byte | ||||
| } | ||||
| 
 | ||||
| // Write implements the standard write interface.  If the last characters are
 | ||||
| // not at a normalization boundary, the bytes will be buffered for the next
 | ||||
| // write. The remaining bytes will be written on close.
 | ||||
| func (w *normWriter) Write(data []byte) (n int, err error) { | ||||
| 	// Process data in pieces to keep w.buf size bounded.
 | ||||
| 	const chunk = 4000 | ||||
| 
 | ||||
| 	for len(data) > 0 { | ||||
| 		// Normalize into w.buf.
 | ||||
| 		m := len(data) | ||||
| 		if m > chunk { | ||||
| 			m = chunk | ||||
| 		} | ||||
| 		w.rb.src = inputBytes(data[:m]) | ||||
| 		w.rb.nsrc = m | ||||
| 		w.buf = doAppend(&w.rb, w.buf, 0) | ||||
| 		data = data[m:] | ||||
| 		n += m | ||||
| 
 | ||||
| 		// Write out complete prefix, save remainder.
 | ||||
| 		// Note that lastBoundary looks back at most 31 runes.
 | ||||
| 		i := lastBoundary(&w.rb.f, w.buf) | ||||
| 		if i == -1 { | ||||
| 			i = 0 | ||||
| 		} | ||||
| 		if i > 0 { | ||||
| 			if _, err = w.w.Write(w.buf[:i]); err != nil { | ||||
| 				break | ||||
| 			} | ||||
| 			bn := copy(w.buf, w.buf[i:]) | ||||
| 			w.buf = w.buf[:bn] | ||||
| 		} | ||||
| 	} | ||||
| 	return n, err | ||||
| } | ||||
| 
 | ||||
| // Close forces data that remains in the buffer to be written.
 | ||||
| func (w *normWriter) Close() error { | ||||
| 	if len(w.buf) > 0 { | ||||
| 		_, err := w.w.Write(w.buf) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Writer returns a new writer that implements Write(b)
 | ||||
| // by writing f(b) to w. The returned writer may use an
 | ||||
| // internal buffer to maintain state across Write calls.
 | ||||
| // Calling its Close method writes any buffered data to w.
 | ||||
| func (f Form) Writer(w io.Writer) io.WriteCloser { | ||||
| 	wr := &normWriter{rb: reorderBuffer{}, w: w} | ||||
| 	wr.rb.init(f, nil) | ||||
| 	return wr | ||||
| } | ||||
| 
 | ||||
| type normReader struct { | ||||
| 	rb           reorderBuffer | ||||
| 	r            io.Reader | ||||
| 	inbuf        []byte | ||||
| 	outbuf       []byte | ||||
| 	bufStart     int | ||||
| 	lastBoundary int | ||||
| 	err          error | ||||
| } | ||||
| 
 | ||||
| // Read implements the standard read interface.
 | ||||
| func (r *normReader) Read(p []byte) (int, error) { | ||||
| 	for { | ||||
| 		if r.lastBoundary-r.bufStart > 0 { | ||||
| 			n := copy(p, r.outbuf[r.bufStart:r.lastBoundary]) | ||||
| 			r.bufStart += n | ||||
| 			if r.lastBoundary-r.bufStart > 0 { | ||||
| 				return n, nil | ||||
| 			} | ||||
| 			return n, r.err | ||||
| 		} | ||||
| 		if r.err != nil { | ||||
| 			return 0, r.err | ||||
| 		} | ||||
| 		outn := copy(r.outbuf, r.outbuf[r.lastBoundary:]) | ||||
| 		r.outbuf = r.outbuf[0:outn] | ||||
| 		r.bufStart = 0 | ||||
| 
 | ||||
| 		n, err := r.r.Read(r.inbuf) | ||||
| 		r.rb.src = inputBytes(r.inbuf[0:n]) | ||||
| 		r.rb.nsrc, r.err = n, err | ||||
| 		if n > 0 { | ||||
| 			r.outbuf = doAppend(&r.rb, r.outbuf, 0) | ||||
| 		} | ||||
| 		if err == io.EOF { | ||||
| 			r.lastBoundary = len(r.outbuf) | ||||
| 		} else { | ||||
| 			r.lastBoundary = lastBoundary(&r.rb.f, r.outbuf) | ||||
| 			if r.lastBoundary == -1 { | ||||
| 				r.lastBoundary = 0 | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Reader returns a new reader that implements Read
 | ||||
| // by reading data from r and returning f(data).
 | ||||
| func (f Form) Reader(r io.Reader) io.Reader { | ||||
| 	const chunk = 4000 | ||||
| 	buf := make([]byte, chunk) | ||||
| 	rr := &normReader{rb: reorderBuffer{}, r: r, inbuf: buf} | ||||
| 	rr.rb.init(f, buf) | ||||
| 	return rr | ||||
| } | ||||
							
								
								
									
										7657
									
								
								vendor/golang.org/x/text/unicode/norm/tables10.0.0.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7657
									
								
								vendor/golang.org/x/text/unicode/norm/tables10.0.0.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										7637
									
								
								vendor/golang.org/x/text/unicode/norm/tables9.0.0.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7637
									
								
								vendor/golang.org/x/text/unicode/norm/tables9.0.0.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										88
									
								
								vendor/golang.org/x/text/unicode/norm/transform.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/golang.org/x/text/unicode/norm/transform.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| import ( | ||||
| 	"unicode/utf8" | ||||
| 
 | ||||
| 	"golang.org/x/text/transform" | ||||
| ) | ||||
| 
 | ||||
| // Reset implements the Reset method of the transform.Transformer interface.
 | ||||
| func (Form) Reset() {} | ||||
| 
 | ||||
| // Transform implements the Transform method of the transform.Transformer
 | ||||
| // interface. It may need to write segments of up to MaxSegmentSize at once.
 | ||||
| // Users should either catch ErrShortDst and allow dst to grow or have dst be at
 | ||||
| // least of size MaxTransformChunkSize to be guaranteed of progress.
 | ||||
| func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { | ||||
| 	// Cap the maximum number of src bytes to check.
 | ||||
| 	b := src | ||||
| 	eof := atEOF | ||||
| 	if ns := len(dst); ns < len(b) { | ||||
| 		err = transform.ErrShortDst | ||||
| 		eof = false | ||||
| 		b = b[:ns] | ||||
| 	} | ||||
| 	i, ok := formTable[f].quickSpan(inputBytes(b), 0, len(b), eof) | ||||
| 	n := copy(dst, b[:i]) | ||||
| 	if !ok { | ||||
| 		nDst, nSrc, err = f.transform(dst[n:], src[n:], atEOF) | ||||
| 		return nDst + n, nSrc + n, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err == nil && n < len(src) && !atEOF { | ||||
| 		err = transform.ErrShortSrc | ||||
| 	} | ||||
| 	return n, n, err | ||||
| } | ||||
| 
 | ||||
| func flushTransform(rb *reorderBuffer) bool { | ||||
| 	// Write out (must fully fit in dst, or else it is an ErrShortDst).
 | ||||
| 	if len(rb.out) < rb.nrune*utf8.UTFMax { | ||||
| 		return false | ||||
| 	} | ||||
| 	rb.out = rb.out[rb.flushCopy(rb.out):] | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| var errs = []error{nil, transform.ErrShortDst, transform.ErrShortSrc} | ||||
| 
 | ||||
| // transform implements the transform.Transformer interface. It is only called
 | ||||
| // when quickSpan does not pass for a given string.
 | ||||
| func (f Form) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { | ||||
| 	// TODO: get rid of reorderBuffer. See CL 23460044.
 | ||||
| 	rb := reorderBuffer{} | ||||
| 	rb.init(f, src) | ||||
| 	for { | ||||
| 		// Load segment into reorder buffer.
 | ||||
| 		rb.setFlusher(dst[nDst:], flushTransform) | ||||
| 		end := decomposeSegment(&rb, nSrc, atEOF) | ||||
| 		if end < 0 { | ||||
| 			return nDst, nSrc, errs[-end] | ||||
| 		} | ||||
| 		nDst = len(dst) - len(rb.out) | ||||
| 		nSrc = end | ||||
| 
 | ||||
| 		// Next quickSpan.
 | ||||
| 		end = rb.nsrc | ||||
| 		eof := atEOF | ||||
| 		if n := nSrc + len(dst) - nDst; n < end { | ||||
| 			err = transform.ErrShortDst | ||||
| 			end = n | ||||
| 			eof = false | ||||
| 		} | ||||
| 		end, ok := rb.f.quickSpan(rb.src, nSrc, end, eof) | ||||
| 		n := copy(dst[nDst:], rb.src.bytes[nSrc:end]) | ||||
| 		nSrc += n | ||||
| 		nDst += n | ||||
| 		if ok { | ||||
| 			if err == nil && n < rb.nsrc && !atEOF { | ||||
| 				err = transform.ErrShortSrc | ||||
| 			} | ||||
| 			return nDst, nSrc, err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/golang.org/x/text/unicode/norm/trie.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/golang.org/x/text/unicode/norm/trie.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| package norm | ||||
| 
 | ||||
| type valueRange struct { | ||||
| 	value  uint16 // header: value:stride
 | ||||
| 	lo, hi byte   // header: lo:n
 | ||||
| } | ||||
| 
 | ||||
| type sparseBlocks struct { | ||||
| 	values []valueRange | ||||
| 	offset []uint16 | ||||
| } | ||||
| 
 | ||||
| var nfcSparse = sparseBlocks{ | ||||
| 	values: nfcSparseValues[:], | ||||
| 	offset: nfcSparseOffset[:], | ||||
| } | ||||
| 
 | ||||
| var nfkcSparse = sparseBlocks{ | ||||
| 	values: nfkcSparseValues[:], | ||||
| 	offset: nfkcSparseOffset[:], | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	nfcData  = newNfcTrie(0) | ||||
| 	nfkcData = newNfkcTrie(0) | ||||
| ) | ||||
| 
 | ||||
| // lookupValue determines the type of block n and looks up the value for b.
 | ||||
| // For n < t.cutoff, the block is a simple lookup table. Otherwise, the block
 | ||||
| // is a list of ranges with an accompanying value. Given a matching range r,
 | ||||
| // the value for b is by r.value + (b - r.lo) * stride.
 | ||||
| func (t *sparseBlocks) lookup(n uint32, b byte) uint16 { | ||||
| 	offset := t.offset[n] | ||||
| 	header := t.values[offset] | ||||
| 	lo := offset + 1 | ||||
| 	hi := lo + uint16(header.lo) | ||||
| 	for lo < hi { | ||||
| 		m := lo + (hi-lo)/2 | ||||
| 		r := t.values[m] | ||||
| 		if r.lo <= b && b <= r.hi { | ||||
| 			return r.value + uint16(b-r.lo)*header.value | ||||
| 		} | ||||
| 		if b < r.lo { | ||||
| 			hi = m | ||||
| 		} else { | ||||
| 			lo = m + 1 | ||||
| 		} | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
							
								
								
									
										117
									
								
								vendor/golang.org/x/text/unicode/norm/triegen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/golang.org/x/text/unicode/norm/triegen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,117 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style
 | ||||
| // license that can be found in the LICENSE file.
 | ||||
| 
 | ||||
| // +build ignore
 | ||||
| 
 | ||||
| // Trie table generator.
 | ||||
| // Used by make*tables tools to generate a go file with trie data structures
 | ||||
| // for mapping UTF-8 to a 16-bit value. All but the last byte in a UTF-8 byte
 | ||||
| // sequence are used to lookup offsets in the index table to be used for the
 | ||||
| // next byte. The last byte is used to index into a table with 16-bit values.
 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| const maxSparseEntries = 16 | ||||
| 
 | ||||
| type normCompacter struct { | ||||
| 	sparseBlocks [][]uint64 | ||||
| 	sparseOffset []uint16 | ||||
| 	sparseCount  int | ||||
| 	name         string | ||||
| } | ||||
| 
 | ||||
| func mostFrequentStride(a []uint64) int { | ||||
| 	counts := make(map[int]int) | ||||
| 	var v int | ||||
| 	for _, x := range a { | ||||
| 		if stride := int(x) - v; v != 0 && stride >= 0 { | ||||
| 			counts[stride]++ | ||||
| 		} | ||||
| 		v = int(x) | ||||
| 	} | ||||
| 	var maxs, maxc int | ||||
| 	for stride, cnt := range counts { | ||||
| 		if cnt > maxc || (cnt == maxc && stride < maxs) { | ||||
| 			maxs, maxc = stride, cnt | ||||
| 		} | ||||
| 	} | ||||
| 	return maxs | ||||
| } | ||||
| 
 | ||||
| func countSparseEntries(a []uint64) int { | ||||
| 	stride := mostFrequentStride(a) | ||||
| 	var v, count int | ||||
| 	for _, tv := range a { | ||||
| 		if int(tv)-v != stride { | ||||
| 			if tv != 0 { | ||||
| 				count++ | ||||
| 			} | ||||
| 		} | ||||
| 		v = int(tv) | ||||
| 	} | ||||
| 	return count | ||||
| } | ||||
| 
 | ||||
| func (c *normCompacter) Size(v []uint64) (sz int, ok bool) { | ||||
| 	if n := countSparseEntries(v); n <= maxSparseEntries { | ||||
| 		return (n+1)*4 + 2, true | ||||
| 	} | ||||
| 	return 0, false | ||||
| } | ||||
| 
 | ||||
| func (c *normCompacter) Store(v []uint64) uint32 { | ||||
| 	h := uint32(len(c.sparseOffset)) | ||||
| 	c.sparseBlocks = append(c.sparseBlocks, v) | ||||
| 	c.sparseOffset = append(c.sparseOffset, uint16(c.sparseCount)) | ||||
| 	c.sparseCount += countSparseEntries(v) + 1 | ||||
| 	return h | ||||
| } | ||||
| 
 | ||||
| func (c *normCompacter) Handler() string { | ||||
| 	return c.name + "Sparse.lookup" | ||||
| } | ||||
| 
 | ||||
| func (c *normCompacter) Print(w io.Writer) (retErr error) { | ||||
| 	p := func(f string, x ...interface{}) { | ||||
| 		if _, err := fmt.Fprintf(w, f, x...); retErr == nil && err != nil { | ||||
| 			retErr = err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ls := len(c.sparseBlocks) | ||||
| 	p("// %sSparseOffset: %d entries, %d bytes\n", c.name, ls, ls*2) | ||||
| 	p("var %sSparseOffset = %#v\n\n", c.name, c.sparseOffset) | ||||
| 
 | ||||
| 	ns := c.sparseCount | ||||
| 	p("// %sSparseValues: %d entries, %d bytes\n", c.name, ns, ns*4) | ||||
| 	p("var %sSparseValues = [%d]valueRange {", c.name, ns) | ||||
| 	for i, b := range c.sparseBlocks { | ||||
| 		p("\n// Block %#x, offset %#x", i, c.sparseOffset[i]) | ||||
| 		var v int | ||||
| 		stride := mostFrequentStride(b) | ||||
| 		n := countSparseEntries(b) | ||||
| 		p("\n{value:%#04x,lo:%#02x},", stride, uint8(n)) | ||||
| 		for i, nv := range b { | ||||
| 			if int(nv)-v != stride { | ||||
| 				if v != 0 { | ||||
| 					p(",hi:%#02x},", 0x80+i-1) | ||||
| 				} | ||||
| 				if nv != 0 { | ||||
| 					p("\n{value:%#04x,lo:%#02x", nv, 0x80+i) | ||||
| 				} | ||||
| 			} | ||||
| 			v = int(nv) | ||||
| 		} | ||||
| 		if v != 0 { | ||||
| 			p(",hi:%#02x},", 0x80+len(b)-1) | ||||
| 		} | ||||
| 	} | ||||
| 	p("\n}\n\n") | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										36
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							| @ -139,6 +139,12 @@ | ||||
| 			"revision": "ca190fb6ffbc076ff49197b7168a760f30182d2e", | ||||
| 			"revisionTime": "2018-04-18T12:24:29Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "GnNfMrYs/4m+HCtDBF7HpPUFFVw=", | ||||
| 			"path": "github.com/gballet/go-libpcsclite", | ||||
| 			"revision": "95b81846253cd854b8bb8f2fd9cc6056d0681ac4", | ||||
| 			"revisionTime": "2019-03-13T11:40:44Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "gxV/cPPLkByTdY8y172t7v4qcZA=", | ||||
| 			"path": "github.com/go-ole/go-ole", | ||||
| @ -429,6 +435,12 @@ | ||||
| 			"revision": "ed27b6fd65218132ee50cd95f38474a3d8a2cd12", | ||||
| 			"revisionTime": "2016-06-18T19:32:21Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "I7gkZEZn9PKJ+dzuBYLbFIUzNO4=", | ||||
| 			"path": "github.com/status-im/keycard-go/derivationpath", | ||||
| 			"revision": "8537d3370df43a30a3d450c023783d2e43432b89", | ||||
| 			"revisionTime": "2019-03-16T09:03:35Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "mGbTYZ8dHVTiPTTJu3ktp+84pPI=", | ||||
| 			"path": "github.com/stretchr/testify/assert", | ||||
| @ -513,6 +525,18 @@ | ||||
| 			"revision": "b001fa50d6b27f3f0bb175a87d0cb55426d0a0ae", | ||||
| 			"revisionTime": "2018-11-28T10:09:59Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "SsMMqb3xn7hg1ZX5ugwZz5rzpx0=", | ||||
| 			"path": "github.com/tyler-smith/go-bip39", | ||||
| 			"revision": "dbb3b84ba2ef14e894f5e33d6c6e43641e665738", | ||||
| 			"revisionTime": "2018-10-17T06:06:43Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "xa+UUbqD3zPfa8mKAxWabRsqSzM=", | ||||
| 			"path": "github.com/tyler-smith/go-bip39/wordlists", | ||||
| 			"revision": "dbb3b84ba2ef14e894f5e33d6c6e43641e665738", | ||||
| 			"revisionTime": "2018-10-17T06:06:43Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "nD6S4KB0S+YHxVMDDE+w3PyXaMk=", | ||||
| 			"path": "github.com/uber/jaeger-client-go", | ||||
| @ -615,6 +639,12 @@ | ||||
| 			"revision": "a51202d6f4a7e5a219e3841a43614ff7187ae7f1", | ||||
| 			"revisionTime": "2018-06-15T20:27:29Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "GLCPuvePAkWT+opcWq3mNdhOfGM=", | ||||
| 			"path": "github.com/wsddn/go-ecdh", | ||||
| 			"revision": "48726bab92085232373de4ec5c51ce7b441c63a0", | ||||
| 			"revisionTime": "2016-12-11T03:23:59Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=", | ||||
| 			"path": "golang.org/x/crypto/cast5", | ||||
| @ -885,6 +915,12 @@ | ||||
| 			"revision": "e3703dcdd614d2d7488fff034c75c551ea25da95", | ||||
| 			"revisionTime": "2018-12-15T16:57:46Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "lBB8oUHgIK0RUuDchkQVfMXJQh0=", | ||||
| 			"path": "golang.org/x/text/unicode/norm", | ||||
| 			"revision": "31e7599a6c37728c25ca34167be099d072ad335d", | ||||
| 			"revisionTime": "2019-04-05T05:38:27Z" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"checksumSHA1": "CEFTYXtWmgSh+3Ik1NmDaJcz4E0=", | ||||
| 			"path": "gopkg.in/check.v1", | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user