adding the existing stuff

This commit is contained in:
Adam D. Ruppe 2011-07-15 08:48:59 -04:00
commit 391628e3d0
15 changed files with 6541 additions and 0 deletions

55
README Normal file
View file

@ -0,0 +1,55 @@
This is a collection of modules I find generally useful.
Modules are usually independent; you don't need this whole directory
but it doesn't hurt to grab it all either.
Currently included are:
Web related
================
cgi.d - base module for making webapps in D
dom.d - an xml/html DOM based on what Javascript provides in browsers
web.d - a fancier way to write web apps. Uses reflection to make functions
accessible via url with minimal boilerplate in your code
Database related
================
database.d - main interface to databases. Includes DataObject
mysql.d - a mysql engine for database.d (most mature of the three)
postgres.d - a postgres engne for database.d
sqlite.d - a sqlite engine for database.d
Desktop app stuff
================
simpledisplay.d - gives quick and easy access to a window for drawing
simpleaudio.d - gives minimal audio output
Other
================
sha.d - implementations of the SHA1 and SHA256 algorithms
png.d - provides some png read/write support
curl.d - a small wrapper around the curl library
csv.d - gives read support to csv files
http.d - a lighterweight alternative to curl.d
Things I might add once I clean up the files (this can be expedited upon
request, to an extent):
httpd.d - an embedded web server
oauth.d - client/server stuff for oauth1
html.d - a bunch of dom translation functions. Think unobstructive javascript
on the server side
browser.d - a very small html widget
netman.d - handles net connections (required by httpd.d)
imagedraft.d - (temporary name) has algorithms for images
bmp.d - gives .bmp read/write
dws.d - a draft of my D windowing system (also includes some Qt code)
wav.d - reading and writing WAV files
midi.d - reading and writing MIDI files

1
cgi.d Symbolic link
View file

@ -0,0 +1 @@
../../djs/proxy/cgi.d

58
csv.d Normal file
View file

@ -0,0 +1,58 @@
module arsd.csv;
import std.string;
import std.array;
string[][] readCsv(string data) {
data = data.replace("\r", "");
auto idx = data.indexOf("\n");
//data = data[idx + 1 .. $]; // skip headers
string[] fields;
string[][] records;
string[] current;
int state = 0;
string field;
foreach(c; data) {
tryit: switch(state) {
default: assert(0);
case 0: // normal
if(c == '"')
state = 1;
else if(c == ',') {
// commit field
current ~= field;
field = null;
} else if(c == '\n') {
// commit record
current ~= field;
records ~= current;
current = null;
field = null;
} else
field ~= c;
break;
case 1: // in quote
if(c == '"')
state = 2;
else
field ~= c;
break;
case 2: // is it a closing quote or an escaped one?
if(c == '"') {
field ~= c;
state = 1;
} else {
state = 0;
goto tryit;
}
}
}
return records;
}

188
curl.d Normal file
View file

@ -0,0 +1,188 @@
module arsd.curl;
pragma(lib, "curl");
import std.string;
extern(C) {
typedef void CURL;
typedef void curl_slist;
alias int CURLcode;
alias int CURLoption;
enum int CURLOPT_URL = 10002;
enum int CURLOPT_WRITEFUNCTION = 20011;
enum int CURLOPT_WRITEDATA = 10001;
enum int CURLOPT_POSTFIELDS = 10015;
enum int CURLOPT_POSTFIELDSIZE = 60;
enum int CURLOPT_POST = 47;
enum int CURLOPT_HTTPHEADER = 10023;
enum int CURLOPT_USERPWD = 0x00002715;
enum int CURLOPT_VERBOSE = 41;
// enum int CURLOPT_COOKIE = 22;
enum int CURLOPT_COOKIEFILE = 10031;
enum int CURLOPT_COOKIEJAR = 10082;
enum int CURLOPT_SSL_VERIFYPEER = 64;
enum int CURLOPT_FOLLOWLOCATION = 52;
CURL* curl_easy_init();
void curl_easy_cleanup(CURL* handle);
CURLcode curl_easy_perform(CURL* curl);
void curl_global_init(int flags);
enum int CURL_GLOBAL_ALL = 0b1111;
CURLcode curl_easy_setopt(CURL* handle, CURLoption option, ...);
curl_slist* curl_slist_append(curl_slist*, const char*);
void curl_slist_free_all(curl_slist*);
// size is size of item, count is how many items
size_t write_data(void* buffer, size_t size, size_t count, void* user) {
string* str = cast(string*) user;
char* data = cast(char*) buffer;
assert(size == 1);
*str ~= data[0..count];
return count;
}
char* curl_easy_strerror(CURLcode errornum );
}
/*
struct CurlOptions {
string username;
string password;
}
*/
import std.md5;
import std.file;
/// this automatically caches to a local file for the given time. it ignores the expires header in favor of your time to keep.
version(linux)
string cachedCurl(string url, int maxCacheHours) {
string res;
auto cacheFile = "/tmp/arsd-curl-cache-" ~ getDigestString(url);
if(!std.file.exists(cacheFile) || std.file.lastModified(cacheFile) > 1000 * 60 * 60 * maxCacheHours) {
res = curl(url);
std.file.write(cacheFile, res);
} else {
res = readText(cacheFile);
}
return res;
}
string curl(string url, string data = null, string contentType = "application/x-www-form-urlencoded") {
return curlAuth(url, data, null, null, contentType);
}
string curlCookie(string cookieFile, string url, string data = null, string contentType = "application/x-www-form-urlencoded") {
return curlAuth(url, data, null, null, contentType, null, null, cookieFile);
}
string curlAuth(string url, string data = null, string username = null, string password = null, string contentType = "application/x-www-form-urlencoded", string methodOverride = null, string[] customHeaders = null, string cookieJar = null) {
CURL* curl = curl_easy_init();
if(curl is null)
throw new Exception("curl init");
scope(exit)
curl_easy_cleanup(curl);
string ret;
int res;
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
res = curl_easy_setopt(curl, CURLOPT_URL, std.string.toStringz(url));
if(res != 0) throw new CurlException(res);
if(username !is null) {
res = curl_easy_setopt(curl, CURLOPT_USERPWD, std.string.toStringz(username ~ ":" ~ password));
if(res != 0) throw new CurlException(res);
}
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data);
if(res != 0) throw new CurlException(res);
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ret);
if(res != 0) throw new CurlException(res);
curl_slist* headers = null;
//if(data !is null)
// contentType = "";
headers = curl_slist_append(headers, toStringz("Content-Type: " ~ contentType));
foreach(h; customHeaders) {
headers = curl_slist_append(headers, toStringz(h));
}
scope(exit)
curl_slist_free_all(headers);
if(data) {
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.ptr);
if(res != 0) throw new CurlException(res);
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.length);
if(res != 0) throw new CurlException(res);
}
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
if(res != 0) throw new CurlException(res);
if(cookieJar !is null) {
res = curl_easy_setopt(curl, CURLOPT_COOKIEJAR, toStringz(cookieJar));
if(res != 0) throw new CurlException(res);
res = curl_easy_setopt(curl, CURLOPT_COOKIEFILE, toStringz(cookieJar));
if(res != 0) throw new CurlException(res);
}
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
if(res != 0) throw new CurlException(res);
//res = curl_easy_setopt(curl, 81, 0); // FIXME verify host
//if(res != 0) throw new CurlException(res);
res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
if(res != 0) throw new CurlException(res);
if(methodOverride !is null) {
switch(methodOverride) {
default: assert(0);
case "POST":
res = curl_easy_setopt(curl, CURLOPT_POST, 1);
break;
case "GET":
//curl_easy_setopt(curl, CURLOPT_POST, 0);
break;
}
}
auto failure = curl_easy_perform(curl);
if(failure != 0)
throw new CurlException(failure, "\nURL" ~ url);
return ret;
}
class CurlException : Exception {
this(CURLcode code, string msg = null, string file = __FILE__, int line = __LINE__) {
string message = file ~ ":" ~ to!string(line) ~ " (" ~ to!string(code) ~ ") ";
auto strerror = curl_easy_strerror(code);
while(*strerror) {
message ~= *strerror;
strerror++;
}
super(message ~ msg);
}
}
import std.conv;

1010
database.d Normal file

File diff suppressed because it is too large Load diff

1
dom.d Symbolic link
View file

@ -0,0 +1 @@
/home/me/program/djs/dom.d

221
http.d Normal file
View file

