Skip to content

Commit

Permalink
Merge pull request #1 from cocoonlife/mda-go1.6
Browse files Browse the repository at this point in the history
go1.6 safe pointer use
  • Loading branch information
willnewton committed Feb 10, 2016
2 parents 697c619 + 73e9679 commit 46168a2
Showing 1 changed file with 75 additions and 10 deletions.
85 changes: 75 additions & 10 deletions libflac.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io"
"reflect"
"runtime"
"sync"
"unsafe"
)

Expand Down Expand Up @@ -80,6 +81,64 @@ get_audio_samples(int32_t *output, const FLAC__int32 **input,
*/
import "C"

// Go 1.6 does not allow us to pass go pointers into C that will be stored and
// used in callbacks and suggests we use a value lookup for pointer callbacks.
// https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md

// Concurrent safe map for mapping pointers between C and Go.
type decoderPtrMap struct {
sync.RWMutex
ptrs map[uintptr]*Decoder
}

func (m decoderPtrMap) get(d *C.FLAC__StreamDecoder) *Decoder {
ptr := uintptr(unsafe.Pointer(d))
m.RLock()
defer m.RUnlock()
return m.ptrs[ptr]
}

func (m decoderPtrMap) add(d *Decoder) {
m.Lock()
defer m.Unlock()
m.ptrs[uintptr(unsafe.Pointer(d.d))] = d
}

func (m decoderPtrMap) del(d *Decoder) {
m.Lock()
defer m.Unlock()
delete(m.ptrs, uintptr(unsafe.Pointer(d.d)))
}

var decoderPtrs = decoderPtrMap{ptrs: make(map[uintptr]*Decoder)}

// Concurrent safe map for mapping pointers between C and Go.
type encoderPtrMap struct {
sync.RWMutex
ptrs map[uintptr]*Encoder
}

func (m encoderPtrMap) get(e *C.FLAC__StreamEncoder) *Encoder {
ptr := uintptr(unsafe.Pointer(e))
m.RLock()
defer m.RUnlock()
return m.ptrs[ptr]
}

func (m encoderPtrMap) add(e *Encoder) {
m.Lock()
defer m.Unlock()
m.ptrs[uintptr(unsafe.Pointer(e.e))] = e
}

func (m encoderPtrMap) del(e *Encoder) {
m.Lock()
defer m.Unlock()
delete(m.ptrs, uintptr(unsafe.Pointer(e.e)))
}

var encoderPtrs = encoderPtrMap{ptrs: make(map[uintptr]*Encoder)}

type FlacWriter interface {
io.Writer
io.Closer
Expand Down Expand Up @@ -117,14 +176,14 @@ type Encoder struct {

//export decoderErrorCallback
func decoderErrorCallback(d *C.FLAC__StreamDecoder, status C.FLAC__StreamDecoderErrorStatus, data unsafe.Pointer) {
decoder := (*Decoder)(data)
decoder := decoderPtrs.get(d)
decoder.error = true
decoder.errorStr = C.GoString(C.get_decoder_error_str(status))
}

//export decoderMetadataCallback
func decoderMetadataCallback(d *C.FLAC__StreamDecoder, metadata *C.FLAC__StreamMetadata, data unsafe.Pointer) {
decoder := (*Decoder)(data)
decoder := decoderPtrs.get(d)
if metadata._type == C.FLAC__METADATA_TYPE_STREAMINFO {
decoder.Channels = int(C.get_decoder_channels(metadata))
decoder.Depth = int(C.get_decoder_depth(metadata))
Expand All @@ -134,7 +193,7 @@ func decoderMetadataCallback(d *C.FLAC__StreamDecoder, metadata *C.FLAC__StreamM

//export decoderWriteCallback
func decoderWriteCallback(d *C.FLAC__StreamDecoder, frame *C.FLAC__Frame, buffer **C.FLAC__int32, data unsafe.Pointer) C.FLAC__StreamDecoderWriteStatus {
decoder := (*Decoder)(data)
decoder := decoderPtrs.get(d)
blocksize := int(frame.header.blocksize)
decoder.frame = new(Frame)
f := decoder.frame
Expand All @@ -148,7 +207,7 @@ func decoderWriteCallback(d *C.FLAC__StreamDecoder, frame *C.FLAC__Frame, buffer

//export decoderReadCallback
func decoderReadCallback(d *C.FLAC__StreamDecoder, buffer *C.FLAC__byte, bytes *C.size_t, data unsafe.Pointer) C.FLAC__StreamDecoderReadStatus {
decoder := (*Decoder)(data)
decoder := decoderPtrs.get(d)
numBytes := int(*bytes)
if numBytes <= 0 {
return C.FLAC__STREAM_DECODER_READ_STATUS_ABORT
Expand Down Expand Up @@ -183,10 +242,11 @@ func NewDecoder(name string) (d *Decoder, err error) {
(C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)),
(C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)),
(C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)),
unsafe.Pointer(d))
nil)
if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK {
return nil, errors.New("failed to open file")
}
decoderPtrs.add(d)
ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d)
if ret == 0 || d.error == true || d.Channels == 0 {
return nil, fmt.Errorf("failed to process metadata %s", d.errorStr)
Expand All @@ -209,10 +269,11 @@ func NewDecoderReader(reader io.ReadCloser) (d *Decoder, err error) {
(C.FLAC__StreamDecoderWriteCallback)(unsafe.Pointer(C.decoderWriteCallback_cgo)),
(C.FLAC__StreamDecoderMetadataCallback)(unsafe.Pointer(C.decoderMetadataCallback_cgo)),
(C.FLAC__StreamDecoderErrorCallback)(unsafe.Pointer(C.decoderErrorCallback_cgo)),
unsafe.Pointer(d))
nil)
if status != C.FLAC__STREAM_DECODER_INIT_STATUS_OK {
return nil, errors.New("failed to open stream")
}
decoderPtrs.add(d)
ret := C.FLAC__stream_decoder_process_until_end_of_metadata(d.d)
if ret == 0 || d.error == true || d.Channels == 0 {
return nil, fmt.Errorf("failed to process metadata %s", d.errorStr)
Expand All @@ -225,6 +286,7 @@ func (d *Decoder) Close() {
if d.d != nil {
C.FLAC__stream_decoder_delete(d.d)
d.d = nil
decoderPtrs.del(d)
}
if d.reader != nil {
d.reader.Close()
Expand Down Expand Up @@ -260,6 +322,7 @@ func NewEncoder(name string, channels int, depth int, rate int) (e *Encoder, err
if e.e == nil {
return nil, errors.New("failed to create decoder")
}
encoderPtrs.add(e)
c := C.CString(name)
defer C.free(unsafe.Pointer(c))
runtime.SetFinalizer(e, (*Encoder).Close)
Expand All @@ -278,7 +341,7 @@ func NewEncoder(name string, channels int, depth int, rate int) (e *Encoder, err

//export encoderWriteCallback
func encoderWriteCallback(e *C.FLAC__StreamEncoder, buffer *C.FLAC__byte, bytes C.size_t, samples, current_frame C.unsigned, data unsafe.Pointer) C.FLAC__StreamEncoderWriteStatus {
encoder := (*Encoder)(data)
encoder := encoderPtrs.get(e)
numBytes := int(bytes)
if numBytes <= 0 {
return C.FLAC__STREAM_DECODER_READ_STATUS_ABORT
Expand All @@ -298,7 +361,7 @@ func encoderWriteCallback(e *C.FLAC__StreamEncoder, buffer *C.FLAC__byte, bytes

//export encoderSeekCallback
func encoderSeekCallback(e *C.FLAC__StreamEncoder, absPos C.FLAC__uint64, data unsafe.Pointer) C.FLAC__StreamEncoderWriteStatus {
encoder := (*Encoder)(data)
encoder := encoderPtrs.get(e)
_, err := encoder.writer.Seek(int64(absPos), 0)
if err != nil {
return C.FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR
Expand All @@ -308,7 +371,7 @@ func encoderSeekCallback(e *C.FLAC__StreamEncoder, absPos C.FLAC__uint64, data u

//export encoderTellCallback
func encoderTellCallback(e *C.FLAC__StreamEncoder, absPos *C.FLAC__uint64, data unsafe.Pointer) C.FLAC__StreamEncoderWriteStatus {
encoder := (*Encoder)(data)
encoder := encoderPtrs.get(e)
newPos, err := encoder.writer.Seek(0, 1)
if err != nil {
return C.FLAC__STREAM_ENCODER_TELL_STATUS_ERROR
Expand All @@ -330,6 +393,7 @@ func NewEncoderWriter(writer FlacWriter, channels int, depth int, rate int) (e *
if e.e == nil {
return nil, errors.New("failed to create decoder")
}
encoderPtrs.add(e)
e.writer = writer
runtime.SetFinalizer(e, (*Encoder).Close)
C.FLAC__stream_encoder_set_channels(e.e, C.uint(channels))
Expand All @@ -339,7 +403,7 @@ func NewEncoderWriter(writer FlacWriter, channels int, depth int, rate int) (e *
(C.FLAC__StreamEncoderWriteCallback)(unsafe.Pointer(C.encoderWriteCallback_cgo)),
(C.FLAC__StreamEncoderSeekCallback)(unsafe.Pointer(C.encoderSeekCallback_cgo)),
(C.FLAC__StreamEncoderTellCallback)(unsafe.Pointer(C.encoderTellCallback_cgo)),
nil, unsafe.Pointer(e))
nil, nil)
if status != C.FLAC__STREAM_ENCODER_INIT_STATUS_OK {
return nil, errors.New("failed to open file")
}
Expand Down Expand Up @@ -369,6 +433,7 @@ func (e *Encoder) Close() {
if e.e != nil {
C.FLAC__stream_encoder_finish(e.e)
e.e = nil
encoderPtrs.del(e)
}
if e.writer != nil {
e.writer.Close()
Expand Down

0 comments on commit 46168a2

Please sign in to comment.