From b257fab3efb5b17a51e231fcb26c485d394a3489 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Wed, 6 Sep 2017 09:05:20 +0200 Subject: Switch hid lib --- vendor/github.com/karalabe/hid/.travis.yml | 25 +++ vendor/github.com/karalabe/hid/LICENSE.md | 8 + vendor/github.com/karalabe/hid/README.md | 53 ++++++ vendor/github.com/karalabe/hid/appveyor.yml | 32 ++++ vendor/github.com/karalabe/hid/hid.go | 37 ++++ vendor/github.com/karalabe/hid/hid_disabled.go | 51 ++++++ vendor/github.com/karalabe/hid/hid_enabled.go | 228 +++++++++++++++++++++++++ vendor/github.com/karalabe/hid/hid_test.go | 28 +++ vendor/github.com/karalabe/hid/wchar.go | 227 ++++++++++++++++++++++++ vendor/github.com/karalabe/hid/wchar_test.go | 66 +++++++ 10 files changed, 755 insertions(+) create mode 100644 vendor/github.com/karalabe/hid/.travis.yml create mode 100644 vendor/github.com/karalabe/hid/LICENSE.md create mode 100644 vendor/github.com/karalabe/hid/README.md create mode 100644 vendor/github.com/karalabe/hid/appveyor.yml create mode 100644 vendor/github.com/karalabe/hid/hid.go create mode 100644 vendor/github.com/karalabe/hid/hid_disabled.go create mode 100644 vendor/github.com/karalabe/hid/hid_enabled.go create mode 100644 vendor/github.com/karalabe/hid/hid_test.go create mode 100644 vendor/github.com/karalabe/hid/wchar.go create mode 100644 vendor/github.com/karalabe/hid/wchar_test.go (limited to 'vendor/github.com/karalabe') diff --git a/vendor/github.com/karalabe/hid/.travis.yml b/vendor/github.com/karalabe/hid/.travis.yml new file mode 100644 index 0000000..18b2067 --- /dev/null +++ b/vendor/github.com/karalabe/hid/.travis.yml @@ -0,0 +1,25 @@ +language: go + +matrix: + include: + - os: linux + dist: trusty + go: 1.5.4 + - os: linux + dist: trusty + go: 1.6.2 + - os: linux + dist: trusty + go: 1.7.5 + - os: linux + dist: trusty + go: 1.8 + - os: linux + dist: precise + go: 1.8 + - os: osx + go: 1.8 + +script: + - go install ./... + - go test -v ./... diff --git a/vendor/github.com/karalabe/hid/LICENSE.md b/vendor/github.com/karalabe/hid/LICENSE.md new file mode 100644 index 0000000..230d1da --- /dev/null +++ b/vendor/github.com/karalabe/hid/LICENSE.md @@ -0,0 +1,8 @@ +The components of `hid` are licensed as such: + + * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license. + * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license. + * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license. + * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license. + +Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms. diff --git a/vendor/github.com/karalabe/hid/README.md b/vendor/github.com/karalabe/hid/README.md new file mode 100644 index 0000000..2851ffe --- /dev/null +++ b/vendor/github.com/karalabe/hid/README.md @@ -0,0 +1,53 @@ +[![Travis][travisimg]][travisurl] +[![AppVeyor][appveyorimg]][appveyorurl] +[![GoDoc][docimg]][docurl] + +[travisimg]: https://travis-ci.org/karalabe/hid.svg?branch=master +[travisurl]: https://travis-ci.org/karalabe/hid +[appveyorimg]: https://ci.appveyor.com/api/projects/status/plroy54odykb0ch3/branch/master?svg=true +[appveyorurl]: https://ci.appveyor.com/project/karalabe/hid +[docimg]: https://godoc.org/github.com/karalabe/hid?status.svg +[docurl]: https://godoc.org/github.com/karalabe/hid + +# Gopher Interface Devices (USB HID) + +The `hid` package is a cross platform library for accessing and communicating with USB Human Interface +Devices (HID). It is an alternative package to [`gousb`](https://github.com/karalabe/gousb) for use +cases where devices support this ligher mode of operation (e.g. input devices, hardware crypto wallets). + +The package wraps [`hidapi`](https://github.com/signal11/hidapi) for accessing OS specific USB HID APIs +directly instead of using low level USB constructs, which might have permission issues on some platforms. +On Linux the package also wraps [`libusb`](https://github.com/libusb/libusb). Both of these dependencies +are vendored directly into the repository and wrapped using CGO, making the `hid` package self-contained +and go-gettable. + +Supported platforms at the moment are Linux, macOS and Windows (exclude constraints are also specified +for Android and iOS to allow smoother vendoring into cross platform projects). + +## Cross-compiling + +Using `go get` the embedded C library is compiled into the binary format of your host OS. Cross compiling to a different platform or architecture entails disabling CGO by default in Go, causing device enumeration `hid.Enumerate()` to yield no results. + +To cross compile a functional version of this library, you'll need to enable CGO during cross compilation via `CGO_ENABLED=1` and you'll need to install and set a cross compilation enabled C toolkit via `CC=your-cross-gcc`. + +## Acknowledgements + +Although the `hid` package is an implementation from scratch, it was heavily inspired by the existing +[`go.hid`](https://github.com/GeertJohan/go.hid) library, which seems abandoned since 2015; is incompatible +with Go 1.6+; and has various external dependencies. Given its inspirational roots, I thought it important +to give credit to the author of said package too. + +Wide character support in the `hid` package is done via the [`gowchar`](https://github.com/orofarne/gowchar) +library, unmaintained since 2013; non buildable with a modern Go release and failing `go vet` checks. As +such, `gowchar` was also vendored in inline (copyright headers and origins preserved). + +## License + +The components of `hid` are licensed as such: + + * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license. + * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license. + * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license. + * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license. + +Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms. diff --git a/vendor/github.com/karalabe/hid/appveyor.yml b/vendor/github.com/karalabe/hid/appveyor.yml new file mode 100644 index 0000000..f439587 --- /dev/null +++ b/vendor/github.com/karalabe/hid/appveyor.yml @@ -0,0 +1,32 @@ +os: Visual Studio 2015 + +# Clone directly into GOPATH. +clone_folder: C:\gopath\src\github.com\karalabe\hid +clone_depth: 1 +version: "{branch}.{build}" +environment: + global: + GOPATH: C:\gopath + CC: gcc.exe + matrix: + - GOARCH: amd64 + MSYS2_ARCH: x86_64 + MSYS2_BITS: 64 + MSYSTEM: MINGW64 + PATH: C:\msys64\mingw64\bin\;%PATH% + - GOARCH: 386 + MSYS2_ARCH: i686 + MSYS2_BITS: 32 + MSYSTEM: MINGW32 + PATH: C:\msys64\mingw32\bin\;%PATH% + +install: + - rmdir C:\go /s /q + - appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.windows-%GOARCH%.zip + - 7z x go1.8.windows-%GOARCH%.zip -y -oC:\ > NUL + - go version + - gcc --version + +build_script: + - go install ./... + - go test -v ./... diff --git a/vendor/github.com/karalabe/hid/hid.go b/vendor/github.com/karalabe/hid/hid.go new file mode 100644 index 0000000..60a40b8 --- /dev/null +++ b/vendor/github.com/karalabe/hid/hid.go @@ -0,0 +1,37 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU LGPL 2.1 or later. + +// Package hid provides an interface for USB HID devices. +package hid + +import "errors" + +// ErrDeviceClosed is returned for operations where the device closed before or +// during the execution. +var ErrDeviceClosed = errors.New("hid: device closed") + +// ErrUnsupportedPlatform is returned for all operations where the underlying +// operating system is not supported by the library. +var ErrUnsupportedPlatform = errors.New("hid: unsupported platform") + +// DeviceInfo is a hidapi info structure. +type DeviceInfo struct { + Path string // Platform-specific device path + VendorID uint16 // Device Vendor ID + ProductID uint16 // Device Product ID + Release uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number + Serial string // Serial Number + Manufacturer string // Manufacturer String + Product string // Product string + UsagePage uint16 // Usage Page for this Device/Interface (Windows/Mac only) + Usage uint16 // Usage for this Device/Interface (Windows/Mac only) + + // The USB interface which this logical device + // represents. Valid on both Linux implementations + // in all cases, and valid on the Windows implementation + // only if the device contains more than one interface. + Interface int +} diff --git a/vendor/github.com/karalabe/hid/hid_disabled.go b/vendor/github.com/karalabe/hid/hid_disabled.go new file mode 100644 index 0000000..1f40263 --- /dev/null +++ b/vendor/github.com/karalabe/hid/hid_disabled.go @@ -0,0 +1,51 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU LGPL 2.1 or later. + +// +build !linux,!darwin,!windows ios !cgo + +package hid + +// Supported returns whether this platform is supported by the HID library or not. +// The goal of this method is to allow programatically handling platforms that do +// not support USB HID and not having to fall back to build constraints. +func Supported() bool { + return false +} + +// Enumerate returns a list of all the HID devices attached to the system which +// match the vendor and product id. On platforms that this file implements the +// function is a noop and returns an empty list always. +func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { + return nil +} + +// Device is a live HID USB connected device handle. On platforms that this file +// implements the type lacks the actual HID device and all methods are noop. +type Device struct { + DeviceInfo // Embed the infos for easier access +} + +// Open connects to an HID device by its path name. On platforms that this file +// implements the method just returns an error. +func (info DeviceInfo) Open() (*Device, error) { + return nil, ErrUnsupportedPlatform +} + +// Close releases the HID USB device handle. On platforms that this file implements +// the method is just a noop. +func (dev *Device) Close() {} + +// Write sends an output report to a HID device. On platforms that this file +// implements the method just returns an error. +func (dev *Device) Write(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} + +// Read retrieves an input report from a HID device. On platforms that this file +// implements the method just returns an error. +func (dev *Device) Read(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} diff --git a/vendor/github.com/karalabe/hid/hid_enabled.go b/vendor/github.com/karalabe/hid/hid_enabled.go new file mode 100644 index 0000000..419273b --- /dev/null +++ b/vendor/github.com/karalabe/hid/hid_enabled.go @@ -0,0 +1,228 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under LGNU GPL 2.1 or later. + +// +build linux,cgo darwin,!ios,cgo windows,cgo + +package hid + +/* +#cgo CFLAGS: -I./hidapi/hidapi + +#cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int +#cgo linux,!android LDFLAGS: -lrt +#cgo darwin CFLAGS: -DOS_DARWIN +#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit +#cgo windows CFLAGS: -DOS_WINDOWS +#cgo windows LDFLAGS: -lsetupapi + +#ifdef OS_LINUX + #include + #include "os/threads_posix.c" + #include "os/poll_posix.c" + + #include "os/linux_usbfs.c" + #include "os/linux_netlink.c" + + #include "core.c" + #include "descriptor.c" + #include "hotplug.c" + #include "io.c" + #include "strerror.c" + #include "sync.c" + + #include "hidapi/libusb/hid.c" +#elif OS_DARWIN + #include "hidapi/mac/hid.c" +#elif OS_WINDOWS + #include "hidapi/windows/hid.c" +#endif +*/ +import "C" +import ( + "errors" + "runtime" + "sync" + "unsafe" +) + +// enumerateLock is a mutex serializing access to USB device enumeration needed +// by the macOS USB HID system calls, which require 2 consecutive method calls +// for enumeration, causing crashes if called concurrently. +// +// For more details, see: +// https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching +// > "subsequent calls will cause the hid manager to release previously enumerated devices" +var enumerateLock sync.Mutex + +func init() { + // Initialize the HIDAPI library + C.hid_init() +} + +// Supported returns whether this platform is supported by the HID library or not. +// The goal of this method is to allow programatically handling platforms that do +// not support USB HID and not having to fall back to build constraints. +func Supported() bool { + return true +} + +// Enumerate returns a list of all the HID devices attached to the system which +// match the vendor and product id: +// - If the vendor id is set to 0 then any vendor matches. +// - If the product id is set to 0 then any product matches. +// - If the vendor and product id are both 0, all HID devices are returned. +func Enumerate(vendorID uint16, productID uint16) []DeviceInfo { + enumerateLock.Lock() + defer enumerateLock.Unlock() + + // Gather all device infos and ensure they are freed before returning + head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID)) + if head == nil { + return nil + } + defer C.hid_free_enumeration(head) + + // Iterate the list and retrieve the device details + var infos []DeviceInfo + for ; head != nil; head = head.next { + info := DeviceInfo{ + Path: C.GoString(head.path), + VendorID: uint16(head.vendor_id), + ProductID: uint16(head.product_id), + Release: uint16(head.release_number), + UsagePage: uint16(head.usage_page), + Usage: uint16(head.usage), + Interface: int(head.interface_number), + } + if head.serial_number != nil { + info.Serial, _ = wcharTToString(head.serial_number) + } + if head.product_string != nil { + info.Product, _ = wcharTToString(head.product_string) + } + if head.manufacturer_string != nil { + info.Manufacturer, _ = wcharTToString(head.manufacturer_string) + } + infos = append(infos, info) + } + return infos +} + +// Open connects to an HID device by its path name. +func (info DeviceInfo) Open() (*Device, error) { + path := C.CString(info.Path) + defer C.free(unsafe.Pointer(path)) + + device := C.hid_open_path(path) + if device == nil { + return nil, errors.New("hidapi: failed to open device") + } + return &Device{ + DeviceInfo: info, + device: device, + }, nil +} + +// Device is a live HID USB connected device handle. +type Device struct { + DeviceInfo // Embed the infos for easier access + + device *C.hid_device // Low level HID device to communicate through + lock sync.Mutex +} + +// Close releases the HID USB device handle. +func (dev *Device) Close() { + dev.lock.Lock() + defer dev.lock.Unlock() + + if dev.device != nil { + C.hid_close(dev.device) + dev.device = nil + } +} + +// Write sends an output report to a HID device. +// +// Write will send the data on the first OUT endpoint, if one exists. If it does +// not, it will send the data through the Control Endpoint (Endpoint 0). +func (dev *Device) Write(b []byte) (int, error) { + // Abort if nothing to write + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Prepend a HID report ID on Windows, other OSes don't need it + var report []byte + if runtime.GOOS == "windows" { + report = append([]byte{0x00}, b...) + } else { + report = b + } + // Execute the write operation + written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report)))) + if written == -1 { + // If the write failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return written, nil +} + +// Read retrieves an input report from a HID device. +func (dev *Device) Read(b []byte) (int, error) { + // Aborth if nothing to read + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Execute the read operation + read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b)))) + if read == -1 { + // If the read failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return read, nil +} diff --git a/vendor/github.com/karalabe/hid/hid_test.go b/vendor/github.com/karalabe/hid/hid_test.go new file mode 100644 index 0000000..470e6b9 --- /dev/null +++ b/vendor/github.com/karalabe/hid/hid_test.go @@ -0,0 +1,28 @@ +// hid - Gopher Interface Devices (USB HID) +// Copyright (c) 2017 Péter Szilágyi. All rights reserved. +// +// This file is released under the 3-clause BSD license. Note however that Linux +// support depends on libusb, released under GNU LGPL 2.1 or later. + +package hid + +import ( + "sync" + "testing" +) + +// Tests that device enumeration can be called concurrently from multiple threads. +func TestThreadedEnumerate(t *testing.T) { + var pend sync.WaitGroup + for i := 0; i < 8; i++ { + pend.Add(1) + + go func(index int) { + defer pend.Done() + for j := 0; j < 512; j++ { + Enumerate(uint16(index), 0) + } + }(i) + } + pend.Wait() +} diff --git a/vendor/github.com/karalabe/hid/wchar.go b/vendor/github.com/karalabe/hid/wchar.go new file mode 100644 index 0000000..d103bef --- /dev/null +++ b/vendor/github.com/karalabe/hid/wchar.go @@ -0,0 +1,227 @@ +// This file is https://github.com/orofarne/gowchar/blob/master/gowchar.go +// +// It was vendored inline to work around CGO limitations that don't allow C types +// to directly cross package API boundaries. +// +// The vendored file is licensed under the 3-clause BSD license, according to: +// https://github.com/orofarne/gowchar/blob/master/LICENSE + +// +build !ios +// +build linux darwin windows + +package hid + +/* +#include + +const size_t SIZEOF_WCHAR_T = sizeof(wchar_t); + +void gowchar_set (wchar_t *arr, int pos, wchar_t val) +{ + arr[pos] = val; +} + +wchar_t gowchar_get (wchar_t *arr, int pos) +{ + return arr[pos]; +} +*/ +import "C" + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +var sizeofWcharT C.size_t = C.size_t(C.SIZEOF_WCHAR_T) + +func stringToWcharT(s string) (*C.wchar_t, C.size_t) { + switch sizeofWcharT { + case 2: + return stringToWchar2(s) // Windows + case 4: + return stringToWchar4(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTToString(s *C.wchar_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2ToString(s) // Windows + case 4: + return wchar4ToString(s) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +func wcharTNToString(s *C.wchar_t, size C.size_t) (string, error) { + switch sizeofWcharT { + case 2: + return wchar2NToString(s, size) // Windows + case 4: + return wchar4NToString(s, size) // Unix + default: + panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT)) + } +} + +// Windows +func stringToWchar2(s string) (*C.wchar_t, C.size_t) { + var slen int + s1 := s + for len(s1) > 0 { + r, size := utf8.DecodeRuneInString(s1) + if er, _ := utf16.EncodeRune(r); er == '\uFFFD' { + slen += 1 + } else { + slen += 2 + } + s1 = s1[size:] + } + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + if r1, r2 := utf16.EncodeRune(r); r1 != '\uFFFD' { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r1)) + i++ + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r2)) + i++ + } else { + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + i++ + } + s = s[size:] + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Unix +func stringToWchar4(s string) (*C.wchar_t, C.size_t) { + slen := utf8.RuneCountInString(s) + slen++ // \0 + res := C.malloc(C.size_t(slen) * sizeofWcharT) + var i int + for len(s) > 0 { + r, size := utf8.DecodeRuneInString(s) + C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r)) + s = s[size:] + i++ + } + C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0 + return (*C.wchar_t)(res), C.size_t(slen) +} + +// Windows +func wchar2ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + } else { + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4ToString(s *C.wchar_t) (string, error) { + var i int + var res string + for { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +} + +// Windows +func wchar2NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + if ch == 0 { + break + } + r := rune(ch) + i++ + if !utf16.IsSurrogate(r) { + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + + res += string(r) + } else { + if i >= N { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + ch2 := C.gowchar_get(s, C.int(i)) + r2 := rune(ch2) + r12 := utf16.DecodeRune(r, r2) + if r12 == '\uFFFD' { + err := fmt.Errorf("Invalid surrogate pair at position %v", i-1) + return "", err + } + res += string(r12) + i++ + } + } + return res, nil +} + +// Unix +func wchar4NToString(s *C.wchar_t, size C.size_t) (string, error) { + var i int + var res string + N := int(size) + for i < N { + ch := C.gowchar_get(s, C.int(i)) + r := rune(ch) + if !utf8.ValidRune(r) { + err := fmt.Errorf("Invalid rune at position %v", i) + return "", err + } + res += string(r) + i++ + } + return res, nil +} diff --git a/vendor/github.com/karalabe/hid/wchar_test.go b/vendor/github.com/karalabe/hid/wchar_test.go new file mode 100644 index 0000000..b80ebb8 --- /dev/null +++ b/vendor/github.com/karalabe/hid/wchar_test.go @@ -0,0 +1,66 @@ +// This file is https://github.com/orofarne/gowchar/blob/master/gowchar_test.go +// +// It was vendored inline to work around CGO limitations that don't allow C types +// to directly cross package API boundaries. +// +// The vendored file is licensed under the 3-clause BSD license, according to: +// https://github.com/orofarne/gowchar/blob/master/LICENSE + +// +build !ios +// +build linux darwin windows + +package hid + +import ( + "fmt" + "testing" +) + +func TestGowcharSimple(t *testing.T) { + str1 := "Привет, 世界. 𪛖" + wstr, size := stringToWcharT(str1) + switch sizeofWcharT { + case 2: + if size != 15 { + t.Errorf("size(%v) != 15", size) + } + case 4: + if size != 14 { + t.Errorf("size(%v) != 14", size) + } + default: + panic(fmt.Sprintf("sizeof(wchar_t) = %v", sizeofWcharT)) + } + str2, err := wcharTToString(wstr) + if err != nil { + t.Errorf("wcharTToString error: %v", err) + } + if str1 != str2 { + t.Errorf("\"%s\" != \"%s\"", str1, str2) + } +} + +func TestGowcharSimpleN(t *testing.T) { + str1 := "Привет, 世界. 𪛖" + wstr, size := stringToWcharT(str1) + switch sizeofWcharT { + case 2: + if size != 15 { + t.Errorf("size(%v) != 15", size) + } + case 4: + if size != 14 { + t.Errorf("size(%v) != 14", size) + } + default: + panic(fmt.Sprintf("sizeof(wchar_t) = %v", sizeofWcharT)) + } + + str2, err := wcharTNToString(wstr, size-1) + if err != nil { + t.Errorf("wcharTToString error: %v", err) + } + if str1 != str2 { + t.Errorf("\"%s\" != \"%s\"", str1, str2) + } +} -- cgit v1.2.3