@ -0,0 +1,221 @@
module arsd.http;
import std.stdio;
/**
Gets a textual document, ignoring headers. Throws on non-text or error.
*/
string get(string url) {
auto hr = httpRequest("GET", url);
if(hr.code != 200)
throw new Exception(format("HTTP answered %d instead of 200 on %s", hr.code, url));
if(hr.contentType.indexOf("text/") == -1)
throw new Exception(hr.contentType ~ " is bad content for conversion to string");
return cast(string) hr.content;
}
static import std.uri;
string post(string url, string[string] args) {
string content;
foreach(name, arg; args) {
if(content.length)
content ~= "&";
content ~= std.uri.encode(name) ~ "=" ~ std.uri.encode(arg);
}
auto hr = httpRequest("POST", url, cast(ubyte[]) content, ["Content-Type: application/x-www-form-urlencoded"]);
if(hr.code != 200)
throw new Exception(format("HTTP answered %d instead of 200", hr.code));
if(hr.contentType.indexOf("text/") == -1)
throw new Exception(hr.contentType ~ " is bad content for conversion to string");
return cast(string) hr.content;
}
struct HttpResponse {
int code;
string contentType;
string[] headers;
ubyte[] content;
}
import std.string;
static import std.algorithm;
import std.conv;
struct UriParts {
string original;
string method;
string host;
ushort port;
string path;
this(string uri) {
original = uri;
if(uri[0..7] != "http://")
throw new Exception("You must use an absolute, unencrypted URL.");
int posSlash = uri[7..$].indexOf("/");
if(posSlash != -1)
posSlash += 7;
if(posSlash == -1)
posSlash = uri.length;
int posColon = uri[7..$].indexOf(":");
if(posColon != -1)
posColon += 7;
port = 80;
if(posColon != -1 && posColon < posSlash) {
host = uri[7..posColon];
port = to!ushort(uri[posColon+1..posSlash]);
} else
host = uri[7..posSlash];
path = uri[posSlash..$];
if(path == "")
path = "/";
}
}
HttpResponse httpRequest(string method, string uri, const(ubyte)[] content = null, string headers[] = null) {
auto u = UriParts(uri);
auto f = openNetwork(u.host, u.port);
return doHttpRequestOnFile(f, method, uri, content, headers);
}
/**
Executes a generic http request, returning the full result. The correct formatting
of the parameters are the caller's responsibility. Content-Length is added automatically,
but YOU must give Content-Type!
*/
HttpResponse doHttpRequestOnFile(File f, string method, string uri, const(ubyte)[] content = null, string headers[] = null)
in {
assert(method == "POST" || method == "GET");
}
body {
auto u = UriParts(uri);
f.writefln("%s %s HTTP/1.1", method, u.path);
f.writefln("Host: %s", u.host);
f.writefln("Connection: close");
if(content !is null)
f.writefln("Content-Length: %d", content.length);
if(headers !is null)
foreach(header; headers)
f.writefln("%s", header);
f.writefln("");
if(content !is null)
f.rawWrite(content);
HttpResponse hr;
cont:
string l = f.readln();
if(l[0..9] != "HTTP/1.1 ")
throw new Exception("Not talking to a http server");
hr.code = to!int(l[9..12]); // HTTP/1.1 ### OK
if(hr.code == 100) { // continue
do {
l = readln();
} while(l.length > 1);
goto cont;
}
bool chunked = false;
foreach(line; f.byLine) {
if(line.length <= 1)
break;
hr.headers ~= line.idup;
if(line.startsWith("Content-Type: "))
hr.contentType = line[14..$-1].idup;
if(line.startsWith("Transfer-Encoding: chunked"))
chunked = true;
}
ubyte[] response;
foreach(ubyte[] chunk; f.byChunk(4096)) {
response ~= chunk;
}
if(chunked) {
// read the hex length, stopping at a \r\n, ignoring everything between the new line but after the first non-valid hex character
// read binary data of that length. it is our content
// repeat until a zero sized chunk
// then read footers as headers.
int state = 0;
int size;
int start = 0;
for(int a = 0; a < response.length; a++) {
switch(state) {
case 0: // reading hex
char c = response[a];
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) {
// just keep reading
} else {
int power = 1;
size = 0;
for(int b = a-1; b >= start; b--) {
char cc = response[b];
if(cc >= 'a' && cc <= 'z')
cc -= 0x20;
int val = 0;
if(cc >= '0' && cc <= '9')
val = cc - '0';
else
val = cc - 'A';
size += power * val;
power *= 16;
}
state++;
continue;
}
break;
case 1: // reading until end of line
char c = response[a];
if(c == '\n') {
if(size == 0)
state = 3;
else
state = 2;
}
break;
case 2: // reading data
hr.content ~= response[a..a+size];
a += size;
a+= 2; // skipping a 13 10
start = a;
state = 0;
break;
case 3: // reading footers
goto done; // FIXME
break;
}
}
} else
hr.content = response;
done:
return hr;
}
/*
void main(string args[]) {
write(post("http://arsdnet.net/bugs.php", ["test" : "hey", "again" : "what"]));
}
*/

733
mysql.d Normal file
View file

