//export GOLUCY_Inverter_Invert_Doc func GOLUCY_Inverter_Invert_Doc(inverter *C.lucy_Inverter, doc *C.lucy_Doc) { ivars := C.lucy_Inverter_IVARS(inverter) fields := (*C.cfish_Hash)(C.LUCY_Doc_Get_Fields(doc)) // Prepare for the new doc. C.LUCY_Inverter_Set_Doc(inverter, doc) // Extract and invert the doc's fields. iter := C.cfish_HashIter_new(fields) for C.CFISH_HashIter_Next(iter) { field := C.CFISH_HashIter_Get_Key(iter) obj := C.CFISH_HashIter_Get_Value(iter) if obj == nil { mess := "Invalid nil value for field" + clownfish.CFStringToGo(unsafe.Pointer(field)) panic(clownfish.NewErr(mess)) } inventry := fetchEntry(ivars, field) inventryIvars := C.lucy_InvEntry_IVARS(inventry) fieldType := inventryIvars._type // Get the field value. var expectedType *C.cfish_Class switch C.LUCY_FType_Primitive_ID(fieldType) & C.lucy_FType_PRIMITIVE_ID_MASK { case C.lucy_FType_TEXT: expectedType = C.CFISH_STRING case C.lucy_FType_BLOB: expectedType = C.CFISH_BLOB case C.lucy_FType_INT32: expectedType = C.CFISH_INTEGER case C.lucy_FType_INT64: expectedType = C.CFISH_INTEGER case C.lucy_FType_FLOAT32: expectedType = C.CFISH_FLOAT case C.lucy_FType_FLOAT64: expectedType = C.CFISH_FLOAT default: panic(clownfish.NewErr("Internal Lucy error: bad type id for field " + clownfish.CFStringToGo(unsafe.Pointer(field)))) } if !C.cfish_Obj_is_a(obj, expectedType) { className := C.cfish_Obj_get_class_name((*C.cfish_Obj)(unsafe.Pointer(fieldType))) mess := fmt.Sprintf("Invalid type for field '%s': '%s'", clownfish.CFStringToGo(unsafe.Pointer(field)), clownfish.CFStringToGo(unsafe.Pointer(className))) panic(clownfish.NewErr(mess)) } if inventryIvars.value != obj { C.cfish_decref(unsafe.Pointer(inventryIvars.value)) inventryIvars.value = C.cfish_inc_refcount(unsafe.Pointer(obj)) } C.LUCY_Inverter_Add_Field(inverter, inventry) } C.cfish_dec_refcount(unsafe.Pointer(iter)) }
func fetchEntry(ivars *C.lucy_InverterIVARS, fieldGo string) *C.lucy_InverterEntry { field := (*C.cfish_String)(clownfish.GoToClownfish(fieldGo, unsafe.Pointer(C.CFISH_STRING), false)) defer C.cfish_decref(unsafe.Pointer(field)) schema := ivars.schema fieldNum := C.LUCY_Seg_Field_Num(ivars.segment, field) if fieldNum == 0 { // This field seems not to be in the segment yet. Try to find it in // the Schema. if C.LUCY_Schema_Fetch_Type(schema, field) != nil { // The field is in the Schema. Get a field num from the Segment. fieldNum = C.LUCY_Seg_Add_Field(ivars.segment, field) } else { // We've truly failed to find the field. The user must // not have spec'd it. fieldGo := clownfish.CFStringToGo(unsafe.Pointer(field)) err := clownfish.NewErr("Unknown field name: '" + fieldGo + "'") panic(err) } } entry := C.CFISH_Vec_Fetch(ivars.entry_pool, C.size_t(fieldNum)) if entry == nil { newEntry := C.lucy_InvEntry_new(schema, field, fieldNum) C.CFISH_Vec_Store(ivars.entry_pool, C.size_t(fieldNum), (*C.cfish_Obj)(unsafe.Pointer(entry))) return newEntry } return (*C.lucy_InverterEntry)(unsafe.Pointer(entry)) }
// Read data into the supplied doc. func (s *SearcherIMP) ReadDoc(docID int32, doc interface{}) error { self := (*C.lucy_Searcher)(clownfish.Unwrap(s, "s")) class := C.cfish_Obj_get_class((*C.cfish_Obj)(unsafe.Pointer(self))) if class == C.LUCY_INDEXSEARCHER { ixReader := C.LUCY_IxSearcher_Get_Reader((*C.lucy_IndexSearcher)(unsafe.Pointer(self))) cfStr := (*C.cfish_String)(clownfish.GoToClownfish("Lucy::Index::DocReader", unsafe.Pointer(C.CFISH_STRING), false)) defer C.cfish_decref(unsafe.Pointer(cfStr)) docReader := C.LUCY_IxReader_Fetch(ixReader, cfStr) if docReader == nil { return clownfish.NewErr("No DocReader available") } docReaderGo := clownfish.WRAPAny(unsafe.Pointer(C.cfish_incref(unsafe.Pointer(docReader)))).(DocReader) return docReaderGo.ReadDoc(docID, doc) } else { return clownfish.NewErr("Support for ReadDoc not implemented") } }
func fetchDocFields(d *C.lucy_Doc) map[string]interface{} { ivars := C.lucy_Doc_IVARS(d) fieldsID := uintptr(ivars.fields) fieldsGo, ok := registry.fetch(fieldsID).(map[string]interface{}) if !ok { panic(clownfish.NewErr(fmt.Sprintf("Failed to fetch doc %d from registry ", fieldsID))) } return fieldsGo }
func readDocPolyDR(pdr *C.lucy_PolyDocReader, docID int32, doc interface{}) error { ivars := C.lucy_PolyDocReader_IVARS(pdr) segTick := C.lucy_PolyReader_sub_tick(ivars.offsets, C.int32_t(docID)) offset := C.LUCY_I32Arr_Get(ivars.offsets, C.size_t(segTick)) defDocReader := (*C.lucy_DefaultDocReader)(C.CFISH_Vec_Fetch(ivars.readers, C.size_t(segTick))) if defDocReader == nil { return clownfish.NewErr(fmt.Sprintf("Invalid docID: %d", docID)) } if !C.cfish_Obj_is_a((*C.cfish_Obj)(unsafe.Pointer(defDocReader)), C.LUCY_DEFAULTDOCREADER) { panic(clownfish.NewErr("Unexpected type")) // sanity check } adjustedDocID := docID - int32(offset) err := doReadDocData(defDocReader, adjustedDocID, doc) if docDoc, ok := doc.(Doc); ok { docDoc.SetDocID(docID) } return err }
func fetchDocFromDocReader(dr DocReader, docID int32, doc interface{}) error { switch v := dr.(type) { case *DefaultDocReaderIMP: return v.readDoc(docID, doc) case *PolyDocReaderIMP: return v.readDoc(docID, doc) default: panic(clownfish.NewErr(fmt.Sprintf("Unexpected type: %T", v))) } }
func NewI32Array(nums []int32) I32Array { size := len(nums) if int(C.size_t(size)) != size { panic(clownfish.NewErr("input too large")) } obj := C.lucy_I32Arr_new_blank(C.size_t(size)) for i := 0; i < size; i++ { C.LUCY_I32Arr_Set(obj, C.size_t(i), C.int32_t(nums[i])) } return WRAPI32Array(unsafe.Pointer(obj)) }
func (d *DocReaderIMP) ReadDoc(docID int32, doc interface{}) error { self := (*C.lucy_DocReader)(clownfish.Unwrap(d, "d")) class := clownfish.GetClass(d) classC := ((*C.cfish_Class)(clownfish.Unwrap(class, "class"))) if classC == C.LUCY_DEFAULTDOCREADER { return doReadDocData((*C.lucy_DefaultDocReader)(self), docID, doc) } else if classC == C.LUCY_POLYDOCREADER { return readDocPolyDR((*C.lucy_PolyDocReader)(self), docID, doc) } else { panic(clownfish.NewErr(fmt.Sprintf("Unexpected type: %s", class.GetName))) } }
func (fh *FileHandleIMP) Write(data []byte, size int) error { return clownfish.TrapErr(func() { self := (*C.lucy_FileHandle)(clownfish.Unwrap(fh, "fh")) if size > len(data) { panic(clownfish.NewErr(fmt.Sprintf("Buf only %d long, can't write %d bytes", len(data), size))) } octets := C.CString(string(data)) defer C.free(unsafe.Pointer(octets)) C.LUCY_FH_Write(self, unsafe.Pointer(octets), C.size_t(size)) }) }
func (out *OutStreamIMP) WriteBytes(content []byte, size int) error { return clownfish.TrapErr(func() { self := (*C.lucy_OutStream)(clownfish.Unwrap(out, "out")) if size > len(content) { panic(clownfish.NewErr(fmt.Sprintf("Buf only %d long, can't write %d bytes", len(content), size))) } octets := C.CString(string(content)) defer C.free(unsafe.Pointer(octets)) C.LUCY_OutStream_Write_Bytes(self, unsafe.Pointer(octets), C.size_t(size)) }) }
func (obj *HitsIMP) Next(hit interface{}) bool { self := ((*C.lucy_Hits)(unsafe.Pointer(obj.TOPTR()))) // TODO: accept a HitDoc object and populate score. // Get reflection value and type for the supplied struct. var hitValue reflect.Value if reflect.ValueOf(hit).Kind() == reflect.Ptr { temp := reflect.ValueOf(hit).Elem() if temp.Kind() == reflect.Struct { if temp.CanSet() { hitValue = temp } } } if hitValue == (reflect.Value{}) { mess := fmt.Sprintf("Arg not writeable struct pointer: %v", reflect.TypeOf(hit)) obj.err = clownfish.NewErr(mess) return false } var docC *C.lucy_HitDoc errCallingNext := clownfish.TrapErr(func() { docC = C.LUCY_Hits_Next(self) }) if errCallingNext != nil { obj.err = errCallingNext return false } if docC == nil { return false } defer C.cfish_dec_refcount(unsafe.Pointer(docC)) fields := (*C.cfish_Hash)(unsafe.Pointer(C.LUCY_HitDoc_Get_Fields(docC))) iterator := C.cfish_HashIter_new(fields) defer C.cfish_dec_refcount(unsafe.Pointer(iterator)) for C.CFISH_HashIter_Next(iterator) { keyC := C.CFISH_HashIter_Get_Key(iterator) valC := C.CFISH_HashIter_Get_Value(iterator) key := clownfish.CFStringToGo(unsafe.Pointer(keyC)) val := clownfish.CFStringToGo(unsafe.Pointer(valC)) match := func(name string) bool { return strings.EqualFold(key, name) } structField := hitValue.FieldByNameFunc(match) if structField != (reflect.Value{}) { structField.SetString(val) } } return true }
func (s *SimpleIMP) AddDoc(doc interface{}) error { self := (*C.lucy_Simple)(clownfish.Unwrap(s, "s")) indexer := s.getIndexer() var docToIndex Doc stockDoc := indexer.getStockDoc() stockDocC := (*C.lucy_Doc)(clownfish.Unwrap(stockDoc, "stockDoc")) docFields := fetchDocFields(stockDocC) for field := range docFields { delete(docFields, field) } switch d := doc.(type) { case map[string]interface{}: for k, v := range d { docFields[k] = v } docToIndex = stockDoc case Doc: docToIndex = d default: docToIndex = stockDoc // Get reflection value and type for the supplied struct. var docValue reflect.Value var success bool if reflect.ValueOf(doc).Kind() == reflect.Ptr { temp := reflect.ValueOf(doc).Elem() if temp.Kind() == reflect.Struct { docValue = temp success = true } } if !success { mess := fmt.Sprintf("Unexpected type for doc: %T", doc) return clownfish.NewErr(mess) } // Copy field values into stockDoc. docType := docValue.Type() for i := 0; i < docValue.NumField(); i++ { field := docType.Field(i).Name value := docValue.Field(i).String() docFields[field] = value } } return clownfish.TrapErr(func() { docC := (*C.lucy_Doc)(clownfish.Unwrap(docToIndex, "docToIndex")) C.LUCY_Simple_Add_Doc(self, docC) }) }
func (obj *IndexerIMP) AddDoc(doc interface{}) error { self := ((*C.lucy_Indexer)(unsafe.Pointer(obj.TOPTR()))) stockDoc := C.LUCY_Indexer_Get_Stock_Doc(self) docFields := (*C.cfish_Hash)(C.LUCY_Doc_Get_Fields(stockDoc)) C.CFISH_Hash_Clear(docFields) // TODO: Support map as doc in addition to struct as doc. // Get reflection value and type for the supplied struct. var docValue reflect.Value if reflect.ValueOf(doc).Kind() == reflect.Ptr { temp := reflect.ValueOf(doc).Elem() if temp.Kind() == reflect.Struct { docValue = temp } } if docValue == (reflect.Value{}) { mess := fmt.Sprintf("Doc not struct pointer: %v", reflect.TypeOf(doc)) return clownfish.NewErr(mess) } docType := docValue.Type() for i := 0; i < docValue.NumField(); i++ { field := docType.Field(i).Name value := docValue.Field(i).String() fieldC := obj.findFieldC(field) valueC := clownfish.NewString(value) C.CFISH_Hash_Store(docFields, (*C.cfish_String)(unsafe.Pointer(fieldC)), C.cfish_inc_refcount(unsafe.Pointer(valueC.TOPTR()))) } // TODO create an additional method AddDocWithBoost which allows the // client to supply `boost`. boost := 1.0 err := clownfish.TrapErr(func() { C.LUCY_Indexer_Add_Doc(self, stockDoc, C.float(boost)) }) return err }
//export GOLUCY_Inverter_Invert_Doc func GOLUCY_Inverter_Invert_Doc(inverter *C.lucy_Inverter, doc *C.lucy_Doc) { ivars := C.lucy_Inverter_IVARS(inverter) fields := fetchDocFields(doc) // Prepare for the new doc. C.LUCY_Inverter_Set_Doc(inverter, doc) // Extract and invert the doc's fields. for field, val := range fields { inventry := fetchEntry(ivars, field) inventryIvars := C.lucy_InvEntry_IVARS(inventry) fieldType := inventryIvars._type // Get the field value. var expectedType *C.cfish_Class switch C.LUCY_FType_Primitive_ID(fieldType) & C.lucy_FType_PRIMITIVE_ID_MASK { case C.lucy_FType_TEXT: expectedType = C.CFISH_STRING case C.lucy_FType_BLOB: expectedType = C.CFISH_BLOB case C.lucy_FType_INT32: expectedType = C.CFISH_INTEGER case C.lucy_FType_INT64: expectedType = C.CFISH_INTEGER case C.lucy_FType_FLOAT32: expectedType = C.CFISH_FLOAT case C.lucy_FType_FLOAT64: expectedType = C.CFISH_FLOAT default: panic(clownfish.NewErr("Internal Lucy error: bad type id for field " + field)) } temp := inventryIvars.value valCF := clownfish.GoToClownfish(val, unsafe.Pointer(expectedType), false) inventryIvars.value = C.cfish_inc_refcount(valCF) C.cfish_decref(unsafe.Pointer(temp)) C.LUCY_Inverter_Add_Field(inverter, inventry) } }
//export GOLUCY_RegexTokenizer_Tokenize_Utf8 func GOLUCY_RegexTokenizer_Tokenize_Utf8(rt *C.lucy_RegexTokenizer, str *C.char, stringLen C.size_t, inversion *C.lucy_Inversion) { ivars := C.lucy_RegexTokenizer_IVARS(rt) rxID := uintptr(ivars.token_re) rx, ok := registry.fetch(rxID).(*regexp.Regexp) if !ok { mess := fmt.Sprintf("Failed to Fetch *RegExp with id %d and pattern %s", rxID, clownfish.CFStringToGo(unsafe.Pointer(ivars.pattern))) panic(clownfish.NewErr(mess)) } buf := C.GoBytes(unsafe.Pointer(str), C.int(stringLen)) found := rx.FindAllIndex(buf, int(stringLen)) lastEnd := 0 cpCount := 0 for _, startEnd := range found { cpCount = int(C.push_token(str, C.int(startEnd[0]), C.int(startEnd[1]), C.int(lastEnd), C.int(cpCount), inversion)) lastEnd = startEnd[1] } }
func (obj *IndexerIMP) findRealField(name string) (string, error) { self := ((*C.lucy_Indexer)(unsafe.Pointer(obj.TOPTR()))) if obj.fieldNames == nil { obj.fieldNames = make(map[string]string) } if field, ok := obj.fieldNames[name]; ok { return field, nil } else { schema := C.LUCY_Indexer_Get_Schema(self) fieldList := C.LUCY_Schema_All_Fields(schema) defer C.cfish_dec_refcount(unsafe.Pointer(fieldList)) for i := 0; i < int(C.CFISH_Vec_Get_Size(fieldList)); i++ { cfString := unsafe.Pointer(C.CFISH_Vec_Fetch(fieldList, C.size_t(i))) field := clownfish.CFStringToGo(cfString) if strings.EqualFold(name, field) { obj.fieldNames[name] = field return field, nil } } } return "", clownfish.NewErr(fmt.Sprintf("Unknown field: '%v'", name)) }
func (obj *IndexerIMP) addStructAsDoc(doc interface{}, boost float32) error { self := (*C.lucy_Indexer)(clownfish.Unwrap(obj, "obj")) d := C.LUCY_Indexer_Get_Stock_Doc(self) docFields := fetchDocFields(d) for field := range docFields { delete(docFields, field) } // Get reflection value and type for the supplied struct. var docValue reflect.Value var success bool if reflect.ValueOf(doc).Kind() == reflect.Ptr { temp := reflect.ValueOf(doc).Elem() if temp.Kind() == reflect.Struct { docValue = temp success = true } } if !success { mess := fmt.Sprintf("Unexpected type for doc: %t", doc) return clownfish.NewErr(mess) } // Copy field values into stockDoc. docType := docValue.Type() for i := 0; i < docValue.NumField(); i++ { field := docType.Field(i).Name value := docValue.Field(i).String() realField, err := obj.findRealField(field) if err != nil { return err } docFields[realField] = value } return clownfish.TrapErr(func() { C.LUCY_Indexer_Add_Doc(self, d, C.float(boost)) }) }
func doReadDocData(ddrC *C.lucy_DefaultDocReader, docID int32, doc interface{}) error { // Adapt for different types of "doc". var setField func(interface{}, string, interface{}) error var fields interface{} switch v := doc.(type) { case Doc: docC := (*C.lucy_Doc)(clownfish.Unwrap(v, "doc")) fieldsMap := fetchDocFields(docC) for field, _ := range fieldsMap { delete(fieldsMap, field) } fields = fieldsMap setField = setMapField case map[string]interface{}: for field, _ := range v { delete(v, field) } fields = v setField = setMapField default: // Get reflection value and type for the supplied struct. var hitValue reflect.Value if reflect.ValueOf(doc).Kind() == reflect.Ptr { temp := reflect.ValueOf(doc).Elem() if temp.Kind() == reflect.Struct { if temp.CanSet() { hitValue = temp } } } if hitValue == (reflect.Value{}) { mess := fmt.Sprintf("Arg not writeable struct pointer: %v", reflect.TypeOf(doc)) return clownfish.NewErr(mess) } fields = hitValue setField = setStructField } ivars := C.lucy_DefDocReader_IVARS(ddrC) schema := ivars.schema datInstream := ivars.dat_in ixInstream := ivars.ix_in fieldNameCap := C.size_t(31) var fieldName *C.char = ((*C.char)(C.malloc(fieldNameCap + 1))) defer C.free(unsafe.Pointer(fieldName)) // Get data file pointer from index, read number of fields. C.LUCY_InStream_Seek(ixInstream, C.int64_t(docID*8)) start := C.LUCY_InStream_Read_U64(ixInstream) C.LUCY_InStream_Seek(datInstream, C.int64_t(start)) numFields := uint32(C.LUCY_InStream_Read_C32(datInstream)) // Decode stored data and build up the doc field by field. for i := uint32(0); i < numFields; i++ { // Read field name. fieldNameLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) if fieldNameLen > fieldNameCap { fieldNameCap = fieldNameLen fieldName = ((*C.char)(C.realloc(unsafe.Pointer(fieldName), fieldNameCap+1))) } C.LUCY_InStream_Read_Bytes(datInstream, fieldName, fieldNameLen) // Find the Field's FieldType. // TODO: Creating and destroying a new string each time is // inefficient. The solution should be to add a privte // Schema_Fetch_Type_Utf8 method which takes char* and size_t. fieldNameStr := C.cfish_Str_new_from_utf8(fieldName, fieldNameLen) fieldNameGo := C.GoStringN(fieldName, C.int(fieldNameLen)) fieldType := C.LUCY_Schema_Fetch_Type(schema, fieldNameStr) C.cfish_dec_refcount(unsafe.Pointer(fieldNameStr)) // Read the field value. switch C.LUCY_FType_Primitive_ID(fieldType) & C.lucy_FType_PRIMITIVE_ID_MASK { case C.lucy_FType_TEXT: valueLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) buf := ((*C.char)(C.malloc(valueLen + 1))) C.LUCY_InStream_Read_Bytes(datInstream, buf, valueLen) val := C.GoStringN(buf, C.int(valueLen)) err := setField(fields, fieldNameGo, val) if err != nil { return err } case C.lucy_FType_BLOB: valueLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) buf := ((*C.char)(C.malloc(valueLen))) C.LUCY_InStream_Read_Bytes(datInstream, buf, valueLen) val := C.GoBytes(unsafe.Pointer(buf), C.int(valueLen)) err := setField(fields, fieldNameGo, val) if err != nil { return err } case C.lucy_FType_FLOAT32: err := setField(fields, fieldNameGo, float32(C.LUCY_InStream_Read_F32(datInstream))) if err != nil { return err } case C.lucy_FType_FLOAT64: err := setField(fields, fieldNameGo, float64(C.LUCY_InStream_Read_F64(datInstream))) if err != nil { return err } case C.lucy_FType_INT32: err := setField(fields, fieldNameGo, int32(C.LUCY_InStream_Read_C32(datInstream))) if err != nil { return err } case C.lucy_FType_INT64: err := setField(fields, fieldNameGo, int64(C.LUCY_InStream_Read_C64(datInstream))) if err != nil { return err } default: return clownfish.NewErr( "Internal Lucy error: bad type id for field " + fieldNameGo) } } return nil }
//export GOLUCY_Doc_Set_Fields func GOLUCY_Doc_Set_Fields(d *C.lucy_Doc, fields unsafe.Pointer) { panic(clownfish.NewErr("Set_Fields unsupported from C-space in Go")) }
//export GOLUCY_Doc_Set_Fields func GOLUCY_Doc_Set_Fields(d *C.lucy_Doc, fields unsafe.Pointer) { panic(clownfish.NewErr("Set_Fields unsupported in Go bindings")) }
//export GOLUCY_DefDocReader_Fetch_Doc func GOLUCY_DefDocReader_Fetch_Doc(ddr *C.lucy_DefaultDocReader, docID C.int32_t) *C.lucy_HitDoc { ivars := C.lucy_DefDocReader_IVARS(ddr) schema := ivars.schema datInstream := ivars.dat_in ixInstream := ivars.ix_in fields := C.cfish_Hash_new(1) fieldNameCap := C.size_t(31) var fieldName *C.char = ((*C.char)(C.malloc(fieldNameCap + 1))) // Get data file pointer from index, read number of fields. C.LUCY_InStream_Seek(ixInstream, C.int64_t(docID*8)) start := C.LUCY_InStream_Read_U64(ixInstream) C.LUCY_InStream_Seek(datInstream, C.int64_t(start)) numFields := uint32(C.LUCY_InStream_Read_C32(datInstream)) // Decode stored data and build up the doc field by field. for i := uint32(0); i < numFields; i++ { // Read field name. fieldNameLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) if fieldNameLen > fieldNameCap { fieldNameCap = fieldNameLen fieldName = ((*C.char)(C.realloc(unsafe.Pointer(fieldName), fieldNameCap+1))) } C.LUCY_InStream_Read_Bytes(datInstream, fieldName, fieldNameLen) // Find the Field's FieldType. // TODO: Creating and destroying a new string each time is // inefficient. The solution should be to add a privte // Schema_Fetch_Type_Utf8 method which takes char* and size_t. fieldNameStr := C.cfish_Str_new_from_utf8(fieldName, fieldNameLen) fieldType := C.LUCY_Schema_Fetch_Type(schema, fieldNameStr) C.cfish_dec_refcount(unsafe.Pointer(fieldNameStr)) // Read the field value. var value *C.cfish_Obj switch C.LUCY_FType_Primitive_ID(fieldType) & C.lucy_FType_PRIMITIVE_ID_MASK { case C.lucy_FType_TEXT: valueLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) buf := ((*C.char)(C.malloc(valueLen + 1))) C.LUCY_InStream_Read_Bytes(datInstream, buf, valueLen) C.null_terminate_string(buf, valueLen) value = ((*C.cfish_Obj)(C.cfish_Str_new_steal_utf8(buf, valueLen))) case C.lucy_FType_BLOB: valueLen := C.size_t(C.LUCY_InStream_Read_C32(datInstream)) buf := ((*C.char)(C.malloc(valueLen))) C.LUCY_InStream_Read_Bytes(datInstream, buf, valueLen) value = ((*C.cfish_Obj)(C.cfish_Blob_new_steal(buf, valueLen))) case C.lucy_FType_FLOAT32: value = ((*C.cfish_Obj)(C.cfish_Float_new(C.double(C.LUCY_InStream_Read_F32(datInstream))))) case C.lucy_FType_FLOAT64: value = ((*C.cfish_Obj)(C.cfish_Float_new(C.LUCY_InStream_Read_F64(datInstream)))) case C.lucy_FType_INT32: value = ((*C.cfish_Obj)(C.cfish_Int_new(C.int64_t(C.LUCY_InStream_Read_C32(datInstream))))) case C.lucy_FType_INT64: value = ((*C.cfish_Obj)(C.cfish_Int_new(C.int64_t(C.LUCY_InStream_Read_C64(datInstream))))) default: value = nil panic(clownfish.NewErr("Internal Lucy error: bad type id for field " + C.GoStringN(fieldName, C.int(fieldNameLen)))) } // Store the value. C.CFISH_Hash_Store_Utf8(fields, fieldName, fieldNameLen, value) } C.free(unsafe.Pointer(fieldName)) retval := C.lucy_HitDoc_new(unsafe.Pointer(fields), docID, 0.0) C.cfish_dec_refcount(unsafe.Pointer(fields)) return retval }