Skip to content

Commit

Permalink
start
Browse files Browse the repository at this point in the history
  • Loading branch information
mdavidsaver committed Sep 26, 2017
1 parent 3234df1 commit 921ed4a
Show file tree
Hide file tree
Showing 16 changed files with 634 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/bin/
/lib/
/db/
/dbd/
/include/
/configure/*.local
O.*/
/FEED.*
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
TOP = .
include $(TOP)/configure/CONFIG

DIRS += configure

DIRS += common
common_DEPEND_DIRS = configure

DIRS += tests
tests_DEPEND_DIRS = configure common

include $(TOP)/configure/RULES_TOP
19 changes: 19 additions & 0 deletions common/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
TOP=..

include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================

LIBRARY += feedcom

feedcom_LIBS += Com

feedcom_SRCS += jblob.cpp

#===========================

include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

290 changes: 290 additions & 0 deletions common/jblob.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@

#include <stdexcept>
#include <list>
#include <string.h>

#include <errlog.h>
#include <yajl_parse.h>

#include "utils.h"
#include "jblob.h"

namespace {

// undef implies API version 0
#ifndef EPICS_YAJL_API_VERSION
typedef long integer_arg;
typedef unsigned size_arg;
#else
typedef long long integer_arg;
typedef size_t size_arg;
#endif

struct context {
JBlob blob;

std::string err, // error message
regname,
param;
unsigned depth;

JRegister scratch;

typedef std::list<std::string> warnings_t;
warnings_t warnings;

void warn(const std::string& msg) {
warnings.push_back(msg);
}

context() :depth(0) {}
};

#define TRY context *self = (context*)ctx; try

#define CATCH() catch(std::exception& e) { if(self->err.empty()) self->err = e.what(); return 0; }

int jblob_null(void *ctx)
{
TRY {
self->warn("ignoring unsupported: null");
}CATCH()
return 1;
}

int jblob_boolean(void *ctx, int val)
{
TRY {
self->warn(SB()<<"ignoring unsupported: boolean value "<<val);
}CATCH()
return 1;
}

int jblob_integer(void *ctx, integer_arg val)
{
TRY {
if(self->depth==2) {
if(self->param=="base_addr") {
self->scratch.base_addr = val;

} else if(self->param=="addr_width") {
self->scratch.addr_width = val;

} else if(self->param=="data_width") {
self->scratch.data_width = val;

} else {
self->warn(SB()<<self->regname<<"."<<self->param<<" ignores integer value");
}
} else
self->warn(SB()<<"ignored integer value at depth="<<self->depth);
return 1;
}CATCH()
}

int jblob_double(void *ctx, double val)
{
TRY {
self->warn(SB()<<"ignoring unsupported: double value "<<val);
}CATCH()
return 1;
}

int jblob_string(void *ctx, const unsigned char *val, size_arg len)
{
TRY {
std::string V((const char*)val, len);

if(self->depth==2) {
if(self->param=="access") {
self->scratch.readable = V.find_first_of('r')!=V.npos;
self->scratch.writable = V.find_first_of('w')!=V.npos;

} else if(self->param=="description") {
self->scratch.description = V;

} else if(self->param=="sign") {
if(V=="unsigned") {
self->scratch.sign = JRegister::Unsigned;

} else if(V=="signed") {
self->scratch.sign = JRegister::Signed;

} else {
self->warn(SB()<<self->regname<<"."<<self->param<<" unknown value "<<V<<" assume Unsigned");
}

} else {
self->warn(SB()<<self->regname<<"."<<self->param<<" ignores string value");
}
} else
self->warn(SB()<<"ignored string value at depth="<<self->depth);
return 1;
}CATCH()
}

int jblob_start_map(void *ctx)
{
TRY {
self->depth++;
if(self->depth>2) {
throw std::runtime_error("Object depth limit (2) exceeded");
}
return 1;
}CATCH()
}

int jblob_map_key(void *ctx, const unsigned char *key, size_arg len)
{
TRY {
if(len==0)
throw std::runtime_error("Zero length key not allowed");
std::string K((const char*)key, len);
if(self->depth==1) {
self->regname = K;
if(self->blob.registers.find(K)!=self->blob.registers.end()) {
self->warn(SB()<<"Duplicate definition for register "<<self->regname);
}
} else if(self->depth==2) {
self->param = K;
} else {
throw std::logic_error("key at unsupported depth");
}
return 1;
}CATCH()
}

int jblob_end_map(void *ctx)
{
TRY {
if(self->depth==2) {
self->scratch.name = self->regname;
self->blob.registers[self->regname] = self->scratch;
self->scratch.clear();
}
if(self->depth==0)
throw std::logic_error("Object depth underflow");
self->depth--;
return 1;
}CATCH()
}

yajl_callbacks jblob_cbs = {
&jblob_null, // null
&jblob_boolean, // boolean
&jblob_integer,
&jblob_double, // double
NULL, // number
&jblob_string,
&jblob_start_map,
&jblob_map_key,
&jblob_end_map,
NULL, // start array
NULL, // end array
};

struct handler {
yajl_handle handle;
handler(yajl_handle handle) :handle(handle)
{
if(!handle)
throw std::runtime_error("Failed to allocate yajl handle");
}
~handler() {
yajl_free(handle);
}
operator yajl_handle() { return handle; }
};

} // namespace