@ -0,0 +1,733 @@
module arsd.mysql;
pragma(lib, "mysqlclient");
public import arsd.database;
import std.stdio;
import std.exception;
import std.string;
import std.conv;
import std.typecons;
class MySqlResult : ResultSet {
private int[string] mapping;
private MYSQL_RES* result;
private int itemsTotal;
private int itemsUsed;
string sql;
this(MYSQL_RES* r, string sql) {
result = r;
itemsTotal = length();
itemsUsed = 0;
this.sql = sql;
// prime it
if(itemsTotal)
fetchNext();
}
~this() {
if(result !is null)
mysql_free_result(result);
}
MYSQL_FIELD[] fields() {
int numFields = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
MYSQL_FIELD[] ret;
for(int i = 0; i < numFields; i++) {
ret ~= fields[i];
}
return ret;
}
override int length() {
if(result is null)
return 0;
return cast(int) mysql_num_rows(result);
}
override bool empty() {
return itemsUsed == itemsTotal;
}
override Row front() {
return row;
}
override void popFront() {
itemsUsed++;
if(itemsUsed < itemsTotal) {
fetchNext();
}
}
override int getFieldIndex(string field) {
if(mapping is null)
makeFieldMapping();
debug {
if(field !in mapping)
throw new Exception(field ~ " not in result");
}
return mapping[field];
}
private void makeFieldMapping() {
int numFields = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
for(int i = 0; i < numFields; i++) {
mapping[fromCstring(fields[i].name)] = i;
}
}
private void fetchNext() {
assert(result);
auto r = mysql_fetch_row(result);
uint numFields = mysql_num_fields(result);
uint* lengths = mysql_fetch_lengths(result);
string[] row;
// potential FIXME: not really binary safe
columnIsNull.length = numFields;
for(int a = 0; a < numFields; a++) {
if(*(r+a) is null) {
row ~= null;
columnIsNull[a] = true;
} else {
row ~= fromCstring(*(r+a), *(lengths + a));
columnIsNull[a] = false;
}
}
this.row.row = row;
this.row.resultSet = this;
}
override string[] fieldNames() {
int numFields = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
string[] names;
for(int i = 0; i < numFields; i++) {
names ~= fromCstring(fields[i].name);
}
return names;
}
bool[] columnIsNull;
Row row;
}
class MySql : Database {
this(string host, string user, string pass, string db) {
mysql = enforceEx!(DatabaseException)(
mysql_init(null),
"Couldn't init mysql");
enforceEx!(DatabaseException)(
mysql_real_connect(mysql, toCstring(host), toCstring(user), toCstring(pass), toCstring(db), 0, null, 0),
error());
dbname = db;
// we want UTF8 for everything
query("SET NAMES 'utf8'");
//query("SET CHARACTER SET utf8");
}
string dbname;
override void startTransaction() {
query("START TRANSACTION");
}
string error() {
return fromCstring(mysql_error(mysql));
}
~this() {
mysql_close(mysql);
}
int lastInsertId() {
return cast(int) mysql_insert_id(mysql);
}
int insert(string table, MySqlResult result, string[string] columnsToModify, string[] columnsToSkip) {
assert(!result.empty);
string sql = "INSERT INTO `" ~ table ~ "` ";
string cols = "(";
string vals = "(";
bool outputted = false;
string[string] columns;
auto cnames = result.fieldNames;
foreach(i, col; result.front.toStringArray) {
bool skipMe = false;
foreach(skip; columnsToSkip) {
if(cnames[i] == skip) {
skipMe = true;
break;
}
}
if(skipMe)
continue;
if(outputted) {
cols ~= ",";
vals ~= ",";
} else
outputted = true;
cols ~= cnames[i];
if(result.columnIsNull[i] && cnames[i] !in columnsToModify)
vals ~= "NULL";
else {
string v = col;
if(cnames[i] in columnsToModify)
v = columnsToModify[cnames[i]];
vals ~= "'" ~ escape(v) ~ "'";
}
}
cols ~= ")";
vals ~= ")";
sql ~= cols ~ " VALUES " ~ vals;
query(sql);
result.popFront;
return lastInsertId;
}
string escape(string str) {
ubyte[] buffer = new ubyte[str.length * 2 + 1];
buffer.length = mysql_real_escape_string(mysql, buffer.ptr, cast(cstring) str.ptr, str.length);
return cast(string) buffer;
}
string escaped(T...)(string sql, T t) {
static if(t.length > 0) {
string fixedup;
int pos = 0;
void escAndAdd(string str, int q) {
ubyte[] buffer = new ubyte[str.length * 2 + 1];
buffer.length = mysql_real_escape_string(mysql, buffer.ptr, cast(cstring) str.ptr, str.length);
fixedup ~= sql[pos..q] ~ '\'' ~ cast(string) buffer ~ '\'';
}
foreach(a; t) {
int q = sql[pos..$].indexOf("?");
if(q == -1)
break;
q += pos;
static if(__traits(compiles, t is null)) {
if(t is null)
fixedup ~= sql[pos..q] ~ "NULL";
else
escAndAdd(to!string(*a), q);
} else {
string str = to!string(a);
escAndAdd(str, q);
}
pos = q+1;
}
fixedup ~= sql[pos..$];
sql = fixedup;
//writefln("\n\nExecuting sql: %s", sql);
}
return sql;
}
ResultByDataObject queryDataObject(T...)(string sql, T t) {
// modify sql for the best data object grabbing
sql = fixupSqlForDataObjectUse(sql);
auto magic = query(sql, t);
return ResultByDataObject(cast(MySqlResult) magic, this);
}
int affectedRows() {
return cast(int) mysql_affected_rows(mysql);
}
override ResultSet queryImpl(string sql, Variant[] args...) {
sql = escapedVariants(this, sql, args);
enforceEx!(DatabaseException)(
!mysql_query(mysql, toCstring(sql)),
error() ~ " :::: " ~ sql);
return new MySqlResult(mysql_store_result(mysql), sql);
}
/+
Result queryOld(T...)(string sql, T t) {
sql = escaped(sql, t);
if(sql.length == 0)
throw new DatabaseException("empty query");
/*
static int queryCount = 0;
queryCount++;
if(sql.indexOf("INSERT") != -1)
stderr.writefln("%d: %s", queryCount, sql.replace("\n", " ").replace("\t", ""));
*/
version(dryRun) {
pragma(msg, "This is a dry run compile, no queries will be run");
writeln(sql);
return Result(null, null);
}
enforceEx!(DatabaseException)(
!mysql_query(mysql, toCstring(sql)),
error() ~ " :::: " ~ sql);
return Result(mysql_store_result(mysql), sql);
}
+/
/+
struct ResultByAssoc {
this(Result* r) {
result = r;
fields = r.fieldNames();
}
ulong length() { return result.length; }
bool empty() { return result.empty; }
void popFront() { result.popFront(); }
string[string] front() {
auto r = result.front;
string[string] ret;
foreach(i, a; r) {
ret[fields[i]] = a;
}
return ret;
}
@disable this(this) { }
string[] fields;
Result* result;
}
struct ResultByStruct(T) {
this(Result* r) {
result = r;
fields = r.fieldNames();
}
ulong length() { return result.length; }
bool empty() { return result.empty; }
void popFront() { result.popFront(); }
T front() {
auto r = result.front;
string[string] ret;
foreach(i, a; r) {
ret[fields[i]] = a;
}
T s;
// FIXME: should use tupleOf
foreach(member; s.tupleof) {
if(member.stringof in ret)
member = to!(typeof(member))(ret[member]);
}
return s;
}
@disable this(this) { }
string[] fields;
Result* result;
}
+/
/+
struct Result {
private Result* heaped() {
auto r = new Result(result, sql, false);
r.tupleof = this.tupleof;
this.itemsTotal = 0;
this.result = null;
return r;
}
this(MYSQL_RES* r, string sql, bool prime = true) {
result = r;
itemsTotal = length;
itemsUsed = 0;
this.sql = sql;
// prime it here
if(prime && itemsTotal)
fetchNext();
}
string sql;
~this() {
if(result !is null)
mysql_free_result(result);
}
/+
string[string][] fetchAssoc() {
}
+/
ResultByAssoc byAssoc() {
return ResultByAssoc(&this);
}
ResultByStruct!(T) byStruct(T)() {
return ResultByStruct!(T)(&this);
}
string[] fieldNames() {
int numFields = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
string[] names;
for(int i = 0; i < numFields; i++) {
names ~= fromCstring(fields[i].name);
}
return names;
}
MYSQL_FIELD[] fields() {
int numFields = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
MYSQL_FIELD[] ret;
for(int i = 0; i < numFields; i++) {
ret ~= fields[i];
}
return ret;
}
ulong length() {
if(result is null)
return 0;
return mysql_num_rows(result);
}
bool empty() {
return itemsUsed == itemsTotal;
}
Row front() {
return row;
}
void popFront() {
itemsUsed++;
if(itemsUsed < itemsTotal) {
fetchNext();
}
}
void fetchNext() {
auto r = mysql_fetch_row(result);
uint numFields = mysql_num_fields(result);
uint* lengths = mysql_fetch_lengths(result);
row.length = 0;
// potential FIXME: not really binary safe
columnIsNull.length = numFields;
for(int a = 0; a < numFields; a++) {
if(*(r+a) is null) {
row ~= null;
columnIsNull[a] = true;
} else {
row ~= fromCstring(*(r+a), *(lengths + a));
columnIsNull[a] = false;
}
}
}
@disable this(this) {}
private MYSQL_RES* result;
ulong itemsTotal;
ulong itemsUsed;
alias string[] Row;
Row row;
bool[] columnIsNull; // FIXME: should be part of the row
}
+/
private:
MYSQL* mysql;
}
struct ResultByDataObject {
this(MySqlResult r, MySql mysql) {
result = r;
auto fields = r.fields();
this.mysql = mysql;
foreach(i, f; fields) {
string tbl = fromCstring(f.org_table is null ? f.table : f.org_table);
mappings[fromCstring(f.name)] = tuple(
tbl,
fromCstring(f.org_name is null ? f.name : f.org_name));
}
}
Tuple!(string, string)[string] mappings;
ulong length() { return result.length; }
bool empty() { return result.empty; }
void popFront() { result.popFront(); }
DataObject front() {
return new DataObject(mysql, result.front.toAA, mappings);
}
// would it be good to add a new() method? would be valid even if empty
// it'd just fill in the ID's at random and allow you to do the rest
@disable this(this) { }
MySqlResult result;
MySql mysql;
}
extern(C) {
typedef void MYSQL;
typedef void MYSQL_RES;
typedef const(ubyte)* cstring;
struct MYSQL_FIELD {
cstring name; /* Name of column */
cstring org_name; /* Original column name, if an alias */
cstring table; /* Table of column if column was a field */
cstring org_table; /* Org table name, if table was an alias */
cstring db; /* Database for table */
cstring catalog; /* Catalog for table */
cstring def; /* Default value (set by mysql_list_fields) */
uint length; /* Width of column (create length) */
uint max_length; /* Max width for selected set */
uint name_length;
uint org_name_length;
uint table_length;
uint org_table_length;
uint db_length;
uint catalog_length;
uint def_length;
uint flags; /* Div flags */
uint decimals; /* Number of decimals in field */
uint charsetnr; /* Character set */
uint type; /* Type of field. See mysql_com.h for types */
// type is actually an enum btw
}
typedef cstring* MYSQL_ROW;
cstring mysql_get_client_info();
MYSQL* mysql_init(MYSQL*);
uint mysql_errno(MYSQL*);
cstring mysql_error(MYSQL*);
MYSQL* mysql_real_connect(MYSQL*, cstring, cstring, cstring, cstring, uint, cstring, ulong);
int mysql_query(MYSQL*, cstring);
void mysql_close(MYSQL*);
ulong mysql_num_rows(MYSQL_RES*);
uint mysql_num_fields(MYSQL_RES*);
bool mysql_eof(MYSQL_RES*);
ulong mysql_affected_rows(MYSQL*);
ulong mysql_insert_id(MYSQL*);
MYSQL_RES* mysql_store_result(MYSQL*);
MYSQL_RES* mysql_use_result(MYSQL*);
MYSQL_ROW mysql_fetch_row(MYSQL_RES *);
uint* mysql_fetch_lengths(MYSQL_RES*);
MYSQL_FIELD* mysql_fetch_field(MYSQL_RES*);
MYSQL_FIELD* mysql_fetch_fields(MYSQL_RES*);
uint mysql_real_escape_string(MYSQL*, ubyte* to, cstring from, uint length);
void mysql_free_result(MYSQL_RES*);
}
import std.string;
cstring toCstring(string c) {
return cast(cstring) toStringz(c);
}
import std.array;
string fromCstring(cstring c, int len = -1) {
string ret;
if(c is null)
return null;
if(len == -1) {
while(*c) {
ret ~= cast(char) *c;
c++;
}
} else
for(int a = 0; a < len; a++)
ret ~= cast(char) *(a+c);
return ret;
}
/*
void main() {
auto mysql = new MySql("localhost", "uname", "password", "test");
scope(exit) delete mysql;
mysql.query("INSERT INTO users (id, password) VALUES (?, ?)", 10, "lol");
foreach(row; mysql.query("SELECT * FROM users")) {
writefln("%s %s %s %s", row["id"], row[0], row[1], row["username"]);
}
}
*/
/*
struct ResultByStruct(T) {
this(MySql.Result* r) {
result = r;
fields = r.fieldNames();
}
ulong length() { return result.length; }
bool empty() { return result.empty; }
void popFront() { result.popFront(); }
T front() {
auto r = result.front;
T ret;
foreach(i, a; r) {
ret[fields[i]] = a;
}
return ret;
}
@disable this(this) { }
string[] fields;
MySql.Result* result;
}
*/
/+
mysql.linq.tablename.field[key] // select field from tablename where id = key
mysql.link["name"].table.field[key] // select field from table where name = key
auto q = mysql.prepQuery("select id from table where something");
q.sort("name");
q.limit(start, count);
q.page(3, pagelength = ?);
q.execute(params here); // returns the same Result range as query
+/
/*
void main() {
auto db = new MySql("localhost", "uname", "password", "test");
foreach(item; db.queryDataObject("SELECT users.*, username
FROM users, password_manager_accounts
WHERE password_manager_accounts.user_id = users.id LIMIT 5")) {
writefln("item: %s, %s", item.id, item.username);
item.first = "new";
item.last = "new2";
item.username = "kill";
//item.commitChanges();
}
}
*/
/*
Copyright: Adam D. Ruppe, 2009 - 2011
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
Authors: Adam D. Ruppe
Copyright Adam D. Ruppe 2009 - 2011.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
*/

