blob: bd9cd31e650764e8b0e6770cd86aa429e99f5f35 [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <Foundation/Foundation.h>
#include "seq.h"
#include "_cgo_export.h"
#ifdef DEBUG
#define LOG_DEBUG(...) NSLog(__VA_ARGS__);
#else
#define LOG_DEBUG(...) ;
#endif
#define LOG_INFO(...) NSLog(__VA_ARGS__);
#define LOG_FATAL(...) \
@throw \
[NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:__VA_ARGS__] \
userInfo:NULL];
// mem_ensure ensures that m has at least size bytes free.
// If m is NULL, it is created.
static void mem_ensure(GoSeq *m, uint32_t size) {
size_t cap = m->cap;
if (cap > m->off + size) {
return;
}
if (cap == 0) {
cap = 64;
}
while (cap < m->off + size) {
cap *= 2;
}
m->buf = (uint8_t *)realloc((void *)m->buf, cap);
if (m->buf == NULL) {
LOG_FATAL(@"mem_ensure realloc failed, off=%zu, size=%u", m->off, size);
}
m->cap = cap;
}
static uint32_t align(uint32_t offset, uint32_t alignment) {
uint32_t pad = offset % alignment;
if (pad > 0) {
pad = alignment - pad;
}
return pad + offset;
}
static uint8_t *mem_read(GoSeq *m, uint32_t size, uint32_t alignment) {
if (size == 0) {
return NULL;
}
if (m == NULL) {
LOG_FATAL(@"mem_read on NULL GoSeq");
}
uint32_t offset = align(m->off, alignment);
if (m->len - offset < size) {
LOG_FATAL(@"short read");
}
uint8_t *res = m->buf + offset;
m->off = offset + size;
return res;
}
static uint8_t *mem_write(GoSeq *m, uint32_t size, uint32_t alignment) {
if (m->off != m->len) {
LOG_FATAL(@"write can only append to seq, size: (off=%zu len=%zu, size=%u)",
m->off, m->len, size);
}
uint32_t offset = align(m->off, alignment);
mem_ensure(m, offset - m->off + size);
uint8_t *res = m->buf + offset;
m->off = offset + size;
m->len = offset + size;
return res;
}
// extern
void go_seq_free(GoSeq *m) {
if (m != NULL) {
free(m->buf);
}
}
#define MEM_READ(seq, ty) ((ty *)mem_read(seq, sizeof(ty), sizeof(ty)))
#define MEM_WRITE(seq, ty) (*(ty *)mem_write(seq, sizeof(ty), sizeof(ty)))
int go_seq_readInt(GoSeq *seq) {
int64_t v = go_seq_readInt64(seq);
return v; // Assume that Go-side used WriteInt to encode 'int' value.
}
void go_seq_writeInt(GoSeq *seq, int v) { go_seq_writeInt64(seq, v); }
BOOL go_seq_readBool(GoSeq *seq) {
int8_t v = go_seq_readInt8(seq);
return v ? YES : NO;
}
void go_seq_writeBool(GoSeq *seq, BOOL v) { go_seq_writeInt8(seq, v ? 1 : 0); }
int8_t go_seq_readInt8(GoSeq *seq) {
int8_t *v = MEM_READ(seq, int8_t);
return v == NULL ? 0 : *v;
}
void go_seq_writeInt8(GoSeq *seq, int8_t v) { MEM_WRITE(seq, int8_t) = v; }
int16_t go_seq_readInt16(GoSeq *seq) {
int16_t *v = MEM_READ(seq, int16_t);
return v == NULL ? 0 : *v;
}
void go_seq_writeInt16(GoSeq *seq, int16_t v) { MEM_WRITE(seq, int16_t) = v; }
int32_t go_seq_readInt32(GoSeq *seq) {
int32_t *v = MEM_READ(seq, int32_t);
return v == NULL ? 0 : *v;
}
void go_seq_writeInt32(GoSeq *seq, int32_t v) { MEM_WRITE(seq, int32_t) = v; }
int64_t go_seq_readInt64(GoSeq *seq) {
int64_t *v = MEM_READ(seq, int64_t);
return v == NULL ? 0 : *v;
}
void go_seq_writeInt64(GoSeq *seq, int64_t v) { MEM_WRITE(seq, int64_t) = v; }
float go_seq_readFloat32(GoSeq *seq) {
float *v = MEM_READ(seq, float);
return v == NULL ? 0 : *v;
}
void go_seq_writeFloat32(GoSeq *seq, float v) { MEM_WRITE(seq, float) = v; }
double go_seq_readFloat64(GoSeq *seq) {
double *v = MEM_READ(seq, double);
return v == NULL ? 0 : *v;
}
void go_seq_writeFloat64(GoSeq *seq, double v) { MEM_WRITE(seq, double) = v; }
NSString *go_seq_readUTF8(GoSeq *seq) {
int32_t len = *MEM_READ(seq, int32_t);
if (len == 0) {
return NULL;
}
const void *buf = (const void *)mem_read(seq, len, 1);
return [[NSString alloc] initWithBytes:buf
length:len
encoding:NSUTF8StringEncoding];
}
void go_seq_writeUTF8(GoSeq *seq, NSString *s) {
int32_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
MEM_WRITE(seq, int32_t) = len;
if (len == 0 && s.length > 0) {
LOG_INFO(@"unable to incode an NSString into UTF-8");
return;
}
char *buf = (char *)mem_write(seq, len, 1);
NSUInteger used;
[s getBytes:buf
maxLength:len
usedLength:&used
encoding:NSUTF8StringEncoding
options:0
range:NSMakeRange(0, [s length])
remainingRange:NULL];
if (used < len) {
buf[used] = '\0';
}
return;
}
NSData *go_seq_readByteArray(GoSeq *seq) {
int64_t sz = *MEM_READ(seq, int64_t);
if (sz == 0) {
return [NSData data];
}
// BUG(hyangah): it is possible that *ptr is already GC'd by Go runtime.
void *ptr = (void *)(*MEM_READ(seq, int64_t));
return [NSData dataWithBytes:ptr length:sz];
}
void go_seq_writeByteArray(GoSeq *seq, NSData *data) {
int64_t sz = data.length;
MEM_WRITE(seq, int64_t) = sz;
if (sz == 0) {
return;
}
int64_t ptr = (int64_t)data.bytes;
MEM_WRITE(seq, int64_t) = ptr;
return;
}
void go_seq_send(char *descriptor, int code, GoSeq *req, GoSeq *res) {
if (descriptor == NULL) {
LOG_FATAL(@"invalid NULL descriptor");
}
uint8_t *req_buf = NULL;
size_t req_len = 0;
if (req != NULL) {
req_buf = req->buf;
req_len = req->len;
}
uint8_t **res_buf = NULL;
size_t *res_len = NULL;
if (res == NULL) {
mem_ensure(res, 64);
}
res_buf = &res->buf;
res_len = &res->len;
GoString desc;
desc.p = descriptor;
desc.n = strlen(descriptor);
Send(desc, (GoInt)code, req_buf, req_len, res_buf, res_len);
}
#define IS_FROM_GO(refnum) ((refnum) < 0)
// init_seq is called when the Go side is initialized.
void init_seq() {
// TODO(hyangah): initialize a map of Objective-C objects passed to Go.
}
GoSeqRef *go_seq_readRef(GoSeq *seq) {
int32_t refnum = go_seq_readInt32(seq);
if (!IS_FROM_GO(refnum)) {
LOG_FATAL(@"passing Objective-C objects is not implemented yet");
return NULL;
}
return [[GoSeqRef alloc] initWithRefnum:refnum obj:NULL];
}
void go_seq_writeRef(GoSeq *seq, GoSeqRef *v) {
int32_t refnum = v.refnum;
if (!IS_FROM_GO(refnum)) {
LOG_FATAL(@"passing Objective-C objects is not implemented yet");
return;
}
go_seq_writeInt32(seq, refnum);
}
@implementation GoSeqRef {
}
- (id)init {
LOG_FATAL(@"GoSeqRef init is disallowed");
return nil;
}
- (id)initWithRefnum:(int32_t)refnum obj:(id)obj {
if (!IS_FROM_GO(refnum)) {
LOG_FATAL(@"GoSeqRef init with non-Go object reference (refnum: %d)",
refnum);
return nil;
}
self = [super init];
if (self) {
_refnum = refnum;
_obj = obj;
}
return self;
}
- (void)dealloc {
if (IS_FROM_GO(_refnum)) {
DestroyRef(_refnum);
}
}
@end