Make it possible to reference Go objects from C.

This patch is a variant on sample code written by Nick Wellnhofer.


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/dab9a88d
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/dab9a88d
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/dab9a88d

Branch: refs/heads/master
Commit: dab9a88d5c654a2539e4a81db4d74f88cc976cf3
Parents: fed6ca7
Author: Marvin Humphrey <[email protected]>
Authored: Thu Jul 16 20:32:45 2015 -0700
Committer: Marvin Humphrey <[email protected]>
Committed: Fri Jul 31 16:48:03 2015 -0700

----------------------------------------------------------------------
 go/lucy/registry.go      | 135 ++++++++++++++++++++++++++++++++++++++++++
 go/lucy/registry_test.go |  84 ++++++++++++++++++++++++++
 2 files changed, 219 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/dab9a88d/go/lucy/registry.go
----------------------------------------------------------------------
diff --git a/go/lucy/registry.go b/go/lucy/registry.go
new file mode 100644
index 0000000..8671931
--- /dev/null
+++ b/go/lucy/registry.go
@@ -0,0 +1,135 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lucy
+
+import "sync"
+
+type indexInt uintptr
+
+type objRegistry struct {
+       // Use pointer to array to guarantee atomic update for lock-free reads.
+       // Assume that loads and stores of the pointer are atomic.
+       entries *[]interface{}
+       freeListHead indexInt
+       mutex sync.Mutex
+}
+
+func newObjRegistry(size uintptr) *objRegistry {
+       entries := make([]interface{}, size)
+
+       // Each empty entry points to the index of the next empty entry.  Index 0
+       // is unused.  The last slot is seet to a terminating sentry value of 0.
+       entries[0] = indexInt(0) // unused
+       for i := uintptr(1); i < size - 1; i++ {
+               entries[i] = indexInt(i + 1)
+       }
+       entries[size-1] = indexInt(0)
+
+       reg := &objRegistry{}
+       reg.entries = &entries
+       reg.freeListHead = indexInt(1)
+
+       return reg
+}
+
+func (reg *objRegistry) store(obj interface{}) uintptr {
+       reg.mutex.Lock()
+
+       // Find the index of the next empty slot.
+       index := uintptr(reg.freeListHead)
+
+       entries := reg.entries
+
+       if (index != 0) {
+               // A slot is available.  It contains the index of the next 
available
+               // slot; put that index into the freeListHead.
+               reg.freeListHead = (*entries)[index].(indexInt)
+       } else {
+               // The sentinel value was encountered, indicating that we are 
out of
+               // space and must grow the entries array.
+
+               // The list head was 0, a slot we don't want to use.  Figure 
out what
+               // slot we're going to use instead.  If the current size of the
+               // entries array is 8, and will soon be 16, use slot 8.
+               index = uintptr(len(*entries))
+
+               // Duplicate the array and copy in the existing entries data.
+               newSize := index * 2
+               newEntries := make([]interface{}, newSize)
+               copy(newEntries, *entries)
+
+               // Set up each new empty slot to point at another new empty 
slot, up
+               // to the final slot which will get the sentinel value 0.
+               for i := index + 1; i < newSize - 1; i++ {
+                       newEntries[i] = indexInt(i + 1)
+               }
+               newEntries[newSize - 1] = indexInt(0)
+               entries = &newEntries
+               reg.entries = entries
+
+               // Set the freeListHead to one greater than the slot we're 
using this
+               // time -- i.e. if the current size is 8, the new size is 16, 
and the
+               // slot we use for the supplied value is 8, then the new list 
head
+               // will be 9.
+               reg.freeListHead = indexInt(index + 1)
+       }
+
+       // Store the supplied value in the slot.
+       (*entries)[index] = obj
+
+       reg.mutex.Unlock()
+
+       return index
+}
+
+func (reg *objRegistry) fetch(index uintptr) interface{} {
+
+       // Ignore an out of range request.
+       if index >= uintptr(len(*reg.entries)) {
+               return nil
+       }
+       entry := (*reg.entries)[index]
+       if _, ok := entry.(indexInt); ok {
+               // Return nil if the slot is empty.
+               return nil
+       }
+       return entry
+}
+
+func (reg *objRegistry) delete(index uintptr) {
+       reg.mutex.Lock()
+
+       // Overwrite the value at the supplied index with the freeListHead.  For
+       // example, if you are storing strings and the entries array consists of
+       // {0, "A", "B", C", 5, 6, 7, 0}, with freeListHead at 4, then deleting
+       // index 2 (string value "B") will result in the following state:
+       // {0, "A", 4, "C", 5, 6, 7, 0} and freeListHead at 2.
+       //
+       // Some potential errors are ignored:
+       // *   Index is greater than the size of the array.
+       // *   Slot is empty.
+       if index < uintptr(len(*reg.entries)) {
+               _, isIndexInt := (*reg.entries)[index].(indexInt)
+               if !isIndexInt {
+                       (*reg.entries)[index] = reg.freeListHead
+                       reg.freeListHead = indexInt(index)
+               }
+       }
+
+       reg.mutex.Unlock()
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/dab9a88d/go/lucy/registry_test.go
----------------------------------------------------------------------
diff --git a/go/lucy/registry_test.go b/go/lucy/registry_test.go
new file mode 100644
index 0000000..ea4f9da
--- /dev/null
+++ b/go/lucy/registry_test.go
@@ -0,0 +1,84 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package lucy
+
+import "testing"
+import "math/rand"
+
+func TestRegistrySingle(t *testing.T) {
+       reg := newObjRegistry(4)
+       index := reg.store(42)
+       if intVal, ok := reg.fetch(index).(int); !ok || intVal != 42 {
+               t.Error("Failed to store/fetch int")
+       }
+       reg.delete(index)
+       if reg.fetch(index) != nil {
+               t.Error("Failed to delete int")
+       }
+}
+
+func TestRegistryMany(t *testing.T) {
+       reg := newObjRegistry(4)
+       stored := make(map[int]uintptr)
+       deleted := make(map[int]uintptr)
+       for i := 0; i < 1000; i++ {
+               if rand.Intn(10) == 0 {
+                       // Randomly delete an element 10% of the time.
+                       goner := rand.Intn(i - 1)
+                       if index, ok := stored[goner]; ok {
+                               reg.delete(index)
+                               delete(stored, goner)
+                               deleted[goner] = index
+                       }
+               }
+               stored[i] = reg.store(i)
+       }
+       for expected, index := range stored {
+               got, ok := reg.fetch(index).(int)
+               if !ok {
+                       t.Errorf("Failed to fetch stored value %d at index %d", 
expected, index)
+               } else if got != expected {
+                       t.Errorf("Expected %d got %d", expected, got)
+               }
+       }
+       for i := 0; i < len(*reg.entries) - 1; i++ {
+               got, ok := reg.fetch(uintptr(i)).(int)
+               if ok {
+                       if _, wasDeleted := deleted[got]; wasDeleted {
+                               t.Errorf("Deleted item %d still present at 
index %d", got, i)
+                       }
+               }
+       }
+}
+
+func TestRegistryStringSlice(t *testing.T) {
+       reg := newObjRegistry(4)
+       s := make([]int, 2)
+       index := reg.store(&s)
+       s2 := reg.fetch(index).(*[]int)
+       (*s2)[1] = 1000
+       if s[1] != 1000 {
+               t.Error("Not the same slice")
+       }
+}
+
+func TestRegistryRange(t *testing.T) {
+       reg := newObjRegistry(4)
+       if reg.fetch(uintptr(10)) != nil {
+               t.Error("Out of range index should return nil")
+       }
+}

Reply via email to