600
png.d Normal file
View file

@ -0,0 +1,600 @@
module arsd.png;
// By Adam D. Ruppe, 2009-2010, released into the public domain
import std.stdio;
import std.conv;
import std.file;
import std.zlib;
public import arsd.image;
/**
The return value should be casted to indexed or truecolor depending on what you need.
To get an image from a png file, do this:
auto i = cast(TrueColorImage) imageFromPng(readPng(cast(ubyte)[]) std.file.read("file.png")));
*/
Image imageFromPng(PNG* png) {
PNGHeader h = getHeader(png);
return new IndexedImage(h.width, h.height);
}
/*
struct PNGHeader {
uint width;
uint height;
ubyte depth = 8;
ubyte type = 6; // 0 - greyscale, 2 - truecolor, 3 - indexed color, 4 - grey with alpha, 6 - true with alpha
ubyte compressionMethod = 0; // should be zero
ubyte filterMethod = 0; // should be zero
ubyte interlaceMethod = 0; // bool
}
*/
PNG* pngFromImage(IndexedImage i) {
PNGHeader h;
h.width = i.width;
h.height = i.height;
h.type = 3;
if(i.numColors() <= 2)
h.depth = 1;
else if(i.numColors() <= 4)
h.depth = 2;
else if(i.numColors() <= 16)
h.depth = 4;
else if(i.numColors() <= 256)
h.depth = 8;
else throw new Exception("can't save this as an indexed png");
auto png = blankPNG(h);
// do palette and alpha
// FIXME: if there is only one transparent color, set it as the special chunk for that
// FIXME: we'd get a smaller file size if the transparent pixels were arranged first
Chunk palette;
palette.type = ['P', 'L', 'T', 'E'];
palette.size = i.palette.length * 3;
palette.payload.length = palette.size;
Chunk alpha;
if(i.hasAlpha) {
alpha.type = ['t', 'R', 'N', 'S'];
alpha.size = i.palette.length;
alpha.payload.length = alpha.size;
}
for(int a = 0; a < i.palette.length; a++) {
palette.payload[a*3+0] = i.palette[a].r;
palette.payload[a*3+1] = i.palette[a].g;
palette.payload[a*3+2] = i.palette[a].b;
if(i.hasAlpha)
alpha.payload[a] = i.palette[a].a;
}
palette.checksum = crc("PLTE", palette.payload);
png.chunks ~= palette;
if(i.hasAlpha) {
alpha.checksum = crc("tRNS", alpha.payload);
png.chunks ~= alpha;
}
// do the datastream
if(h.depth == 8) {
addImageDatastreamToPng(i.data, png);
} else {
// gotta convert it
ubyte[] datastream = new ubyte[i.width * i.height * 8 / h.depth]; // FIXME?
int shift = 0;
switch(h.depth) {
case 1: shift = 7; break;
case 2: shift = 6; break;
case 4: shift = 4; break;
case 8: shift = 0; break;
}
int dsp = 0;
int dpos = 0;
bool justAdvanced;
for(int y = 0; y < i.height; y++) {
for(int x = 0; x < i.width; x++) {
datastream[dsp] |= i.data[dpos++] << shift;
switch(h.depth) {
case 1: shift-= 1; break;
case 2: shift-= 2; break;
case 4: shift-= 4; break;
case 8: shift-= 8; break;
}
justAdvanced = shift < 0;
if(shift < 0) {
dsp++;
switch(h.depth) {
case 1: shift = 7; break;
case 2: shift = 6; break;
case 4: shift = 4; break;
case 8: shift = 0; break;
}
}
}
if(!justAdvanced)
dsp++;
switch(h.depth) {
case 1: shift = 7; break;
case 2: shift = 6; break;
case 4: shift = 4; break;
case 8: shift = 0; break;
}
}
addImageDatastreamToPng(datastream, png);
}
return png;
}
PNG* pngFromImage(TrueColorImage i) {
PNGHeader h;
h.width = i.width;
h.height = i.height;
// FIXME: optimize it if it is greyscale or doesn't use alpha alpha
auto png = blankPNG(h);
addImageDatastreamToPng(i.data, png);
return png;
}
/*
void main(string[] args) {
auto a = readPng(cast(ubyte[]) read(args[1]));
auto f = getDatastream(a);
foreach(i; f) {
writef("%d ", i);
}
writefln("\n\n%d", f.length);
}
*/
struct Chunk {
uint size;
ubyte[4] type;
ubyte[] payload;
uint checksum;
}
struct PNG {
uint length;
ubyte[8] header;
Chunk[] chunks;
Chunk* getChunk(string what) {
foreach(ref c; chunks) {
if(cast(string) c.type == what)
return &c;
}
throw new Exception("no such chunk " ~ what);
}
Chunk* getChunkNullable(string what) {
foreach(ref c; chunks) {
if(cast(string) c.type == what)
return &c;
}
return null;
}
}
ubyte[] writePng(PNG* p) {
ubyte[] a;
if(p.length)
a.length = p.length;
else {
a.length = 8;
foreach(c; p.chunks)
a.length += c.size + 12;
}
uint pos;
a[0..8] = p.header[0..8];
pos = 8;
foreach(c; p.chunks) {
a[pos++] = (c.size & 0xff000000) >> 24;
a[pos++] = (c.size & 0x00ff0000) >> 16;
a[pos++] = (c.size & 0x0000ff00) >> 8;
a[pos++] = (c.size & 0x000000ff) >> 0;
a[pos..pos+4] = c.type[0..4];
pos += 4;
a[pos..pos+c.size] = c.payload[0..c.size];
pos += c.size;
a[pos++] = (c.checksum & 0xff000000) >> 24;
a[pos++] = (c.checksum & 0x00ff0000) >> 16;
a[pos++] = (c.checksum & 0x0000ff00) >> 8;
a[pos++] = (c.checksum & 0x000000ff) >> 0;
}
return a;
}
PNG* readPng(ubyte[] data) {
auto p = new PNG;
p.length = data.length;
p.header[0..8] = data[0..8];
uint pos = 8;
while(pos < data.length) {
Chunk n;
n.size |= data[pos++] << 24;
n.size |= data[pos++] << 16;
n.size |= data[pos++] << 8;
n.size |= data[pos++] << 0;
n.type[0..4] = data[pos..pos+4];
pos += 4;
n.payload.length = n.size;
n.payload[0..n.size] = data[pos..pos+n.size];
pos += n.size;
n.checksum |= data[pos++] << 24;
n.checksum |= data[pos++] << 16;
n.checksum |= data[pos++] << 8;
n.checksum |= data[pos++] << 0;
p.chunks ~= n;
}
return p;
}
PNG* blankPNG(PNGHeader h) {
auto p = new PNG;
p.header = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
Chunk c;
c.size = 13;
c.type = ['I', 'H', 'D', 'R'];
c.payload.length = 13;
int pos = 0;
c.payload[pos++] = h.width >> 24;
c.payload[pos++] = (h.width >> 16) & 0xff;
c.payload[pos++] = (h.width >> 8) & 0xff;
c.payload[pos++] = h.width & 0xff;
c.payload[pos++] = h.height >> 24;
c.payload[pos++] = (h.height >> 16) & 0xff;
c.payload[pos++] = (h.height >> 8) & 0xff;
c.payload[pos++] = h.height & 0xff;
c.payload[pos++] = h.depth;
c.payload[pos++] = h.type;
c.payload[pos++] = h.compressionMethod;
c.payload[pos++] = h.filterMethod;
c.payload[pos++] = h.interlaceMethod;
c.checksum = crc("IHDR", c.payload);
p.chunks ~= c;
return p;
}
// should NOT have any idata already.
// FIXME: doesn't handle palettes
void addImageDatastreamToPng(const(ubyte)[] data, PNG* png) {
// we need to go through the lines and add the filter byte
// then compress it into an IDAT chunk
// then add the IEND chunk
PNGHeader h = getHeader(png);
auto bytesPerLine = h.width * 4;
if(h.type == 3)
bytesPerLine = h.width * 8 / h.depth;
Chunk dat;
dat.type = ['I', 'D', 'A', 'T'];
int pos = 0;
const(ubyte)[] output;
while(pos+bytesPerLine <= data.length) {
output ~= 0;
output ~= data[pos..pos+bytesPerLine];
pos += bytesPerLine;
}
auto com = cast(ubyte[]) compress(output);
dat.size = com.length;
dat.payload = com;
dat.checksum = crc("IDAT", dat.payload);
png.chunks ~= dat;
Chunk c;
c.size = 0;
c.type = ['I', 'E', 'N', 'D'];
c.checksum = crc("IEND", c.payload);
png.chunks ~= c;
}
struct PNGHeader {
uint width;
uint height;
ubyte depth = 8;
ubyte type = 6; // 0 - greyscale, 2 - truecolor, 3 - indexed color, 4 - grey with alpha, 6 - true with alpha
ubyte compressionMethod = 0; // should be zero
ubyte filterMethod = 0; // should be zero
ubyte interlaceMethod = 0; // bool
}
// bKGD - palette entry for background or the RGB (16 bits each) for that. or 16 bits of grey
ubyte[] getDatastream(PNG* p) {
ubyte[] compressed;
foreach(c; p.chunks) {
if(cast(string) c.type != "IDAT")
continue;
compressed ~= c.payload;
}
return cast(ubyte[]) uncompress(compressed);
}
// FIXME: Assuming 8 bits per pixel
ubyte[] getUnfilteredDatastream(PNG* p) {
PNGHeader h = getHeader(p);
assert(h.filterMethod == 0);
assert(h.type == 3); // FIXME
assert(h.depth == 8); // FIXME
ubyte[] data = getDatastream(p);
ubyte[] ufdata = new ubyte[data.length - h.height];
int bytesPerLine = ufdata.length / h.height;
int pos = 0, pos2 = 0;
for(int a = 0; a < h.height; a++) {
assert(data[pos2] == 0);
ufdata[pos..pos+bytesPerLine] = data[pos2+1..pos2+bytesPerLine+1];
pos+= bytesPerLine;
pos2+= bytesPerLine + 1;
}
return ufdata;
}
ubyte[] getFlippedUnfilteredDatastream(PNG* p) {
PNGHeader h = getHeader(p);
assert(h.filterMethod == 0);
assert(h.type == 3); // FIXME
assert(h.depth == 8 || h.depth == 4); // FIXME
ubyte[] data = getDatastream(p);
ubyte[] ufdata = new ubyte[data.length - h.height];
int bytesPerLine = ufdata.length / h.height;
int pos = ufdata.length - bytesPerLine, pos2 = 0;
for(int a = 0; a < h.height; a++) {
assert(data[pos2] == 0);
ufdata[pos..pos+bytesPerLine] = data[pos2+1..pos2+bytesPerLine+1];
pos-= bytesPerLine;
pos2+= bytesPerLine + 1;
}
return ufdata;
}
ubyte getHighNybble(ubyte a) {
return cast(ubyte)(a >> 4); // FIXME
}
ubyte getLowNybble(ubyte a) {
return a & 0x0f;
}
// Takes the transparency info and returns
ubyte[] getANDMask(PNG* p) {
PNGHeader h = getHeader(p);
assert(h.filterMethod == 0);
assert(h.type == 3); // FIXME
assert(h.depth == 8 || h.depth == 4); // FIXME
assert(h.width % 8 == 0); // might actually be %2
ubyte[] data = getDatastream(p);
ubyte[] ufdata = new ubyte[h.height*((((h.width+7)/8)+3)&~3)]; // gotta pad to DWORDs...
Color[] colors = fetchPalette(p);
int pos = 0, pos2 = (h.width/((h.depth == 8) ? 1 : 2)+1)*(h.height-1);
bool bits = false;
for(int a = 0; a < h.height; a++) {
assert(data[pos2++] == 0);
for(int b = 0; b < h.width; b++) {
if(h.depth == 4) {
ufdata[pos/8] |= ((colors[bits? getLowNybble(data[pos2]) : getHighNybble(data[pos2])].a <= 30) << (7-(pos%8)));
} else
ufdata[pos/8] |= ((colors[data[pos2]].a == 0) << (7-(pos%8)));
pos++;
if(h.depth == 4) {
if(bits) {
pos2++;
}
bits = !bits;
} else
pos2++;
}
int pad = 0;
for(; pad < ((pos/8) % 4); pad++) {
ufdata[pos/8] = 0;
pos+=8;
}
if(h.depth == 4)
pos2 -= h.width + 2;
else
pos2-= 2*(h.width) +2;
}
return ufdata;
}
// Done with assumption
PNGHeader getHeader(PNG* p) {
PNGHeader h;
ubyte[] data = p.getChunk("IHDR").payload;
int pos = 0;
h.width |= data[pos++] << 24;
h.width |= data[pos++] << 16;
h.width |= data[pos++] << 8;
h.width |= data[pos++] << 0;
h.height |= data[pos++] << 24;
h.height |= data[pos++] << 16;
h.height |= data[pos++] << 8;
h.height |= data[pos++] << 0;
h.depth = data[pos++];
h.type = data[pos++];
h.compressionMethod = data[pos++];
h.filterMethod = data[pos++];
h.interlaceMethod = data[pos++];
return h;
}
struct Color {
ubyte r;
ubyte g;
ubyte b;
ubyte a;
}
/+
class Image {
Color[][] trueColorData;
ubyte[] indexData;
Color[] palette;
uint width;
uint height;
this(uint w, uint h) {}
}
Image fromPNG(PNG* p) {
}
PNG* toPNG(Image i) {
}
+/ struct RGBQUAD {
ubyte rgbBlue;
ubyte rgbGreen;
ubyte rgbRed;
ubyte rgbReserved;
}
RGBQUAD[] fetchPaletteWin32(PNG* p) {
RGBQUAD[] colors;
auto palette = p.getChunk("PLTE");
colors.length = (palette.size) / 3;
for(int i = 0; i < colors.length; i++) {
colors[i].rgbRed = palette.payload[i*3+0];
colors[i].rgbGreen = palette.payload[i*3+1];
colors[i].rgbBlue = palette.payload[i*3+2];
colors[i].rgbReserved = 0;
}
return colors;
}
Color[] fetchPalette(PNG* p) {
Color[] colors;
auto palette = p.getChunk("PLTE");
Chunk* alpha = p.getChunkNullable("tRNS");
colors.length = palette.size / 3;
for(int i = 0; i < colors.length; i++) {
colors[i].r = palette.payload[i*3+0];
colors[i].g = palette.payload[i*3+1];
colors[i].b = palette.payload[i*3+2];
if(alpha !is null && i < alpha.size)
colors[i].a = alpha.payload[i];
else
colors[i].a = 255;
//writefln("%2d: %3d %3d %3d %3d", i, colors[i].r, colors[i].g, colors[i].b, colors[i].a);
}
return colors;
}
void replacePalette(PNG* p, Color[] colors) {
auto palette = p.getChunk("PLTE");
auto alpha = p.getChunk("tRNS");
assert(colors.length == alpha.size);
for(int i = 0; i < colors.length; i++) {
palette.payload[i*3+0] = colors[i].r;
palette.payload[i*3+1] = colors[i].g;
palette.payload[i*3+2] = colors[i].b;
alpha.payload[i] = colors[i].a;
}
palette.checksum = crc("PLTE", palette.payload);
alpha.checksum = crc("tRNS", alpha.payload);
}
uint update_crc(in uint crc, in ubyte[] buf){
static const uint[256] crc_table = [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117];
uint c = crc;
foreach(b; buf)
c = crc_table[(c ^ b) & 0xff] ^ (c >> 8);
return c;
}
// lol is just the chunk name
uint crc(in string lol, in ubyte[] buf){
uint c = update_crc(0xffffffffL, cast(ubyte[]) lol);
return update_crc(c, buf) ^ 0xffffffffL;
}

