From 6c24a31ff7518a0e02ad69e93c994a5d7b923fab Mon Sep 17 00:00:00 2001 From: Daniel Hunsaker Date: Sat, 21 Jul 2018 06:32:21 -0600 Subject: [PATCH] Switch C StdLib time implementation Not all platforms even _have_ the required functions. --- .travis.yml | 19 +++ build-all | 28 +++++ calendars/gregorian.go | 8 +- calends.go | 2 +- dist/.gitignore | 2 + libcalends/calends.go | 220 ++++------------------------------ libcalends/calends_exports.go | 184 ++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 203 deletions(-) create mode 100755 build-all create mode 100644 dist/.gitignore create mode 100644 libcalends/calends_exports.go diff --git a/.travis.yml b/.travis.yml index 3e3abbf..88e96da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -90,3 +90,22 @@ after_failure: after_success: - bash <(curl -s https://codecov.io/bash) + - | + if [[ -n ${TRAVIS_TAG} && ${TRAVIS_GO_VERSION} = '1.10.x' ]] + then + set -e + cd ${TRAVIS_BUILD_DIR} + ./build-all + go get github.com/aktau/github-release + github-release info --user danhunsaker --repo calends --tag ${TRAVIS_TAG} || + github-release release --user danhunsaker --repo calends --tag ${TRAVIS_TAG} --draft + for archive in $(find dist/ -iname '*.tgz') + do + github-release upload \ + --user danhunsaker \ + --repo calends \ + --tag ${TRAVIS_TAG} \ + --name "${archive#dist/}" \ + --file "${archive}" + done + fi diff --git a/build-all b/build-all new file mode 100755 index 0000000..a484e92 --- /dev/null +++ b/build-all @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -e + +GOPATH=$(go env GOPATH) +TARGETS=${TARGETS:-linux/386,linux/amd64,linux/arm-5,linux/arm-6,linux/arm-7,linux/arm64,windows-4.0/*,windows-6.0/*,windows-6.1/*,windows-6.3/*,windows-10.0/*} + +v=$(grep 'Version =' calends.go | awk '{ print $4 }') +VERSION=$(echo v${v//\"/}) +unset v + +go get github.com/karalabe/xgo +${GOPATH}/bin/xgo -buildmode=c-shared -dest=$(pwd)/dist/ -out=libcalends-${VERSION} -targets=${TARGETS} github.com/danhunsaker/calends/libcalends + +for h in $(find dist/ -iname '*.h') +do + dir=${h/.h/} + mkdir -p ${dir} + mv ${dir}.h ${dir}/libcalends.h + # Linux + [ -e ${dir}.so ] && mv ${dir}.so ${dir}/libcalends.so + # Darwin + [ -e ${dir}.dylib ] && mv ${dir}.dylib ${dir}/libcalends.dylib + # Windows + [ -e ${dir}.dll ] && mv ${dir}.dll ${dir}/libcalends.dll + tar czf ${dir}.tgz -C ${dir} . + rm -rf ${dir} +done diff --git a/calendars/gregorian.go b/calendars/gregorian.go index 81da8a5..11f353b 100644 --- a/calendars/gregorian.go +++ b/calendars/gregorian.go @@ -11,7 +11,7 @@ Supported Input Types: Supported Format Strings: - any format supported by the time library or - github.com/danhunsaker/go-datefmt (or github.com/olebedev/when for Offset) + github.com/knz/strtime (or github.com/olebedev/when for Offset) */ package calendars @@ -21,7 +21,7 @@ import ( "strings" "time" - datefmt "github.com/danhunsaker/go-datefmt" + "github.com/knz/strtime" when "github.com/olebedev/when" when_common "github.com/olebedev/when/rules/common" when_en "github.com/olebedev/when/rules/en" @@ -50,7 +50,7 @@ func init() { if str != "" { if strings.ContainsRune(format, '%') { - in, err = datefmt.Strptime(format, str) + in, err = strtime.Strptime(str, format) } else { in, err = time.Parse(format, str) } @@ -68,7 +68,7 @@ func init() { func(stamp TAI64NAXURTime, format string) (date string, err error) { tmp := time.Unix(stamp.Seconds, int64(stamp.Nano)).UTC() if strings.ContainsRune(format, '%') { - date, err = datefmt.Strftime(format, tmp) + date, err = strtime.Strftime(tmp, format) } else { date = tmp.Format(format) } diff --git a/calends.go b/calends.go index 2ac17bb..6ba8be4 100644 --- a/calends.go +++ b/calends.go @@ -41,7 +41,7 @@ type Calends struct { } // Version of the library -var Version = "0.0.3" +var Version = "0.0.4" // Create is the mechanism for constructing new Calends objects. /* diff --git a/dist/.gitignore b/dist/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/dist/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/libcalends/calends.go b/libcalends/calends.go index 66813b9..0b711ad 100644 --- a/libcalends/calends.go +++ b/libcalends/calends.go @@ -16,35 +16,35 @@ import ( "github.com/danhunsaker/calends" ) -var SHARDS = uint64(64) +var maxShards = uint64(64) -type ConcurrentMap []*ConcurrentMapShard +type concurrentMap []*concurrentMapShard -type ConcurrentMapShard struct { +type concurrentMapShard struct { items map[uint64]interface{} sync.RWMutex } -func NewConcurrentMap() ConcurrentMap { - m := make(ConcurrentMap, SHARDS) - for i := uint64(0); i < SHARDS; i++ { - m[i] = &ConcurrentMapShard{items: make(map[uint64]interface{})} +func newConcurrentMap() concurrentMap { + m := make(concurrentMap, maxShards) + for i := uint64(0); i < maxShards; i++ { + m[i] = &concurrentMapShard{items: make(map[uint64]interface{})} } return m } -func (m ConcurrentMap) getShard(key uint64) *ConcurrentMapShard { - return m[key%SHARDS] +func (m concurrentMap) getShard(key uint64) *concurrentMapShard { + return m[key%maxShards] } -func (m ConcurrentMap) Store(key uint64, value interface{}) { +func (m concurrentMap) Store(key uint64, value interface{}) { shard := m.getShard(key) shard.Lock() shard.items[key] = value shard.Unlock() } -func (m ConcurrentMap) Load(key uint64) (interface{}, bool) { +func (m concurrentMap) Load(key uint64) (interface{}, bool) { shard := m.getShard(key) shard.RLock() val, ok := shard.items[key] @@ -52,7 +52,7 @@ func (m ConcurrentMap) Load(key uint64) (interface{}, bool) { return val, ok } -func (m ConcurrentMap) Length() (out uint64) { +func (m concurrentMap) Length() (out uint64) { for _, shard := range m { shard.RLock() out += uint64(len(shard.items)) @@ -61,7 +61,7 @@ func (m ConcurrentMap) Length() (out uint64) { return } -func (m ConcurrentMap) All() (out []interface{}) { +func (m concurrentMap) All() (out []interface{}) { for _, shard := range m { shard.RLock() for _, item := range shard.items { @@ -72,30 +72,30 @@ func (m ConcurrentMap) All() (out []interface{}) { return } -func (m ConcurrentMap) Delete(key uint64) { +func (m concurrentMap) Delete(key uint64) { shard := m.getShard(key) shard.Lock() delete(shard.items, key) shard.Unlock() } -type IdGenerator struct { +type idGenerator struct { id uint64 } -func (generator *IdGenerator) Id() uint64 { +func (generator *idGenerator) Id() uint64 { return atomic.AddUint64(&generator.id, 1) } -var panicHandlers ConcurrentMap -var nextPanHandle IdGenerator -var instances ConcurrentMap -var nextInst IdGenerator +var panicHandlers concurrentMap +var nextPanHandle idGenerator +var instances concurrentMap +var nextInst idGenerator func init() { C.Calends_version = C.CString(calends.Version) - panicHandlers = NewConcurrentMap() - instances = NewConcurrentMap() + panicHandlers = newConcurrentMap() + instances = newConcurrentMap() } func instNum(c calends.Calends) C.ulonglong { @@ -122,182 +122,6 @@ func calends_create(stamp interface{}, calendar, format *C.char) C.ulonglong { return instNum(c) } -//export Calends_release -func Calends_release(p C.ulonglong) { - instances.Delete(uint64(p)) -} - -//export Calends_create_string -func Calends_create_string(stamp, calendar, format *C.char) C.ulonglong { - return calends_create(C.GoString(stamp), calendar, format) -} - -//export Calends_create_string_range -func Calends_create_string_range(start, end, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": C.GoString(start), - "end": C.GoString(end), - }, calendar, format) -} - -//export Calends_create_string_start_period -func Calends_create_string_start_period(start, duration, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": C.GoString(start), - "duration": C.GoString(duration), - }, calendar, format) -} - -//export Calends_create_string_end_period -func Calends_create_string_end_period(duration, end, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "duration": C.GoString(duration), - "end": C.GoString(end), - }, calendar, format) -} - -//export Calends_create_long_long -func Calends_create_long_long(stamp C.longlong, calendar, format *C.char) C.ulonglong { - return calends_create(int(stamp), calendar, format) -} - -//export Calends_create_long_long_range -func Calends_create_long_long_range(start, end C.longlong, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": int(start), - "end": int(end), - }, calendar, format) -} - -//export Calends_create_long_long_start_period -func Calends_create_long_long_start_period(start, duration C.longlong, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": int(start), - "duration": int(duration), - }, calendar, format) -} - -//export Calends_create_long_long_end_period -func Calends_create_long_long_end_period(duration, end C.longlong, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "duration": int(duration), - "end": int(end), - }, calendar, format) -} - -//export Calends_create_double -func Calends_create_double(stamp C.double, calendar, format *C.char) C.ulonglong { - return calends_create(float64(stamp), calendar, format) -} - -//export Calends_create_double_range -func Calends_create_double_range(start, end C.double, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": float64(start), - "end": float64(end), - }, calendar, format) -} - -//export Calends_create_double_start_period -func Calends_create_double_start_period(start, duration C.double, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "start": float64(start), - "duration": float64(duration), - }, calendar, format) -} - -//export Calends_create_double_end_period -func Calends_create_double_end_period(duration, end C.double, calendar, format *C.char) C.ulonglong { - return calends_create(map[string]interface{}{ - "duration": float64(duration), - "end": float64(end), - }, calendar, format) -} - -//export Calends_date -func Calends_date(p C.ulonglong, calendar, format *C.char) *C.char { - defer handlePanic() - c := instGet(p) - out, err := c.Date(C.GoString(calendar), C.GoString(format)) - if err != nil { - panic(err) - } - return C.CString(out) -} - -//export Calends_duration -func Calends_duration(p C.ulonglong) *C.char { - c := instGet(p) - return C.CString(c.Duration().String()) -} - -//export Calends_end_date -func Calends_end_date(p C.ulonglong, calendar, format *C.char) *C.char { - defer handlePanic() - c := instGet(p) - out, err := c.EndDate(C.GoString(calendar), C.GoString(format)) - if err != nil { - panic(err) - } - return C.CString(out) -} - -//export Calends_string -func Calends_string(p C.ulonglong) *C.char { - c := instGet(p) - return C.CString(c.String()) -} - -//export Calends_encode_text -func Calends_encode_text(p C.ulonglong) *C.char { - defer handlePanic() - c := instGet(p) - out, err := c.MarshalText() - if err != nil { - panic(err) - } - return C.CString(string(out)) -} - -//export Calends_decode_text -func Calends_decode_text(in *C.char) C.ulonglong { - defer handlePanic() - c := calends.Calends{} - err := c.UnmarshalText([]byte(C.GoString(in))) - if err != nil { - panic(err) - } - return instNum(c) -} - -//export Calends_encode_json -func Calends_encode_json(p C.ulonglong) *C.char { - defer handlePanic() - c := instGet(p) - out, err := c.MarshalJSON() - if err != nil { - panic(err) - } - return C.CString(string(out)) -} - -//export Calends_decode_json -func Calends_decode_json(in *C.char) C.ulonglong { - defer handlePanic() - c := calends.Calends{} - err := c.UnmarshalJSON([]byte(C.GoString(in))) - if err != nil { - panic(err) - } - return instNum(c) -} - -//export Calends_register_panic_handler -func Calends_register_panic_handler(handler C.Calends_panic_handler) { - id := nextPanHandle.Id() - panicHandlers.Store(id, handler) -} - func handlePanic() { var err string diff --git a/libcalends/calends_exports.go b/libcalends/calends_exports.go new file mode 100644 index 0000000..a370733 --- /dev/null +++ b/libcalends/calends_exports.go @@ -0,0 +1,184 @@ +package main + +//typedef void (*Calends_panic_handler) (char*); +import "C" + +import ( + "github.com/danhunsaker/calends" +) + +//export Calends_release +func Calends_release(p C.ulonglong) { + instances.Delete(uint64(p)) +} + +//export Calends_create_string +func Calends_create_string(stamp, calendar, format *C.char) C.ulonglong { + return calends_create(C.GoString(stamp), calendar, format) +} + +//export Calends_create_string_range +func Calends_create_string_range(start, end, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": C.GoString(start), + "end": C.GoString(end), + }, calendar, format) +} + +//export Calends_create_string_start_period +func Calends_create_string_start_period(start, duration, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": C.GoString(start), + "duration": C.GoString(duration), + }, calendar, format) +} + +//export Calends_create_string_end_period +func Calends_create_string_end_period(duration, end, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "duration": C.GoString(duration), + "end": C.GoString(end), + }, calendar, format) +} + +//export Calends_create_long_long +func Calends_create_long_long(stamp C.longlong, calendar, format *C.char) C.ulonglong { + return calends_create(int(stamp), calendar, format) +} + +//export Calends_create_long_long_range +func Calends_create_long_long_range(start, end C.longlong, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": int(start), + "end": int(end), + }, calendar, format) +} + +//export Calends_create_long_long_start_period +func Calends_create_long_long_start_period(start, duration C.longlong, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": int(start), + "duration": int(duration), + }, calendar, format) +} + +//export Calends_create_long_long_end_period +func Calends_create_long_long_end_period(duration, end C.longlong, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "duration": int(duration), + "end": int(end), + }, calendar, format) +} + +//export Calends_create_double +func Calends_create_double(stamp C.double, calendar, format *C.char) C.ulonglong { + return calends_create(float64(stamp), calendar, format) +} + +//export Calends_create_double_range +func Calends_create_double_range(start, end C.double, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": float64(start), + "end": float64(end), + }, calendar, format) +} + +//export Calends_create_double_start_period +func Calends_create_double_start_period(start, duration C.double, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "start": float64(start), + "duration": float64(duration), + }, calendar, format) +} + +//export Calends_create_double_end_period +func Calends_create_double_end_period(duration, end C.double, calendar, format *C.char) C.ulonglong { + return calends_create(map[string]interface{}{ + "duration": float64(duration), + "end": float64(end), + }, calendar, format) +} + +//export Calends_date +func Calends_date(p C.ulonglong, calendar, format *C.char) *C.char { + defer handlePanic() + c := instGet(p) + out, err := c.Date(C.GoString(calendar), C.GoString(format)) + if err != nil { + panic(err) + } + return C.CString(out) +} + +//export Calends_duration +func Calends_duration(p C.ulonglong) *C.char { + c := instGet(p) + return C.CString(c.Duration().String()) +} + +//export Calends_end_date +func Calends_end_date(p C.ulonglong, calendar, format *C.char) *C.char { + defer handlePanic() + c := instGet(p) + out, err := c.EndDate(C.GoString(calendar), C.GoString(format)) + if err != nil { + panic(err) + } + return C.CString(out) +} + +//export Calends_string +func Calends_string(p C.ulonglong) *C.char { + c := instGet(p) + return C.CString(c.String()) +} + +//export Calends_encode_text +func Calends_encode_text(p C.ulonglong) *C.char { + defer handlePanic() + c := instGet(p) + out, err := c.MarshalText() + if err != nil { + panic(err) + } + return C.CString(string(out)) +} + +//export Calends_decode_text +func Calends_decode_text(in *C.char) C.ulonglong { + defer handlePanic() + c := calends.Calends{} + err := c.UnmarshalText([]byte(C.GoString(in))) + if err != nil { + panic(err) + } + return instNum(c) +} + +//export Calends_encode_json +func Calends_encode_json(p C.ulonglong) *C.char { + defer handlePanic() + c := instGet(p) + out, err := c.MarshalJSON() + if err != nil { + panic(err) + } + return C.CString(string(out)) +} + +//export Calends_decode_json +func Calends_decode_json(in *C.char) C.ulonglong { + defer handlePanic() + c := calends.Calends{} + err := c.UnmarshalJSON([]byte(C.GoString(in))) + if err != nil { + panic(err) + } + return instNum(c) +} + +//export Calends_register_panic_handler +func Calends_register_panic_handler(handler C.Calends_panic_handler) { + id := nextPanHandle.Id() + panicHandlers.Store(id, handler) +}