parent
26ed1e9961
commit
cca4818eb7
29 changed files with 713 additions and 455 deletions
@ -0,0 +1,20 @@ |
||||
obj-m = upb.o
|
||||
|
||||
upb-objs = \
|
||||
../../upb/upb.o \
|
||||
../../upb/bytestream.o \
|
||||
../../upb/def.o \
|
||||
../../upb/handlers.o \
|
||||
../../upb/table.o \
|
||||
../../upb/refcount.o \
|
||||
../../upb/msg.o \
|
||||
|
||||
KVERSION = $(shell uname -r)
|
||||
|
||||
ccflags-y := -I$(PWD) -I$(PWD)/../.. -Wno-declaration-after-statement -std=gnu99
|
||||
|
||||
all: |
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||
|
||||
clean: |
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
@ -0,0 +1,20 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> |
||||
|
||||
#ifndef UPB_LINUX_ASSERT_H |
||||
#define UPB_LINUX_ASSERT_H |
||||
|
||||
#ifdef NDEBUG |
||||
#define assert(x) |
||||
#else |
||||
#define assert(x) \ |
||||
if (!(x)) panic("Assertion failed: %s at %s:%d", #x, __FILE__, __LINE__); |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,8 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#include <linux/errno.h> |
@ -0,0 +1,8 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#include <linux/types.h> |
@ -0,0 +1,10 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* Linux-kernel implementations of some stdlib.h functions. |
||||
*/ |
||||
|
||||
#include <linux/kernel.h> // For sprintf and friends. |
@ -0,0 +1,22 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* Linux-kernel implementations of some stdlib.h functions. |
||||
*/ |
||||
|
||||
#include <linux/slab.h> |
||||
|
||||
#ifndef UPB_LINUX_STDLIB_H |
||||
#define UPB_LINUX_STDLIB_H |
||||
|
||||
static inline void *malloc(size_t size) { return kmalloc(size, GFP_ATOMIC); } |
||||
static inline void free(void *p) { kfree(p); } |
||||
|
||||
static inline void *realloc(void *p, size_t size) { |
||||
return krealloc(p, size, GFP_ATOMIC); |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,26 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#ifndef UPB_LINUX_STRING_H_ |
||||
#define UPB_LINUX_STRING_H_ |
||||
|
||||
#include <linux/string.h> |
||||
#include <stdlib.h> |
||||
#include "upb/upb.h" // For INLINE. |
||||
|
||||
INLINE char *strdup(const char *s) { |
||||
size_t len = strlen(s); |
||||
char *ret = malloc(len + 1); |
||||
if (ret == NULL) return NULL; |
||||
// Be particularly defensive and guard against buffer overflow if there
|
||||
// is a concurrent mutator.
|
||||
strncpy(ret, s, len); |
||||
ret[len] = '\0'; |
||||
return ret; |
||||
} |
||||
|
||||
#endif /* UPB_DEF_H_ */ |
@ -0,0 +1,15 @@ |
||||
This directory contains code that is ANSI C but uses parts of the |
||||
standard library that are not available to very limited environments |
||||
like Linux Kernel modules. The standard calls environments like this |
||||
"freestanding implementations." |
||||
|
||||
This does *not* imply that the upb core can be compiled directly on a |
||||
freestanding implementation. Even the core uses library functions |
||||
that are not directly available on freestanding implementations |
||||
(notably malloc()/free(), vsnprintf(), and assert()). So compiling on |
||||
freestanding implementations may require implementing compatibility |
||||
versions of functions like malloc(). |
||||
|
||||
Also, Linux is not technically a freestanding implementation either, |
||||
since it does not accept functions that return float or double on |
||||
x86-64 (these use SSE registers which are disabled in kernel mode). |
@ -0,0 +1,44 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2009 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* Handling of errno. |
||||
*/ |
||||
|
||||
#include "upb/stdc/error.h" |
||||
|
||||
#include <errno.h> |
||||
#include <string.h> |
||||
|
||||
void upb_status_fromerrno(upb_status *status, int code) { |
||||
if (code != 0 && !upb_errno_is_wouldblock(code)) { |
||||
status->error = true; |
||||
upb_status_setcode(status, &upb_stdc_errorspace, code); |
||||
} |
||||
} |
||||
|
||||
bool upb_errno_is_wouldblock(int code) { |
||||
return |
||||
#ifdef EAGAIN |
||||
code == EAGAIN || |
||||
#endif |
||||
#ifdef EWOULDBLOCK |
||||
code == EWOULDBLOCK || |
||||
#endif |
||||
false; |
||||
} |
||||
|
||||
bool upb_stdc_codetostr(int code, char *buf, size_t len) { |
||||
// strerror() may use static buffers and is not guaranteed to be thread-safe,
|
||||
// but it appears that it is not subject to buffer overflows in practice, and
|
||||
// it used by other portable and high-quality software like Lua. For more
|
||||
// discussion see: http://thread.gmane.org/gmane.comp.lang.lua.general/89506
|
||||
char *err = strerror(code); |
||||
if (strlen(err) >= len) return false; |
||||
strcpy(buf, err); |
||||
return true; |
||||
} |
||||
|
||||
upb_errorspace upb_stdc_errorspace = {"stdc", &upb_stdc_codetostr}; |
@ -0,0 +1,27 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* Handling of errno. |
||||
*/ |
||||
|
||||
#include "upb/upb.h" |
||||
|
||||
#ifndef UPB_STDC_ERROR_H_ |
||||
#define UPB_STDC_ERROR_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
extern upb_errorspace upb_stdc_errorspace; |
||||
void upb_status_fromerrno(upb_status *status, int code); |
||||
bool upb_errno_is_wouldblock(int code); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_STDC_ERROR_H_ */ |
@ -0,0 +1,175 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
*/ |
||||
|
||||
#include "upb/stdc/io.h" |
||||
|
||||
#include "upb/stdc/error.h" |
||||
|
||||
// We can make this configurable if necessary.
|
||||
#define BUF_SIZE 32768 |
||||
|
||||
/* upb_stdio ******************************************************************/ |
||||
|
||||
int upb_stdio_cmpbuf(const void *_key, const void *_elem) { |
||||
const uint64_t *ofs = _key; |
||||
const upb_stdio_buf *buf = _elem; |
||||
return (*ofs / BUF_SIZE) - (buf->ofs / BUF_SIZE); |
||||
} |
||||
|
||||
static upb_stdio_buf *upb_stdio_findbuf(const upb_stdio *s, uint64_t ofs) { |
||||
// TODO: it is probably faster to linear search short lists, and to
|
||||
// special-case the last one or two bufs.
|
||||
return bsearch(&ofs, s->bufs, s->nbuf, sizeof(*s->bufs), &upb_stdio_cmpbuf); |
||||
} |
||||
|
||||
static upb_stdio_buf *upb_stdio_rotatebufs(upb_stdio *s) { |
||||
upb_stdio_buf **reuse = NULL; // XXX
|
||||
int num_reused = 0, num_inuse = 0; |
||||
|
||||
// Could sweep only a subset of bufs if this was a hotspot.
|
||||
for (int i = 0; i < s->nbuf; i++) { |
||||
upb_stdio_buf *buf = s->bufs[i]; |
||||
if (buf->refcount > 0) { |
||||
s->bufs[num_inuse++] = buf; |
||||
} else { |
||||
reuse[num_reused++] = buf; |
||||
} |
||||
} |
||||
assert(num_reused + num_inuse == s->nbuf); |
||||
memcpy(s->bufs + num_inuse, reuse, num_reused * sizeof(upb_stdio_buf*)); |
||||
if (num_reused == 0) { |
||||
++s->nbuf; |
||||
s->bufs = realloc(s->bufs, s->nbuf * sizeof(*s->bufs)); |
||||
s->bufs[s->nbuf-1] = malloc(sizeof(upb_stdio_buf) + BUF_SIZE); |
||||
return s->bufs[s->nbuf-1]; |
||||
} |
||||
return s->bufs[s->nbuf-num_reused]; |
||||
} |
||||
|
||||
void upb_stdio_discard(void *src, uint64_t ofs) { |
||||
(void)src; |
||||
(void)ofs; |
||||
} |
||||
|
||||
upb_bytesuccess_t upb_stdio_fetch(void *src, uint64_t ofs, size_t *bytes_read) { |
||||
(void)ofs; |
||||
upb_stdio *stdio = (upb_stdio*)src; |
||||
upb_stdio_buf *buf = upb_stdio_rotatebufs(stdio); |
||||
retry: |
||||
*bytes_read = fread(&buf->data, 1, BUF_SIZE, stdio->file); |
||||
buf->len = *bytes_read; |
||||
if (*bytes_read < (size_t)BUF_SIZE) { |
||||
// Error or EOF.
|
||||
if (feof(stdio->file)) { |
||||
upb_status_seteof(&stdio->src.status); |
||||
return UPB_BYTE_EOF; |
||||
} |
||||
if (ferror(stdio->file)) { |
||||
#ifdef EINTR |
||||
// If we encounter a client who doesn't want to retry EINTR, we can easily
|
||||
// add a boolean property of the stdio that controls this behavior.
|
||||
if (errno == EINTR) { |
||||
clearerr(stdio->file); |
||||
goto retry; |
||||
} |
||||
#endif |
||||
upb_status_fromerrno(&stdio->src.status, errno); |
||||
return upb_errno_is_wouldblock(errno) ? |
||||
UPB_BYTE_WOULDBLOCK : UPB_BYTE_ERROR; |
||||
} |
||||
assert(false); |
||||
} |
||||
return UPB_BYTE_OK; |
||||
} |
||||
|
||||
void upb_stdio_copy(const void *src, uint64_t ofs, size_t len, char *dst) { |
||||
upb_stdio_buf *buf = upb_stdio_findbuf(src, ofs); |
||||
ofs -= buf->ofs; |
||||
memcpy(dst, buf->data + ofs, BUF_SIZE - ofs); |
||||
len -= (BUF_SIZE - ofs); |
||||
dst += (BUF_SIZE - ofs); |
||||
while (len > 0) { |
||||
++buf; |
||||
size_t bytes = UPB_MIN(len, BUF_SIZE); |
||||
memcpy(dst, buf->data, bytes); |
||||
len -= bytes; |
||||
dst += bytes; |
||||
} |
||||
} |
||||
|
||||
const char *upb_stdio_getptr(const void *src, uint64_t ofs, size_t *len) { |
||||
upb_stdio_buf *buf = upb_stdio_findbuf(src, ofs); |
||||
ofs -= buf->ofs; |
||||
*len = BUF_SIZE - ofs; |
||||
return &buf->data[ofs]; |
||||
} |
||||
|
||||
#if 0 |
||||
upb_strlen_t upb_stdio_putstr(upb_bytesink *sink, upb_string *str, upb_status *status) { |
||||
upb_stdio *stdio = (upb_stdio*)((char*)sink - offsetof(upb_stdio, sink)); |
||||
upb_strlen_t len = upb_string_len(str); |
||||
upb_strlen_t written = fwrite(upb_string_getrobuf(str), 1, len, stdio->file); |
||||
if (written < len) { |
||||
upb_status_setf(status, UPB_ERROR, "Error writing to stdio stream."); |
||||
return -1; |
||||
} |
||||
return written; |
||||
} |
||||
|
||||
uint32_t upb_stdio_vprintf(upb_bytesink *sink, upb_status *status, |
||||
const char *fmt, va_list args) { |
||||
upb_stdio *stdio = (upb_stdio*)((char*)sink - offsetof(upb_stdio, sink)); |
||||
int written = vfprintf(stdio->file, fmt, args); |
||||
if (written < 0) { |
||||
upb_status_seterrf(status, "Error writing to stdio stream."); |
||||
return -1; |
||||
} |
||||
return written; |
||||
} |
||||
#endif |
||||
|
||||
void upb_stdio_init(upb_stdio *stdio) { |
||||
static upb_bytesrc_vtbl bytesrc_vtbl = { |
||||
&upb_stdio_fetch, |
||||
&upb_stdio_discard, |
||||
&upb_stdio_copy, |
||||
&upb_stdio_getptr, |
||||
}; |
||||
upb_bytesrc_init(&stdio->src, &bytesrc_vtbl); |
||||
|
||||
//static upb_bytesink_vtbl bytesink_vtbl = {
|
||||
// upb_stdio_putstr,
|
||||
// upb_stdio_vprintf
|
||||
//};
|
||||
//upb_bytesink_init(&stdio->bytesink, &bytesink_vtbl);
|
||||
} |
||||
|
||||
void upb_stdio_reset(upb_stdio* stdio, FILE *file) { |
||||
stdio->file = file; |
||||
stdio->should_close = false; |
||||
} |
||||
|
||||
void upb_stdio_open(upb_stdio *stdio, const char *filename, const char *mode, |
||||
upb_status *s) { |
||||
FILE *f = fopen(filename, mode); |
||||
if (!f) { |
||||
upb_status_fromerrno(s, errno); |
||||
return; |
||||
} |
||||
setvbuf(stdio->file, NULL, _IONBF, 0); // Disable buffering; we do our own.
|
||||
upb_stdio_reset(stdio, f); |
||||
stdio->should_close = true; |
||||
} |
||||
|
||||
void upb_stdio_uninit(upb_stdio *stdio) { |
||||
// Can't report status; caller should flush() to ensure data is written.
|
||||
if (stdio->should_close) fclose(stdio->file); |
||||
stdio->file = NULL; |
||||
} |
||||
|
||||
upb_bytesrc* upb_stdio_bytesrc(upb_stdio *stdio) { return &stdio->src; } |
||||
upb_bytesink* upb_stdio_bytesink(upb_stdio *stdio) { return &stdio->sink; } |
@ -0,0 +1,73 @@ |
||||
/*
|
||||
* upb - a minimalist implementation of protocol buffers. |
||||
* |
||||
* Copyright (c) 2012 Google Inc. See LICENSE for details. |
||||
* Author: Josh Haberman <jhaberman@gmail.com> |
||||
* |
||||
* ANSI C file I/O. |
||||
*/ |
||||
|
||||
#ifndef UPB_STDC_IO_H_ |
||||
#define UPB_STDC_IO_H_ |
||||
|
||||
#include <stdio.h> |
||||
#include "upb/bytestream.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* upb_stdio ******************************************************************/ |
||||
|
||||
// bytesrc/bytesink for ANSI C stdio, which is less efficient than posixfd, but
|
||||
// more portable.
|
||||
//
|
||||
// Specifically, stdio functions acquire locks on every operation (unless you
|
||||
// use the f{read,write,...}_unlocked variants, which are not standard) and
|
||||
// performs redundant buffering (unless you disable it with setvbuf(), but we
|
||||
// can only do this on newly-opened filehandles).
|
||||
|
||||
typedef struct { |
||||
uint64_t ofs; |
||||
size_t len; |
||||
uint32_t refcount; |
||||
char data[]; |
||||
} upb_stdio_buf; |
||||
|
||||
// We use a single object for both bytesrc and bytesink for simplicity.
|
||||
// The object is still not thread-safe, and may only be used by one reader
|
||||
// and one writer at a time.
|
||||
typedef struct { |
||||
upb_bytesrc src; |
||||
upb_bytesink sink; |
||||
FILE *file; |
||||
bool should_close; |
||||
upb_stdio_buf **bufs; |
||||
int nbuf; |
||||
uint32_t szbuf; |
||||
} upb_stdio; |
||||
|
||||
void upb_stdio_init(upb_stdio *stdio); |
||||
// Caller should call upb_stdio_flush prior to calling this to ensure that
|
||||
// all data is flushed, otherwise data can be silently dropped if an error
|
||||
// occurs flushing the remaining buffers.
|
||||
void upb_stdio_uninit(upb_stdio *stdio); |
||||
|
||||
// Resets the object to read/write to the given "file." The caller is
|
||||
// responsible for closing the file, which must outlive this object.
|
||||
void upb_stdio_reset(upb_stdio *stdio, FILE *file); |
||||
|
||||
// As an alternative to upb_stdio_reset(), initializes the object by opening a
|
||||
// file, and will handle closing it. This may result in more efficient I/O
|
||||
// than the previous since we can call setvbuf() to disable buffering.
|
||||
void upb_stdio_open(upb_stdio *stdio, const char *filename, const char *mode, |
||||
upb_status *s); |
||||
|
||||
upb_bytesrc *upb_stdio_bytesrc(upb_stdio *stdio); |
||||
upb_bytesink *upb_stdio_bytesink(upb_stdio *stdio); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UPB_STDC_IO_H_ */ |
Loading…
Reference in new issue