218
postgres.d Normal file
View file

@ -0,0 +1,218 @@
module arsd.postgres;
pragma(lib, "pq");
public import arsd.database;
import std.string;
import std.exception;
// remember to CREATE DATABASE name WITH ENCODING 'utf8'
class PostgreSql : Database {
// dbname = name is probably the most common connection string
this(string connectionString) {
conn = PQconnectdb(toStringz(connectionString));
if(conn is null)
throw new DatabaseException("Unable to allocate PG connection object");
if(PQstatus(conn) != CONNECTION_OK)
throw new DatabaseException(error());
query("SET NAMES 'utf8'"); // D does everything with utf8
}
~this() {
PQfinish(conn);
}
override void startTransaction() {
query("START TRANSACTION");
}
ResultSet queryImpl(string sql, Variant[] args...) {
sql = escapedVariants(this, sql, args);
auto res = PQexec(conn, toStringz(sql));
int ress = PQresultStatus(res);
if(ress != PGRES_TUPLES_OK
&& ress != PGRES_COMMAND_OK)
throw new DatabaseException(error());
return new PostgresResult(res);
}
string escape(string sqlData) {
char* buffer = (new char[sqlData.length * 2 + 1]).ptr;
int size = PQescapeString (buffer, sqlData.ptr, sqlData.length);
string ret = assumeUnique(buffer[0..size]);
return ret;
}
string error() {
return copyCString(PQerrorMessage(conn));
}
private:
PGconn* conn;
}
class PostgresResult : ResultSet {
// name for associative array to result index
int getFieldIndex(string field) {
if(mapping is null)
makeFieldMapping();
return mapping[field];
}
string[] fieldNames() {
if(mapping is null)
makeFieldMapping();
return columnNames;
}
// this is a range that can offer other ranges to access it
bool empty() {
return position == numRows;
}
Row front() {
return row;
}
void popFront() {
position++;
if(position < numRows)
fetchNext;
}
int length() {
return numRows;
}
this(PGresult* res) {
this.res = res;
numFields = PQnfields(res);
numRows = PQntuples(res);
if(numRows)
fetchNext();
}
~this() {
PQclear(res);
}
private:
PGresult* res;
int[string] mapping;
string[] columnNames;
int numFields;
int position;
int numRows;
Row row;
void fetchNext() {
Row r;
r.resultSet = this;
string[] row;
for(int i = 0; i < numFields; i++) {
string a;
if(PQgetisnull(res, position, i))
a = null;
else {
a = copyCString(PQgetvalue(res, position, i), PQgetlength(res, position, i));
}
row ~= a;
}
r.row = row;
this.row = r;
}
void makeFieldMapping() {
for(int i = 0; i < numFields; i++) {
string a = copyCString(PQfname(res, i));
columnNames ~= a;
mapping[a] = i;
}
}
}
string copyCString(const char* c, int actualLength = -1) {
const(char)* a = c;
if(a is null)
return null;
string ret;
if(actualLength == -1)
while(*a) {
ret ~= *a;
a++;
}
else {
ret = a[0..actualLength].idup;
}
return ret;
}
extern(C) {
struct PGconn;
struct PGresult;
void PQfinish(PGconn*);
PGconn* PQconnectdb(const char*);
int PQstatus(PGconn*); // FIXME check return value
const (char*) PQerrorMessage(PGconn*);
PGresult* PQexec(PGconn*, const char*);
void PQclear(PGresult*);
int PQresultStatus(PGresult*); // FIXME check return value
int PQnfields(PGresult*); // number of fields in a result
const(char*) PQfname(PGresult*, int); // name of field
int PQntuples(PGresult*); // number of rows in result
const(char*) PQgetvalue(PGresult*, int row, int column);
size_t PQescapeString (char *to, const char *from, size_t length);
enum int CONNECTION_OK = 0;
enum int PGRES_COMMAND_OK = 1;
enum int PGRES_TUPLES_OK = 2;
int PQgetlength(const PGresult *res,
int row_number,
int column_number);
int PQgetisnull(const PGresult *res,
int row_number,
int column_number);
}
/*
import std.stdio;
void main() {
auto db = new PostgreSql("dbname = test");
db.query("INSERT INTO users (id, name) values (?, ?)", 30, "hello mang");
foreach(line; db.query("SELECT * FROM users")) {
writeln(line[0], line["name"]);
}
}
*/