void JBlob::parse(const char *buf)
{
parse(buf, strlen(buf));
}

void JBlob::parse(const char *buf, size_t buflen)
{
#ifndef EPICS_YAJL_API_VERSION
yajl_parser_config conf;
memset(&conf, 0, sizeof(conf));
conf.allowComments = 1;
conf.checkUTF8 = 1;
#endif

context ctxt;

#ifndef EPICS_YAJL_API_VERSION
handler handle(yajl_alloc(&jblob_cbs, &conf, NULL, &ctxt));
#else
handler handle(yajl_alloc(&jblob_cbs, NULL, &ctxt));
#endif

yajl_status sts = yajl_parse(handle, (const unsigned char*)buf, buflen);
#ifndef EPICS_YAJL_API_VERSION
if(sts==yajl_status_insufficient_data) {
sts = yajl_parse_complete(handle);
}
#else
if(sts==yajl_status_ok)
sts = yajl_complete_parse(handle);
#endif
switch(sts) {
case yajl_status_ok:
break;
case yajl_status_error: {
std::string msg;
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)buf, buflen);
try {
msg = (char*)raw;
yajl_free_error(handle, raw);
}catch(...){
yajl_free_error(handle, raw);
throw;
}
throw std::runtime_error(msg);
}
case yajl_status_client_canceled:
throw std::runtime_error(ctxt.err);
#ifndef EPICS_YAJL_API_VERSION
case yajl_status_insufficient_data:
throw std::runtime_error("Unexpected end of input");
#endif
}

for(context::warnings_t::const_iterator it=ctxt.warnings.begin(), end=ctxt.warnings.end();
it!=end; ++it)
{
errlogPrintf("JSON parser warning: %s\n", it->c_str());
}

registers.swap(ctxt.blob.registers);
}

std::ostream& operator<<(std::ostream& strm, const JBlob& blob)
{
strm<<"{";
bool first=true;
for(JBlob::registers_t::const_iterator it=blob.registers.begin(), end=blob.registers.end();
it!=end; ++it)
{
if(first) first=false;
else strm<<", ";
strm<<"\""<<it->first<<"\":"<<it->second;
}
strm<<"}";
return strm;
}

std::ostream& operator<<(std::ostream& strm, const JRegister& reg)
{
strm<<"{"
"\"access\":\""<<(reg.readable?"r":"")<<(reg.writable?"w":"")<<"\", "
"\"addr_width\":"<<reg.addr_width<<", "
"\"base_addr\":"<<reg.base_addr<<", "
"\"data_width\":"<<reg.data_width<<", "
"\"description\":\""<<reg.description<<"\", "
"\"sign\":\""<<(reg.sign==JRegister::Unsigned?"un":"")<<"signed\""
"}";
return strm;
}
50 changes: 50 additions & 0 deletions common/jblob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#ifndef JBLOB_H
#define JBLOB_H

#include <ostream>
#include <string>
#include <map>

#include <epicsTypes.h>

struct JRegister {
std::string name,
description;
epicsUInt32 base_addr,
addr_width, //??
data_width;
enum sign_t {
Unsigned, Signed
} sign;
bool readable,
writable;

void clear() {
name.clear();
description.clear();
base_addr = addr_width = data_width = 0u;
sign = Unsigned;
readable = writable = false;
}

JRegister() {clear();}
};

struct JBlob {
typedef std::map<std::string, JRegister> registers_t;
registers_t registers;

// Parse and replace current contents
void parse(const char *buf);
void parse(const char *buf, size_t buflen);

typedef registers_t::const_iterator const_iterator;
const_iterator begin() const { return registers.begin(); }
const_iterator end() const { return registers.end(); }
const_iterator find(const std::string& k) const { return registers.find(k); }
};

std::ostream& operator<<(std::ostream& strm, const JBlob& blob);
std::ostream& operator<<(std::ostream& strm, const JRegister& reg);

#endif // JBLOB_H
15 changes: 15 additions & 0 deletions common/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef UTILS_H
#define UTILS_H

#include <sstream>

// in-line string builder (eg. for exception messages)
struct SB {
std::ostringstream strm;
SB() {}
operator std::string() const { return strm.str(); }
template<typename T>
SB& operator<<(T i) { strm<<i; return *this; }
};

#endif // UTILS_H
Loading

0 comments on commit 921ed4a

Please sign in to comment.