diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..cc3c613 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,6 @@ +#### Relevant Issue (if applicable) +(If there are Issues related to this PullRequest, please list it.) + +#### Details +(Please describe the details of PullRequest.) + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3cf6be2 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,44 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + release: + types: + - published + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + if: "! contains(toJSON(github.event.commits.*.message), '[skip ci]') && ! contains(toJSON(github.event.commits.*.message), '[ci skip]')" + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: '^1.14' + - name: Run a one-line script + run: echo Hello, world! + - name: Run a multi-line script + run: | + echo Add other actions to build, + echo test, and deploy your project. + - name: Check golang version + run: go version + - name: Check env + run: env + - name: Init + run: make init + - name: Test + run: make test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca7c436 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# emacs backup files +*.go~ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1a5d4aa --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Hirotaka Wakabayashi +Takeshi Nakatani diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ebae5d9 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,5 @@ +k2hash_go (1.0.0) stable; urgency=low + + * Initial commit + + -- Hirotaka Wakabayashi Fri, 17 Apr 2020 07:40:05 +0000 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b974626 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Yahoo Japan Corporation + +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..db1efd1 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +# +# k2hash_go +# +# Copyright 2018 Yahoo Japan Corporation. +# +# Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +# For k2hash, see https://github.com/yahoojapan/k2hash for the details. +# +# For the full copyright and license information, please view +# the license file that was distributed with this source code. +# +# AUTHOR: Hirotaka Wakabayashi +# CREATE: Fri, 14 Sep 2018 +# REVISION: +# + +build: + # use _build as the GOPATH-base + @/bin/echo "Running k2hash_go build" + GOPATH=$(PWD)/_build go install -v github.com/yahoojapan/k2hash_go/... + @echo "OK - built the following binaries:" + ls -l _build/bin + +init: + # 0. remove the build directory + rm -fr _build + # 1. get source. + @echo "Running k2hash_go init (fetching source code)" + git clone https://github.com/yahoojapan/k2hash_go.git _build/src/github.com/yahoojapan/k2hash_go + # 2. syntax check. + BAD_GOFMT_FILES=$(find ./_build -name '*.go' | xargs gofmt -l) + @echo ".go files that are not gofmt-compliant (empty if all are fine): [$(BAD_GOFMT_FILES)]" + # 3. install libk2hash + sh utils/libk2hash.sh + +test: + @echo "Running k2hash_go test" + GOPATH=$(PWD)/_build go test -v github.com/yahoojapan/k2hash_go/tests + GOPATH=$(PWD)/_build go test -v github.com/yahoojapan/k2hash_go/tests -coverprofile=c.out + GOPATH=$(PWD)/_build go tool cover -html=c.out + +publish: + @echo "Running k2hash_go publish" + # TODO: add scp of binaries to Artifactory (or RPM package creation and uploading) + +# Local Variables: +# c-basic-offset: 4 +# tab-width: 4 +# indent-tabs-mode: t +# End: +# vim600: noexpandtab sw=4 ts=4 fdm=marker +# vim<600: noexpandtab sw=4 ts=4 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..db642a9 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# k2hash_go + +### Overview + +**k2hash_go** implements a [k2hash](https://k2hash.antpick.ax/) client in golang. + +### Install + +Firstly you must install the [k2hash](https://k2hash.antpick.ax/) shared library. +``` +$ curl -o- https://raw.github.com/yahoojapan/k2hash_go/master/utils/libk2hash.sh | bash +``` +You can install **k2hash** library step by step from [source code](https://github.com/yahoojapan/k2hash). See [Build](https://k2hash.antpick.ax/build.html) for details. + +After you make sure you set the [GOPATH](https://github.com/golang/go/wiki/SettingGOPATH) environment, download the **k2hash_go** package. +``` +$ go get -u github.com/yahoojapan/k2hash_go +``` + +### Usage + +Here is a simple example of **k2hash_go** which save a key and get it. + +```golang +package main + +import ( + "fmt" + "os" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +func SetAndGet() { + // 1. Instantiate K2hash class + file, _ := k2hash.NewK2hash("/tmp/test.k2h") + defer file.Close() + ok, err := file.Set("hello", "world") + if ok != true { + fmt.Fprintf(os.Stderr, "file.Set(hello, world) returned false, err %v\n", err) + } + // 2. Get + // 2.1. no args + val, err := file.Get("hello") + if val == nil || err != nil { + fmt.Fprintf(os.Stderr, "file.Get(hello) returned val %v err %v\n", val, err) + return + } + fmt.Printf("val = %v, err = %v\n", val.String(), err) +} + +func main() { + SetAndGet() +} +``` + +### Development + +Here is the step to start developing **k2hash_go**. + +- Debian / Ubuntu + +```bash +#!/bin/sh + +sudo apt-get update -y && sudo apt-get install curl git -y && curl -s https://packagecloud.io/install/repositories/antpickax/stable/script.deb.sh | sudo bash +sudo apt-get install libfullock-dev k2hash-dev -y +go get github.com/yahoojapan/k2hash_go/k2hash + +exit 0 +``` + +- CentOS / Fedora + +```bash +#!/bin/sh + +sudo dnf makecache && sudo yum install curl git -y && curl -s https://packagecloud.io/install/repositories/antpickax/stable/script.rpm.sh | sudo bash +sudo dnf install libfullock-devel k2hash-devel -y +go get github.com/yahoojapan/k2hash_go/k2hash + +exit 0 +``` + +### Documents + - [About K2HASH](https://k2hash.antpick.ax/) + - [About AntPickax](https://antpick.ax/) + +### License + +MIT License. See the LICENSE file. + +## AntPickax + +[AntPickax](https://antpick.ax/) is + - an open source team in [Yahoo Japan Corporation](https://about.yahoo.co.jp/info/en/company/). + - a product family of open source software developed by [AntPickax](https://antpick.ax/). + diff --git a/examples/getattr/README.md b/examples/getattr/README.md new file mode 100644 index 0000000..1526f73 --- /dev/null +++ b/examples/getattr/README.md @@ -0,0 +1,6 @@ +# getattr + +``` +$ go build +$ ./getattr +``` diff --git a/examples/getattr/main.go b/examples/getattr/main.go new file mode 100644 index 0000000..3703174 --- /dev/null +++ b/examples/getattr/main.go @@ -0,0 +1,311 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package main + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include + // #include "k2hash.h" + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt) { + // return k2h_free_keypack(pkeys, keycnt); + // } + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt); + "C" +) + +import ( + "fmt" + "os" + "unsafe" +) + +// K2hash keeps configurations, and it is responsible for creating request handles with a k2hash database files and closing them. +type K2hash struct { + // filepath is a path to K2HASH file. + filepath string + // readonly enables read only file access if true. default is false. + readonly bool + // removefile enables automatic file deletion if no process attaches the file. default is false. + removefile bool + // fullmap enables memory mapped io for a whole data in a file. default is false. + fullmap bool + // key mask bit. default is 8. + maskbitcnt int + // key collision mask bit. default is 4. + cmaskbitcnt int + // maxelementcnt is a max number of duplicated elements if a hash collision occurs. default is 1024(bytes). + maxelementcnt int + // pagesize is a block size of data. default is 512(bytes). + pagesize int + // waitms is a time to wait until a transaction is completed. default is -1. + waitms int + // handle is a file descriptor to a K2HASH file. + handle C.k2h_h +} + +// String returns a text representation of the object. +func (k2h *K2hash) String() string { + return fmt.Sprintf("[%v, %v, %v, %v, %v, %v, %v, %v, %v, %v]", + k2h.filepath, k2h.readonly, k2h.removefile, k2h.fullmap, k2h.maskbitcnt, k2h.cmaskbitcnt, k2h.maxelementcnt, k2h.pagesize, k2h.waitms, k2h.handle) +} + +// NewK2hash returns a new k2hash instance. +func NewK2hash(f string, options ...func(*K2hash)) (*K2hash, error) { + // 1. set defaults + k2h := K2hash{ + filepath: f, + readonly: false, + removefile: false, + fullmap: false, + maskbitcnt: 8, + cmaskbitcnt: 4, + maxelementcnt: 1024, + pagesize: 512, + handle: 0, + } + // 2. set options + for _, option := range options { + option(&k2h) + } + // 3. open + ok, err := k2h.Open() + if ok == false { + return nil, err + } + return &k2h, nil +} + +// Open opens a k2hash file. +func (k2h *K2hash) Open() (bool, error) { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + k2h.handle = C.k2h_open( + cK2h, + C._Bool(k2h.readonly), + C._Bool(k2h.removefile), + C._Bool(k2h.fullmap), + C.int(k2h.maskbitcnt), + C.int(k2h.cmaskbitcnt), + C.int(k2h.maxelementcnt), + C.size_t(k2h.pagesize)) + + if k2h.handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", k2h.handle) + } + return true, nil +} + +// Close closes a k2hash file. +func (k2h *K2hash) Close() (bool, error) { + ok := C.k2h_close(k2h.handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +// Get returns data from a k2hash file. +func (k2h *K2hash) Get(k interface{}) string { + // 1. binary or text + var key string + switch k.(type) { + default: + return "" + case string: + key = k.(string) + } + + // 2. Gets a text(string) type variable and retrives it by using k2h_get_str_value_wp and k2h_set_str_value_wa + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char + defer C.free(unsafe.Pointer(cRetValue)) + ok := C.k2h_get_str_value(k2h.handle, (*C.char)(cKey), &cRetValue) + if ok { + return C.GoString(cRetValue) + } + return "" +} + +// Set returns true if successfully set a key with a value. +func (k2h *K2hash) Set(k interface{}, v interface{}) (bool, error) { + // 1. binary or text + var key string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + + // 2. Sets a text(string) type variable and retrives it by using k2h_get_str_value_wp and k2h_set_str_value_wa + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + ok := C.k2h_set_str_value_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cVal), nil, nil) + fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +type kv struct { + d string // description + k []byte // key + v []byte // val + r bool // remove subkeys if true. + s bool // true if use string format when saving the data. + p string // password + e int64 // expire +} + +// AddAttr adds an attribute with a value to a key. +func (k2h *K2hash) AddAttr(k interface{}, ak interface{}, av interface{}) (bool, error) { + // 1. binary or text + var key string + var attrkey string + var attrval string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch ak.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", ak) + case string: + attrkey = ak.(string) + } + switch av.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", av) + case string: + attrval = av.(string) + } + + // 2. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cAttrKey := C.CString(attrkey) + defer C.free(unsafe.Pointer(cAttrKey)) + cAttrVal := C.CString(attrval) + defer C.free(unsafe.Pointer(cAttrVal)) + ok := C.k2h_add_str_attr(k2h.handle, (*C.char)(cKey), (*C.char)(cAttrKey), (*C.char)(cAttrVal)) + //fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.k2h_add_str_attr return false") + } + return true, nil +} + +// Attr holds attribute names and values. +type Attr struct { + key string + val string +} + +// GetAttrs returns a slice of string +func (k2h *K2hash) GetAttrs(k string) ([]Attr, error) { + // 1. retrieve an attribute using k2h_get_attrs + // bool k2h_get_attrs(k2h_h handle, const unsigned char* pkey, size_t keylength, PK2HATTRPCK* ppattrspck, int* pattrspckcnt) + cKey := C.CBytes([]byte(k)) + defer C.free(unsafe.Pointer(cKey)) + var attrpack C.PK2HATTRPCK + var attrpackCnt C.int + ok := C.k2h_get_attrs( + k2h.handle, + (*C.uchar)(cKey), + C.size_t(len([]byte(k))+1), // plus one for a null termination + &attrpack, + &attrpackCnt, + ) + defer C.k2h_free_attrpack(attrpack, attrpackCnt) // free the memory for the keypack for myself(GC doesn't know the area) + + if ok == false { + fmt.Println("C.k2h_get_attrs returns false") + return []Attr{}, fmt.Errorf("C.k2h_get_attrs() = %v", ok) + } else if attrpackCnt == 0 { + fmt.Printf("attrpackLen is zero") + return []Attr{}, nil + } else { + fmt.Printf("attrpackLen is %v\n", attrpackCnt) + } + // 2. copy an attribute data to a slice + var CAttrs C.PK2HATTRPCK = attrpack + count := (int)(attrpackCnt) + slice := (*[1 << 28]C.K2HATTRPCK)(unsafe.Pointer(CAttrs))[:count:count] + fmt.Printf("slice size is %v\n", len(slice)) + // + attrs := make([]Attr, count) // copy + for i, data := range slice { + // copy the data with len-1 length, which exclude a null termination. + attrkey := C.GoBytes(unsafe.Pointer(data.pkey), (C.int)(data.keylength-1)) + fmt.Printf("i %v data %T pkey %v length %v attrkey %v\n", i, data, data.pkey, data.keylength, string(attrkey)) + attrval := C.GoBytes(unsafe.Pointer(data.pval), (C.int)(data.vallength-1)) + fmt.Printf("i %v data %T pval %v length %v attrval %v\n", i, data, data.pval, data.vallength, string(attrval)) + // cast bytes to a string + attrs[i].key = string(attrkey) + attrs[i].val = string(attrval) + } + return attrs, nil +} + +func main() { + // 1. define test data. + k, err := NewK2hash("/tmp/sample.k2h") + if err != nil { + fmt.Printf("err %v¥n", err) + os.Exit(1) + } + + defer k.Close() + ok, err := k.Set("key1", "val1") + fmt.Printf("Set(key1) ok %v err %v\n", ok, err) + + ok, err = k.AddAttr("set1", "attrkey1", "attrval1") + fmt.Printf("AddAttr(sey1, attrkey1, attrval1) ok %v err %v\n", ok, err) + + attrs, err := k.GetAttrs("set1") + if err == nil { + for _, data := range attrs { + fmt.Printf("GetAttrs(key1) ok key %v val %v\n", data.key, data.val) + } + } else { + fmt.Printf("GetAttrs(key1) not ok %v\n", err) + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/examples/getsubkey/README.md b/examples/getsubkey/README.md new file mode 100644 index 0000000..7ffe56f --- /dev/null +++ b/examples/getsubkey/README.md @@ -0,0 +1,6 @@ +# getsubkey + +``` +$ go build +$ ./getsubkey +``` diff --git a/examples/getsubkey/main.go b/examples/getsubkey/main.go new file mode 100644 index 0000000..a211d55 --- /dev/null +++ b/examples/getsubkey/main.go @@ -0,0 +1,322 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package main + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include + // #include "k2hash.h" + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt) { + // return k2h_free_keypack(pkeys, keycnt); + // } + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt); + "C" +) + +import ( + "errors" + "fmt" + "os" + "unsafe" +) + +// K2hash keeps configurations, and it is responsible for creating request handles with a k2hash database files and closing them. +type K2hash struct { + // filepath is a path to K2HASH file. + filepath string + // readonly enables read only file access if true. default is false. + readonly bool + // removefile enables automatic file deletion if no process attaches the file. default is false. + removefile bool + // fullmap enables memory mapped io for a whole data in a file. default is false. + fullmap bool + // key mask bit. default is 8. + maskbitcnt int + // key collision mask bit. default is 4. + cmaskbitcnt int + // maxelementcnt is a max number of duplicated elements if a hash collision occurs. default is 1024(bytes). + maxelementcnt int + // pagesize is a block size of data. default is 512(bytes). + pagesize int + // waitms is a time to wait until a transaction is completed. default is -1. + waitms int + // handle is a file descriptor to a K2HASH file. + handle C.k2h_h +} + +// String returns a text representation of the object. +func (k2h *K2hash) String() string { + return fmt.Sprintf("[%v, %v, %v, %v, %v, %v, %v, %v, %v, %v]", + k2h.filepath, k2h.readonly, k2h.removefile, k2h.fullmap, k2h.maskbitcnt, k2h.cmaskbitcnt, k2h.maxelementcnt, k2h.pagesize, k2h.waitms, k2h.handle) +} + +// NewK2hash returns a new k2hash instance. +func NewK2hash(f string, options ...func(*K2hash)) (*K2hash, error) { + // 1. set defaults + k2h := K2hash{ + filepath: f, + readonly: false, + removefile: false, + fullmap: false, + maskbitcnt: 8, + cmaskbitcnt: 4, + maxelementcnt: 1024, + pagesize: 512, + handle: 0, + } + // 2. set options + for _, option := range options { + option(&k2h) + } + // 3. open + ok, err := k2h.Open() + if ok == false { + return nil, err + } + return &k2h, nil +} + +// Open opens a k2hash file. +func (k2h *K2hash) Open() (bool, error) { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + k2h.handle = C.k2h_open( + cK2h, + C._Bool(k2h.readonly), + C._Bool(k2h.removefile), + C._Bool(k2h.fullmap), + C.int(k2h.maskbitcnt), + C.int(k2h.cmaskbitcnt), + C.int(k2h.maxelementcnt), + C.size_t(k2h.pagesize)) + + if k2h.handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", k2h.handle) + } + return true, nil +} + +// Close closes a k2hash file. +func (k2h *K2hash) Close() (bool, error) { + ok := C.k2h_close(k2h.handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +// Get returns data from a k2hash file. +func (k2h *K2hash) Get(k interface{}) string { + // 1. binary or text + var key string + switch k.(type) { + default: + return "" + case string: + key = k.(string) + } + + // 2. Sets a text(string) type variable and retrives it by using k2h_get_str_value_wp and k2h_set_str_value_wa + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char + defer C.free(unsafe.Pointer(cRetValue)) + ok := C.k2h_get_str_value(k2h.handle, (*C.char)(cKey), &cRetValue) + if ok { + return C.GoString(cRetValue) + } + return "" +} + +// Set returns true if successfully set a key with a value. +func (k2h *K2hash) Set(k interface{}, v interface{}) (bool, error) { + // 1. binary or text + var key string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + + // 2. Gets a text(string) type variable and retrives it by using k2h_get_str_value_wp and k2h_set_str_value_wa + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + ok := C.k2h_set_str_value_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cVal), nil, nil) + fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +type kv struct { + d string // description + k []byte // key + v []byte // val + r bool // remove subkeys if true. + s bool // true if use string format when saving the data. + p string // password + e int64 // expire +} + +func clearIfExists(d kv) (bool, error) { + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + // 1. open + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. remove + cKey := C.CBytes(d.k) + defer C.free(cKey) + ok := C.k2h_remove_all( + handler, + (*C.uchar)(cKey), + C.size_t(len(d.k))) + if ok == false { + return false, errors.New("C.k2h_remove_all returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, nil +} + +// SetSubKeys add subkeys to a key. +func (k2h *K2hash) SetSubKeys(k string, sk []string) (bool, error) { + skeys := make([]*C.char, len(sk)+1, len(sk)+1) + for i, s := range sk { + fmt.Printf("i %v s %v\n", i, s) + skeys[i] = C.CString(s) + defer C.free(unsafe.Pointer(skeys[i])) + } + skeys[len(sk)] = nil + // 3. get from k2hash get API + cKey := C.CString(k) + defer C.free(unsafe.Pointer(cKey)) + //ok := true + if &skeys[0] != nil { + ok := C.k2h_set_str_subkeys(k2h.handle, (*C.char)(cKey), (**C.char)(&skeys[0])) + if ok != true { + return false, fmt.Errorf("C.C.k2h_set_str_str_wa return false") + } + } + return true, nil +} + +// GetSubKeys returns subkeys to a key. +func (k2h *K2hash) GetSubKeys(k string) ([]string, error) { + // 1. retrieve subkeys using k2h_get_subkeys + cKey := C.CBytes([]byte(k)) + defer C.free(unsafe.Pointer(cKey)) + var keypack C.PK2HKEYPCK + var keypackLen C.int + ok := C.k2h_get_subkeys( + k2h.handle, + (*C.uchar)(cKey), + C.size_t(len([]byte(k))+1), // plus one for a null termination + &keypack, + &keypackLen, + ) + defer C.k2h_free_keypack(keypack, keypackLen) // free the memory for the keypack for myself(GC doesn't know the area) + + if ok == false { + fmt.Println("C.k2h_get_subkeys returns false") + return []string{""}, fmt.Errorf("C.k2h_get_subkeys() = %v", ok) + } else if keypackLen == 0 { + fmt.Printf("keypackLen is zero") + return []string{""}, nil + } else { + fmt.Printf("keypackLen is %v\n", keypackLen) + } + // 2. copy a subkey data to a slice + var CSubKey C.PK2HKEYPCK = keypack + length := (int)(keypackLen) + slice := (*[1 << 28]C.K2HKEYPCK)(unsafe.Pointer(CSubKey))[:length:length] + fmt.Printf("slice size is %v\n", len(slice)) + skeys := make([]string, length) // copy + for i, data := range slice { + // copy the data with len-1 length, which exclude a null termination. + sk := C.GoBytes(unsafe.Pointer(data.pkey), (C.int)(data.length-1)) + fmt.Printf("i %v data %T pkey %v length %v sk %v\n", i, data, data.pkey, data.length, string(sk)) + skeys[i] = string(sk) + } + return skeys, nil +} + +func main() { + // 1. define test data. + k, err := NewK2hash("/tmp/sample.k2h") + if err != nil { + fmt.Printf("err %v¥n", err) + os.Exit(1) + } + + defer k.Close() + ok, err := k.Set("key1", "val1") + fmt.Printf("Set(key1) ok %v err %v\n", ok, err) + + ok, err = k.Set("set1", "setval1") + fmt.Printf("Set(sey1) ok %v err %v\n", ok, err) + + ok, err = k.Set("set2", "setval2") + fmt.Printf("Set(sey2) ok %v err %v\n", ok, err) + + ok, err = k.SetSubKeys("key1", []string{"set1", "set2"}) + fmt.Printf("SetSubKeys(key1, []string{subkey1, subkey2}) ok %v err %v\n", ok, err) + + skeys, err := k.GetSubKeys("key1") + for i, key := range skeys { + fmt.Printf("i %v key %v err %v\n", i, key, err) + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/examples/queue/README.md b/examples/queue/README.md new file mode 100644 index 0000000..a544b12 --- /dev/null +++ b/examples/queue/README.md @@ -0,0 +1,6 @@ +# queue + +``` +$ go build +$ ./queue +``` diff --git a/examples/queue/main.go b/examples/queue/main.go new file mode 100644 index 0000000..4101df5 --- /dev/null +++ b/examples/queue/main.go @@ -0,0 +1,284 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package main + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include + // #include "k2hash.h" + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt) { + // return k2h_free_keypack(pkeys, keycnt); + // } + // bool free_keypack(PK2HKEYPCK pkeys, int keycnt); + "C" +) + +import ( + "fmt" + "os" + "unsafe" +) + +// K2hashParams stores parameters for k2hash C API +type K2hashParams struct { + password string + expirationDuration int64 +} + +// K2hashQueue keeps a queue configurations. +type K2hashQueue struct { + // K2HASH file handle + handle C.k2h_h + // K2HASH queue handle + qhandle C.k2h_q_h + // fifo + fifo bool + // prefix + prefix string +} + +// String returns a text representation of the object. +func (q *K2hashQueue) String() string { + return fmt.Sprintf("[%v, %v, %v, %v]", q.handle, q.qhandle, q.fifo, q.prefix) +} + +// NewK2hashQueue returns a new k2hash queue instance. +func NewK2hashQueue(h C.k2h_h, options ...func(*K2hashQueue)) (*K2hashQueue, error) { + // 1. set defaults + q := K2hashQueue{ + handle: h, + qhandle: C.K2H_INVALID_HANDLE, + fifo: true, + prefix: "", + } + // 2. set options + for _, option := range options { + option(&q) + } + // 3. open + var qh C.k2h_q_h + if q.prefix == "" { + qh = C.k2h_q_handle(q.handle, C._Bool(q.fifo)) + } else { + cPrefix := C.CBytes([]byte(q.prefix)) + defer C.free(unsafe.Pointer(cPrefix)) + qh = C.k2h_q_handle_prefix(q.handle, C._Bool(q.fifo), (*C.uchar)(cPrefix), C.size_t(len([]byte(q.prefix)))) + } + // 4. check qeueu handle + if qh == C.K2H_INVALID_HANDLE { + return nil, fmt.Errorf("C.k2h_q_handle return false") + } + // 5. reset qhandle + q.qhandle = qh + return &q, nil +} + +// Push adds a value to the queue. +func (q *K2hashQueue) Push(v interface{}, options ...func(*K2hashParams)) (bool, error) { + // 1. binary or text + var val string + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + // 2. set params + params := K2hashParams{ + password: "", + expirationDuration: 0, + } + for _, option := range options { + option(¶ms) + } + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + if ok := C.k2h_q_str_push_wa(q.qhandle, (*C.char)(cVal), nil, 0, cPass, expire); !ok { + return false, fmt.Errorf("C.k2h_q_str_push return false") + } + return true, nil +} + +// Pop retrieves a value from the queue. +func (q *K2hashQueue) Pop(options ...func(*K2hashParams)) (string, error) { + // 2. set params + params := K2hashParams{ + password: "", + expirationDuration: 0, + } + for _, option := range options { + option(¶ms) + } + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var cRetVal (*C.char) + defer C.free(unsafe.Pointer(cRetVal)) + ok := C.k2h_q_str_pop_wa(q.qhandle, &cRetVal, nil, nil, cPass) + defer C.free(unsafe.Pointer(cRetVal)) + if !ok { + return "", fmt.Errorf("C.k2h_q_str_pop return false") + } + val := C.GoString(cRetVal) + return val, nil +} + +// Free destroys a k2hash queue handle. +func (q *K2hashQueue) Free() (bool, error) { + ok := C.k2h_q_free(q.qhandle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + q.qhandle = C.K2H_INVALID_HANDLE + return true, nil +} + +// Count returns the number of k2hash queue elements. +func (q *K2hashQueue) Count() (int, error) { + count := C.k2h_q_count(q.qhandle) + return int(count), nil +} + +// K2hash keeps configurations, and it is responsible for creating request handles with a k2hash database files and closing them. +type K2hash struct { + // filepath is a path to K2HASH file. + filepath string + // readonly enables read only file access if true. default is false. + readonly bool + // removefile enables automatic file deletion if no process attaches the file. default is false. + removefile bool + // fullmap enables memory mapped io for a whole data in a file. default is false. + fullmap bool + // key mask bit. default is 8. + maskbitcnt int + // key collision mask bit. default is 4. + cmaskbitcnt int + // maxelementcnt is a max number of duplicated elements if a hash collision occurs. default is 1024(bytes). + maxelementcnt int + // pagesize is a block size of data. default is 512(bytes). + pagesize int + // waitms is a time to wait until a transaction is completed. default is -1. + waitms int + // handle is a file descriptor to a K2HASH file. + handle C.k2h_h +} + +// String returns a text representation of the object. +func (k2h *K2hash) String() string { + return fmt.Sprintf("[%v, %v, %v, %v, %v, %v, %v, %v, %v, %v]", + k2h.filepath, k2h.readonly, k2h.removefile, k2h.fullmap, k2h.maskbitcnt, k2h.cmaskbitcnt, k2h.maxelementcnt, k2h.pagesize, k2h.waitms, k2h.handle) +} + +// NewK2hash returns a new k2hash instance. +func NewK2hash(f string, options ...func(*K2hash)) (*K2hash, error) { + // 1. set defaults + k2h := K2hash{ + filepath: f, + readonly: false, + removefile: false, + fullmap: false, + maskbitcnt: 8, + cmaskbitcnt: 4, + maxelementcnt: 1024, + pagesize: 512, + handle: 0, + } + // 2. set options + for _, option := range options { + option(&k2h) + } + // 3. open + ok, err := k2h.Open() + if ok == false { + return nil, err + } + return &k2h, nil +} + +// Open opens a k2hash file. +func (k2h *K2hash) Open() (bool, error) { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + k2h.handle = C.k2h_open( + cK2h, + C._Bool(k2h.readonly), + C._Bool(k2h.removefile), + C._Bool(k2h.fullmap), + C.int(k2h.maskbitcnt), + C.int(k2h.cmaskbitcnt), + C.int(k2h.maxelementcnt), + C.size_t(k2h.pagesize)) + + if k2h.handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", k2h.handle) + } + return true, nil +} + +// Close closes a k2hash file. +func (k2h *K2hash) Close() (bool, error) { + ok := C.k2h_close(k2h.handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +func main() { + // 1. define test data. + k, err := NewK2hash("/tmp/sample.k2h") + if err != nil { + fmt.Printf("err %v¥n", err) + os.Exit(1) + } + defer k.Close() + + q, err := NewK2hashQueue(k.handle) + defer q.Free() + fmt.Printf("q %v err %v\n", q.String(), err) + // 1. count + c, _ := q.Count() + fmt.Printf("count %v\n", c) + // 2. push + q.Push("val") + // 3. count + c, _ = q.Count() + fmt.Printf("count %v\n", c) + // 4. pop + s, _ := q.Pop() + fmt.Printf("pop %v\n", s) + // 5. count + c, _ = q.Count() + fmt.Printf("count %v\n", c) +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/examples/setget/README.md b/examples/setget/README.md new file mode 100644 index 0000000..820ef1f --- /dev/null +++ b/examples/setget/README.md @@ -0,0 +1,6 @@ +# setget + +``` +$ go build +$ ./setget +``` diff --git a/examples/setget/main.go b/examples/setget/main.go new file mode 100644 index 0000000..9479e3a --- /dev/null +++ b/examples/setget/main.go @@ -0,0 +1,303 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package main + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include + // #include "k2hash.h" + "C" +) + +import ( + "errors" + "fmt" + "os" + "unsafe" +) + +type kv struct { + d string // description + k []byte // key + v []byte // val + r bool // remove subkeys if true. + s bool // true if use string format when saving the data. + p string // password + e int64 // expire +} + +func clearIfExists(d kv) (bool, error) { + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + // 1. open + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. remove + cKey := C.CBytes(d.k) + defer C.free(cKey) + ok := C.k2h_remove_all( + handler, + (*C.uchar)(cKey), + C.size_t(len(d.k))) + if ok == false { + return false, errors.New("C.k2h_remove_all returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, nil +} + +func setKey(d kv) (bool, error) { + if len(d.k) == 0 { + return false, errors.New("len(k) == 0") + } + + // 1. open + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. set_value + cKey := C.CBytes(d.k) + defer C.free(cKey) + cVal := C.CBytes(d.v) + defer C.free(cVal) + cPass := C.CString(d.p) + defer C.free(unsafe.Pointer(cPass)) + expire := (C.time_t)(60) // expire in 60sec. + ok := C.k2h_set_value_wa(handler, (*C.uchar)(cKey), C.size_t(len(d.k)), (*C.uchar)(cVal), C.size_t(len(d.v)), cPass, &expire) + if ok == false { + return false, errors.New("C.k2h_set_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, nil +} + +func setKeyByString(d kv) (bool, error) { + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + + // 1. open + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. get + cKey := C.CString(string(d.k)) // d.k's type is a Go's byte array. string(d.k)'s type is a Go String, C.CString(string(d.k))'s type is *C.char + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(string("text")) // Cast a Go string to a *C.char + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(d.p) + defer C.free(unsafe.Pointer(cPass)) + expire := (C.time_t)(60) // expire in 60sec. + ok := C.k2h_set_str_value_wa(handler, cKey, cVal, cPass, &expire) + if ok == false { + return false, errors.New("C.k2h_set_str_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, nil +} + +func getKey(d kv) (bool, []byte, error) { + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + + // 1. open + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, nil, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. get + cKey := C.CBytes(d.k) + defer C.free(cKey) + var cRetValue *C.uchar // value:(*main._Ctype_char)(nil) type:*main._Ctype_char + var valLen C.size_t // valLen value:0x0 type:main._Ctype_size_t + cPass := C.CString(d.p) // func C.CString(string) *C.char + defer C.free(unsafe.Pointer(cPass)) + ok := C.k2h_get_value_wp( + handler, + (*C.uchar)(cKey), + C.size_t(len(d.k)), + &cRetValue, + &valLen, + cPass) + defer C.free(unsafe.Pointer(cRetValue)) + val := C.GoBytes(unsafe.Pointer(cRetValue), C.int(valLen)) + if ok == false { + return false, nil, errors.New("C.k2h_set_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, nil, fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, val, nil +} + +func getKeyByString(d kv) (bool, string, error) { + filepath := "/tmp/test.k2h" + cK2h := C.CString(filepath) + defer C.free(unsafe.Pointer(cK2h)) + + // 1. open + handler := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handler == C.K2H_INVALID_HANDLE { + return false, "", fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handler) + } + + // 2. get + cKey := C.CString(string(d.k)) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char // value:(*main._Ctype_char)(nil) type:*main._Ctype_char + cPass := C.CString(d.p) // func C.CString(string) *C.char + defer C.free(unsafe.Pointer(cPass)) + ok := C.k2h_get_str_value_wp( + handler, + cKey, + &cRetValue, + cPass) + defer C.free(unsafe.Pointer(cRetValue)) + val := C.GoString(cRetValue) + if ok == false { + return false, "", errors.New("C.k2h_set_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handler) + if ok != true { + return false, "", fmt.Errorf("k2h_close() returns false") + } + handler = C.K2H_INVALID_HANDLE + return true, val, nil +} + +func main() { + fmt.Printf("params\n") + // 1. define test data. + var testData = []kv{ + {d: "binary", k: []byte("set1"), v: []byte("bin"), p: "", e: 0, r: false, s: false}, + {d: "string", k: []byte("set2"), v: []byte("bin"), p: "", e: 0, r: false, s: true}, + } + for _, d := range testData { + if ok, err := clearIfExists(d); !ok { + fmt.Printf("error %v\n", err) + os.Exit(1) + } + if ok, err := setKey(d); !ok { + fmt.Printf("error %v\n", err) + os.Exit(1) + } + ok, val, err := getKey(d) + if !ok { + fmt.Printf("error %v\n", err) + os.Exit(1) + } + fmt.Printf("getKey %v val %v\n", string(d.k), string(val)) + if ok, err = setKeyByString(d); !ok { + fmt.Printf("error %v\n", err) + os.Exit(1) + } + ok, valString, err := getKeyByString(d) + if !ok { + fmt.Printf("error %v\n", err) + os.Exit(1) + } + fmt.Printf("getKeyByString %v valString %v\n", string(d.k), valString) + + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/examples/subkeys/README.md b/examples/subkeys/README.md new file mode 100644 index 0000000..e9a1d71 --- /dev/null +++ b/examples/subkeys/README.md @@ -0,0 +1,6 @@ +# subkeys + +``` +$ go build +$ ./subkeys +``` diff --git a/examples/subkeys/main.go b/examples/subkeys/main.go new file mode 100644 index 0000000..636d60b --- /dev/null +++ b/examples/subkeys/main.go @@ -0,0 +1,536 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package main + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include + // #include "k2hash.h" + // + // static PK2HKEYPCK my_get_subkeypack(k2h_h handle, const char* pkey) { + // PK2HKEYPCK pskeypck = NULL; + // size_t len = strlen(pkey) + 1; // plus one for a null char + // int* pskeypckcnt = NULL; + // bool b = false; + // b = k2h_get_subkeys(handle, (const unsigned char*)pkey, len, &pskeypck, pskeypckcnt); + // if (b == false) { + // return NULL; // error + // } + // return pskeypck; + // } + "C" +) + +import ( + "flag" + "fmt" + "os" + "unsafe" +) + +type kv struct { + d string // description + k []byte // key + v []byte // val + r bool // remove subkeys if true. + s bool // true if use string format when saving the data. + p string // password + e int64 // expire +} + +// K2hashParams stores parameters for k2hash C API +type K2hashParams struct { + password string + expirationDuration int64 +} + +// String returns a text representation of the object. +func (p *K2hashParams) String() string { + return fmt.Sprintf("[%v, %v]", p.password, p.expirationDuration) +} + +// K2hash keeps configurations, and it is responsible for creating request handles with a k2hash database files and closing them. +type K2hash struct { + // filepath is a path to K2HASH file. + filepath string + // readonly enables read only file access if true. default is false. + readonly bool + // removefile enables automatic file deletion if no process attaches the file. default is false. + removefile bool + // fullmap enables memory mapped io for a whole data in a file. default is false. + fullmap bool + // key mask bit. default is 8. + maskbitcnt int + // key collision mask bit. default is 4. + cmaskbitcnt int + // maxelementcnt is a max number of duplicated elements if a hash collision occurs. default is 1024(bytes). + maxelementcnt int + // pagesize is a block size of data. default is 512(bytes). + pagesize int + // waitms is a time to wait until a transaction is completed. default is -1. + waitms int + // handle is a file descriptor to a K2HASH file. + handle C.k2h_h +} + +// String returns a text representation of the object. +func (k2h *K2hash) String() string { + return fmt.Sprintf("[%v, %v, %v, %v, %v, %v, %v, %v, %v, %v]", + k2h.filepath, k2h.readonly, k2h.removefile, k2h.fullmap, k2h.maskbitcnt, k2h.cmaskbitcnt, k2h.maxelementcnt, k2h.pagesize, k2h.waitms, k2h.handle) +} + +// NewK2hash returns a new k2hash instance. +func NewK2hash(f string, options ...func(*K2hash)) (*K2hash, error) { + // 1. set defaults + k2h := K2hash{ + filepath: f, + readonly: false, + removefile: false, + fullmap: false, + maskbitcnt: 8, + cmaskbitcnt: 4, + maxelementcnt: 1024, + pagesize: 512, + handle: 0, + } + // 2. set options + for _, option := range options { + option(&k2h) + } + // 3. open + ok, err := k2h.Open() + if ok == false { + return nil, err + } + return &k2h, nil +} + +// Create creates a k2hash file. It returns a error if the file already exists. +func (k2h *K2hash) Create() bool { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + ok := C.k2h_create(cK2h, C.int(k2h.maskbitcnt), C.int(k2h.cmaskbitcnt), C.int(k2h.maxelementcnt), C.ulong(k2h.pagesize)) + if ok == true { + return true + } + return false +} + +// Open opens a k2hash file. +func (k2h *K2hash) Open() (bool, error) { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + k2h.handle = C.k2h_open( + cK2h, + C._Bool(k2h.readonly), + C._Bool(k2h.removefile), + C._Bool(k2h.fullmap), + C.int(k2h.maskbitcnt), + C.int(k2h.cmaskbitcnt), + C.int(k2h.maxelementcnt), + C.size_t(k2h.pagesize)) + + if k2h.handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", k2h.handle) + } + return true, nil +} + +// Close closes a k2hash file. +func (k2h *K2hash) Close() (bool, error) { + ok := C.k2h_close(k2h.handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +// CloseWait closes a k2hash file after designated sleep time. +func (k2h *K2hash) CloseWait() (bool, error) { + ok := C.k2h_close_wait(k2h.handle, C.long(k2h.waitms)) + if ok != true { + return false, fmt.Errorf("k2h_close_wait() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +// GetResult holds the result of Get. +type GetResult struct { + val []byte // text or binary + ok bool // true if success +} + +// Bytes returns the value in binary format. +func (r *GetResult) Bytes() []byte { + return r.val +} + +// String returns the value in text format. +func (r *GetResult) String() string { + return string(r.val) +} + +// Get returns data from a k2hash file. +func (k2h *K2hash) Get(k interface{}, options ...func(*K2hashParams)) (*GetResult, error) { + // 1. binary or text + var key string + switch k.(type) { + default: + return nil, fmt.Errorf("unsupported key data format %T", key) + case string: + key = k.(string) + } + fmt.Printf("key %v\n", key) + + // 2. set params + params := K2hashParams{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char + defer C.free(unsafe.Pointer(cRetValue)) + //cRetValue = C.k2h_get_str_direct_value(k2h.handle, (*C.char)(cKey)) + ok := C.k2h_get_str_value(k2h.handle, (*C.char)(cKey), &cRetValue) + rv := C.GoString(cRetValue) + fmt.Printf("ok %v val %v\n", ok, rv) + r := &GetResult{ + val: []byte(rv), + ok: (bool)(ok), + } + return r, nil +} + +// Set returns true if successfully set a key with a value. +func (k2h *K2hash) Set(k interface{}, v interface{}, options ...func(*K2hashParams)) (bool, error) { + // 1. binary or text + var key string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + fmt.Printf("key %v val %v\n", key, val) + + // 2. set params + params := K2hashParams{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. Sets a text(string) type variable and retrives it by using k2h_get_str_value_wp and k2h_set_str_value_wa + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + ok := C.k2h_set_str_value_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cVal), cPass, expire) + fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +// AddSubKey add a subkey to a key. +func (k2h *K2hash) AddSubKey(k interface{}, s interface{}, v interface{}, options ...func(*K2hashParams)) (bool, error) { + // 1. binary or text + var key string + var subkey string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch s.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", s) + case string: + subkey = s.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + //fmt.Printf("key %v val %v\n", key, val) + + // 2. set params + params := K2hashParams{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cSubKey := C.CString(subkey) + defer C.free(unsafe.Pointer(cSubKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + ok := C.k2h_add_str_subkey_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cSubKey), (*C.char)(cVal), cPass, expire) + //fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.C.k2h_add_str_subkey_wa return false") + } + return true, nil +} + +// K2hashRemoveParams holds parameters to be removed. +type K2hashRemoveParams struct { + all bool + subkey string +} + +// Remove removes a key using a K2hashRemoveParams. +func (k2h *K2hash) Remove(k interface{}, options ...func(*K2hashRemoveParams)) (bool, error) { + // 1. binary or text + var key string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + fmt.Printf("key %v\n", key) + + // 2. remove params + params := K2hashRemoveParams{ + all: false, + subkey: "", + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + var ok C._Bool = false + if params.all == true { + fmt.Printf("C.k2h_remove_str_all\n") + ok = C.k2h_remove_str_all(k2h.handle, (*C.char)(cKey)) + } else if params.subkey != "" { + cSubKey := C.CString(params.subkey) + defer C.free(unsafe.Pointer(cSubKey)) + fmt.Printf("C.k2h_remove_str_subkey\n") + ok = C.k2h_remove_str_subkey(k2h.handle, (*C.char)(cKey), (*C.char)(cSubKey)) + } else { + fmt.Printf("C.k2h_remove_str\n") + ok = C.k2h_remove_str(k2h.handle, (*C.char)(cKey)) + } + fmt.Printf("ok %v\n", ok) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +// SetSubKeys add subkeys to a key. +func (k2h *K2hash) SetSubKeys(k string, sk []string) (bool, error) { + skeys := make([]*C.char, len(sk)+1, len(sk)+1) + for i, s := range sk { + fmt.Printf("i %v s %v\n", i, s) + skeys[i] = C.CString(s) + defer C.free(unsafe.Pointer(skeys[i])) + } + skeys[len(sk)] = nil + // 3. get from k2hash get API + cKey := C.CString(k) + defer C.free(unsafe.Pointer(cKey)) + //ok := true + if &skeys[0] != nil { + ok := C.k2h_set_str_subkeys(k2h.handle, (*C.char)(cKey), (**C.char)(&skeys[0])) + if ok != true { + return false, fmt.Errorf("C.C.k2h_set_str_str_wa return false") + } + } + return true, nil +} + +// GetSubKeys retrieves subkeys to a key. +func (k2h *K2hash) GetSubKeys(k string) (bool, error) { + cKey := C.CString(k) + defer C.free(unsafe.Pointer(cKey)) + keypack := C.my_get_subkeypack(k2h.handle, (*C.char)(cKey)) + if keypack != nil { + return false, fmt.Errorf("C.C.k2h_set_str_str_wa return false") + } + return true, nil +} + +func main() { + + var loopCount int + flag.IntVar(&loopCount, "loopCount", 1, "loopCount to detect memory leak") + flag.Parse() + fmt.Printf("loopCount %v\n", loopCount) + + // 1. define test data. + k, err := NewK2hash("/tmp/sample.k2h") + if err != nil { + fmt.Printf("err %v¥n", err) + os.Exit(1) + } + defer k.Close() + + // 1. key1 + ok, err := k.Set("key1", "val1") + // 2. subkey1 + ok, err = k.Set("s1", "subval1") + // 3. subkey2 + ok, err = k.Set("s2", "subval2") + // 4. subkey3 + ok, err = k.Set("s3", "subval3") + //for i := 0; i < loopCount; i++ { // memory leak check + a := []string{"s1", "s2"} + ok, err = k.SetSubKeys("key1", a) + //} + fmt.Printf("SetSubKeys(key1, []string{subkey1, subkey2}) ok %v err %v\n", ok, err) + // 5. get + val1, err := k.Get("key1") + fmt.Printf("val1 should be val1(%v) err %v\n", val1.String(), err) +} + +func main2() { + // 1. define test data. + k, err := NewK2hash("/tmp/sample.k2h") + if err != nil { + fmt.Printf("err %v¥n", err) + os.Exit(1) + } + defer func() { + ok, error := k.Close() + if ok != true { + fmt.Printf("Close() error %v\n", error) + } + }() + + // 1. set + ok, err := k.Set("key1", "val1") + fmt.Printf("Set() ok %v err %v\n", ok, err) + // 2. get + val1, err := k.Get("key1") + fmt.Printf("val1 should be val1(%v) err %v\n", val1.String(), err) + + // 3. add a subkey + ok, err = k.AddSubKey("key1", "sub1", "subval1") + fmt.Printf("AddSubKey() ok %v err %v\n", ok, err) + + // 3.1. get the value of sub1 + subval1, err := k.Get("sub1") + fmt.Printf("subval1 should be subval1(%v) err %v\n", subval1.String(), err) + + // 4. remove + // 4.1. remove key1 and keep sub1 + ok, err = k.Remove("key1") + fmt.Printf("Remove() ok %v err %v\n", ok, err) + + // 4.1.1. confirm key1 is removed + val1, err = k.Get("key1") + fmt.Printf("val1 should be removed(%v) err %v\n", val1.String(), err) + + // 4.1.2. confirm sub1 exists + subval1, err = k.Get("sub1") + fmt.Printf("subval1 should be subval1(%v) err %v\n", subval1.String(), err) + + // 4.2. remove all + ok, err = k.Set("key1", "val1") + ok, err = k.AddSubKey("key1", "sub1", "subval") + opts := func(params *K2hashRemoveParams) { + params.all = true + } + ok, err = k.Remove("key1", opts) + fmt.Printf("Remove() ok %v err %v\n", ok, err) + + // 4.2.1. confirm key1 is removed + val1, err = k.Get("key1") + fmt.Printf("val1 should be removed(%v) err %v\n", val1.String(), err) + + // 4.2.2. confirm sub1 is removed + subval1, err = k.Get("sub1") + fmt.Printf("subval1 should be removed(%v) err %v\n", subval1.String(), err) + + // 4.3. remove subkey + ok, err = k.Set("key1", "val1") + ok, err = k.AddSubKey("key1", "sub1", "subval") + opts = func(params *K2hashRemoveParams) { + params.subkey = "sub1" + } + ok, err = k.Remove("key1", opts) + fmt.Printf("ok %v err %v\n", ok, err) + + // 4.3.1. confirm key1 exists + val1, err = k.Get("key1") + fmt.Printf("val1 should be val1(%v) err %v\n", val1.String(), err) + + // 4.3.2. confirm sub1 is removed + subval1, err = k.Get("sub1") + fmt.Printf("subval1 should be removed(%v) err %v\n", subval1.String(), err) +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e65fdda --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/yahoojapan/k2hash_go + +go 1.15 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/k2hash/addattr.go b/k2hash/addattr.go new file mode 100644 index 0000000..062864b --- /dev/null +++ b/k2hash/addattr.go @@ -0,0 +1,77 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// AddAttr adds an attribute with a value to a key. +func (k2h *K2hash) AddAttr(k interface{}, ak interface{}, av interface{}, options ...func(*Params)) (bool, error) { + // 1. binary or text + var key string + var attrkey string + var attrval string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch ak.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", ak) + case string: + attrkey = ak.(string) + } + switch av.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", av) + case string: + attrval = av.(string) + } + + // 2. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cAttrKey := C.CString(attrkey) + defer C.free(unsafe.Pointer(cAttrKey)) + cAttrVal := C.CString(attrval) + defer C.free(unsafe.Pointer(cAttrVal)) + ok := C.k2h_add_str_attr(k2h.handle, (*C.char)(cKey), (*C.char)(cAttrKey), (*C.char)(cAttrVal)) + if ok != true { + return false, fmt.Errorf("C.k2h_add_str_attr return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/addsubkey.go b/k2hash/addsubkey.go new file mode 100644 index 0000000..3923b84 --- /dev/null +++ b/k2hash/addsubkey.go @@ -0,0 +1,94 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// AddSubKey add a subkey to a key. +func (k2h *K2hash) AddSubKey(k interface{}, s interface{}, v interface{}, options ...func(*Params)) (bool, error) { + // 1. binary or text + var key string + var subkey string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch s.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", s) + case string: + subkey = s.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + + // 2. set params + params := Params{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cSubKey := C.CString(subkey) + defer C.free(unsafe.Pointer(cSubKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + ok := C.k2h_add_str_subkey_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cSubKey), (*C.char)(cVal), cPass, expire) + if ok != true { + return false, fmt.Errorf("C.C.k2h_add_str_subkey_wa return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/attribute.go b/k2hash/attribute.go new file mode 100644 index 0000000..a741c0a --- /dev/null +++ b/k2hash/attribute.go @@ -0,0 +1,123 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// EnableMtime enables the k2hash file attributes of modification time. +func (k2h *K2hash) EnableMtime(enable bool) (bool, error) { + cBool := C._Bool(enable) + ok := C.k2h_set_common_attr(k2h.handle, (*C._Bool)(&cBool), nil, nil, nil, nil) + if ok != true { + return false, fmt.Errorf("k2h_set_common_attr() returns false") + } + return true, nil +} + +// EnableEncryption enables the k2hash file attributes of data encryption. +func (k2h *K2hash) EnableEncryption(enable bool, file string) (bool, error) { + cFile := C.CString(file) + defer C.free(unsafe.Pointer(cFile)) + cBool := C._Bool(enable) + ok := C.k2h_set_common_attr(k2h.handle, nil, (*C._Bool)(&cBool), cFile, nil, nil) + if ok != true { + return false, fmt.Errorf("k2h_set_common_attr() returns false") + } + return true, nil +} + +// EnableHistory enables the k2hash file attributes of history. +func (k2h *K2hash) EnableHistory(enable bool) (bool, error) { + cBool := C._Bool(enable) + ok := C.k2h_set_common_attr(k2h.handle, nil, nil, nil, (*C._Bool)(&cBool), nil) + if ok != true { + return false, fmt.Errorf("k2h_set_common_attr() returns false") + } + return true, nil +} + +// SetExpirationDuration enables the k2hash file attributes of modification time. +func (k2h *K2hash) SetExpirationDuration(duration int) (bool, error) { + cDuration := C.time_t(duration) + ok := C.k2h_set_common_attr(k2h.handle, nil, nil, nil, nil, (*C.time_t)(&cDuration)) + if ok != true { + return false, fmt.Errorf("k2h_set_common_attr() returns false") + } + return true, nil +} + +// AddAttrPluginLibrary loads a shared library for processing attribute data. +func (k2h *K2hash) AddAttrPluginLibrary(file string) (bool, error) { + cFile := C.CString(file) + defer C.free(unsafe.Pointer(cFile)) + ok := C.k2h_add_attr_plugin_library(k2h.handle, cFile) + if ok != true { + return false, fmt.Errorf("k2h_add_attr_plugin_library() returns false") + } + return true, nil +} + +// AddDecryptionPassword sets the decryption passphrase. +func (k2h *K2hash) AddDecryptionPassword(pass string) (bool, error) { + cPass := C.CString(pass) + defer C.free(unsafe.Pointer(cPass)) + ok := C.k2h_add_attr_crypt_pass(k2h.handle, cPass, false) + if ok != true { + return false, fmt.Errorf("k2h_add_attr_crypt_pass() returns false") + } + return true, nil +} + +// SetDefaultEncryptionPassword sets the encryption passphrase. +func (k2h *K2hash) SetDefaultEncryptionPassword(pass string) (bool, error) { + cPass := C.CString(pass) + defer C.free(unsafe.Pointer(cPass)) + ok := C.k2h_add_attr_crypt_pass(k2h.handle, cPass, true) + if ok != true { + return false, fmt.Errorf("k2h_add_attr_crypt_pass() returns false") + } + return true, nil +} + +// PrintAttrVersion prints attribute plugins to stderr. +func (k2h *K2hash) PrintAttrVersion() { + C.k2h_print_attr_version(k2h.handle, nil) +} + +// PrintAttrInformation prints attributes to stderr. +func (k2h *K2hash) PrintAttrInformation() { + C.k2h_print_attr_information(k2h.handle, nil) +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/debug.go b/k2hash/debug.go new file mode 100644 index 0000000..538cdc3 --- /dev/null +++ b/k2hash/debug.go @@ -0,0 +1,192 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +// BumpDebugLevel changes the log level. +func (k2h *K2hash) BumpDebugLevel() { + C.k2h_bump_debug_level() +} + +// SetDebugLevelSilent disables logging. +func (k2h *K2hash) SetDebugLevelSilent() { + C.k2h_set_debug_level_silent() +} + +// SetDebugLevelError logs error level messages. +func (k2h *K2hash) SetDebugLevelError() { + C.k2h_set_debug_level_error() +} + +// SetDebugLevelWarning logs warning or higher level messages. +func (k2h *K2hash) SetDebugLevelWarning() { + C.k2h_set_debug_level_warning() +} + +// SetDebugLevelMessage logs info or higher level messages. +func (k2h *K2hash) SetDebugLevelMessage() { + C.k2h_set_debug_level_message() +} + +// SetDebugFile defines the file where log messages are saved. +func (k2h *K2hash) SetDebugFile(filepath string) bool { + cFilePath := C.CString(filepath) + ok := C.k2h_set_debug_file(cFilePath) // ok is a C._Bool type, which is aliased by bool. + if ok == true { + return true + } + return false +} + +// UnsetDebugFile disables saving log messages to a file. +func (k2h *K2hash) UnsetDebugFile() bool { + ok := C.k2h_unset_debug_file() + if ok == true { + return true + } + return false +} + +// LoadDebugEnv defines the log level and the file by using K2HDBGMODE, K2HDBGFILE. +func (k2h *K2hash) LoadDebugEnv() bool { + ok := C.k2h_load_debug_env() + if ok == true { + return true + } + return false +} + +// SetSignalUser1 changes log level by receiving SIGUSR1 signal. +func (k2h *K2hash) SetSignalUser1() bool { + ok := C.k2h_set_bumpup_debug_signal_user1() + if ok == true { + return true + } + return false +} + +// DumpHead dumps K2HASH header information to a file referred by FILE pointer. +func (k2h *K2hash) DumpHead() bool { + ok := C.k2h_dump_head(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// DumpKeyTable dumps K2HASH's hash key table information to a file referred by FILE pointer. +func (k2h *K2hash) DumpKeyTable() bool { + ok := C.k2h_dump_keytable(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// DumpFullKeyTable dumps K2HASH's hash key and subkey information to a file referred by FILE pointer. +func (k2h *K2hash) DumpFullKeyTable() bool { + ok := C.k2h_dump_full_keytable(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// DumpElementTable dumps K2HASH's Element table information to a file referred by FILE pointer. +func (k2h *K2hash) DumpElementTable() bool { + ok := C.k2h_dump_elementtable(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// DumpFull dumps K2HASH's all information. +func (k2h *K2hash) DumpFull() bool { + ok := C.k2h_dump_full(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// PrintState prints k2hash file stats. +func (k2h *K2hash) PrintState() bool { + ok := C.k2h_print_state(k2h.handle, nil) + if ok == true { + return true + } + return false +} + +// PrintVersion prints the k2hash library version and the credit. +func (k2h *K2hash) PrintVersion() { + C.k2h_print_version(nil) +} + +// LoadHashLibrary loads the hash library. +func (k2h *K2hash) LoadHashLibrary(filepath string) bool { + cFilePath := C.CString(filepath) + ok := C.k2h_load_hash_library(cFilePath) // ok is a C._Bool type, which is aliased by bool. + if ok == true { + return true + } + return false +} + +// UnloadHashLibrary unloads the loaded hash library. +func (k2h *K2hash) UnloadHashLibrary() bool { + ok := C.k2h_unload_hash_library() + if ok == true { + return true + } + return false +} + +// LoadTxLibrary loads the hash library. +func (k2h *K2hash) LoadTxLibrary(filepath string) bool { + cFilePath := C.CString(filepath) + ok := C.k2h_load_transaction_library(cFilePath) // ok is a C._Bool type, which is aliased by bool. + if ok == true { + return true + } + return false +} + +// UnloadTxLibrary unloads the loaded hash library. +func (k2h *K2hash) UnloadTxLibrary() bool { + ok := C.k2h_unload_transaction_library() + if ok == true { + return true + } + return false +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/doc.go b/k2hash/doc.go new file mode 100644 index 0000000..912de65 --- /dev/null +++ b/k2hash/doc.go @@ -0,0 +1,26 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +// Package k2hash implements a k2hash library. +package k2hash + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/get.go b/k2hash/get.go new file mode 100644 index 0000000..0762ef0 --- /dev/null +++ b/k2hash/get.go @@ -0,0 +1,93 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// GetResult holds the result of Get. +type GetResult struct { + val []byte // text or binary +} + +// Bytes returns the value in binary format. +func (r *GetResult) Bytes() []byte { + return r.val +} + +// String returns the value in text format. +func (r *GetResult) String() string { + return string(r.val) +} + +// Get returns data from a k2hash file. +func (k2h *K2hash) Get(k interface{}, options ...func(*Params)) (*GetResult, error) { + // 1. binary or text + var key string + switch k.(type) { + default: + return nil, fmt.Errorf("unsupported key data format %T", key) + case string: + key = k.(string) + } + + // 2. set params + params := Params{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char + defer C.free(unsafe.Pointer(cRetValue)) + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + //cRetValue = C.k2h_get_str_direct_value(k2h.handle, (*C.char)(cKey)) + ok := C.k2h_get_str_value_wp(k2h.handle, (*C.char)(cKey), &cRetValue, cPass) + if ok != true { + return nil, fmt.Errorf("C.k2h_get_str_value return false") + } + rv := C.GoString(cRetValue) + r := &GetResult{ + val: []byte(rv), + } + return r, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/getattrs.go b/k2hash/getattrs.go new file mode 100644 index 0000000..1a05caa --- /dev/null +++ b/k2hash/getattrs.go @@ -0,0 +1,87 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// Attr holds attribute names and values. +type Attr struct { + key string + val string +} + +// String returns a text representation of the object. +func (r *Attr) String() string { + return fmt.Sprintf("[%v, %v]", r.key, r.val) +} + +// GetAttrs returns a slice of string. +func (k2h *K2hash) GetAttrs(k string) ([]Attr, error) { + // 1. retrieve an attribute using k2h_get_attrs + // bool k2h_get_attrs(k2h_h handle, const unsigned char* pkey, size_t keylength, PK2HATTRPCK* ppattrspck, int* pattrspckcnt) + cKey := C.CBytes([]byte(k)) + defer C.free(unsafe.Pointer(cKey)) + var attrpack C.PK2HATTRPCK + var attrpackCnt C.int + ok := C.k2h_get_attrs( + k2h.handle, + (*C.uchar)(cKey), + C.size_t(len([]byte(k))+1), // plus one for a null termination + &attrpack, + &attrpackCnt, + ) + defer C.k2h_free_attrpack(attrpack, attrpackCnt) // free the memory for the keypack for myself(GC doesn't know the area) + + if ok == false { + return []Attr{}, fmt.Errorf("C.k2h_get_attrs() = %v", ok) + } else if attrpackCnt == 0 { + return []Attr{}, nil + } + // 2. copy an attribute data to a slice + var CAttrs C.PK2HATTRPCK = attrpack + count := (int)(attrpackCnt) + slice := (*[1 << 28]C.K2HATTRPCK)(unsafe.Pointer(CAttrs))[:count:count] + attrs := make([]Attr, count) // copy + for i, data := range slice { + // copy the data with len-1 length, which exclude a null termination. + attrkey := C.GoBytes(unsafe.Pointer(data.pkey), (C.int)(data.keylength-1)) + attrval := C.GoBytes(unsafe.Pointer(data.pval), (C.int)(data.vallength-1)) + // cast bytes to a string + attrs[i].key = string(attrkey) + attrs[i].val = string(attrval) + } + return attrs, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/getsubkeys.go b/k2hash/getsubkeys.go new file mode 100644 index 0000000..dcca749 --- /dev/null +++ b/k2hash/getsubkeys.go @@ -0,0 +1,73 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// GetSubKeys returns subkeys to a key. +func (k2h *K2hash) GetSubKeys(k string) ([]string, error) { + // 1. retrieve subkeys using k2h_get_subkeys + cKey := C.CBytes([]byte(k)) + defer C.free(unsafe.Pointer(cKey)) + var keypack C.PK2HKEYPCK + var keypackLen C.int + ok := C.k2h_get_subkeys( + k2h.handle, + (*C.uchar)(cKey), + C.size_t(len([]byte(k))+1), // plus one for a null termination + &keypack, + &keypackLen, + ) + defer C.k2h_free_keypack(keypack, keypackLen) // free the memory for the keypack for myself(GC doesn't know the area) + + if ok == false { + return []string{""}, fmt.Errorf("C.k2h_get_subkeys() = %v", ok) + } else if keypackLen == 0 { + return []string{""}, nil + } + // 2. copy a subkey data to a slice + var CSubKey C.PK2HKEYPCK = keypack + length := (int)(keypackLen) + slice := (*[1 << 28]C.K2HKEYPCK)(unsafe.Pointer(CSubKey))[:length:length] + skeys := make([]string, length) // copy + for i, data := range slice { + // copy the data with len-1 length, which exclude a null termination. + sk := C.GoBytes(unsafe.Pointer(data.pkey), (C.int)(data.length-1)) + // cast byte array to string + skeys[i] = string(sk) + } + return skeys, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/k2hash.go b/k2hash/k2hash.go new file mode 100644 index 0000000..ec3c7e6 --- /dev/null +++ b/k2hash/k2hash.go @@ -0,0 +1,159 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// Params stores parameters for k2hash C API. +type Params struct { + password string + expirationDuration int64 +} + +// QueueParams stores parameters for k2hash queue C API. +type QueueParams struct { + password string + expirationDuration int64 + attrs C.PK2HATTRPCK +} + +// String returns a text representation of the object. +func (p *Params) String() string { + return fmt.Sprintf("[%v, %v]", p.password, p.expirationDuration) +} + +// K2hash keeps configurations, and it is responsible for creating request handles with a k2hash database files and closing them. +type K2hash struct { + // filepath is a path to K2HASH file. + filepath string + // readonly enables read only file access if true. default is false. + readonly bool + // removefile enables automatic file deletion if no process attaches the file. default is false. + removefile bool + // fullmap enables memory mapped io for a whole data in a file. default is false. + fullmap bool + // key mask bit. default is 8. + maskbitcnt int + // key collision mask bit. default is 4. + cmaskbitcnt int + // maxelementcnt is a max number of duplicated elements if a hash collision occurs. default is 1024(bytes). + maxelementcnt int + // pagesize is a block size of data. default is 512(bytes). + pagesize int + // waitms is a time to wait until a transaction is completed. default is -1. + waitms int + // handle is a file descriptor to a K2HASH file. + handle C.k2h_h +} + +// String returns a text representation of the object. +func (k2h *K2hash) String() string { + return fmt.Sprintf("[%v, %v, %v, %v, %v, %v, %v, %v, %v, %v]", + k2h.filepath, k2h.readonly, k2h.removefile, k2h.fullmap, k2h.maskbitcnt, k2h.cmaskbitcnt, k2h.maxelementcnt, k2h.pagesize, k2h.waitms, k2h.handle) +} + +// NewK2hash returns a new k2hash instance. +func NewK2hash(f string, options ...func(*K2hash)) (*K2hash, error) { + // 1. set defaults + k2h := K2hash{ + filepath: f, + readonly: false, + removefile: false, + fullmap: false, + maskbitcnt: 8, + cmaskbitcnt: 4, + maxelementcnt: 1024, + pagesize: 512, + waitms: 0, + handle: 0, + } + // 2. set options + for _, option := range options { + option(&k2h) + } + // 3. open + ok, err := k2h.Open() + if ok == false { + return nil, err + } + return &k2h, nil +} + +// Create creates a k2hash file. It returns a error if the file already exists. +func (k2h *K2hash) Create() bool { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + ok := C.k2h_create(cK2h, C.int(k2h.maskbitcnt), C.int(k2h.cmaskbitcnt), C.int(k2h.maxelementcnt), C.ulong(k2h.pagesize)) + if ok == true { + return true + } + return false +} + +// Open opens a k2hash file. +func (k2h *K2hash) Open() (bool, error) { + cK2h := C.CString(k2h.filepath) + defer C.free(unsafe.Pointer(cK2h)) + k2h.handle = C.k2h_open( + cK2h, + C._Bool(k2h.readonly), + C._Bool(k2h.removefile), + C._Bool(k2h.fullmap), + C.int(k2h.maskbitcnt), + C.int(k2h.cmaskbitcnt), + C.int(k2h.maxelementcnt), + C.size_t(k2h.pagesize)) + + if k2h.handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", k2h.handle) + } + return true, nil +} + +// Close closes a k2hash file. +func (k2h *K2hash) Close() (bool, error) { + ok := C.k2h_close_wait(k2h.handle, C.long(k2h.waitms)) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + k2h.handle = C.K2H_INVALID_HANDLE + return true, nil +} + +// GetHandle returns a k2hash file handle. +func (k2h *K2hash) GetHandle() C.k2h_h { + return k2h.handle +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/keyqueue.go b/k2hash/keyqueue.go new file mode 100644 index 0000000..d4e8f4d --- /dev/null +++ b/k2hash/keyqueue.go @@ -0,0 +1,149 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// KeyQueue keeps a queue configurations. +type KeyQueue struct { + // K2HASH file handle + handle C.k2h_h + // K2HASH queue handle + keyqhandle C.k2h_keyq_h + // fifo + fifo bool + // prefix + prefix string +} + +// String returns a text representation of the object. +func (q *KeyQueue) String() string { + return fmt.Sprintf("[%v, %v, %v, %v]", q.handle, q.keyqhandle, q.fifo, q.prefix) +} + +// NewKeyQueue returns a new k2hash queue instance. +func NewKeyQueue(h *K2hash, options ...func(*KeyQueue)) (*KeyQueue, error) { + // 1. set defaults + q := KeyQueue{ + handle: h.GetHandle(), + keyqhandle: C.K2H_INVALID_HANDLE, + fifo: true, + prefix: "", + } + // 2. set options + for _, option := range options { + option(&q) + } + // 3. open + var qh C.k2h_keyq_h + if q.prefix == "" { + qh = C.k2h_keyq_handle(q.handle, C._Bool(q.fifo)) + } else { + cPrefix := C.CBytes([]byte(q.prefix)) + defer C.free(unsafe.Pointer(cPrefix)) + qh = C.k2h_keyq_handle_prefix(q.handle, C._Bool(q.fifo), (*C.uchar)(cPrefix), C.size_t(len([]byte(q.prefix)))) + } + // 4. check qeueu handle + if qh == C.K2H_INVALID_HANDLE { + return nil, fmt.Errorf("C.k2h_keyq_handle return false") + } + // 5. reset keyqhandle + q.keyqhandle = qh + return &q, nil +} + +/* -- QueueQueue methods -- */ + +// Push adds a value to the queue. +func (q *KeyQueue) Push(v interface{}, options ...func(*Params)) (bool, error) { + // 1. binary or text + var val string + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + // 2. set params + params := Params{ + password: "", + expirationDuration: 0, + } + for _, option := range options { + option(¶ms) + } + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + if ok := C.k2h_keyq_str_push_wa(q.keyqhandle, (*C.char)(cVal), cPass, expire); !ok { + return false, fmt.Errorf("C.k2h_keyq_str_push return false") + } + return true, nil +} + +// Pop retrieves a value from the queue. +func (q *KeyQueue) Pop(options ...func(*Params)) (string, error) { + var cRetVal (*C.char) + defer C.free(unsafe.Pointer(cRetVal)) + ok := C.k2h_keyq_str_pop(q.keyqhandle, &cRetVal) + defer C.free(unsafe.Pointer(cRetVal)) + if !ok { + return "", fmt.Errorf("C.k2h_keyq_str_pop return false") + } + val := C.GoString(cRetVal) + return val, nil +} + +// Free destroys a k2hash queue handle. +func (q *KeyQueue) Free() (bool, error) { + if ok := C.k2h_keyq_free(q.keyqhandle); !ok { + return false, fmt.Errorf("k2h_keyq_free() returns false") + } + q.keyqhandle = C.K2H_INVALID_HANDLE + return true, nil +} + +// Count returns the number of k2hash queue elements. +func (q *KeyQueue) Count() (int, error) { + count := C.k2h_keyq_count(q.keyqhandle) + return int(count), nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/queue.go b/k2hash/queue.go new file mode 100644 index 0000000..0cbaf9e --- /dev/null +++ b/k2hash/queue.go @@ -0,0 +1,149 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// Queue keeps a queue configurations. +type Queue struct { + // K2HASH file handle + handle C.k2h_h + // K2HASH queue handle + qhandle C.k2h_q_h + // fifo + fifo bool + // prefix + prefix string +} + +// String returns a text representation of the object. +func (q *Queue) String() string { + return fmt.Sprintf("[%v, %v, %v, %v]", q.handle, q.qhandle, q.fifo, q.prefix) +} + +// NewQueue returns a new k2hash queue instance. +func NewQueue(h *K2hash, options ...func(*Queue)) (*Queue, error) { + // 1. set defaults + q := Queue{ + handle: h.GetHandle(), + qhandle: C.K2H_INVALID_HANDLE, + fifo: true, + prefix: "", + } + // 2. set options + for _, option := range options { + option(&q) + } + // 3. open + var qh C.k2h_q_h + if q.prefix == "" { + qh = C.k2h_q_handle(q.handle, C._Bool(q.fifo)) + } else { + cPrefix := C.CBytes([]byte(q.prefix)) + defer C.free(unsafe.Pointer(cPrefix)) + qh = C.k2h_q_handle_prefix(q.handle, C._Bool(q.fifo), (*C.uchar)(cPrefix), C.size_t(len([]byte(q.prefix)))) + } + // 4. check qeueu handle + if qh == C.K2H_INVALID_HANDLE { + return nil, fmt.Errorf("C.k2h_q_handle return false") + } + // 5. reset qhandle + q.qhandle = qh + return &q, nil +} + +/* -- QueueQueue methods -- */ + +// Push adds a value to the queue. +func (q *Queue) Push(v interface{}, options ...func(*Params)) (bool, error) { + // 1. binary or text + var val string + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + // 2. set params + params := Params{ + password: "", + expirationDuration: 0, + } + for _, option := range options { + option(¶ms) + } + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + if ok := C.k2h_q_str_push_wa(q.qhandle, (*C.char)(cVal), nil, 0, cPass, expire); !ok { + return false, fmt.Errorf("C.k2h_q_str_push return false") + } + return true, nil +} + +// Pop retrieves a value from the queue. +func (q *Queue) Pop(options ...func(*Params)) (string, error) { + var cRetVal (*C.char) + defer C.free(unsafe.Pointer(cRetVal)) + ok := C.k2h_q_str_pop(q.qhandle, &cRetVal) + defer C.free(unsafe.Pointer(cRetVal)) + if !ok { + return "", fmt.Errorf("C.k2h_q_str_pop return false") + } + val := C.GoString(cRetVal) + return val, nil +} + +// Free destroys a k2hash queue handle. +func (q *Queue) Free() (bool, error) { + if ok := C.k2h_q_free(q.qhandle); !ok { + return false, fmt.Errorf("k2h_q_free() returns false") + } + q.qhandle = C.K2H_INVALID_HANDLE + return true, nil +} + +// Count returns the number of k2hash queue elements. +func (q *Queue) Count() (int, error) { + count := C.k2h_q_count(q.qhandle) + return int(count), nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/remove.go b/k2hash/remove.go new file mode 100644 index 0000000..9570e59 --- /dev/null +++ b/k2hash/remove.go @@ -0,0 +1,84 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// RemoveParams is a parameter set of remove. +type RemoveParams struct { + all bool + subkey string +} + +// Remove removes a key or a subkey. +func (k2h *K2hash) Remove(k interface{}, options ...func(*RemoveParams)) (bool, error) { + // 1. binary or text + var key string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + + // 2. remove params + params := RemoveParams{ + all: false, + subkey: "", + } + + for _, option := range options { + option(¶ms) + } + + // 3. remove + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + ok := C._Bool(false) + if params.all == true { + ok = C.k2h_remove_str_all(k2h.handle, (*C.char)(cKey)) + } else if params.subkey != "" { + cSubKey := C.CString(params.subkey) + defer C.free(unsafe.Pointer(cSubKey)) + ok = C.k2h_remove_str_subkey(k2h.handle, (*C.char)(cKey), (*C.char)(cSubKey)) + } else { + ok = C.k2h_remove_str(k2h.handle, (*C.char)(cKey)) + } + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/rename.go b/k2hash/rename.go new file mode 100644 index 0000000..17d6224 --- /dev/null +++ b/k2hash/rename.go @@ -0,0 +1,68 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// Rename renames an old key with a new key. +func (k2h *K2hash) Rename(o interface{}, n interface{}) (bool, error) { + // 1. binary or text + var old string + var new string + switch o.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", o) + case string: + old = o.(string) + } + switch n.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", n) + case string: + new = n.(string) + } + + // 3. get from k2hash get API + cOld := C.CString(old) + defer C.free(unsafe.Pointer(cOld)) + cNew := C.CString(new) + defer C.free(unsafe.Pointer(cNew)) + ok := C.k2h_rename_str(k2h.handle, (*C.char)(cOld), (*C.char)(cNew)) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/set.go b/k2hash/set.go new file mode 100644 index 0000000..4236565 --- /dev/null +++ b/k2hash/set.go @@ -0,0 +1,85 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// Set returns true if successfully set a key with a value. +func (k2h *K2hash) Set(k interface{}, v interface{}, options ...func(*Params)) (bool, error) { + // 1. binary or text + var key string + var val string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch v.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", v) + case string: + val = v.(string) + } + + // 2. set params + params := Params{ + password: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. get from k2hash get API + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(val) + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(params.password) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + ok := C.k2h_set_str_value_wa(k2h.handle, (*C.char)(cKey), (*C.char)(cVal), cPass, expire) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_value_wa return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/setsubkeys.go b/k2hash/setsubkeys.go new file mode 100644 index 0000000..339c5e3 --- /dev/null +++ b/k2hash/setsubkeys.go @@ -0,0 +1,73 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// SetSubKeys links another key as a child to the key. +func (k2h *K2hash) SetSubKeys(k interface{}, sk interface{}) (bool, error) { + // 1. binary or text + var key string + var skeys []string + switch k.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", k) + case string: + key = k.(string) + } + switch sk.(type) { + default: + return false, fmt.Errorf("unsupported key data format %T", sk) + case []string: + skeys = sk.([]string) + } + + // 2. set subkeys + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + cSkeys := make([]*C.char, len(skeys)+1, len(skeys)+1) + for i, s := range skeys { + cSkeys[i] = C.CString(s) + defer C.free(unsafe.Pointer(cSkeys[i])) + } + cSkeys[len(skeys)] = nil + + ok := C.k2h_set_str_subkeys(k2h.handle, (*C.char)(cKey), (**C.char)(&cSkeys[0])) + if ok != true { + return false, fmt.Errorf("C.k2h_set_str_subkeys return false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/k2hash/transaction.go b/k2hash/transaction.go new file mode 100644 index 0000000..3c06720 --- /dev/null +++ b/k2hash/transaction.go @@ -0,0 +1,140 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hash + +import ( + // #cgo CFLAGS: -g -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "fmt" + "unsafe" +) + +// TxParams is a parameter set of transaction. +type TxParams struct { + prefix string + params string + expirationDuration int64 +} + +// BeginTx enables transaction. +func (k2h *K2hash) BeginTx(file string, options ...func(*TxParams)) (bool, error) { + params := TxParams{ + prefix: "", + params: "", + expirationDuration: 0, + } + + for _, option := range options { + option(¶ms) + } + + // 3. remove + cFile := C.CString(file) + defer C.free(unsafe.Pointer(cFile)) + cPrefix := C.CBytes([]byte(params.prefix)) + defer C.free(unsafe.Pointer(cPrefix)) + cParams := C.CBytes([]byte(params.params)) + defer C.free(unsafe.Pointer(cParams)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if params.expirationDuration != 0 { + expire = (*C.time_t)(¶ms.expirationDuration) + } + ok := C.k2h_enable_transaction_param_we(k2h.handle, cFile, (*C.uchar)(cPrefix), (C.size_t)(len(params.prefix)+1), (*C.uchar)(cParams), (C.size_t)(len(params.params)+1), expire) + if ok != true { + return false, fmt.Errorf("C.k2h_enable_transaction_param_we return false") + } + return true, nil +} + +// StopTx disables transaction. +func (k2h *K2hash) StopTx() (bool, error) { + ok := C.k2h_disable_transaction(k2h.handle) + if ok != true { + return false, fmt.Errorf("C.k2h_disable_transaction return false") + } + return true, nil +} + +// GetTxFileFD returns the file descriptor for transaction file. +func (k2h *K2hash) GetTxFileFD() int32 { + fd := C.k2h_get_transaction_archive_fd(k2h.handle) + return (int32)(fd) +} + +// GetTxThreadPoolSize returns the number of thread pools. +func (k2h *K2hash) GetTxThreadPoolSize() (int32, error) { + pool := C.k2h_get_transaction_thread_pool() + if pool < 0 { + return 0, fmt.Errorf("C.k2h_get_transaction_thread_pool return error") + } + return (int32)(pool), nil +} + +// SetTxThreadPoolSize set the number of thread pools. +func (k2h *K2hash) SetTxThreadPoolSize(pool int32) (bool, error) { + ok := C.k2h_set_transaction_thread_pool((C.int)(pool)) + if ok == false { + return false, fmt.Errorf("C.k2h_set_transaction_thread_pool return false") + } + return true, nil +} + +// UnsetTxThreadPoolSize set the number of thread pools zero. +func (k2h *K2hash) UnsetTxThreadPoolSize() (bool, error) { + ok := C.k2h_unset_transaction_thread_pool() + if ok == false { + return false, fmt.Errorf("C.k2h_unset_transaction_thread_pool return false") + } + return true, nil +} + +// LoadFromFile loads data from archive files. +func (k2h *K2hash) LoadFromFile(file string, ignoreError bool) (bool, error) { + cFile := C.CString(file) + defer C.free(unsafe.Pointer(cFile)) + ok := C.k2h_load_archive(k2h.handle, cFile, (C._Bool)(ignoreError)) + if ok == false { + return false, fmt.Errorf("C.k2h_load_archive return false") + } + return true, nil +} + +// DumpToFile saves data from a file as a serialized data. +func (k2h *K2hash) DumpToFile(file string, ignoreError bool) (bool, error) { + cFile := C.CString(file) + defer C.free(unsafe.Pointer(cFile)) + ok := C.k2h_put_archive(k2h.handle, cFile, (C._Bool)(ignoreError)) + if ok != true { + return false, fmt.Errorf("k2h_put_archive() returns false") + } + return true, nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/.set.go.swp b/tests/.set.go.swp new file mode 100644 index 0000000..bc8debb Binary files /dev/null and b/tests/.set.go.swp differ diff --git a/tests/addattr.go b/tests/addattr.go new file mode 100644 index 0000000..6a826a0 --- /dev/null +++ b/tests/addattr.go @@ -0,0 +1,81 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testAddAttr tests k2hash.AddAttr method +func testAddAttr(t *testing.T) { + // 1. define test data. + pkey := kv{ + d: "parent key", + k: []byte("addsubkeys1_parent"), + v: []byte("p1"), + s: true, + p: "", + e: 0, + } + + // 2. open a k2h file + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + + // 3. set a key + ok, err := k.Set(string(pkey.k), string(pkey.v)) + if ok == false { + t.Errorf("k2hash.Set(%v, %v) return false. want true. err %v", pkey.k, pkey.v, err) + } + ok, val, err := getKey("/tmp/test.k2h", string(pkey.k)) + if ok != true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(pkey.k), ok, val, err) + } + if string(val) != string(pkey.v) { + t.Errorf("getKey(%v, %v) = (%v, %v, %v), want %v", "/tmp/test.k2h", string(pkey.k), ok, val, err, string(pkey.v)) + } + // 4. add a subkey + ok, err = k.AddAttr(string(pkey.k), "sample_attrkey", "sample_attrval") + if ok != true { + t.Errorf("AddAttr(%v) = (%v, %v)", string(pkey.k), ok, err) + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/addsubkey.go b/tests/addsubkey.go new file mode 100644 index 0000000..ffa5962 --- /dev/null +++ b/tests/addsubkey.go @@ -0,0 +1,91 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testAddSubKey tests k2hash.AddSubkey method. +func testAddSubKey(t *testing.T) { + // 1. define test data. + pkey := kv{ + d: "parent key", + k: []byte("addsubkeys1_parent"), + v: []byte("p1"), + s: true, + p: "", + e: 0, + } + skey := kv{ + d: "sub key", + k: []byte("addsubkeys1_sub1"), + v: []byte("s1"), + s: true, + p: "", + e: 0, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + + // 1. set a parent key + ok, err := k.Set(string(pkey.k), string(pkey.v)) + if ok == false { + t.Errorf("k2hash.Set(%v, %v) return false. want true. err %v", pkey.k, pkey.v, err) + } + ok, val, err := getKey("/tmp/test.k2h", string(pkey.k)) + if ok != true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(pkey.k), ok, val, err) + } + if string(val) != string(pkey.v) { + t.Errorf("getKey(%v, %v) = (%v, %v, %v), want %v", "/tmp/test.k2h", string(pkey.k), ok, val, err, string(pkey.v)) + } + // 2. Add a subkey + ok, err = k.AddSubKey(string(pkey.k), string(skey.k), string(skey.v)) + ok, val, err = getKey("/tmp/test.k2h", string(skey.k)) + if ok != true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(skey.k), ok, val, err) + } + if string(val) != string(skey.v) { + t.Errorf("getKey(%v, %v) = (%v, %v, %v), want %v", "/tmp/test.k2h", string(skey.k), ok, val, err, string(skey.v)) + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/attribute.go b/tests/attribute.go new file mode 100644 index 0000000..a45aa3c --- /dev/null +++ b/tests/attribute.go @@ -0,0 +1,185 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testEnableMtime tests k2hash.EnableMtime method +func testEnableMtime(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.EnableMtime(true) + // 3. print + //f.PrintAttrInformation() +} + +// testEnableEncryption tests k2hash.EnableEncryption method +func testEnableEncryption(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 3. print + //f.PrintAttrInformation() + // 2. set + f.EnableEncryption(true, "/tmp/pass.txt") + // 3. print + //f.PrintAttrInformation() +} + +// testEnableHistory tests k2hash.EnableHistory method +func testEnableHistory(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.EnableHistory(true) + // 3. print + //f.PrintAttrInformation() +} + +// testSetExpirationDuration tests k2hash.SetExpirationDuration method +func testSetExpirationDuration(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetExpirationDuration(1) + // 3. print + //f.PrintAttrInformation() +} + +// testAddDecryptionPassword tests k2hash.AddDecryptionPassword method +func testAddDecryptionPassword(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.AddDecryptionPassword("password") + // 3. print + //f.PrintAttrInformation() +} + +// testSetDefaultEncryptionPassword tests k2hash.SetDefaultEncryptionPassword method +func testSetDefaultEncryptionPassword(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDefaultEncryptionPassword("password") + // 3. print + //f.PrintAttrInformation() +} + +// testPrintAttrVersion tests k2hash.PrintAttrVersion method +func testPrintAttrVersion(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.PrintAttrVersion() +} + +// testPrintAttrInformation tests k2hash.PrintAttrInformation method +func testPrintAttrInformation(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.PrintAttrInformation() +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/debug.go b/tests/debug.go new file mode 100644 index 0000000..7d7aabf --- /dev/null +++ b/tests/debug.go @@ -0,0 +1,363 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testBumpDebugLevel tests k2hash.BumpDebugLevel method +func testBumpDebugLevel(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.BumpDebugLevel() +} + +// testSetDebugLevelSilent tests k2hash.SetDebugLevelSilent method +func testSetDebugLevelSilent(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDebugLevelSilent() +} + +// testSetDebugLevelError tests k2hash.SetDebugLevelError method +func testSetDebugLevelError(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDebugLevelError() +} + +// testSetDebugLevelWarning tests k2hash.SetDebugLevelWarning method +func testSetDebugLevelWarning(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDebugLevelWarning() +} + +// testSetDebugLevelMessage tests k2hash.SetDebugLevelMessage method +func testSetDebugLevelMessage(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDebugLevelMessage() +} + +// testSetDebugFile tests k2hash.SetDebugFile method +func testSetDebugFile(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetDebugFile("/tmp/debug.log") +} + +// testUnsetDebugFile tests k2hash.UnsetDebugFile method +func testUnsetDebugFile(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.UnsetDebugFile() +} + +// testLoadDebugEnv tests k2hash.LoadDebugEnv method +func testLoadDebugEnv(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.LoadDebugEnv() +} + +// testSetSignalUser1 tests k2hash.SetSignalUser1 method +func testSetSignalUser1(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.SetSignalUser1() +} + +// testDumpHead tests k2hash.DumpHead method +func testDumpHead(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.DumpHead() +} + +// testDumpKeyTable tests k2hash.DumpKeyTable method +func testDumpKeyTable(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.DumpKeyTable() +} + +// testDumpFullKeyTable tests k2hash.DumpFullKeyTable method +func testDumpFullKeyTable(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.DumpFullKeyTable() +} + +// testDumpElementTable tests k2hash.DumpElementTable method +func testDumpElementTable(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.DumpElementTable() +} + +// testDumpFull tests k2hash.DumpFull method +func testDumpFull(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.DumpFull() +} + +// testPrintState tests k2hash.PrintState method +func testPrintState(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.PrintState() +} + +// testPrintVersion tests k2hash.PrintVersion method +func testPrintVersion(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.PrintVersion() +} + +// testLoadHashLibrary tests k2hash.LoadHashLibrary method +func testLoadHashLibrary(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.LoadHashLibrary("/tmp/debug.log") +} + +// testUnloadHashLibrary tests k2hash.UnloadHashLibrary method +func testUnloadHashLibrary(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.UnloadHashLibrary() +} + +// testLoadTxLibrary tests k2hash.LoadTxLibrary method +func testLoadTxLibrary(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.LoadTxLibrary("/tmp/debug.log") +} + +// testUnloadTxLibrary tests k2hash.UnloadTxLibrary method +func testUnloadTxLibrary(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.UnloadTxLibrary() +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/get.go b/tests/get.go new file mode 100644 index 0000000..bd49042 --- /dev/null +++ b/tests/get.go @@ -0,0 +1,87 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testGet tests k2hash.Get method. +func testGet(t *testing.T) { + // 1. define test data. + testData := []kv{ + { + d: "test for string data", + k: []byte("strkey"), + v: []byte("strval"), + s: true, + p: "", + e: 0, + }, + } + // 2. exec tests + for _, d := range testData { + if ok, err := clearIfExists("/tmp/test.k2h", string(d.k)); !ok { + t.Errorf("clearIfExists(%v, %v = (%v, %v)", "/tmp/test.k2h", string(d.k), ok, err) + } + if ok, err := setKey("/tmp/test.k2h", d); !ok { + t.Errorf("saveData(%v, %v) = (%v, %v)", "/tmp/test.k2h", d, ok, err) + } + testGetArgs(d, t) + } +} + +func testGetArgs(d kv, t *testing.T) { + // 1. Instantiate K2hash class + file, _ := k2hash.NewK2hash("/tmp/test.k2h") + defer file.Close() + + // 2. Get + if d.e == 0 && d.p == "" { + // 2.1. no args + val, err := file.Get(string(d.k)) + if err != nil { + t.Errorf("Get(%v) = (%v, %v)", string(d.v), val.String(), err) + } + //fmt.Printf("val = %v, err = %v\n", val.String(), err) + if val.String() != string(d.v) { + t.Errorf("GetResult().String() = %v, want %v", val.String(), string(d.v)) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/getattrs.go b/tests/getattrs.go new file mode 100644 index 0000000..c5e3592 --- /dev/null +++ b/tests/getattrs.go @@ -0,0 +1,96 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testGetAttrs tests k2hash.Get method. +func testGetAttrs(t *testing.T) { + // 1. define test data. + testData := []kv{ + { + d: "test for string data", + k: []byte("strkey"), + v: []byte("strval"), + s: true, + p: "", + e: 0, + }, + } + // 2. exec tests + for _, d := range testData { + if ok, err := clearIfExists("/tmp/test.k2h", string(d.k)); !ok { + t.Errorf("clearIfExists(%v, %v) = (%v, %v)", "/tmp/test.k2h", string(d.k), ok, err) + } + k, err := k2hash.NewK2hash("/tmp/sample.k2h") + if err != nil { + t.Errorf("NewK2hash(%v) = (%v, %v)", "/tmp/test.k2h", k, err) + } + defer k.Close() + if ok, err := k.Set(string(d.k), string(d.v)); !ok { + t.Errorf("k.Set(%v, %v) = (%v, %v)", string(d.k), string(d.v), ok, err) + } + if ok, err := k.AddAttr(string(d.k), "attrkey1", "attrval1"); !ok { + t.Errorf("AddAttr(%v, %v, %v) = (%v, %v)", string(d.k), "attrkey1", "attrval1", ok, err) + } + + testGetAttrsArgs(d, "attrkey1", "attrval1", t) + } +} + +func testGetAttrsArgs(d kv, attrkey string, attrval string, t *testing.T) { + // 1. Instantiate K2hash class + file, _ := k2hash.NewK2hash("/tmp/test.k2h") + defer file.Close() + + // 2. Get + _, err := file.GetAttrs(string(d.k)) + if err != nil { + //if strings.Compare(attrs[0].key, attrkey) != 0 { + // t.Errorf("GetAttrs(%v) = (%v, %v)", string(d.k), attrs, err) + //} + //if strings.Compare(attrs[0].val, attrval) != 0 { + // t.Errorf("GetAttrs(%v) = (%v, %v)", string(d.k), attrs, err) + //} + } else { + t.Errorf("GetAttrs(%v) = (%v, %v)", string(d.k), "attrkey1", "attrval1") + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/getsubkeys.go b/tests/getsubkeys.go new file mode 100644 index 0000000..8bcadf0 --- /dev/null +++ b/tests/getsubkeys.go @@ -0,0 +1,112 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "strings" + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testGetSubKeys tests k2hash.GetSubKeys method. +func testGetSubKeys(t *testing.T) { + // 1. define test data. + testData := []sks{ + { + d: "binary", + key: kv{ + k: []byte("getsubkeys1_parent"), + v: []byte("p1"), + p: "", + }, + keys: []kv{ + { + k: []byte("getsubkeys1_sub1"), + v: []byte("s1"), + p: "", + }, + { + k: []byte("getsubkeys1_sub2"), + v: []byte("s2"), + p: "", + }, + }, + }, + } + // 2. exec tests + for _, d := range testData { + if ok, err := clearIfExists("/tmp/test.k2h", string(d.key.k)); !ok { + t.Errorf("clearIfExists(%v, %v) = (%v, %v)", "/tmp/test.k2h", string(d.key.k), ok, err) + } + file, _ := k2hash.NewK2hash("/tmp/test.k2h") + defer file.Close() + if ok, err := file.Set(string(d.key.k), string(d.key.v)); !ok { + t.Errorf("file.Set(%v, %v) = (%v, %v)", string(d.key.k), string(d.key.v), ok, err) + } + var skeys []string + for i, key := range d.keys { + if ok, err := file.Set(key.k, key.v); !ok { + t.Errorf("file.Set(%v, %v) = (%v, %v)", key.k, key.v, ok, err) + } + skeys[i] = string(key.k) + } + if ok, err := file.SetSubKeys(d.key.k, skeys); !ok { + t.Errorf("file.Set(%v, %v) = (%v, %v)", d.key.k, skeys, ok, err) + } + file.Close() + testGetSubKeysArgs(string(d.key.k), skeys, t) + } +} + +func testGetSubKeysArgs(key string, skeys []string, t *testing.T) { + // 1. Instantiate K2hash class + file, _ := k2hash.NewK2hash("/tmp/test.k2h") + defer file.Close() + + // 2. Get + // 2.1. no args + rskeys, err := file.GetSubKeys(key) + if err != nil { + t.Errorf("GetSubKeys(%v) = (%v, %v)", key, rskeys, err) + } + for i, key := range skeys { + if strings.Compare(key, rskeys[i]) != 0 { + t.Errorf("%v = %v", key, rskeys[i]) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/k2hash_test.go b/tests/k2hash_test.go new file mode 100644 index 0000000..08f3dd0 --- /dev/null +++ b/tests/k2hash_test.go @@ -0,0 +1,108 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + "fmt" + "os" + "runtime" + "testing" + "unsafe" +) + +// TestMain is the main function of testing. +func TestMain(m *testing.M) { + if runtime.GOOS != "linux" && runtime.GOARCH != "amd64" { + fmt.Fprintf(os.Stderr, "k2hash currently works on linux only") + os.Exit(-1) + } + i := uint32(1) + b := (*[4]byte)(unsafe.Pointer(&i)) + if b[0] != 1 { + fmt.Fprintf(os.Stderr, "k2hash_go currently works on little endian alignment only") + os.Exit(-1) + } + // rpm or deb install the k2hash.so in /usr/lib or /usr/lib64 + if _, err := os.Stat("/usr/lib/libk2hash.so"); err != nil { + if _, err := os.Stat("/usr/lib64/libk2hash.so"); err != nil { + fmt.Fprintf(os.Stderr, "Please install the k2hash package at first") + os.Exit(-1) + } + } + setUp(m) + status := m.Run() + tearDown(m) + os.Exit(status) +} + +func setUp(m *testing.M) { + //fmt.Println("test.go setUp") +} + +func tearDown(m *testing.M) { + //fmt.Println("test.go tearDown") +} +func TestBumpDebugLevel(t *testing.T) { testBumpDebugLevel(t) } +func TestSetDebugLevelSilent(t *testing.T) { testSetDebugLevelSilent(t) } +func TestSetDebugLevelError(t *testing.T) { testSetDebugLevelError(t) } +func TestSetDebugLevelWarning(t *testing.T) { testSetDebugLevelWarning(t) } +func TestSetDebugLevelMessage(t *testing.T) { testSetDebugLevelMessage(t) } +func TestSetDebugFile(t *testing.T) { testSetDebugFile(t) } +func TestUnsetDebugFile(t *testing.T) { testUnsetDebugFile(t) } +func TestLoadDebugEnv(t *testing.T) { testLoadDebugEnv(t) } +func TestSetSignalUser1(t *testing.T) { testSetSignalUser1(t) } +func TestDumpHead(t *testing.T) { testDumpHead(t) } +func TestDumpKeyTable(t *testing.T) { testDumpKeyTable(t) } +func TestDumpElementTable(t *testing.T) { testDumpElementTable(t) } +func TestDumpFull(t *testing.T) { testDumpFull(t) } +func TestPrintState(t *testing.T) { testPrintState(t) } +func TestPrintVersion(t *testing.T) { testPrintVersion(t) } +func TestLoadHashLibrary(t *testing.T) { testLoadHashLibrary(t) } +func TestUnloadHashLibrary(t *testing.T) { testUnloadHashLibrary(t) } +func TestLoadTxLibrary(t *testing.T) { testLoadTxLibrary(t) } +func TestUnloadTxLibrary(t *testing.T) { testUnloadTxLibrary(t) } + +func TestSet(t *testing.T) { testSet(t) } +func TestGet(t *testing.T) { testGet(t) } +func TestRemove(t *testing.T) { testRemove(t) } +func TestAddSubKey(t *testing.T) { testAddSubKey(t) } + +func TestEnableMtime(t *testing.T) { testEnableMtime(t) } +func TestEnableEncryption(t *testing.T) { testEnableEncryption(t) } +func TestEnableHistory(t *testing.T) { testEnableHistory(t) } +func TestSetExpirationDuration(t *testing.T) { testSetExpirationDuration(t) } + +func TestSetDefaultEncryptionPassword(t *testing.T) { testSetDefaultEncryptionPassword(t) } +func TestPrintAttrVersion(t *testing.T) { testPrintAttrVersion(t) } +func TestPrintAttrInformation(t *testing.T) { testPrintAttrInformation(t) } + +func TestBeginTx(t *testing.T) { testBeginTx(t) } +func TestStopTx(t *testing.T) { testStopTx(t) } +func TestGetTxFileFD(t *testing.T) { testGetTxFileFD(t) } +func TestGetTxThreadPoolSize(t *testing.T) { testGetTxThreadPoolSize(t) } +func TestSetTxThreadPoolSize(t *testing.T) { testSetTxThreadPoolSize(t) } +func TestUnsetTxThreadPoolSize(t *testing.T) { testUnsetTxThreadPoolSize(t) } +func TestLoadFromFile(t *testing.T) { testLoadFromFile(t) } +func TestDumpToFile(t *testing.T) { testDumpToFile(t) } + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/keyqueue.go b/tests/keyqueue.go new file mode 100644 index 0000000..9e2ad6e --- /dev/null +++ b/tests/keyqueue.go @@ -0,0 +1,121 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +type qdata struct { + p []byte // prefix + v []byte // val + k []byte // key + f bool // fifo + c bool // copy attr if true + s bool // string or binary + w string // password + e int64 // duration of expiration(unit seconds) +} + +// testKeyQueuePush tests K2hashQueue.Push method. +func testKeyQueuePush(t *testing.T) { + // 1. define test data. + testData := []qdata{ + // 1. default + { + p: []byte("push_prefix_2"), + v: []byte("push_prefix_2_val"), + k: nil, + f: true, + c: true, + w: "", + e: 0, + s: false, + }, + // 2. default + key + { + p: []byte("push_prefix_2"), + v: []byte("push_prefix_2_val"), + k: []byte("push_prefix_2_key"), + f: true, + c: true, + w: "", + e: 0, + s: false, + }, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + + for _, d := range testData { + if ok, err := clearIfExists("/tmp/test.k2h", string(d.k)); !ok { + t.Errorf("clearIfExists(%v, %v = (%v, %v)", "/tmp/test.k2h", string(d.k), ok, err) + } + testKeyQPushArgs(d, t) + } +} + +func testKeyQPushArgs(d qdata, t *testing.T) { + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + q, err := k2hash.NewKeyQueue(k) + if err != nil { + t.Errorf("k2hash.NewKeyQueue(%v) return err %v", k, err) + } + defer q.Free() + // 1. push + if ok, err := q.Push(string(d.v)); !ok { + t.Errorf("KeyQueue.Push(%v) return false. wants true. err %v", string(d.k), err) + } + // 2. count + if c, _ := q.Count(); c != 1 { + t.Errorf("KeyQueue.Count() return not 1. wants 1") + } + // 3. pop + if s, err := q.Pop(); s != string(d.v) { + t.Errorf("KeyQueue.Pop( return %v. wants true. err %v", string(d.k), err) + } + +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/queue.go b/tests/queue.go new file mode 100644 index 0000000..a002492 --- /dev/null +++ b/tests/queue.go @@ -0,0 +1,110 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testQueuePush tests K2hashQueue.Push method. +func testQueuePush(t *testing.T) { + // 1. define test data. + testData := []qdata{ + // 1. default + { + p: []byte("push_prefix_2"), + v: []byte("push_prefix_2_val"), + k: nil, + f: true, + c: true, + w: "", + e: 0, + s: false, + }, + // 2. default + key + { + p: []byte("push_prefix_2"), + v: []byte("push_prefix_2_val"), + k: []byte("push_prefix_2_key"), + f: true, + c: true, + w: "", + e: 0, + s: false, + }, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + + for _, d := range testData { + if ok, err := clearIfExists("/tmp/test.k2h", string(d.k)); !ok { + t.Errorf("clearIfExists(%v, %v = (%v, %v)", "/tmp/test.k2h", string(d.k), ok, err) + } + testQPushArgs(d, t) + } +} + +func testQPushArgs(d qdata, t *testing.T) { + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + q, err := k2hash.NewQueue(k) + if err != nil { + t.Errorf("k2hash.NewK2hashQueue(%v) return err %v", k, err) + } + defer q.Free() + // 1. push + if ok, err := q.Push(string(d.v)); !ok { + t.Errorf("K2hashQueue.Push(%v) return false. wants true. err %v", string(d.k), err) + } + // 2. count + if c, _ := q.Count(); c != 1 { + t.Errorf("K2hashQueue.Count() return not 1. wants 1") + } + // 3. pop + if s, err := q.Pop(); s != string(d.v) { + t.Errorf("K2hashQueue.Pop( return %v. wants true. err %v", string(d.k), err) + } + +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/remove.go b/tests/remove.go new file mode 100644 index 0000000..5ea8ed8 --- /dev/null +++ b/tests/remove.go @@ -0,0 +1,84 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testRemove tests k2hash.Remove method. +func testRemove(t *testing.T) { + // 1. define test data. + testData := []kv{ + { + d: "test for string data", + k: []byte("strkey"), + v: []byte("strval"), + s: true, + p: "", + e: 0, + }, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + for _, d := range testData { + ok, err := k.Set(string(d.k), string(d.v)) + if ok == false { + t.Errorf("k2hash.Set(%v, %v) return false. want true. err %v", string(d.k), string(d.v), err) + } + ok, val, err := getKey("/tmp/test.k2h", string(d.k)) + if ok != true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(d.k), ok, val, err) + } + if string(val) != string(d.v) { + t.Errorf("getKey(%v, %v) = (%v, %v, %v), want %v", "/tmp/test.k2h", string(d.k), ok, val, err, string(d.v)) + } + ok, err = k.Remove(string(d.k)) + if ok == false { + t.Errorf("k2hash.Remove(%v) return false. want true. err %v", string(d.k), err) + } + ok, val, err = getKey("/tmp/test.k2h", string(d.k)) + if ok == true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(d.k), ok, val, err) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/rename.go b/tests/rename.go new file mode 100644 index 0000000..0af31a8 --- /dev/null +++ b/tests/rename.go @@ -0,0 +1,77 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testRename tests k2hash.Set method. +func testRename(t *testing.T) { + // 1. define test data. + testData := []kk{ + { + d: "test for string data", + o: []byte("oldkey"), + v: []byte("value"), + n: []byte("newkey"), + }, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + for _, d := range testData { + // 1. set the old data. + ok, err := k.Set(string(d.o), string(d.v)) + if ok == false { + t.Errorf("k2hash.Set(%v, %v) return false. want true. err %v", string(d.o), string(d.v), err) + } + // 2. rename the old data with the new data + ok, err = k.Rename(string(d.o), string(d.n)) + if ok == false { + t.Errorf("k2hash.Rename(%v, %v) return false. want true. err %v", string(d.o), string(d.n), err) + } + ok, val, err := getKey("/tmp/test.k2h", string(d.n)) + if ok != true { + t.Errorf("getKey(/tmp/test.k2h, %v) = (%v, %v, %v)", string(d.n), ok, val, err) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/set.go b/tests/set.go new file mode 100644 index 0000000..fd9f011 --- /dev/null +++ b/tests/set.go @@ -0,0 +1,76 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testSet tests k2hash.Set method. +func testSet(t *testing.T) { + // 1. define test data. + testData := []kv{ + { + d: "test for string data", + k: []byte("strkey"), + v: []byte("strval"), + s: true, + p: "", + e: 0, + }, + } + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + for _, d := range testData { + ok, err := k.Set(string(d.k), string(d.v)) + if ok == false { + t.Errorf("k2hash.Set(%v, %v) return false. want true. err %v", d.k, d.v, err) + } + ok, val, err := getKey("/tmp/test.k2h", string(d.k)) + if ok != true { + t.Errorf("getKey(%v, %v) = (%v, %v, %v)", "/tmp/test.k2h", string(d.k), ok, val, err) + } + if string(val) != string(d.v) { + t.Errorf("getKey(%v, %v) = (%v, %v, %v), want %v", "/tmp/test.k2h", string(d.k), ok, val, err, string(d.v)) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/setsubkeys.go b/tests/setsubkeys.go new file mode 100644 index 0000000..da86bee --- /dev/null +++ b/tests/setsubkeys.go @@ -0,0 +1,101 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + "unsafe" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testSet tests k2hash.Set method. +func testSetSubKeys(t *testing.T) { + // 1. define test data. + testData := []sks{ + { + d: "binary", + key: kv{ + k: []byte("setsubkeys1_parent"), + v: []byte("p1"), + p: "", + }, + keys: []kv{ + { + k: []byte("setsubkeys1_sub1"), + v: []byte("s1"), + p: "", + }, + { + k: []byte("setsubkeys1_sub2"), + v: []byte("s2"), + p: "", + }, + }, + }, + } + + k, err := k2hash.NewK2hash("/tmp/test.k2h") + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) return err %v", err) + } + defer k.Close() + for _, d := range testData { + // 1. removes the current parent key if exists + if ok, err := clearIfExists("/tmp/test.k2h", string(d.key.k)); !ok { + t.Errorf("clearIfExists(%v, %v) = (%v, %v)", "/tmp/test.k2h", string(d.key.k), ok, err) + } + // 2. make a new parent key + if ok, err := setKey("/tmp/test.k2h", d.key); !ok { + t.Errorf("setKey(%v, %v) = (%v, %v)", "/tmp/test.k2h", d.key, ok, err) + } + // 3. make a child key + skeys := make([]*C.char, len(d.keys)+1, len(d.keys)+1) + for i, key := range d.keys { + if ok, err := setKey("/tmp/test.k2h", key); !ok { + t.Errorf("saveData(%q, %q, %q) = (%v, %v)", key.k, key.v, key.p, ok, err) + } + skeys[i] = C.CString(string(key.k)) + defer C.free(unsafe.Pointer(skeys[i])) + } + skeys[len(d.keys)] = nil + // 4. link the child key to the parent key as a child key + if ok, err := k.SetSubKeys(d.key, skeys); !ok { + t.Errorf("SetSubKeys(%v, %v) = (%v, %v)", d.key, skeys, ok, err) + } + } +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/transaction.go b/tests/transaction.go new file mode 100644 index 0000000..0760007 --- /dev/null +++ b/tests/transaction.go @@ -0,0 +1,176 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "testing" + + "github.com/yahoojapan/k2hash_go/k2hash" +) + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +// testBeginTx tests k2hash.BeginTx method +func testBeginTx(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. set + f.BeginTx("/tmp/transaction.log") +} + +// testStopTx tests k2hash.StopTx method +func testStopTx(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 3. print + //f.PrintAttrInformation() + // 2. set + _, error := f.StopTx() + if error != nil { + t.Errorf("k2hash.StopTx() = ()") + } +} + +// testGetTxFileFD tests k2hash.GetTxFileFD method +func testGetTxFileFD(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.GetTxFileFD() +} + +// testGetTxThreadPoolSize tests k2hash.GetTxThreadPoolSize method +func testGetTxThreadPoolSize(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.GetTxThreadPoolSize() +} + +// testSetTxThreadPoolSize tests k2hash.SetTxThreadPoolSize method +func testSetTxThreadPoolSize(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.SetTxThreadPoolSize(1) +} + +// testUnsetTxThreadPoolSize tests k2hash.UnsetTxThreadPoolSize method +func testUnsetTxThreadPoolSize(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.UnsetTxThreadPoolSize() +} + +// testLoadFromFile tests k2hash.LoadFromFile method +func testLoadFromFile(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.LoadFromFile("/tmp/testarchive.k2h", true) +} + +// testDumpToFile tests k2hash.DumpToFile method +func testDumpToFile(t *testing.T) { + // 1. Instantiate K2hash class + f, err := k2hash.NewK2hash("/tmp/test.k2h") + if f == nil { + defer f.Close() + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) = (nil, error), want not nil") + } + if err != nil { + t.Errorf("k2hash.NewK2hash(/tmp/test.k2h) error %v", err) + } + + // 2. print + f.DumpToFile("/tmp/testarchive.k2h", true) +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/tests/util.go b/tests/util.go new file mode 100644 index 0000000..39119aa --- /dev/null +++ b/tests/util.go @@ -0,0 +1,187 @@ +// +// k2hash_go +// +// Copyright 2018 Yahoo Japan Corporation. +// +// Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +// For k2hash, see https://github.com/yahoojapan/k2hash for the details. +// +// For the full copyright and license information, please view +// the license file that was distributed with this source code. +// +// AUTHOR: Hirotaka Wakabayashi +// CREATE: Fri, 14 Sep 2018 +// REVISION: +// + +package k2hashtest + +import ( + // #cgo CFLAGS: -g -Wall -O2 -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -I. -I/usr/include/k2hash + // #cgo LDFLAGS: -L/usr/lib -lk2hash + // #include + // #include "k2hash.h" + "C" +) + +import ( + "errors" + "fmt" + "unsafe" +) + +type kv struct { + d string // description + k []byte // key + v []byte // val + r bool // remove subkeys if true. + s bool // true if use string format when saving the data. + p string // password + e int64 // expire +} +type kk struct { + d string // description + o []byte // new + v []byte // key + n []byte // old +} +type sk struct { + d string + key kv // key(parent) + skey kv // subkey +} +type sks struct { + d string // description + key kv // parentkey + keys []kv // subkeys +} + +func clearIfExists(f string, key string) (bool, error) { + cK2h := C.CString(f) + defer C.free(unsafe.Pointer(cK2h)) + // 1. open + handle := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handle) + } + + // 2. remove + cKey := C.CString(key) + defer C.free(unsafe.Pointer(cKey)) + ok := C.k2h_remove_str_all( + handle, + (*C.char)(cKey)) + if ok == false { + return false, errors.New("C.k2h_remove_str_all returned false") + } + + // 3. close + ok = C.k2h_close(handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handle = C.K2H_INVALID_HANDLE + return true, nil +} + +func setKey(f string, d kv) (bool, error) { + if len(d.k) == 0 { + return false, errors.New("len(k) == 0") + } + + // 1. open + cK2h := C.CString(f) + defer C.free(unsafe.Pointer(cK2h)) + handle := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handle == C.K2H_INVALID_HANDLE { + return false, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handle) + } + + // 2. set_value + cKey := C.CString(string(d.k)) + defer C.free(unsafe.Pointer(cKey)) + cVal := C.CString(string(d.v)) + defer C.free(unsafe.Pointer(cVal)) + cPass := C.CString(d.p) + defer C.free(unsafe.Pointer(cPass)) + var expire *C.time_t + // WARNING: You can't set zero expire. + if d.e != 0 { + expire = (*C.time_t)(&d.e) + } + ok := C.k2h_set_str_value_wa(handle, (*C.char)(cKey), (*C.char)(cVal), cPass, expire) + if ok == false { + return false, errors.New("C.k2h_set_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handle) + if ok != true { + return false, fmt.Errorf("k2h_close() returns false") + } + handle = C.K2H_INVALID_HANDLE + return true, nil +} + +func getKey(f string, k string) (bool, []byte, error) { + cK2h := C.CString(f) + defer C.free(unsafe.Pointer(cK2h)) + + // 1. open + handle := C.k2h_open( + cK2h, + C._Bool(false), + C._Bool(false), + C._Bool(false), + C.int(8), + C.int(4), + C.int(1024), + C.size_t(512)) + if handle == C.K2H_INVALID_HANDLE { + return false, nil, fmt.Errorf("k2h_open() returns K2H_INVALID_HANDLE(%v)", handle) + } + + // 2. get + cKey := C.CString(k) + defer C.free(unsafe.Pointer(cKey)) + var cRetValue *C.char + defer C.free(unsafe.Pointer(cRetValue)) + //cRetValue = C.k2h_get_str_direct_value(k2h.handle, (*C.char)(cKey)) + ok := C.k2h_get_str_value(handle, (*C.char)(cKey), &cRetValue) + rv := C.GoString(cRetValue) + if ok == false { + return false, nil, errors.New("C.k2h_set_value_wa returned false") + } + + // 3. close + ok = C.k2h_close(handle) + if ok != true { + return false, nil, fmt.Errorf("k2h_close() returns false") + } + handle = C.K2H_INVALID_HANDLE + return true, []byte(rv), nil +} + +// Local Variables: +// c-basic-offset: 4 +// tab-width: 4 +// indent-tabs-mode: t +// End: +// vim600: noexpandtab sw=4 ts=4 fdm=marker +// vim<600: noexpandtab sw=4 ts=4 diff --git a/utils/libk2hash.sh b/utils/libk2hash.sh new file mode 100644 index 0000000..f0ab35a --- /dev/null +++ b/utils/libk2hash.sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# k2hash_go +# +# Copyright 2018 Yahoo Japan Corporation. +# +# Go driver for k2hash that is a NoSQL Key Value Store(KVS) library. +# For k2hash, see https://github.com/yahoojapan/k2hash for the details. +# +# For the full copyright and license information, please view +# the license file that was distributed with this source code. +# +# AUTHOR: Hirotaka Wakabayashi +# CREATE: Fri, 14 Sep 2018 +# REVISION: +# + +# Sets the default locale. LC_ALL has precedence over other LC* variables. +unset LANG +unset LANGUAGE +LC_ALL=en_US.utf8 +export LC_ALL + +# Sets PATH. setup_*.sh uses useradd command +PATH=${PATH}:/usr/sbin:/sbin + +# an unset parameter expansion will fail +set -u + +# umask 022 is enough +umask 022 + +# environments +SRCDIR=$(cd $(dirname "$0") && pwd) +DEBUG=1 +if test "${DEBUG}" -eq 1; then + TAG="$(basename $0) -s" +else + TAG=$(basename $0) +fi +USER=$(whoami) +LOGLEVEL=info + +# Checks if k2hash is installed +# +# Params:: +# no params +# +# Returns:: +# 0 on installed +# 1 on not installed +# +which_k2hash() { + which k2hlinetool >/dev/null 2>&1 + if test "${?}" = "0"; then + logger -t ${TAG} -p user.info "k2hash already installed" + return 0 + fi + return 1 +} + +# Determines the current OS +# +# Params:: +# no params +# +# Returns:: +# 0 on success +# 1 on failure +# +setup_os_env() { + if test -f "/etc/os-release"; then + . /etc/os-release + OS_NAME=$ID + OS_VERSION=$VERSION_ID + else + logger -t ${TAG} -p user.warn "unknown OS, no /etc/os-release and /etc/centos-release falling back to CentOS-7" + OS_NAME=centos + OS_VERSION=7 + fi + + if test "${OS_NAME}" = "ubuntu"; then + logger -t ${TAG} -p user.notice "ubuntu configurations are currently equal to debian one" + OS_NAME=debian + elif test "${OS_NAME}" = "centos"; then + if test "${OS_VERSION}" != "7"; then + logger -t ${TAG} -p user.err "centos7 only currently supported, not ${OS_NAME} ${OS_VERSION}" + exit 1 + fi + fi + + HOSTNAME=$(hostname) + logger -t ${TAG} -p user.debug "HOSTNAME=${HOSTNAME} OS_NAME=${OS_NAME} OS_VERSION=${OS_VERSION}" +} + +# Builds k2hash from source code +# +# Params:: +# $1 os_name +# +# Returns:: +# 0 on success +# 1 on failure(exit) +# +make_k2hash() { + + _os_name=${1:?"os_name should be nonzero"} + + if test "${_os_name}" = "debian" -o "${_os_name}" = "ubuntu"; then + _configure_opt="--with-gcrypt" + sudo apt-get update -y + sudo apt-get install -y git curl autoconf autotools-dev gcc g++ make gdb libtool pkg-config libyaml-dev libgcrypt20-dev + elif test "${_os_name}" = "fedora"; then + _configure_opt="--with-nss" + sudo dnf install -y git curl autoconf automake gcc gcc-c++ gdb make libtool pkgconfig libyaml-devel nss-devel + elif test "${_os_name}" = "centos" -o "${_os_name}" = "rhel"; then + _configure_opt="--with-nss" + sudo yum install -y git curl autoconf automake gcc gcc-c++ gdb make libtool pkgconfig libyaml-devel nss-devel + else + logger -t ${TAG} -p user.error "OS should be debian, ubuntu, fedora, centos or rhel" + exit 1 + fi + + logger -t ${TAG} -p user.debug "git clone https://github.com/yahoojapan/k2hash" + git clone https://github.com/yahoojapan/k2hash + cd k2hash + + logger -t ${TAG} -p user.debug "git clone https://github.com/yahoojapan/fullock" + git clone https://github.com/yahoojapan/fullock + logger -t ${TAG} -p user.debug "git clone https://github.com/yahoojapan/k2hash" + git clone https://github.com/yahoojapan/k2hash + + if ! test -d "fullock"; then + echo "no fullock" + exit 1 + fi + cd fullock + ./autogen.sh + ./configure --prefix=/usr + make + sudo make install + + if ! test -d "../k2hash"; then + echo "no k2hash" + exit 1 + fi + cd ../k2hash + ./autogen.sh + ./configure --prefix=/usr ${_configure_opt} + make + sudo make install + + return 0 +} + +# +# main loop +# + +setup_os_env + +which_k2hash +if test "${?}" = "0"; then + exit 0 +fi + +if test "${OS_NAME}" = "fedora"; then + which sudo + if test "${?}" = "1"; then + dnf install -y sudo + fi + if test "${?}" = "1"; then + sudo dnf install -y bc + fi + which bc + if test "${?}" = "1"; then + sudo dnf install -y bc + fi + if test "${OS_VERSION}" = "28"; then + sudo dnf install -y curl + curl -s https://packagecloud.io/install/repositories/antpickax/stable/script.rpm.sh | sudo bash + sudo dnf install k2hash-devel -y + elif test "${OS_VERSION}" = "29"; then + sudo dnf install -y curl + curl -s https://packagecloud.io/install/repositories/antpickax/current/script.rpm.sh | sudo bash + sudo dnf install k2hash-devel -y + else + make_k2hash ${OS_NAME} + fi +elif test "${OS_NAME}" = "debian" -o "${OS_NAME}" = "ubuntu"; then + which sudo + if test "${?}" = "1"; then + apt-get update -y + apt-get install -y sudo + fi + which bc + if test "${?}" = "1"; then + sudo apt-get install -y bc + fi + if test "${OS_VERSION}" = "9"; then + sudo apt-get install -y curl + curl -s https://packagecloud.io/install/repositories/antpickax/stable/script.deb.sh | sudo bash + sudo apt-get install -y k2hash-dev + else + make_k2hash ${OS_NAME} + fi +elif test "${OS_NAME}" = "centos" -o "${OS_NAME}" = "rhel"; then + which sudo + if test "${?}" = "1"; then + yum install -y sudo + fi + which bc + if test "${?}" = "1"; then + sudo yum install -y bc + fi + yum install -y k2hash-devel + if test "${?}" != "0"; then + sudo yum install -y curl + curl -s https://packagecloud.io/install/repositories/antpickax/stable/script.rpm.sh | bash + sudo yum install -y k2hash-devel + if test "${?}" != "0"; then + make_k2hash ${OS_NAME} + fi + fi +else + echo "[NO] OS must be either fedora or centos or debian or ubuntu, not ${OS_NAME}" + exit 1 +fi + +if ! test -f "/dev/log"; then + if test -f "/run/systemd/journal/dev-log"; then + sudo ln -s /run/systemd/journal/dev-log /dev/log + else + echo "/dev/log unavailable" + fi +fi + +which_k2hash +if test "${?}" = "0"; then + exit 0 +fi + +exit 0 + +# Local Variables: +# c-basic-offset: 4 +# tab-width: 4 +# indent-tabs-mode: t +# End: +# vim600: noexpandtab sw=4 ts=4 fdm=marker +# vim<600: noexpandtab sw=4 ts=4