366
sha.d Normal file
View file

@ -0,0 +1,366 @@
module arsd.sha;
/*
By Adam D. Ruppe, 26 Nov 2009
I release this file into the public domain
*/
import std.stdio;
immutable(ubyte)[/*20*/] SHA1(T)(T data) if(isInputRange!(T)) /*const(ubyte)[] data)*/ {
uint[5] h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0];
SHARange!(T) range;
static if(is(data == SHARange))
range = data;
else {
range.r = data;
}
/*
ubyte[] message = data.dup;
message ~= 0b1000_0000;
while(((message.length+8) * 8) % 512)
message ~= 0;
ulong originalLength = cast(ulong) data.length * 8;
for(int a = 7; a >= 0; a--)
message ~= (originalLength >> (a*8)) & 0xff; // to big-endian
assert(((message.length * 8) % 512) == 0);
uint pos = 0;
while(pos < message.length) {
*/
while(!range.empty) {
uint[80] words;
for(int a = 0; a < 16; a++) {
for(int b = 3; b >= 0; b--) {
words[a] |= cast(uint)(range.front()) << (b*8);
range.popFront;
// words[a] |= cast(uint)(message[pos]) << (b*8);
// pos++;
}
}
for(int a = 16; a < 80; a++) {
uint t = words[a-3];
t ^= words[a-8];
t ^= words[a-14];
t ^= words[a-16];
asm { rol t, 1; }
words[a] = t;
}
uint a = h[0];
uint b = h[1];
uint c = h[2];
uint d = h[3];
uint e = h[4];
for(int i = 0; i < 80; i++) {
uint f, k;
if(i >= 0 && i < 20) {
f = (b & c) | ((~b) & d);
k = 0x5A827999;
} else
if(i >= 20 && i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
} else
if(i >= 40 && i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
} else
if(i >= 60 && i < 80) {
f = b ^ c ^ d;
k = 0xCA62C1D6;
} else assert(0);
uint temp;
asm {
mov EAX, a;
rol EAX, 5;
add EAX, f;
add EAX, e;
add EAX, k;
mov temp, EAX;
}
temp += words[i];
e = d;
d = c;
asm {
mov EAX, b;
rol EAX, 30;
mov c, EAX;
}
b = a;
a = temp;
}
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
}
ubyte[] hash;
for(int j = 0; j < 5; j++)
for(int i = 3; i >= 0; i--) {
hash ~= cast(ubyte)(h[j] >> (i*8))&0xff;
}
return hash.idup;
}
import std.range;
// This does the preprocessing of input data, fetching one byte at a time of the data until it is empty, then the padding and length at the end
template SHARange(T) if(isInputRange!(T)) {
struct SHARange {
T r;
bool empty() {
return state == 5;
}
void popFront() {
static int lol = 0;
if(state == 0) {
r.popFront;
/*
static if(__traits(compiles, r.front.length))
length += r.front.length;
else
length += r.front().sizeof;
*/
length++; // FIXME
if(r.empty) {
state = 1;
position = 2;
current = 0x80;
}
} else {
if(state == 1) {
current = 0x0;
state = 2;
position++;
} else if( state == 2) {
if(!(((position + length + 8) * 8) % 512)) {
state = 3;
position = 7;
length *= 8;
} else
position++;
} else if (state == 3) {
current = (length >> (position*8)) & 0xff;
if(position == 0)
state = 4;
else
position--;
} else if (state == 4) {
current = 0xff;
state = 5;
}
}
}
ubyte front() {
if(state == 0) {
return cast(ubyte) r.front();
}
return current;
}
ubyte current;
uint position;
ulong length;
int state = 0; // reading range, reading appended bit, reading padding, reading length, done
}
}
immutable(ubyte)[] SHA256(T)(T data) if ( isInputRange!(T)) {
uint[8] h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
immutable(uint[64]) k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
SHARange!(T) range;
static if(is(data == SHARange))
range = data;
else {
range.r = data;
}
/*
ubyte[] message = cast(ubyte[]) data.dup;
message ~= 0b1000_0000;
while(((message.length+8) * 8) % 512)
message ~= 0;
ulong originalLength = cast(ulong) data.length * 8;
for(int a = 7; a >= 0; a--)
message ~= (originalLength >> (a*8)) & 0xff; // to big-endian
assert(((message.length * 8) % 512) == 0);
*/
// uint pos = 0;
while(!range.empty) {
// while(pos < message.length) {
uint[64] words;
for(int a = 0; a < 16; a++) {
for(int b = 3; b >= 0; b--) {
words[a] |= cast(uint)(range.front()) << (b*8);
//words[a] |= cast(uint)(message[pos]) << (b*8);
range.popFront;
// pos++;
}
}
for(int a = 16; a < 64; a++) {
uint t1 = words[a-15];
asm {
mov EAX, t1;
mov EBX, EAX;
mov ECX, EAX;
ror EAX, 7;
ror EBX, 18;
shr ECX, 3;
xor EAX, EBX;
xor EAX, ECX;
mov t1, EAX;
}
uint t2 = words[a-2];
asm {
mov EAX, t2;
mov EBX, EAX;
mov ECX, EAX;
ror EAX, 17;
ror EBX, 19;
shr ECX, 10;
xor EAX, EBX;
xor EAX, ECX;
mov t2, EAX;
}
words[a] = words[a-16] + t1 + words[a-7] + t2;
}
uint A = h[0];
uint B = h[1];
uint C = h[2];
uint D = h[3];
uint E = h[4];
uint F = h[5];
uint G = h[6];
uint H = h[7];
for(int i = 0; i < 64; i++) {
uint s0;
asm {
mov EAX, A;
mov EBX, EAX;
mov ECX, EAX;
ror EAX, 2;
ror EBX, 13;
ror ECX, 22;
xor EAX, EBX;
xor EAX, ECX;
mov s0, EAX;
}
uint maj = (A & B) ^ (A & C) ^ (B & C);
uint t2 = s0 + maj;
uint s1;
asm {
mov EAX, E;
mov EBX, EAX;
mov ECX, EAX;
ror EAX, 6;
ror EBX, 11;
ror ECX, 25;
xor EAX, EBX;
xor EAX, ECX;
mov s1, EAX;
}
uint ch = (E & F) ^ ((~E) & G);
uint t1 = H + s1 + ch + k[i] + words[i];
H = G;
G = F;
F = E;
E = D + t1;
D = C;
C = B;
B = A;
A = t1 + t2;
}
h[0] += A;
h[1] += B;
h[2] += C;
h[3] += D;
h[4] += E;
h[5] += F;
h[6] += G;
h[7] += H;
}
ubyte[] hash;
for(int j = 0; j < 8; j++)
for(int i = 3; i >= 0; i--) {
hash ~= cast(ubyte)(h[j] >> (i*8))&0xff;
}
return hash.idup;
}
import std.exception;
string hashToString(const(ubyte)[] hash) {
char[] s;
s.length = hash.length * 2;
char toHex(int a) {
if(a < 10)
return cast(char) (a + '0');
else
return cast(char) (a + 'a' - 10);
}
for(int a = 0; a < hash.length; a++) {
s[a*2] = toHex(hash[a] >> 4);
s[a*2+1] = toHex(hash[a] & 0x0f);
}
return assumeUnique(s);
}
/*
string tee(string t) {
writefln("%s", t);
return t;
}
*/
unittest {
assert(hashToString(SHA1("abc")) == "a9993e364706816aba3e25717850c26c9cd0d89d");
assert(hashToString(SHA1("sdfj983yr2ih")) == "335f1f5a4af4aa2c8e93b88d69dda2c22baeb94d");
assert(hashToString(SHA1("$%&^54ylkufg09fd7f09sa7udsiouhcx987yw98etf7yew98yfds987f632<F7>uw90ruds09fudsf09dsuhfoidschyds98fydovipsdaidsd9fsa GA UIA duisguifgsuifgusaufisgfuisafguisagasuidgsaufsauifhuisahfuisafaoisahasiosafhffdasasdisayhfdoisayf8saiuhgduifyds8fiydsufisafoisayf8sayfd98wqyr98wqy98sayd98sayd098sayd09sayd98sayd98saicxyhckxnvjbpovc pousa09cusa 09csau csa9 dusa90d usa9d0sau dsa90 as09posufpodsufodspufdspofuds 9tu sapfusaa daosjdoisajdsapoihdsaiodyhsaioyfg d98ytewq89rysa 98yc98sdxych sa89ydsa89dy sa98ydas98c ysx9v8y cxv89ysd f8ysa89f ysa89fd sg8yhds9g8 rfjcxhvslkhdaiosy09wq7r987t98e7ys98aIYOIYOIY)(*YE (*WY *A(YSA* HDUIHDUIAYT&*ATDAUID AUI DUIAT DUIAG saoidusaoid ysqoid yhsaduiayh UIZYzuI YUIYEDSA UIDYUIADYISA YTDGS UITGUID")) == "e38a1220eaf8103d6176df2e0dd0a933e2f52001");
assert(hashToString(SHA256("abc")) == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
assert(hashToString(SHA256("$%&^54ylkufg09fd7f09sa7udsiouhcx987yw98etf7yew98yfds987f632<F7>uw90ruds09fudsf09dsuhfoidschyds98fydovipsdaidsd9fsa GA UIA duisguifgsuifgusaufisgfuisafguisagasuidgsaufsauifhuisahfuisafaoisahasiosafhffdasasdisayhfdoisayf8saiuhgduifyds8fiydsufisafoisayf8sayfd98wqyr98wqy98sayd98sayd098sayd09sayd98sayd98saicxyhckxnvjbpovc pousa09cusa 09csau csa9 dusa90d usa9d0sau dsa90 as09posufpodsufodspufdspofuds 9tu sapfusaa daosjdoisajdsapoihdsaiodyhsaioyfg d98ytewq89rysa 98yc98sdxych sa89ydsa89dy sa98ydas98c ysx9v8y cxv89ysd f8ysa89f ysa89fd sg8yhds9g8 rfjcxhvslkhdaiosy09wq7r987t98e7ys98aIYOIYOIY)(*YE (*WY *A(YSA* HDUIHDUIAYT&*ATDAUID AUI DUIAT DUIAG saoidusaoid ysqoid yhsaduiayh UIZYzuI YUIYEDSA UIDYUIADYISA YTDGS UITGUID")) == "64ff79c67ad5ddf9ba5b2d83e07a6937ef9a5b4eb39c54fe1e913e21aad0e95c");
}
/*
void main() {
auto hash = SHA256(InputByChar(stdin));
writefln("%s", hashToString(hash));
}
*/

1
simpleaudio.d Symbolic link
View file

@ -0,0 +1 @@
../../dimage/simpleaudio.d

1
simpledisplay.d Symbolic link
View file

@ -0,0 +1 @@
../../dimage/simpledisplay.d

740
sqlite.d Normal file
View file

@ -0,0 +1,740 @@
/*
Compile with version=sqlite_extended_metadata_available
if your sqlite is compiled with the
SQLITE_ENABLE_COLUMN_METADATA C-preprocessor symbol.
If you enable that, you get the ability to use the
queryDataObject() function with sqlite. (You can still
use DataObjects, but you'll have to set up the mappings
manually without the extended metadata.)
*/
module arsd.sqlite;
pragma(lib, "sqlite3");
version(linux)
pragma(lib, "dl"); // apparently sqlite3 depends on this
public import arsd.database;
import std.exception;
import std.string;
import std.c.stdlib;
import core.exception;
import core.memory;
import std.file;
import std.conv;
/*
NOTE:
This only works correctly on INSERTs if the user can grow the
database file! This means he must have permission to write to
both the file and the directory it is in.
*/
/**
The Database interface provides a consistent and safe way to access sql RDBMSs.
Why are all the classes scope? To ensure the database connection is closed when you are done with it.
The destructor cleans everything up.
(maybe including rolling back a transaction if one is going and it errors.... maybe, or that could bne
scope(exit))
*/
Sqlite openDBAndCreateIfNotPresent(string filename, string sql, void delegate(Sqlite db) initalize = null){
if(exists(filename))
return new Sqlite(filename);
else {
auto db = new Sqlite(filename);
db.exec(sql);
if(initalize !is null)
initalize(db);
return db;
}
}
/*
import std.stdio;
void main() {
Database db = new Sqlite("test.sqlite.db");
db.query("CREATE TABLE users (id integer, name text)");
db.query("INSERT INTO users values (?, ?)", 1, "hello");
foreach(line; db.query("SELECT * FROM users")) {
writefln("%s %s", line[0], line["name"]);
}
}
*/
class Sqlite : Database {
public:
this(string filename, int flags = SQLITE_OPEN_READWRITE) {
/+
int error = sqlite3_open_v2(toStringz(filename), &db, flags, null);
if(error == SQLITE_CANTOPEN)
throw new DatabaseException("omg cant open");
if(error != SQLITE_OK)
throw new DatabaseException("db open " ~ error());
+/
int error = sqlite3_open(toStringz(filename), &db);
if(error != SQLITE_OK)
throw new DatabaseException(this.error());
}
~this(){
if(sqlite3_close(db) != SQLITE_OK)
throw new DatabaseException(error());
}
// my extension for easier editing
version(sqlite_extended_metadata_available) {
ResultByDataObject queryDataObject(T...)(string sql, T t) {
// modify sql for the best data object grabbing
sql = fixupSqlForDataObjectUse(sql);
auto s = Statement(this, sql);
foreach(i, arg; t) {
s.bind(i + 1, arg);
}
auto magic = s.execute(true); // fetch extended metadata
return ResultByDataObject(cast(SqliteResult) magic, magic.extendedMetadata, this);
}
}
override void startTransaction() {
query("BEGIN TRANSACTION");
}
override ResultSet queryImpl(string sql, Variant[] args...) {
auto s = Statement(this, sql);
foreach(i, arg; args) {
s.bind(i + 1, arg);
}
return s.execute();
}
override string escape(string sql) {
if(sql is null)
return null;
char* got = sqlite3_mprintf("%q", toStringz(sql)); // FIXME: might have to be %Q, need to check this, but I think the other impls do the same as %q
auto orig = got;
string esc;
while(*got) {
esc ~= (*got);
got++;
}
sqlite3_free(orig);
return esc;
}
string error(){
char* mesg = sqlite3_errmsg(db);
char[] m;
int a = std.c.string.strlen(mesg);
m.length = a;
for(int v = 0; v < a; v++)
m[v] = mesg[v];
return assumeUnique(m);
}
int affectedRows(){
return sqlite3_changes(db);
}
int lastInsertId(){
return cast(int) sqlite3_last_insert_rowid(db);
}
int exec(string sql, void delegate (char[][char[]]) onEach = null) {
char* mesg;
if(sqlite3_exec(db, toStringz(sql), &callback, &onEach, &mesg) != SQLITE_OK) {
char[] m;
int a = std.c.string.strlen(mesg);
m.length = a;
for(int v = 0; v < a; v++)
m[v] = mesg[v];
sqlite3_free(mesg);
throw new DatabaseException("exec " ~ m.idup);
}
return 0;
}
/*
Statement prepare(string sql){
sqlite3_stmt * s;
if(sqlite3_prepare_v2(db, toStringz(sql), cast(int) sql.length, &s, null) != SQLITE_OK)
throw new DatabaseException("prepare " ~ error());
Statement a = new Statement(s);
return a;
}
*/
private:
sqlite3* db;
}
class SqliteResult : ResultSet {
int getFieldIndex(string field) {
foreach(i, n; columnNames)
if(n == field)
return i;
throw new Exception("no such field " ~ field);
}
string[] fieldNames() {
return columnNames;
}
// this is a range that can offer other ranges to access it
bool empty() {
return position == rows.length;
}
Row front() {
Row r;
r.resultSet = this;
if(rows.length <= position)
throw new Exception("Result is empty");
foreach(c; rows[position]) {
r.row ~= c.coerce!(string);
}
return r;
}
void popFront() {
position++;
}
int length() {
return rows.length;
}
this(Variant[][] rows, char[][] columnNames) {
this.rows = rows;
foreach(c; columnNames)
this.columnNames ~= c.idup;
}
private:
string[] columnNames;
Variant[][] rows;
int position = 0;
}
struct Statement {
private this(Sqlite db, sqlite3_stmt * S) {
this.db = db;
s = S;
finalized = false;
}
Sqlite db;
this(Sqlite db, string sql) {
this.db = db;
if(sqlite3_prepare_v2(db.db, toStringz(sql), cast(int) sql.length, &s, null) != SQLITE_OK)
throw new DatabaseException(db.error());
}
version(sqlite_extended_metadata_available)
Tuple!(string, string)[string] extendedMetadata;
ResultSet execute(bool fetchExtendedMetadata = false) {
bool first = true;
int count;
int numRows = 0;
int r = 0;
// FIXME: doesn't handle busy database
while( SQLITE_ROW == sqlite3_step(s) ){
numRows++;
if(numRows >= rows.length)
rows.length = rows.length + 8;
if(first){
count = sqlite3_column_count(s);
columnNames.length = count;
for(int a = 0; a < count; a++){
char* str = sqlite3_column_name(s, a);
int l = std.c.string.strlen(str);
columnNames[a].length = l;
for(int b = 0; b < l; b++)
columnNames[a][b] = str[b];
version(sqlite_extended_metadata_available) {
if(fetchExtendedMetadata) {
string origtbl;
string origcol;
const(char)* rofl;
rofl = sqlite3_column_table_name(s, a);
if(rofl is null)
throw new Exception("null table name pointer");
while(*rofl) {
origtbl ~= *rofl;
rofl++;
}
rofl = sqlite3_column_origin_name(s, a);
if(rofl is null)
throw new Exception("null colum name pointer");
while(*rofl) {
origcol ~= *rofl;
rofl++;
}
extendedMetadata[columnNames[a].idup] = tuple(origtbl, origcol);
}
}
}
first = false;
}
rows[r].length = count;
for(int a = 0; a < count; a++){
Variant v;
switch(sqlite3_column_type(s, a)){
case SQLITE_INTEGER:
v = sqlite3_column_int(s, a);
break;
case SQLITE_FLOAT:
v = sqlite3_column_double(s, a);
break;
case SQLITE3_TEXT:
char* str = sqlite3_column_text(s, a);
char[] st;
int l = std.c.string.strlen(str);
st.length = l;
for(int aa = 0; aa < l; aa++)
st[aa] = str[aa];
v = assumeUnique(st);
break;
case SQLITE_BLOB:
byte* str = cast(byte*) sqlite3_column_blob(s, a);
byte[] st;
int l = sqlite3_column_bytes(s, a);
st.length = l;
for(int aa = 0; aa < l; aa++)
st[aa] = str[aa];
v = assumeUnique(st);
break;
case SQLITE_NULL:
v = null;
break;
}
rows[r][a] = v;
}
r++;
}
rows.length = numRows;
length = numRows;
position = 0;
executed = true;
reset();
return new SqliteResult(rows.dup, columnNames);
}
/*
template extract(A, T, R...){
void extract(A args, out T t, out R r){
if(r.length + 1 != args.length)
throw new DatabaseException("wrong places");
args[0].to(t);
static if(r.length)
extract(args[1..$], r);
}
}
*/
/*
bool next(T, R...)(out T t, out R r){
if(position == length)
return false;
extract(rows[position], t, r);
position++;
return true;
}
*/
bool step(out Variant[] row){
assert(executed);
if(position == length)
return false;
row = rows[position];
position++;
return true;
}
bool step(out Variant[char[]] row){
assert(executed);
if(position == length)
return false;
for(int a = 0; a < length; a++)
row[columnNames[a].idup] = rows[position][a];
position++;
return true;
}
void reset(){
if(sqlite3_reset(s) != SQLITE_OK)
throw new DatabaseException("reset " ~ db.error());
}
void resetBindings(){
sqlite3_clear_bindings(s);
}
void resetAll(){
reset;
resetBindings;
executed = false;
}
int bindNameLookUp(const char[] name){
int a = sqlite3_bind_parameter_index(s, toStringz(name));
if(a == 0)
throw new DatabaseException("bind name lookup failed " ~ db.error());
return a;
}
bool next(T, R...)(out T t, out R r){
assert(executed);
if(position == length)
return false;
extract(rows[position], t, r);
position++;
return true;
}
template bindAll(T, R...){
void bindAll(T what, R more){
bindAllHelper(1, what, more);
}
}
template exec(T, R...){
void exec(T what, R more){
bindAllHelper(1, what, more);
execute();
}
}
void bindAllHelper(A, T, R...)(A where, T what, R more){
bind(where, what);
static if(more.length)
bindAllHelper(where + 1, more);
}
//void bind(T)(string name, T value) {
//bind(bindNameLookUp(name), value);
//}
// This should be a template, but grrrr.
void bind (const char[] name, const char[] value){ bind(bindNameLookUp(name), value); }
void bind (const char[] name, int value){ bind(bindNameLookUp(name), value); }
void bind (const char[] name, float value){ bind(bindNameLookUp(name), value); }
void bind (const char[] name, const byte[] value){ bind(bindNameLookUp(name), value); }
void bind(int col, const char[] value){
if(value is null) {
if(sqlite3_bind_null(s, col) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
} else {
if(sqlite3_bind_text(s, col, value.ptr, value.length, cast(void*)-1) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
}
}
void bind(int col, float value){
if(sqlite3_bind_double(s, col, value) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
}
void bind(int col, int value){
if(sqlite3_bind_int(s, col, value) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
}
void bind(int col, const byte[] value){
if(value is null) {
if(sqlite3_bind_null(s, col) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
} else {
if(sqlite3_bind_blob(s, col, cast(void*)value.ptr, value.length, cast(void*)-1) != SQLITE_OK)
throw new DatabaseException("bind " ~ db.error());
}
}
void bind(int col, Variant v) {
if(v.peek!int)
bind(col, v.get!int);
if(v.peek!string)
bind(col, v.get!string);
if(v.peek!float)
bind(col, v.get!float);
if(v.peek!(byte[]))
bind(col, v.get!(byte[]));
if(v.peek!(void*) && v.get!(void*) is null)
bind(col, cast(string) null);
}
~this(){
if(!finalized)
finalize();
}
void finalize(){
if(finalized)
return;
if(sqlite3_finalize(s) != SQLITE_OK)
throw new DatabaseException("finalize " ~ db.error());
finalized = true;
}
private:
Variant[][] rows;
char[][] columnNames;
int length;
int position;
bool finalized;
sqlite3_stmt * s;
bool executed;
new(size_t sz)
{
void* p;
p = std.c.stdlib.malloc(sz);
if (!p)
throw new OutOfMemoryError(__FILE__, __LINE__);
GC.addRange(p, sz);
return p;
}
delete(void* p)
{
if (p)
{ GC.removeRange(p);
std.c.stdlib.free(p);
}
}
}
version(sqlite_extended_metadata_available) {
import std.typecons;
struct ResultByDataObject {
this(SqliteResult r, Tuple!(string, string)[string] mappings, Sqlite db) {
result = r;
this.db = db;
this.mappings = mappings;
}
Tuple!(string, string)[string] mappings;
ulong length() { return result.length; }
bool empty() { return result.empty; }
void popFront() { result.popFront(); }
DataObject front() {
return new DataObject(db, result.front.toAA, mappings);
}
// would it be good to add a new() method? would be valid even if empty
// it'd just fill in the ID's at random and allow you to do the rest
@disable this(this) { }
SqliteResult result;
Sqlite db;
}
}
extern(C) int callback(void* cb, int howmany, char** text, char** columns){
if(cb is null)
return 0;
void delegate(char[][char[]]) onEach = *cast(void delegate(char[][char[]])*)cb;
char[][char[]] row;
for(int a = 0; a < howmany; a++){
int b = std.c.string.strlen(columns[a]);
char[] buf;
buf.length = b;
for(int c = 0; c < b; c++)
buf[c] = columns[a][c];
int d = std.c.string.strlen(text[a]);
char[] t;
t.length = d;
for(int c = 0; c < d; c++)
t[c] = text[a][c];
row[buf.idup] = t;
}
onEach(row);
return 0;
}
extern(C){
typedef void sqlite3;
typedef void sqlite3_stmt;
int sqlite3_changes(sqlite3*);
int sqlite3_close(sqlite3 *);
int sqlite3_exec(
sqlite3*, /* An open database */
const(char) *sql, /* SQL to be evaluted */
int function(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
int sqlite3_open(
const(char) *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
/+
int sqlite3_open_v2(
char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb, /* OUT: SQLite db handle */
int flags, /* Flags */
char *zVfs /* Name of VFS module to use */
);
+/
int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const(char) *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
char **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_finalize(sqlite3_stmt *pStmt);
int sqlite3_step(sqlite3_stmt*);
long sqlite3_last_insert_rowid(sqlite3*);
const int SQLITE_OK = 0;
const int SQLITE_ROW = 100;
const int SQLITE_DONE = 101;
const int SQLITE_INTEGER = 1; // int
const int SQLITE_FLOAT = 2; // float
const int SQLITE3_TEXT = 3; // char[]
const int SQLITE_BLOB = 4; // byte[]
const int SQLITE_NULL = 5; // void* = null
char *sqlite3_mprintf(const char*,...);
int sqlite3_reset(sqlite3_stmt *pStmt);
int sqlite3_clear_bindings(sqlite3_stmt*);
int sqlite3_bind_parameter_index(sqlite3_stmt*, const(char) *zName);
int sqlite3_bind_blob(sqlite3_stmt*, int, void*, int n, void*);
//int sqlite3_bind_blob(sqlite3_stmt*, int, void*, int n, void(*)(void*));
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_text(sqlite3_stmt*, int, const(char)*, int n, void*);
//int sqlite3_bind_text(sqlite3_stmt*, int, char*, int n, void(*)(void*));
void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
char *sqlite3_column_text(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
char *sqlite3_column_name(sqlite3_stmt*, int N);
int sqlite3_column_count(sqlite3_stmt *pStmt);
void sqlite3_free(void*);
char *sqlite3_errmsg(sqlite3*);
const int SQLITE_OPEN_READONLY = 0x1;
const int SQLITE_OPEN_READWRITE = 0x2;
const int SQLITE_OPEN_CREATE = 0x4;
const int SQLITE_CANTOPEN = 14;
// will need these to enable support for DataObjects here
const (char *)sqlite3_column_database_name(sqlite3_stmt*,int);
const (char *)sqlite3_column_table_name(sqlite3_stmt*,int);
const (char *)sqlite3_column_origin_name(sqlite3_stmt*,int);
}

2348
web.d Normal file

File diff suppressed because it is too large Load diff