WONKY



LOG | FILES | OVERVIEW


#ifndef GALIB_C
#define GALIB_C GALIB_C

#if defined(GALIB_ALL)

/*this is the list of all the options that galib takes*/

	#define GALIB_ALL
	#define GALIB_REGEX
	#define GALIB_CURL /*-lcurl*/
	#define GALIB_POSIX
	#define GALIB_WEB
	#define GALIB_SYSTEM
	#define GALIB_TIDY /*-ltidy*/
	#define GALIB_RESOLV /*-lresolv*/
	#define GALIB_XML /*-lxml2*/
	#define GALIB_MYSQL /*-lmysqlclient*/
	#define GALIB_OPENSSL /*-lcrypto*/
#endif

#ifdef GALIB_SYSTEM
	#define GALIB_POSIX
	#define GALIB_REGEX
#endif

#ifdef GALIB_WEB
	#define GALIB_CURL
	#define GALIB_TIDY
	#define GALIB_RESOLV
#endif

#ifdef GALIB_RSS
	#define GALIB_CURL
	#define GALIB_XML
#endif

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include <limits.h>
#include <stdarg.h>
#include <math.h>

#ifdef GALIB_REGEX
	#include <regex.h>
#endif

#ifdef GALIB_POSIX
	#include <sys/types.h>
	#include <sys/wait.h>
	#include <unistd.h>
#endif

#ifdef GALIB_CURL
	#include <curl/curl.h> /*curl-config --libs --cflags*/
#endif

#ifdef GALIB_TIDY
	#include <tidy/tidy.h>
	#include <tidy/tidybuffio.h>
#endif

#ifdef GALIB_RESOLV
	#include <netinet/in.h>
	//#include <arpa/nameserver.h>
	#include <resolv.h>
#endif

#ifdef GALIB_XML
	#include <libxml/parser.h> /*xml2-config --libs --cflags*/
	#include <libxml/tree.h>
#endif

#ifdef GALIB_MYSQL
	#include <mysql.h> /*mysql_config --include --libs ; -lmysqlclient*/
#endif

#ifdef GALIB_OPENSSL
	#include <openssl/evp.h>
	#include <openssl/rand.h>
#endif

#ifndef ga_malloc
	#define ga_malloc(x) malloc(x)
#endif
#ifndef ga_free
	#define ga_free(x) free(x)
#endif
#ifndef ga_realloc
	#define ga_realloc realloc
#endif
#ifndef ga_abort
	#define ga_abort abort
#endif
#ifndef ga_exit
	#define ga_exit exit
#endif

/*use this to tag pointers to ga_arrays*/
#define _garray

/*
	TODO:
		openssl stuff
		sqlite / etc ...
		general graph stuff
		utf8 stuff
		command line argument parsing
		signal stuff
		seccomp
		ssh
		dns stuff
		fuzzing stuff
		tor stuff
		torrent stuff
		gdb stuff
		image magick
		str_explode with regex
		gnuplot/general plotting stuff (over points mostly)
		opencl stuff
		curl user agent randomisation
		str to ascii art 
		sdl stuff
		opengl stuff
		game 2d stuff
		game 3d stuff
		elf file parser
		ncurses stuff
		change uint32_t to glyph_t in string operations
*/



typedef int ga_hash_t;
typedef unsigned long ga_usek_t;

enum ga__scanformat_modifier
{
	GA__MOD_NONE,
	GA__MOD_SHORT,
	GA__MOD_SHORT_SHORT,
	GA__MOD_LONG,
	GA__MOD_LONG_LONG,
	GA__MOD_LONG_DOUBLE,
	GA__MOD_INTMAX,
	GA__MOD_SIZE_T,
	GA__MOD_PTRDIFF,
	GA__MOD_END
};
enum ga__scanformat_conversion 
{
	GA__CONVERSION_INT_DECIMAL,
	GA__CONVERSION_INT_UNSIGNED_DECIMAL,
	GA__CONVERSION_INT_OCTAL,
	GA__CONVERSION_INT_HEXADECIMAL,
	GA__CONVERSION_DOUBLE_EXPONENT,
	GA__CONVERSION_DOUBLE_DECIMAL,
	GA__CONVERSION_DOUBLE_HEXADECIMAL,
	GA__CONVERSION_CHAR,
	GA__CONVERSION_CSTRING,
	GA__CONVERSION_POINTER,
	GA__CONVERSION_PERCENT,
	/*custom*/
	/*
	 * prints n bits from memory at addres p starting from bit m
	 * takes argumens p , n , m
	 * */
	GA__CONVERSION_BITS, 
	GA__CONVERSION_END
};
struct ga__scanformat
{
	size_t forward_crawl;
	enum ga__scanformat_modifier modifier;
	enum ga__scanformat_conversion conversion;
	_Bool alternate_form;
};
void ga__parse_scan_format(const char *begining, struct ga__scanformat *destination);

enum ga_stream_type
{
	GA_STREAM_TYPE_OOM,
	GA_STREAM_TYPE_STRING,
	GA_STREAM_TYPE_FILE,
	GA_STREAM_TYPE_SYSTEM,
	GA_STREAM_TYPE_CURL,
	GA_STREAM_TYPE_END
};

struct ga_stream
{
	ssize_t (*read)(void *state,void *dst,size_t num_bytes);
	ssize_t (*write)(void *state,void *src,size_t num_bytes);
	_Bool (*fseek)(void *state,size_t where,int whence);
	_Bool (*eof)(void *state);
	enum ga_stream_type type;

	void *state;
};


ssize_t ga_read(struct ga_stream *s,void *dst,size_t num_bytes);
ssize_t ga_write(struct ga_stream *s,void *src,size_t num_bytes);
ssize_t ga_fseek(struct ga_stream *s,size_t where,int whence);
ssize_t ga_eof(struct ga_stream *s);
int ga_printf(const char *format,...);
int ga_vprintf(const char *format,va_list args);
int ga_fprintf(struct ga_stream *s,const char *format,...);
int ga_vfprintf(struct ga_stream *s,const char *format,va_list args);

void ga__from_scanformat(struct ga__scanformat *fmt,va_list args,struct ga_stream *destination);

ssize_t ga_stream_int_to_decimal(struct ga_stream *s,intmax_t a);
ssize_t ga_stream_uint_to_decimal(struct ga_stream *s,uintmax_t a);
ssize_t ga_stream_uint_to_hexadecimal(struct ga_stream *s,uintmax_t a);
ssize_t ga_stream_uint_to_octal(struct ga_stream *s,uintmax_t a);
ssize_t ga_stream_double_to_decimal(struct ga_stream *s,double d);
ssize_t ga_stream_from_fraction(struct ga_stream *s,uint64_t a,uint64_t b,int precision);


ssize_t ga__FILE_read(void *state,void *dst,size_t num_bytes);
ssize_t ga__FILE_write(void *state,void *src,size_t num_bytes);
_Bool ga__FILE_fseek(void *state,size_t where,int whence);
_Bool ga__FILE_eof(void *state);

ssize_t ga__fail_read(void *state,void *dst,size_t num_bytes);
ssize_t ga__fail_write(void *state,void *src,size_t num_bytes);
_Bool ga__fail_fseek(void *state,size_t where,int whence);
_Bool ga__fail_eof(void *state);

struct ga_stream ga_stream_from_file(FILE *f);

void ga_stream_delete(struct ga_stream *s);


/*generic resizable array stuff*/
#define gar__hash_sneed 0xBABACECA
struct ga_array_internals
{
	size_t size;
	size_t capacity;
	size_t element_size;
	unsigned char bytes[];
};

void* _garray gar_alloc(size_t number_of_elements,size_t element_size);
void* _garray gar_expand(void * _garray arr,size_t expansion_size);
void* _garray gar_resize(void * _garray arr,size_t new_size);
void gar_delete_elements(void * _garray arr,size_t start_index,size_t number_of_elements);
void gar_shrink(void * _garray arr,size_t shrink_size);
void gar_delete(void * _garray arr);
size_t gar_size(void * _garray arr);
size_t gar_last_index(void * _garray arr);
_Bool gar_oom(void * _garray arr);
void _garray * gar_copy(void * _garray arr);
struct ga_array_internals* gar_get_internals(void * _garray arr);
ga_hash_t gar_hash(void * _garray arr);

ga_hash_t ga_hash_bytes(void *bytes,size_t number_of_bytes);



/*text stuff*/
struct ga_str
{
	/*cstring*/
	unsigned char * _garray cs; /*ga_array that is also \0 terminated*/
	size_t number_of_glyphs;
};

struct ga_str_mark
{
	size_t beginning_byte_index;
	size_t size;
};


struct ga_str gas_make();
struct ga_str gas_copy(const struct ga_str str);
struct ga_str gas_read_file(const char *filename);
struct ga_str gas_cstr(const char *str);
struct ga_str gas_fread_line(FILE *in);
struct ga_str gas_fread_input(FILE *in);
size_t gas_number_of_bytes(const struct ga_str str);
size_t gas_recount_glyphs(struct ga_str *str);
size_t gas_length(const struct ga_str str);
_Bool gas_oom(struct ga_str str);

/*get element(unicode) value from index'th glyph, indices starting from 0*/
uint32_t gas_glyph(const struct ga_str str,size_t glyph_index);
/*returns the index of the starting byte of the utf8 encoded glyph_indexed glyph*/
size_t gas_glyph_position(const struct ga_str str,size_t glyph_index);
/*returns index of starting byte of next valid glyph after byte_index'th byte in string*/
size_t gas_following_glyph_position(const struct ga_str str,size_t byte_index);
/* 
 returns index of starting byte of next valid glyph going backwards in string after
 byte_index'th byte in string.
 TODO: currently not implemented and returns byte_index
*/
size_t gas_previous_glyph_position(const struct ga_str str,size_t byte_index);
/*
 returns glyph value of utf8 encoded glyph starting at byte_index'th index'th byte of string
 if utf8 encoded glyph at byte_index'th byte is invalid this function returns 0 
 */
uint32_t gas_glyph_at_position(const struct ga_str str,size_t byte_index);
struct ga_str_mark* gas_mark_lines(const struct ga_str str); 



_Bool gas_push_codepoint(struct ga_str *str,uint32_t ch);
_Bool gas_push_byte(struct ga_str *str,unsigned char ch);
_Bool gas_append(struct ga_str *str,const char *right); /*right is \0 terminated*/
_Bool gas_preppend(struct ga_str *str,char *left); /*left is \0 terminated*/
_Bool gas_insert(struct ga_str *str,char *infix,size_t glyph_index);/*infix is \0 terminated*/
_Bool gas_delete_chars(struct ga_str *str,size_t starting_glyph_index,size_t number_of_glyphs);

_Bool gas_to_file(const struct ga_str str,const char *filename);
struct ga_str gas_read_file(const char *filename);

struct ga_str gas_read_line(FILE *in);
struct ga_str gas_read_input(FILE *in);

_Bool gas_eqal(const struct ga_str a,const struct ga_str b);
_Bool gas_cequal(const struct ga_str a,const char *b);


int gas_printf(struct ga_str *destination,const char *format,...);
int gas_vprintf(struct ga_str *destination,const char *format,va_list args);
void gas__from_scanformat(struct ga__scanformat *fmt,va_list args,struct ga_str *destination);

int gas_sscanf(struct ga_str *source,const char *format,...);
int gas_vsscanf(struct ga_str *source,const char *format,va_list args);

void gas__from_scanformat(struct ga__scanformat *fmt,va_list args,struct ga_str *destination);

void gas_normalize(struct ga_str *str); 

ga_hash_t gas_hash(struct ga_str str);



struct gas__stream_state
{
	struct ga_str *str;
	size_t where;
};

ssize_t gas__stream_read(void *state,void *dst,size_t num_bytes);
ssize_t gas__stream_write(void *state,void *src,size_t num_bytes);
_Bool gas__stream_fseek(void *state,size_t where,int whence);
_Bool gas__stream_eof(void *state);

struct ga_stream gas_stream(struct ga_str *str);
void gas_stream_delete(struct ga_stream *stream);


void gas_delete(struct ga_str *str);

short ga__utf8_get_glyph_size_from_starting_codepoint(unsigned char leading_byte);
short ga__utf8_glyph_size(struct ga_str str,size_t byte_index);
uint32_t ga__utf8_glyph_value(struct ga_str str,size_t byte_index,short glyph_size);
short ga__utf8_codepoint_size(uint32_t codepoint);
void ga__utf8_encode_codepoint(unsigned char *where,uint32_t codepoint,short size);


/*virtual filesystem stuff*/
enum ga_vfs_entry_type
{
	GA_VFS_ENTRY_TYPE_FILE,
	GA_VFS_ENTRY_TYPE_DIRECTORY,
	GA_VFS_ENTRY_TYPE_END
};
struct ga_vfs_entry
{
	enum ga_vfs_entry_type type;
};
struct ga_vfs
{
	struct ga_vfs_entry* (*get_root)();
	struct ga_vfs_entry* (*get_first_child)(struct ga_vfs_entry *parent);
	struct ga_vfs_entry* (*get_next_sibling)(struct ga_vfs_entry *sibling);
	char* (*get_name)(struct ga_vfs_entry *entry);
	struct ga_stream (*open)(struct ga_vfs_entry *entry);
};





#ifdef GALIB_REGEX
void ga_grep_lines(ga_lines_t *lines,char *regex);/*deletes non matching lines*/
_Bool ga_grep_line(char *line,char *regex);/*works with C strings and ga_str_t*/
#endif


/*float = 1 bit sign | 8 bit exponent | 23 bit mantissa*/
_Bool ga_float_is_negative(float f);
int16_t ga_float_get_exponent(float f);
uint32_t ga_float_get_mantissa(float f);
float ga_float_set_sign(float f,_Bool is_negative);
float ga_float_set_exponent(float f,int16_t exponent);
float ga_float_set_base(float f,uint32_t mantissa);

/*double = 1 bit sign | 11 bit exponent | 52 bit mantissa*/
_Bool ga_double_is_negative(double d);
int16_t ga_double_get_exponent(double d);
uint64_t ga_double_get_mantissa(double d);
double ga_double_set_sign(double d,_Bool is_negative);
double ga_double_set_exponent(double d,int16_t exponent);
double ga_double_set_mantissa(double d,uint64_t mantissa);


size_t ga_file_number_of_remaning_bytes(FILE *file);
size_t ga_file_size(FILE *file);
size_t ga_file_number_of_remaining_lines(FILE *file);







/*hash map stuff*/





struct ga__hash_table_underarray_slot
{
	_Bool is_alive:1;
	_Bool is_tombstone:1;
	ga_hash_t hash;
	size_t where_in_array;
};
struct ga_hash_table
{
	size_t used_slots;
	size_t tombstones;

	size_t used_slots_threshold;
	size_t tombstones_threshold;

	size_t number_of_slots;

	_Bool oom;

	struct ga__hash_table_underarray_slot slots[];
};

#define ga__hash_table_init_slot_number 128
#define ga__hash_table_init_slots_thresholds 10
#define ga__hash_table_init_tombstones_thresholds 10


struct ga_hash_table* gah_make();
_Bool gah_oom(struct ga_hash_table *h);
ga_hash_t gah_hash_on_step(ga_hash_t hash,size_t step);
_Bool gah_slot_is_taken(struct ga_hash_table *h,ga_hash_t hash,size_t step);
_Bool gah_slot_is_tombstone(struct ga_hash_table *h,ga_hash_t hash,size_t step);
size_t gah_geti(struct ga_hash_table *h,ga_hash_t hash,size_t step);
size_t gah_get_slot_index(struct ga_hash_table *h,ga_hash_t hash,size_t step);
struct ga_hash_table* gah_expand(struct ga_hash_table *h);
struct ga_hash_table* gah_clean_tombstones(struct ga_hash_table *h);
struct ga_hash_table* gah_push(struct ga_hash_table *h,ga_hash_t hash,size_t step,size_t index);
struct ga_hash_table* gah_remove(struct ga_hash_table *h,ga_hash_t hash,size_t step);
void gah_delete(struct ga_hash_table *h);



struct ga_string_hash_table
{
	struct ga_hash_table *table;
	struct ga_str * _garray keys;
	void * _garray values;
	size_t value_size;
	_Bool oom;
};

struct ga_string_hash_table* gahs_make(size_t value_size);
struct ga_string_hash_table* gahs_push(struct ga_string_hash_table *h,struct ga_str key,void *value);
struct ga_string_hash_table* gahs_remove(struct ga_string_hash_table *h,struct ga_str key);
void* gahs_get(struct ga_string_hash_table *h,struct ga_str key);
_Bool gahs_oom(struct ga_string_hash_table *h);
void gahs_delete(struct ga_string_hash_table *h);


struct ga_memory_hash_table
{
	struct ga_hash_table *table;
	void* _garray keys;
	void* _garray values;
	size_t key_size;
	size_t value_size;
	_Bool oom;
};

struct ga_memory_hash_table* gahm_make(size_t key_size,size_t value_size);
struct ga_memory_hash_table* gahm_push(struct ga_memory_hash_table *h,void* key,void* value);
struct ga_memory_hash_table* gahm_remove(struct ga_memory_hash_table *h,void* key);
void* gahm_get(struct ga_memory_hash_table *h,void* key);
_Bool gahm_oom(struct ga_memory_hash_table *h);
void gahm_delete(struct ga_memory_hash_table *h);



struct ga_list_head
{
	struct ga_list_head *next,*previous;
};

void ga_list_head_init(struct ga_list_head *node);
struct ga_list_head* ga_list_find_first(struct ga_list_head *node);
struct ga_list_head* ga_list_find_last(struct ga_list_head *node);
void ga_list_push_front(struct ga_list_head *list_node,struct ga_list_head *new_node);
void ga_list_delete_node(struct ga_list_head *list_node);
void ga_list_push_after(struct ga_list_head *list_node,struct ga_list_head *new_node);
void ga_list_push_before(struct ga_list_head *list_node,struct ga_list_head *new_node);

struct ga_stack_head
{
	struct ga_stack_head *next;
};
void ga_stack_init(struct ga_stack_head *node);
void ga_stack_push(struct ga_stack_head *top,struct ga_stack_head *new);
struct ga_stack_head* ga_stack_pop(struct ga_stack_head *top);



/*system stuff*/
void ga_die(const char *fmt, ...);


#ifdef GALIB_POSIX



struct ga_child
{
	struct ga_str * _garray args;
	struct ga_stream stdio;
	pid_t pid;
};
struct ga__system_stream_state
{
	int pipe_out;
	int pipe_in;
	_Bool has_cached_byte;
	unsigned char cached_byte;
};
struct ga_child* ga_system(const char *format,...);
_Bool ga_child_executed(struct ga_child *child);
int ga_wait_child(struct ga_child *child);
_Bool ga_wait_child_timed(struct ga_child *child,ga_usek_t usek,int *ret);
void ga_delete_child(struct ga_child *child);

void ga__system_stream_delete(struct ga_stream *s);
ssize_t ga__system_stream_read(void *state,void *dst,size_t num_bytes);
ssize_t ga__system_stream_write(void *state,void *src,size_t num_bytes);
_Bool ga__system_stream_fseek(void *state,size_t where,int whence);
_Bool ga__system_stream_eof(void *state);
_Bool ga_is_executable_present(const char *exe);
#endif


/*internet stuff*/
#ifdef GALIB_CURL

enum ga_curl_type
{
	GA_CURL_TYPE_HTTP_GET,
	GA_CURL_TYPE_HTTP_POST,
	GA_CURL_TYPE_END
};
struct ga_curl
{
	enum ga_curl_type type;
	struct ga_str url;
	CURL *handle;
};

struct ga_curl_http_get
{
	enum ga_curl_type type;
	struct ga_str url;
	CURL *handle;
	CURLM *mhandle;
	struct curl_slist *sent_headers;

	struct ga_stream out;

	struct ga_str * _garray returned_header_names;
	struct ga_str * _garray returned_header_values;

	_Bool still_running;

	unsigned char * _garray buffer;
};


struct ga_curl_http_get* ga_curl_make_http_get_request(const char *url);
_Bool ga_curl_set_add_http_header(struct ga_curl *curl,const char *header_name,const char *header_value);
_Bool ga_curl_set_credentials(struct ga_curl *curl,const char *username,const char *password);
void ga_curl_delete(struct ga_curl *curl);

size_t ga__curl_get_read_callback(void *data,size_t size,size_t nmemb,void *closure);
ssize_t ga__curl_get_read(void *state,void *dst,size_t num_bytes);
ssize_t ga__curl_post_write(void *state,void *src,size_t num_bytes);
_Bool ga__curl_get_fseek(void *state,size_t where,int whence);
_Bool ga__curl_get_eof(void *state);

#endif

enum ga_html_node_type
{
	GA_HTML_NODE_TYPE_TAG,
	GA_HTML_NODE_TYPE_TEXT,
	GA_HTML_NODE_TYPE_END
};
struct ga_html_document
{
	struct ga_html_node * _garray nodes;
};
struct ga_html_node
{
	enum ga_html_node_type type;
	struct ga_str name;
	struct ga_str text;
	struct ga_str * _garray attribute_names;
	struct ga_str * _garray attribute_values;
	struct ga_html_node *parent;
	struct ga_html_node ** _garray children; 
};
struct ga__html_parse_info
{
	unsigned char *buff;
	struct ga_stream *input;
};
void ga_delete_html_document(struct ga_html_document *doc);
void ga_print_html_node(struct ga_stream *out,struct ga_html_node *node);
void ga_print_html_document(struct ga_stream *out,struct ga_html_document *doc);
struct ga_html_node* ga_html_get_path(struct ga_html_node *node,int depth,...);
ssize_t ga_html_attribute_index(struct ga_html_node *node,char *attr_name);
Bool ga__html_parse_eof_helper(void *closure);
int ga__html_parse_get_byte_helper(void *closure);
void ga__html_parse_unget_byte_helper(void *closure,byte bt);
void ga__print_html_node_inner(struct ga_stream *out,struct ga_html_node *node,size_t indent);

#ifdef GALIB_TIDY
#define GALIB__TIDY_PARSE_BUFFER_SIZE 1024
struct ga_html_node* _garray ga__tidy_node_to_ga_node_internal(TidyNode node,TidyDoc doc,struct ga_html_node **where_to_push,struct ga_html_node *parent);
size_t ga__get_number_of_html_nodes(TidyNode node,TidyDoc doc);
struct ga_html_document* _garray ga_html_parse(struct ga_stream *in);
struct ga_html_document* _garray ga__parse_tidy(struct ga_stream *in,_Bool is_xml);
#endif

#if 0

#ifdef GALIB_XML
struct ga_rss_channel_item
{
	ga_str_t title;
	ga_str_t link;
	ga_str_t description;
	ga_str_t author;
};
struct ga_rss_channel
{
	/*these are only the mandatory elements (the other types are really stupid and not needed)*/
	ga_str_t title;
	ga_str_t author;
	ga_str_t description;
	struct ga_rss_channel_item *items; /*ga array of items*/
};
struct ga_html_node* ga_parse_xml(ga_str_t xml);
struct ga_rss_channel* ga_parse_rss_feed(ga_str_t rss);
void ga_delete_rss_feed(struct ga_rss_channel **feed);
#endif


#ifdef GALIB_MYSQL
enum ga_mysql_state
{
	GA_MYSQL_STATE_START,
	GA_MYSQL_STATE_USING_DATABASE,
	GA_MYSQL_STATE_LOADING_ARGUMENTS,
	GA_MYSQL_STATE_ERROR,
	GA_MYSQL_STATE_END
};
struct ga_mysql
{
	enum ga_mysql_state state;
	MYSQL *connection;
	MYSQL_STMT *statement;
	MYSQL_BIND *bind;/*ga_array of binds: holds input*/
	MYSQL_BIND *result_bind; /*ga_array of binds: holds ouput row*/
	_Bool has_remaining_rows;
};

struct ga_mysql* ga_mysql_connect(const char *url,const char *user,const char *pass);
_Bool ga_mysql_use(struct ga_mysql *mysql,const char *database);
_Bool ga_mysql_prepare(struct ga_mysql *mysql,const char *statement);
void ga_mysql_disconnect(struct ga_mysql *mysql);


_Bool ga_mysql_bind_int(struct ga_mysql *mysql,size_t index,int data);
_Bool ga_mysql_bind_double(struct ga_mysql *mysql,size_t index,double data);
_Bool ga_mysql_bind_string(struct ga_mysql *mysql,size_t index,char *data);
_Bool ga_mysql_bind_blob(struct ga_mysql *mysql,size_t index,void *data,size_t data_size);
_Bool ga_mysql_execute(struct ga_mysql *mysql);
_Bool ga_mysql_has_remaining_rows(struct ga_mysql *mysql);
size_t ga_mysql_number_of_remaining_rows(struct ga_mysql *mysql);
_Bool ga_mysql_next_row(struct ga_mysql *mysql);

_Bool ga_mysql_get_int(struct ga_mysql *mysql,size_t index,int *destination);
_Bool ga_mysql_get_double(struct ga_mysql *mysql,size_t index,double *destination);
ga_str_t ga_mysql_get_string(struct ga_mysql *mysql,size_t index);
ga_str_t ga_mysql_get_blob(struct ga_mysql *mysql,size_t index);

_Bool ga_mysql_start_transaction(struct ga_mysql *mysql);
_Bool ga_mysql_end_transaction(struct ga_mysql *mysql);

_Bool ga_mysql_has_errors(struct ga_mysql *mysql);

_Bool ga__mysql_drop_prepared_statement(struct ga_mysql *mysql);
_Bool ga__mysql_reset_args(struct ga_mysql *mysql);
_Bool ga__mysql_reset_arg(MYSQL_BIND *bind,size_t arg_index);
void ga__mysql_clear_stored_results(struct ga_mysql *mysql);
#endif


#ifdef GALIB_OPENSSL
ga_bytes_t ga_openssl_hash(void *bytes,size_t num_bytes,char *hash_name);
ga_bytes_t ga_openssl_get_random_bytes(size_t num_bytes);
void ga_openssl_put_random_bytes(void *where,size_t num_bytes);
#endif

/*json stuff*/
enum ga_json_type
{
	GA_JSON_ARRAY,
	GA_JSON_OBJECT,
	GA_JSON_STRING,
	GA_JSON_FALSE,
	GA_JSON_TRUE,
	GA_JSON_NULL,
	GA_JSON_NUMBER,
	GA_JSON_TYPE_END
};
struct ga_json
{
	enum ga_json_type type;
};
struct ga_json_array
{
	enum ga_json_type type;
	size_t number_of_elements;
	struct ga_json **elements;
};
struct ga_json_object
{
	enum ga_json_type type;
	size_t number_of_attributes;
	char **attribute_names;/*c strings are \0 terminated*/
	struct ga_json **attribute_values;
};
struct ga_json_string
{
	enum ga_json_type type;
	size_t str_len;
	ga_str_t str;
};
struct ga_json_number
{
	enum ga_json_type type;
	long double number;
};


struct ga_json* ga_parse_json(char *json,size_t json_size);
struct ga_json* ga__parse_object_inner(ga_str_t json);





#endif


ssize_t ga_read(struct ga_stream *s,void *dst,size_t num_bytes)
{
	return s->read(s->state,dst,num_bytes);
}
ssize_t ga_write(struct ga_stream *s,void *src,size_t num_bytes)
{
	return s->write(s->state,src,num_bytes);
}
ssize_t ga_fseek(struct ga_stream *s,size_t where,int whence)
{
	return s->fseek(s->state,where,whence);
}
ssize_t ga_eof(struct ga_stream *s)
{
	return s->eof(s->state);
}

int ga_printf(const char *format,...)
{
	va_list args;
	int ret;
	struct ga_stream s=ga_stream_from_file(stdout); 

	va_start(args,format);
	ret=ga_vfprintf(&s,format,args);
	va_end(args);

	fflush(stdout);
	return ret;
}
int ga_vprintf(const char *format,va_list args)
{
	struct ga_stream s=ga_stream_from_file(stdout); 
	return ga_vfprintf(&s,format,args);
}

int ga_fprintf(struct ga_stream *s,const char *format,...)
{
	va_list args;
	int ret;

	va_start(args,format);
	ret=ga_vfprintf(s,format,args);	
	va_end(args);

	return ret;
}
int ga_vfprintf(struct ga_stream *s,const char *format,va_list args)
{
	if(format==NULL || s==NULL)
		return 0;

	struct ga__scanformat fmt;

	for(size_t i=0;format[i]!='\0';)
	{
		if(format[i]=='%')
		{
			ga__parse_scan_format(format+i,&fmt);
			ga__from_scanformat(&fmt,args,s);
			i+=fmt.forward_crawl;
		}else
		{
			ga_write(s,(void*)format+i,1);
			++i;
		}
	}

}

ssize_t ga_stream_int_to_decimal(struct ga_stream *s,intmax_t a)
{
	char hold[2]={0,0};
	ssize_t ret=0;
	ssize_t hret;

	if(a==0)
		return ga_write(s,"0",1);
	else if(a<0)
	{
		ret=ga_write(s,"-",1);
		a=-a;
	}


	while(a)
	{
		hold[0]=a%10+'0';
		hret=ga_write(s,hold,1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
		a/=10;
	}
	return ret;
}
ssize_t ga_stream_uint_to_decimal(struct ga_stream *s,uintmax_t a)
{
	char hold[2]={0,0};
	ssize_t ret=0;
	ssize_t hret;

	if(a==0)
		return ga_write(s,"0",1);

	while(a)
	{
		hold[0]=a%10+'0';
		hret=ga_write(s,hold,1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
		a/=10;
	}
	return ret;
}
ssize_t ga_stream_uint_to_hexadecimal(struct ga_stream *s,uintmax_t a)
{
	char hold[2]={0,0};
	ssize_t ret=0;
	ssize_t hret;
	if(a==0)
		return ga_write(s,"0",1);

	while(a)
	{
		if(a%16<10)
			hold[0]=a%16+'0';
		else
			hold[0]=a%16-10+'A';
		hret=ga_write(s,hold,1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
		a/=16;
	}
	return ret;
}
ssize_t ga_stream_uint_to_octal(struct ga_stream *s,uintmax_t a)
{
	char hold[2]={0,0};
	ssize_t ret=0;
	ssize_t hret;

	if(a==0)
		return ga_write(s,"0",1);

	while(a)
	{
		hold[0]=a%8+'0';
		hret=ga_write(s,hold,1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
		a/=8;
	}
	return ret;
}
ssize_t ga_stream_double_to_decimal(struct ga_stream *s,double d)
{
	int32_t exponent;
	uint64_t mantissa;
	ssize_t ret=0;
	ssize_t hret;

	exponent=ga_double_get_exponent(d)-1023;
	mantissa=ga_double_get_mantissa(d)|0x0010000000000000;


	for(mantissa;(mantissa&1)==0;mantissa>>=1,++exponent);
	exponent-=52;

	if(ga_double_is_negative(d))
	{
		hret=ga_write(s,"-",1);
		if(hret<0)
			return hret;
		else
			ret+=hret;
	}

	if(exponent<0)
	{
		if(-exponent<64)
		{
			hret=ga_stream_from_fraction(s,mantissa,(uint64_t)1<<-exponent,10);
			if(hret<0)
				return hret;
		}else
		{
			hret=ga_stream_uint_to_decimal(s,mantissa);
			if(hret<0)
				return hret;
			else
				ret+=hret;
			hret=ga_write(s,"e",1);
			if(hret<0)
				return -ret;
			else
				ret+=hret;
			hret=ga_stream_int_to_decimal(s,exponent);
			if(hret<0)
				return -ret;
			else
				ret+=hret;
		}
	}else
	{
		hret=ga_stream_uint_to_decimal(s,mantissa);
		if(hret<0)
			return hret;
		else
			ret+=hret;
		hret=ga_write(s,"e",1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;

		hret=ga_stream_int_to_decimal(s,exponent);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
	}


	return ret;

}
ssize_t ga_stream_from_fraction(struct ga_stream *s,uint64_t a,uint64_t b,int precision)
{
	char hold[2]={0,0};
	ssize_t ret=0;
	ssize_t hret;
	if(a==0)
	{
		return ga_write(s,"0",1);
	}else
	{
		hret=ga_stream_int_to_decimal(s,a/b);
		if(hret<0)
			return hret;
		else
			ret+=hret;

		hret=ga_write(s,".",1);
		if(hret<0)
			return -ret;
		else
			ret+=hret;
		a=a%b*10;
		--precision;
		while(precision>=0 && a!=0)
		{
			hold[0]=a/b+'0';
			hret=ga_write(s,hold,1);
			a=(a%b)*10;
			--precision;
		}
	}

	return ret;
}
void ga__from_scanformat(struct ga__scanformat *fmt,va_list args,struct ga_stream *s)
{
	switch(fmt->conversion)
	{
		case GA__CONVERSION_PERCENT:
			ga_write(s,"%",1);
			break;
		case GA__CONVERSION_INT_DECIMAL:
			{
				switch(fmt->modifier)
				{
					case GA__MOD_LONG:
						ga_stream_int_to_decimal(s,va_arg(args,long int));
						break;
					case GA__MOD_LONG_LONG:
						ga_stream_int_to_decimal(s,va_arg(args,long long int));
						break;
					case GA__MOD_INTMAX:
						ga_stream_int_to_decimal(s,va_arg(args,intmax_t));
						break;
					case GA__MOD_SIZE_T:
						ga_stream_int_to_decimal(s,va_arg(args,ssize_t));
						break;
					case GA__MOD_PTRDIFF:
						ga_stream_int_to_decimal(s,va_arg(args,ptrdiff_t));
						break;
					default:
						ga_stream_int_to_decimal(s,va_arg(args,int));
						break;
				}
			}
			break;
		case GA__CONVERSION_INT_OCTAL:
			{
				switch(fmt->modifier)
				{
					case GA__MOD_LONG:
						ga_stream_uint_to_octal(s,va_arg(args,long unsigned int));
						break;
					case GA__MOD_LONG_LONG:
						ga_stream_uint_to_octal(s,va_arg(args,long long unsigned int));
						break;
					case GA__MOD_INTMAX:
						ga_stream_uint_to_octal(s,va_arg(args,uintmax_t));
						break;
					case GA__MOD_SIZE_T:
						ga_stream_uint_to_octal(s,va_arg(args,size_t));
						break;
					case GA__MOD_PTRDIFF:
						ga_stream_uint_to_octal(s,va_arg(args,ptrdiff_t));
						break;
					default:
						ga_stream_uint_to_octal(s,va_arg(args,unsigned int));
						break;
				}
			}
			break;
		case GA__CONVERSION_INT_HEXADECIMAL:
			{
				switch(fmt->modifier)
				{
					case GA__MOD_LONG:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,long unsigned int));
						break;
					case GA__MOD_LONG_LONG:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,long long unsigned int));
						break;
					case GA__MOD_INTMAX:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,uintmax_t));
						break;
					case GA__MOD_SIZE_T:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,size_t));
						break;
					case GA__MOD_PTRDIFF:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,ptrdiff_t));
						break;
					default:
						ga_stream_uint_to_hexadecimal(s,va_arg(args,unsigned int));
						break;
				}
			}
			break;
		case GA__CONVERSION_INT_UNSIGNED_DECIMAL:
			{
				switch(fmt->modifier)
				{
					case GA__MOD_LONG:
						ga_stream_uint_to_decimal(s,va_arg(args,long unsigned int));
						break;
					case GA__MOD_LONG_LONG:
						ga_stream_uint_to_decimal(s,va_arg(args,long long unsigned int));
						break;
					case GA__MOD_INTMAX:
						ga_stream_uint_to_decimal(s,va_arg(args,uintmax_t));
						break;
					case GA__MOD_SIZE_T:
						ga_stream_uint_to_decimal(s,va_arg(args,size_t));
						break;
					case GA__MOD_PTRDIFF:
						ga_stream_uint_to_decimal(s,va_arg(args,ptrdiff_t));
						break;
					default:
						ga_stream_uint_to_decimal(s,va_arg(args,unsigned int));
						break;
				}
			}
			break;
		case GA__CONVERSION_CHAR:
			{
				char a=va_arg(args,int);
				ga_write(s,&a,1);
			}
			break;
		case GA__CONVERSION_DOUBLE_DECIMAL:
			{
				switch(fmt->modifier)
				{
					case GA__MOD_LONG:
					case GA__MOD_LONG_LONG:
						ga_stream_double_to_decimal(s,va_arg(args,long double));
						break;
					default:
						ga_stream_double_to_decimal(s,va_arg(args,double));
						break;
				}
			}
			break;
		case GA__CONVERSION_CSTRING:
			{
				char *st=va_arg(args,char*);
				if(st==NULL)
				{
					ga_write(s,"(null)",sizeof("(null)")-1);
				}else
				{
					size_t l=strlen(st);
					ga_write(s,st,l);
				}
			}
			break;
		case GA__CONVERSION_BITS:
			{
				const size_t start_bit=va_arg(args,size_t);
				const size_t number_of_bits=va_arg(args,size_t);
				const unsigned char *bytes=va_arg(args,unsigned char*);
				size_t i;
				char a;
				for(i=start_bit;i<8 && i<number_of_bits+start_bit; ++i)
				{
					a=(!!(bytes[0]&(1<<(7-i)))+'0');
					ga_write(s,&a,1);
				}
				for(i;i<number_of_bits+start_bit;++i)
				{
					a=(!!(bytes[i/8]&(1<<(7-i%8)))+'0');
					ga_write(s,&a,1);
				}
			}
			break;

	}
}

ssize_t ga__FILE_read(void *state,void *dst,size_t num_bytes)
{
	return fread(dst,1,num_bytes,(FILE*)state);
}
ssize_t ga__FILE_write(void *state,void *src,size_t num_bytes)
{
	return fwrite(src,1,num_bytes,(FILE*)state);
}
_Bool ga__FILE_fseek(void *state,size_t where,int whence)
{
	return !fseek((FILE*)state,where,whence);
}
_Bool ga__FILE_eof(void *state)
{
	return feof((FILE*)state);
}

ssize_t ga__fail_read(void *state,void *dst,size_t num_bytes)
{
	return -1;
}
ssize_t ga__fail_write(void *state,void *src,size_t num_bytes)
{
	return -1;
}
_Bool ga__fail_fseek(void *state,size_t where,int whence)
{
	return 0;
}
_Bool ga__fail_eof(void *state)
{
	return 0;
}
struct ga_stream ga_stream_from_file(FILE *f)
{
	return (struct ga_stream) {
		.read=ga__FILE_read,
		.write=ga__FILE_write,
		.fseek=ga__FILE_fseek,
		.eof=ga__FILE_eof,
		.type=GA_STREAM_TYPE_FILE,
		.state=f

	};
}
void ga_stream_delete(struct ga_stream *s)
{
	switch(s->type)
	{
		case GA_STREAM_TYPE_STRING:
			gas_stream_delete(s);
			break;
		#ifdef GALIB_POSIX
		case GA_STREAM_TYPE_SYSTEM:
			ga__system_stream_delete(s);
			break;
		#endif
	}
}

void* gar_alloc(size_t number_of_elements,size_t element_size)
{
	struct ga_array_internals *internals;
	if(element_size==0)
		return NULL;
	/*
	  allocate one element extra so that ga_array_last_index 
	  can always give an index for valid memory
	 */
	internals=ga_malloc(sizeof(struct ga_array_internals)+element_size*(number_of_elements?number_of_elements:1)); 
	if(internals==NULL)
		return NULL;

	internals->size=number_of_elements;
	internals->capacity=1;

	if((element_size*number_of_elements)/element_size!=number_of_elements)
		internals->element_size=0;
	else
		internals->element_size=element_size;

	return internals->bytes;
}
void* gar_expand(void *arr,size_t expansion_size)
{
	struct ga_array_internals *internals;
	internals=gar_get_internals(arr);	


	if(internals->element_size==0 || expansion_size==0) /*check for oom state or no expansion*/
	{
		return arr;
	}else if(internals->size+expansion_size<=internals->size) /*check for size overflow*/
	{
		internals->element_size=0;/*oom state*/
		return arr;
	}else
	{
		if(internals->size+expansion_size<=internals->capacity) 
		{
			internals->size+=expansion_size;
			return arr;
		}else 
		{
			struct ga_array_internals *new_internals=NULL;
			size_t new_capacity=internals->capacity;	

			while(new_capacity<internals->size+expansion_size)
			{
				/*check for overflow*/
				if( (new_capacity<<1) > new_capacity && 
						(new_capacity<<1)*internals->element_size+sizeof(struct ga_array_internals)
							>
						new_capacity*internals->element_size+sizeof(struct ga_array_internals) 
				) 
				{
					new_capacity<<=1;
				}else
				{
					internals->element_size=0;/*oom state*/
					return arr;
				}
			}
			new_internals=ga_realloc(internals,sizeof(struct ga_array_internals)+new_capacity*internals->element_size);

			if(new_internals==NULL)
			{
				internals->element_size=0; /*oom state*/
				return arr;
			}else
			{
				new_internals->capacity=new_capacity;
				new_internals->size+=expansion_size;
				return new_internals->bytes;
			}
		}

	}
}
void* gar_resize(void *arr,size_t new_size)
{
	struct ga_array_internals *internals;
	internals=gar_get_internals(arr);
	if(new_size>internals->capacity)
	{
		arr=gar_expand(arr,new_size-internals->size);
		internals=gar_get_internals(arr);
	}
	internals->size=new_size;
	return arr;
}
void gar_delete_elements(void *arr,size_t start_index,size_t number_of_elements)
{
	if(arr)
	{
		struct ga_array_internals *internals;
		internals=gar_get_internals(arr);
		if(start_index<internals->size)
		{
			if(number_of_elements>internals->size ||
				start_index>internals->size-number_of_elements ||
				start_index>SIZE_MAX-number_of_elements)
			{
				internals->size=start_index+1;
				return;
			}
			memmove(internals->bytes+start_index*internals->element_size,
					internals->bytes+(start_index+number_of_elements)*internals->element_size,
					(internals->size-start_index-number_of_elements)*internals->element_size);
			internals->size-=number_of_elements;

		}
	}
}
void gar_shrink(void *arr,size_t shrink_size)
{
	struct ga_array_internals *internals;
	internals=gar_get_internals(arr);
	if(internals->size<shrink_size)
		internals->size=0;
	else
		internals->size-=shrink_size;
}
void gar_delete(void *arr)
{
	if(arr)
	{
		struct ga_array_internals *internals;
		internals=gar_get_internals(arr);
		ga_free(internals);
	}
}
size_t gar_size(void *arr)
{
	return gar_get_internals(arr)->size;
}
size_t gar_last_index(void *arr)
{
	struct ga_array_internals *internals;
	internals=gar_get_internals(arr);
	if(internals->size==0)
		return 0;
	else
		return internals->size-1;
}
_Bool gar_oom(void *arr)
{
	if(arr)
		return gar_get_internals(arr)->element_size==0;
	else
		return 1;
}
void* gar_copy(void *arr)
{
	if(arr)
	{
		struct ga_array_internals *internals;
		void* ret_arr;

		internals=gar_get_internals(arr);
		ret_arr=gar_alloc(internals->size,internals->element_size);
		if(!gar_oom(ret_arr))
		{
			memcpy(ret_arr,internals->bytes,internals->size*internals->element_size);
			return ret_arr;
		}else
		{
			return ret_arr;
		}
	}else
	{
		return NULL;
	}
}	
struct ga_array_internals* gar_get_internals(void *arr)
{
	return (struct ga_array_internals*)(arr-offsetof(struct ga_array_internals,bytes));
}

ga_hash_t gar_hash(void * _garray arr)
{
	if(arr==NULL || gar_oom(arr))
		return (ga_hash_t)gar__hash_sneed;
	struct ga_array_internals *internals=gar_get_internals(arr);

	return ga_hash_bytes(arr,internals->size*internals->element_size);
}
ga_hash_t ga_hash_bytes(void *bytes,size_t number_of_bytes)
{
	unsigned char *key;
	static const unsigned int magic = 0x5bd1e995;
	static const int reeeee = 24;
	ga_hash_t hash = gar__hash_sneed ^ number_of_bytes;
	uint32_t hold_word;
	size_t key_size;

	key=bytes;
	key_size=number_of_bytes;

	while(key_size >= 4)
	{
		hold_word = *(uint32_t *)key;
		hold_word *= magic;
		hold_word ^= hold_word >> reeeee;
		hold_word *= magic;
		hash *= magic;
		hash ^= hold_word;
		key += 4;
		key_size -= 4;
	}

	switch(key_size)
	{
		case 3:
			hash ^= key[2] << 16;
		case 2:
			hash ^= key[1] << 8;
		case 1:
			hash ^= key[0];

		hash *= magic;
	}

	hash ^= hash >> 13;
	hash *= magic;
	hash ^= hash >> 15;

	return hash;

}

struct ga_str gas_make()
{
	struct ga_str ret;
	ret.cs=gar_alloc(1,1);
	if(!gar_oom(ret.cs))
	{
		ret.cs[0]='\0';
		ret.number_of_glyphs=1;/*\0 is a glyph*/
	}else
	{
		ret.cs=NULL;
		ret.number_of_glyphs=0;
	}
	return ret; //ret is basically a pointer so there is no problem returning it
}
struct ga_str gas_copy(const struct ga_str str)
{
	struct ga_str ret=(struct ga_str){.cs=gar_copy(str.cs),.number_of_glyphs=str.number_of_glyphs};
	gas_recount_glyphs(&ret);
	return ret;
}
struct ga_str gas_read_file(const char *filename)
{
	if(filename)
	{
		FILE *f;
		f=fopen(filename,"r");
		if(f)
		{
			if(fseek(f,0,SEEK_END))
			{
				fclose(f);
				return gas_cstr("");
			}else
			{
				struct ga_str ret;	
				long int file_size;

				file_size=ftell(f);
				if(file_size==-1)
				{
					fclose(f);
					return gas_cstr("");
				}
				ret.cs=gar_alloc(file_size+1,1);
				if(!gar_oom(ret.cs))
				{
					if(fread(ret.cs,1,file_size,f)!=file_size)
					{
						gar_delete(ret.cs);
						fclose(f);
						return gas_cstr("");
					}
					ret.cs[gar_last_index(ret.cs)]='\0';
					fclose(f);
					ret.number_of_glyphs=1;
					gas_recount_glyphs(&ret);
					return ret;
				}else
				{
					fclose(f);
					return gas_cstr("");
				}
			}

		}else
		{
			return gas_cstr("");
		}
	}else
	{
		return gas_cstr("");
	}
}
struct ga_str gas_read_line(FILE *in)
{
	struct ga_str ret;
	int temp;
	ret=gas_make();
	while( (temp=fgetc(in)) != EOF  && temp!='\n' )
		gas_push_byte(&ret,temp);
	return ret;
}
struct ga_str gas_read_input(FILE *in)
{
	struct ga_str ret;
	int temp;
	ret=gas_make();
	while( (temp=fgetc(in)) != EOF )
		gas_push_byte(&ret,temp);
	return ret;
}
struct ga_str gas_cstr(const char *str)
{
	struct ga_str ret;
	if(str)
	{
		size_t str_len;
		str_len=strlen(str);
		ret.cs=gar_alloc(str_len+1,1);
		ret.number_of_glyphs=1;
		if(!gar_oom(ret.cs))
			memcpy(ret.cs,str,str_len+1);
		gas_recount_glyphs(&ret);
		return ret;
	}else
	{
		ret.cs=NULL;
		ret.number_of_glyphs=0;
	}
	return ret;
}
struct ga_str gas_fread_line(FILE *in)
{
	struct ga_str ret;
	int temp;

	ret=gas_make();

	while((temp=fgetc(in))!=EOF && temp!='\n')
		gas_push_codepoint(&ret,(uint32_t)temp);
	gas_recount_glyphs(&ret);
	return ret;
}
struct ga_str gas_fread_input(FILE *in)
{
	struct ga_str ret;
	int temp;

	ret=gas_make();

	while((temp=fgetc(in))!=EOF)
		gas_push_codepoint(&ret,(uint32_t)temp);
	gas_recount_glyphs(&ret);
	return ret;
}
size_t gas_number_of_bytes(const struct ga_str str)
{
	if(!gar_oom(str.cs))
		return gar_size(str.cs);
	else
		return 0;
}
size_t gas_recount_glyphs(struct ga_str *str)
{
	size_t glyph_count=0;
	for(size_t i=0,j=1;j!=i && i<gas_number_of_bytes(*str);j=i,i=gas_following_glyph_position(*str,i))
	{
		++glyph_count;
	}
	str->number_of_glyphs=glyph_count;
	return glyph_count;
}
size_t gas_length(const struct ga_str str)
{
	return str.number_of_glyphs;
}
_Bool gas_oom(struct ga_str str)
{
	return gar_oom(str.cs);
}
uint32_t gas_glyph(const struct ga_str str,size_t index)
{
	/*get byte index of glyph then extract the value and return it*/
	return gas_glyph_at_position(str,gas_glyph_position(str,index));
}
size_t gas_glyph_position(const struct ga_str str,size_t glyph_index)
{
	size_t byte_index,i;
	/*walk glyphs till we reach glyph_index or run out of bytes*/
	for(byte_index=0,i=0;i<glyph_index;++i,byte_index=gas_following_glyph_position(str,byte_index));
	/*if this could be last byte index in string*/
	return byte_index;
}
size_t gas_following_glyph_position(const struct ga_str str,size_t byte_index)
{
	short glyph_size=0;
	/*skip invalid bytes*/
	while(byte_index<gas_number_of_bytes(str) && (glyph_size=ga__utf8_glyph_size(str,byte_index))==0)
	{
		++byte_index;
	}
	byte_index+=glyph_size;
	return (byte_index<gas_number_of_bytes(str)?byte_index:gar_last_index(str.cs)+1);
}
size_t gas_previous_glyph_position(const struct ga_str str,size_t byte_index)
{
	/*TODO*/
	return byte_index;
}
uint32_t gas_glyph_at_position(const struct ga_str str,size_t byte_index)
{
	short glyph_size;
	glyph_size=ga__utf8_glyph_size(str,byte_index);
	if(glyph_size==0)
		return 0;
	else
		return ga__utf8_glyph_value(str,byte_index,glyph_size);
}
struct ga_str_mark* _garray gas_mark_lines(const struct ga_str str)
{
	struct ga_str_mark *ret;
	size_t last_beginning=0;
	size_t last_size=0;
	uint32_t hold_glyph;
	ret=gar_alloc(0,sizeof(struct ga_str_mark));

	if(gar_oom(ret))
		return ret;

	for(size_t byte_index=0;byte_index<gas_number_of_bytes(str);byte_index=gas_following_glyph_position(str,byte_index))
	{
		hold_glyph=gas_glyph_at_position(str,byte_index);
		if( (hold_glyph==(uint32_t)'\n' || hold_glyph==(uint32_t)'\r') )
		{
			if(last_size>0)
			{
				ret=gar_expand(ret,1);
				ret[gar_last_index(ret)]=(struct ga_str_mark){.beginning_byte_index=last_beginning,.size=last_size};
				last_beginning=byte_index;
				last_size=0;
			}else
			{
				++last_beginning;
			}
		}else
		{
			++last_size;
		}
	}
	if(last_size>0)
	{
		ret=gar_expand(ret,1);
		ret[gar_last_index(ret)]=(struct ga_str_mark){.beginning_byte_index=last_beginning,.size=last_size};
	}
	return ret;
}
_Bool gas_push_codepoint(struct ga_str *str,uint32_t ch)
{
	short codepoint_size;
	
	codepoint_size=ga__utf8_codepoint_size(ch);	
	str->cs=gar_expand(str->cs,codepoint_size);

	if(gar_oom(str->cs))
		return 0;


	str->cs[gar_last_index(str->cs)]='\0';
	ga__utf8_encode_codepoint(str->cs+gar_last_index(str->cs)-codepoint_size,ch,codepoint_size);

	++str->number_of_glyphs;
	return 1;
}
_Bool gas_push_byte(struct ga_str *str,unsigned char ch)
{
	if(str==NULL || gar_oom(str->cs))
		return 0;
	str->cs=gar_expand(str->cs,1);
	if(gar_oom(str->cs))
		return 0;

	str->cs[gar_last_index(str->cs)]='\0';
	str->cs[gar_last_index(str->cs)-1]=ch;
	return 1;
}

_Bool gas_append(struct ga_str *str,const char *right)
{
	if(str==NULL || right==NULL)
		return 0;
		
	size_t right_size;
	right_size=strlen(right);
	str->cs=gar_expand(str->cs,right_size);

	if(gar_oom(str->cs))
		return 0;

	memmove(str->cs+gar_last_index(str->cs)-right_size,right,right_size);
	str->cs[gar_last_index(str->cs)]='\0';

	gas_recount_glyphs(str);
	return 1;

}
_Bool gas_preppend(struct ga_str *str,char *left)
{
	if(str==NULL || left==NULL)
		return 0;
		
	size_t left_size;
	left_size=strlen(left);
	str->cs=gar_expand(str->cs,left_size);

	if(gar_oom(str->cs))
		return 0;

	memmove(str->cs+left_size,str->cs,gas_number_of_bytes(*str)-left_size);
	memmove(str->cs,left,left_size);

	gas_recount_glyphs(str);
	return 1;
}
_Bool gas_insert(struct ga_str *str,char *infix,size_t glyph_index)
{
	if(str==NULL || infix==NULL || glyph_index>gas_length(*str)-1)
		return 0;
		
	size_t infix_size;
	size_t where;
	infix_size=strlen(infix);
	where=gas_glyph_position(*str,glyph_index);
	str->cs=gar_expand(str->cs,infix_size);

	if(gar_oom(str->cs))
		return 0;

	memmove(str->cs+where+infix_size,str->cs+where,gas_number_of_bytes(*str)-where-infix_size);
	memmove(str->cs+where,infix,infix_size);

	gas_recount_glyphs(str);
	return 1;
}
_Bool gas_delete_chars(struct ga_str *str,size_t starting_glyph_index,size_t number_of_glyphs)
{
	if(str==NULL)
		return 0;
	size_t start_offset=gas_glyph_position(*str,starting_glyph_index);
	size_t offset=start_offset;
	for(size_t i=0;i<number_of_glyphs;++i,offset=gas_following_glyph_position(*str,offset));
	if(offset==0 || offset>gas_number_of_bytes(*str))
		return 0;
	memmove(str->cs+start_offset,str->cs+offset,gas_number_of_bytes(*str)-offset+start_offset);
	gar_shrink(str->cs,offset-start_offset);
	gas_recount_glyphs(str);
	return 1;
}

_Bool gas_to_file(const struct ga_str str,const char *filename)
{
	FILE *out;
	if(filename==NULL)
		return 0;
	out=fopen(filename,"w");
	if(out==NULL)
		return 0;

	if(fwrite(str.cs,1,gas_number_of_bytes(str)-1,out)!=gas_number_of_bytes(str)-1)
	{
		fclose(out);
		return 0;
	}

	fclose(out);
	return 1;
}

_Bool gas_seq(const struct ga_str a,const struct ga_str b)
{
	if(gas_number_of_bytes(a)!=gas_number_of_bytes(b))
		return 0;
	return !memcmp(a.cs,b.cs,gas_number_of_bytes(a));
}
_Bool gas_ceq(const struct ga_str a,const char *b)
{
	return !strncmp(a.cs,b,gas_number_of_bytes(a));
}

void ga__parse_scan_format(const char *begining, struct ga__scanformat *destination)
{
	destination->forward_crawl=1;
	destination->modifier=GA__MOD_END;
	destination->conversion=GA__CONVERSION_END;
	destination->alternate_form=0;
	switch(begining[destination->forward_crawl])
	{
		case 'h':
			++destination->forward_crawl;
			if(begining[destination->forward_crawl]=='h')
			{
				++destination->forward_crawl;
				destination->modifier=GA__MOD_SHORT_SHORT;
			}else
			{
				destination->modifier=GA__MOD_SHORT;
			}
			break;
		case 'l':
			++destination->forward_crawl;
			if(begining[destination->forward_crawl]=='l')
			{
				++destination->forward_crawl;
				destination->modifier=GA__MOD_LONG_LONG;
			}else
			{
				destination->modifier=GA__MOD_LONG;
			}
			break;
		case 'L':
			++destination->forward_crawl;
			destination->modifier=GA__MOD_LONG_DOUBLE;
			break;
		case 'j':
			++destination->forward_crawl;
			destination->modifier=GA__MOD_INTMAX;
			break;
		case 'z':
			++destination->forward_crawl;
			destination->modifier=GA__MOD_SIZE_T;
			break;
		case 't':
			++destination->forward_crawl;
			destination->modifier=GA__MOD_PTRDIFF;
			break;
	}
	switch(begining[destination->forward_crawl])
	{
		case 'd':
		case 'i':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_INT_DECIMAL;
			break;
		case 'o':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_INT_OCTAL;
			break;
		case 'u':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_INT_UNSIGNED_DECIMAL;
			break;
		case 'x':
		case 'X':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_INT_HEXADECIMAL;
			break;
		case 'e':
		case 'E':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_DOUBLE_EXPONENT;
			break;
		case 'f':
		case 'F':
		case 'g':
		case 'G':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_DOUBLE_DECIMAL;
			break;
		case 'a':
		case 'A':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_DOUBLE_HEXADECIMAL;
			break;
		case 'c':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_CHAR;
			break;
		case 's':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_CSTRING;
			break;
		case 'p':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_POINTER;
			break;
		case '%':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_PERCENT;
			break;
		case 'b':
			++destination->forward_crawl;
			destination->conversion=GA__CONVERSION_BITS;
			break;

	}
}
int gas_printf(struct ga_str *destination,const char *format,...)
{
	va_list args;
	int ret;

	va_start(args,format);
	ret=gas_vprintf(destination,format,args);	
	va_end(args);

	return ret;
}
void gas__from_scanformat(struct ga__scanformat *fmt,va_list args,struct ga_str *destination)
{
	struct ga_stream s=gas_stream(destination);
	ga_fseek(&s,0,SEEK_END);
	ga__from_scanformat(fmt,args,&s);
	ga_stream_delete(&s);
}
int gas_vprintf(struct ga_str *destination,const char *format,va_list args)
{
	if(format==NULL || destination==NULL)
		return 0;

	struct ga__scanformat fmt;
	struct ga_stream s=gas_stream(destination);
	ga_fseek(&s,0,SEEK_END);

	for(size_t i=0;format[i]!='\0';)
	{
		if(format[i]=='%')
		{
			ga__parse_scan_format(format+i,&fmt);
			ga__from_scanformat(&fmt,args,&s);
			i+=fmt.forward_crawl;
		}else
		{
			ga_write(&s,(void*)format+i,1);
			++i;
		}
	}
	ga_stream_delete(&s);

	return 0;
}

int ga_str_sscanf(struct ga_str *source,const char *format,...);
int ga_str_vsscanf(struct ga_str *source,const char *format,va_list args);


ga_hash_t gas_hash(struct ga_str str)
{
	return gar_hash(str.cs);
}
ssize_t gas__stream_read(void *state,void *dst,size_t num_bytes)
{
	if(state==NULL)
		return -1;
	struct gas__stream_state *s=(struct gas__stream_state*)state;
	uint8_t *d=(uint8_t*)dst;
	size_t i;

	if(gas_oom(*s->str))
		return -1;
	
	for(s->where,i=0;s->where<gar_size(s->str->cs)-1 && i<num_bytes;++i,++s->where)
		d[i]=s->str->cs[i];

	return i;
}
ssize_t gas__stream_write(void *state,void *src,size_t num_bytes)
{
	if(state==NULL)
		return -1;
	struct gas__stream_state *s=(struct gas__stream_state*)state;
	uint8_t *sr=(uint8_t*)src;
	size_t i;

	if(gas_oom(*s->str))
		return -1;

	for(s->where,i=0;s->where<gar_size(s->str->cs)-1 && i<num_bytes;++i,++s->where)
		s->str->cs[i]=sr[i];
	for(i;i<num_bytes && !gas_oom(*s->str);++i,++s->where)
		gas_push_byte(s->str,(unsigned char)sr[i]);
	gas_recount_glyphs(s->str);

	return i;
}
_Bool gas__stream_fseek(void *state,size_t where,int whence)
{
	if(state==NULL)
		return 0;

	struct gas__stream_state *s=(struct gas__stream_state*)state;

	if(gas_oom(*s->str))
		return -1;

	switch(whence)
	{
		case SEEK_SET:
			if(where<gar_size(s->str->cs)-1)
			{
				s->where=where;
				return 1;
			}else
			{
				s->where=gar_size(s->str->cs)-1;
				return 0;
			}
		case SEEK_CUR:
			if(where<gar_size(s->str->cs)-s->where-1)
			{
				s->where+=where;
				return 1;
			}else
			{
				s->where=gar_size(s->str->cs)-1;
				return 0;
			}
		case SEEK_END:
			if(where<gar_size(s->str->cs)-1)
			{
				s->where=gar_size(s->str->cs)-1-where;
				return 1;
			}else
			{
				s->where=gar_size(s->str->cs)-1;
				return 0;
			}
		default:
			return 0;
	}
	return 0;
}
_Bool gas__stream_eof(void *state)
{
	if(state==NULL)
		return 1;

	struct gas__stream_state *s=(struct gas__stream_state*)state;
	return s->where<gar_size(s->str->cs)-1;
}
struct ga_stream gas_stream(struct ga_str *str)
{
	struct gas__stream_state *state=ga_malloc(sizeof(struct gas__stream_state));

	if(state)
	{
		state->str=str;
		state->where=0;
	}
	return (struct ga_stream){
		.read=gas__stream_read,
		.write=gas__stream_write,
		.fseek=gas__stream_fseek,
		.eof=gas__stream_eof,
		.type=GA_STREAM_TYPE_STRING,
		.state=state
	};			
}
void gas_stream_delete(struct ga_stream *stream)
{
	if(stream)
		if(stream->state)
		{
			ga_free(stream->state);
			stream->state=NULL;
		}
}
void gas_delete(struct ga_str *str)
{
	str->number_of_glyphs=0;
	gar_delete(str->cs);
}


short ga__utf8_get_glyph_size_from_starting_codepoint(unsigned char leading_byte)
{
	if(leading_byte<0x7F)
		return 1;
	else if(leading_byte&0xE0==0xC0)
		return 2;
	else if(leading_byte&0xF0==0xE0)
		return 3;
	else if(leading_byte&0xF8==0xF0)
		return 4;
	else if(leading_byte&0xFC==0xF8)
		return 5;
	else if(leading_byte&0xFE==0xFC)
		return 6;
	else
		return 0;
}
short ga__utf8_glyph_size(struct ga_str str,size_t byte_index)
{

	if(str.cs[byte_index]<0x7F) /*we were on top of an ascii char*/
	{
		return 1;
	}else if((str.cs[byte_index]&0xE0) == 0xC0)
	{
		if(byte_index+2<gas_number_of_bytes(str)
				&& (str.cs[byte_index+1]&0xC0)==0x80)
			return 2;
		else
			return 0;
	}else if( (str.cs[byte_index]&0xF0) == 0xE0)
	{
		if(byte_index+3<gas_number_of_bytes(str)
			     && (str.cs[byte_index+1]&0xC0)==0x80 
			     && (str.cs[byte_index+2]&0xC0)==0x80)
			return 3;
		else
			return 0;
	}else if( (str.cs[byte_index]&0xF8) == 0xF0)
	{
		if(byte_index+4<gas_number_of_bytes(str)
			     && (str.cs[byte_index+1]&0xC0)==0x80 
			     && (str.cs[byte_index+2]&0xC0)==0x80 
			     && (str.cs[byte_index+3]&0xC0)==0x80)
			return 4;
		else
			return 0;
	}else if( (str.cs[byte_index]&0xFC) == 0xF8)
	{
		if(byte_index+5<gas_number_of_bytes(str)
			     && (str.cs[byte_index+1]&0xC0)==0x80 
			     && (str.cs[byte_index+2]&0xC0)==0x80 
			     && (str.cs[byte_index+3]&0xC0)==0x80 
			     && (str.cs[byte_index+4]&0xC0)==0x80)
			return byte_index+5;
		else
			++byte_index;
	}else if( (str.cs[byte_index]&0xFE) == 0xFC)
	{
		if(byte_index+5<gas_number_of_bytes(str)
			     && (str.cs[byte_index+1]&0xC0)==0x80 
			     && (str.cs[byte_index+2]&0xC0)==0x80 
			     && (str.cs[byte_index+3]&0xC0)==0x80 
			     && (str.cs[byte_index+4]&0xC0)==0x80 
			     && (str.cs[byte_index+5]&0xC0)==0x80)
			return 6;
		else
			return 0;
	}else
	{
		return 0;
	}
}

uint32_t ga__utf8_glyph_value(struct ga_str str,size_t byte_index,short glyph_size)
{
	uint32_t ret=str.cs[byte_index]&(0xff>>glyph_size);
	for(short i=1;i<glyph_size;++i)
	{
		ret<<=6;
		ret+=(0x3F&str.cs[byte_index+i]);
	}
	return ret;
}
short ga__utf8_codepoint_size(uint32_t codepoint)
{
	if(codepoint<=0x7Fu)
		return 1;
	else if(codepoint<=0x7FFu)
		return 2;
	else if(codepoint<=0xFFFFu)
		return 3;
	else if(codepoint<=0x1FFFFFu)
		return 4;
	else if(codepoint<=0x3FFFFFFu)
		return 5;
	else if(codepoint<=0x7FFFFFFFu)
		return 6;
	else
		return 0;

}
void ga__utf8_encode_codepoint(unsigned char *where,uint32_t codepoint,short size)
{
	if(size==1)
	{
		where[0]=codepoint;
	}else
	{
		where[0]=(0xFF<<(8-size))|(codepoint>>((size-1)*6));
		for(short i=1;i<size;++i)
			where[i]=0x80|((codepoint>>((size-i-1)*6))&0x3F);
	}
}

void ga_die(const char *fmt, ...)
{
	va_list args;
	struct ga_stream s=ga_stream_from_file(stderr); 
	va_start(args,fmt);
	ga_vfprintf(&s,fmt,args);
	va_end(args);
	ga_stream_delete(&s);
	ga_exit(1);
}


#ifdef GALIB_REGEX
void ga_grep_lines(ga_lines_t *lines,char *regex);/*deletes non matching lines*/
_Bool ga_grep_line(char *line,char *regex);/*works with C strings and ga_str_t*/
#endif


/*############################################ TODO REMOVE THIS COMMENT ####################################################*/

struct ga_hash_table* gah_make()
{
	struct ga_hash_table *ret;

	ret=ga_malloc(sizeof(struct ga_hash_table)+ga__hash_table_init_slot_number*sizeof(struct ga__hash_table_underarray_slot));

	if(ret!=NULL)
	{
		ret->used_slots=0;
		ret->tombstones=0;
		ret->used_slots_threshold=ga__hash_table_init_slots_thresholds;
		ret->tombstones_threshold=ga__hash_table_init_tombstones_thresholds;
		ret->number_of_slots=ga__hash_table_init_slot_number;
		ret->oom=0;

		for(size_t i=0;i<ret->number_of_slots;++i)
			ret->slots[i]=(struct ga__hash_table_underarray_slot){.is_alive=0,.is_tombstone=0,.where_in_array=0};
	}

	return ret;
}
_Bool gah_oom(struct ga_hash_table *h)
{
	return (h==NULL) || h->oom;
}
ga_hash_t gah_hash_on_step(ga_hash_t hash,size_t step)
{
	if(step%2)
		return ((~((hash+step)<<1))/3);
	else
		return (((hash-step)<<1)/3);
}
_Bool gah_slot_is_taken(struct ga_hash_table *h,ga_hash_t hash,size_t step)
{
	hash=gah_hash_on_step(hash,step);
	return h->slots[hash%h->number_of_slots].is_alive || h->slots[hash%h->number_of_slots].is_tombstone;
}
_Bool gah_slot_is_tombstone(struct ga_hash_table *h,ga_hash_t hash,size_t step)
{
	hash=gah_hash_on_step(hash,step);
	return h->slots[hash%h->number_of_slots].is_tombstone;
}
size_t gah_geti(struct ga_hash_table *h,ga_hash_t hash,size_t step)
{
	hash=gah_hash_on_step(hash,step);
	return h->slots[hash%h->number_of_slots].where_in_array;
}
size_t gah_get_slot_index(struct ga_hash_table *h,ga_hash_t hash,size_t step)
{
	return gah_hash_on_step(hash,step)%h->number_of_slots;
}
struct ga_hash_table* gah_expand(struct ga_hash_table *h)
{
	struct ga_hash_table *ret;

	if(h->number_of_slots>SIZE_MAX/2-sizeof(struct ga_hash_table))
	{
		h->oom=1;
		return h;
	}

	ret=ga_malloc(sizeof(struct ga_hash_table)+h->number_of_slots*2*sizeof(struct ga__hash_table_underarray_slot));

	if(ret==NULL)
	{
		h->oom=1;
		return h;
	}

	ret->used_slots=h->used_slots;
	ret->tombstones=0;

	ret->used_slots_threshold=h->used_slots_threshold;
	ret->tombstones_threshold=h->tombstones_threshold;

	ret->number_of_slots=h->number_of_slots*2;

	ret->oom=0;

	for(size_t i=0;i<ret->number_of_slots;++i)
		ret->slots[i]=(struct ga__hash_table_underarray_slot){.is_alive=0,.is_tombstone=0,.where_in_array=0};
		
	for(size_t i=0;i<h->number_of_slots;++i)
	{
		if(h->slots[i].is_alive)
		{
			size_t j;
			for(j=0;gah_slot_is_taken(ret,h->slots[i].hash,j);++j);

			j=gah_get_slot_index(ret,h->slots[i].hash,j);
			/*
			 * we assume that distinct element in the previous hash table is unique
			 * so we skip the check for equality (we can't do it even if we want to though)
			 **/

			ret->slots[j].is_alive=1;
			ret->slots[j].hash=h->slots[i].hash;
			ret->slots[j].where_in_array=h->slots[i].where_in_array;
		}
	}

	ga_free(h);
	
	return ret;
}
struct ga_hash_table* gah_clean_tombstones(struct ga_hash_table *h)
{
	struct ga_hash_table *ret;


	ret=ga_malloc(sizeof(struct ga_hash_table)+h->number_of_slots*sizeof(struct ga__hash_table_underarray_slot));

	if(ret==NULL)
	{
		h->oom=1;
		return h;
	}

	ret->used_slots=h->used_slots;
	ret->tombstones=0;

	ret->used_slots_threshold=h->used_slots_threshold;
	ret->tombstones_threshold=h->tombstones_threshold;

	ret->number_of_slots=h->number_of_slots;

	ret->oom=0;

	for(size_t i=0;i<ret->number_of_slots;++i)
		ret->slots[i]=(struct ga__hash_table_underarray_slot){.is_alive=0,.is_tombstone=0,.where_in_array=0};
		
	for(size_t i=0;i<h->number_of_slots;++i)
	{
		if(h->slots[i].is_alive)
		{
			size_t j;
			for(j=0;gah_slot_is_taken(ret,h->slots[i].hash,j);++j);

			j=gah_get_slot_index(ret,h->slots[i].hash,j);
			/*
			 * we assume that distinct element in the previous hash table is unique
			 * so we skip the check for equality (we can't do it even if we want to though)
			 **/

			ret->slots[j].is_alive=1;
			ret->slots[j].hash=h->slots[i].hash;
			ret->slots[j].where_in_array=h->slots[i].where_in_array;
		}
	}

	ga_free(h);
	
	return ret;

}
struct ga_hash_table* gah_push(struct ga_hash_table *h,ga_hash_t hash,size_t step,size_t index)
{
	size_t j;
	if(h->used_slots>=h->number_of_slots/h->used_slots_threshold)
	{
		h=gah_expand(h);
		if(h->oom)
			return h;
		for(j=0;gah_slot_is_taken(h,hash,j);++j);
		j=gah_get_slot_index(h,hash,j);
		h->slots[j].is_alive=1;
		h->slots[j].is_tombstone=0;
		h->slots[j].hash=hash;
		h->slots[j].where_in_array=index;
		h->used_slots++;

		return h;
	}else
	{
		j=gah_get_slot_index(h,hash,step);
		h->slots[j].is_alive=1;
		h->slots[j].is_tombstone=0;
		h->slots[j].hash=hash;
		h->slots[j].where_in_array=index;
		h->used_slots++;
		return h;
	}

}
struct ga_hash_table* gah_remove(struct ga_hash_table *h,ga_hash_t hash,size_t step)
{
	size_t j;
	if(h->tombstones>=h->number_of_slots/h->tombstones_threshold)
	{
		h=gah_clean_tombstones(h);
		if(h->oom)
			return h;
		for(j=0;gah_slot_is_taken(h,hash,j);++j);
		j=gah_get_slot_index(h,hash,j);
		h->slots[j].is_alive=0;
		h->slots[j].is_tombstone=1;
		h->slots[j].where_in_array=0;
		h->tombstones++;
		h->used_slots--;

		return h;

	}else
	{

		j=gah_get_slot_index(h,hash,step);
		h->slots[j].is_alive=0;
		h->slots[j].is_tombstone=1;
		h->slots[j].where_in_array=0;
		h->used_slots--;
		h->tombstones++;
		return h;

	}

}
void gah_delete(struct ga_hash_table *h)
{
	ga_free(h);
}

struct ga_string_hash_table* gahs_make(size_t value_size)
{
	struct ga_string_hash_table *ret;

	if(value_size==0)
		return NULL;

	ret=ga_malloc(sizeof(struct ga_string_hash_table));
	if(ret==NULL)
		return ret;
	ret->table=gah_make();
	ret->keys=gar_alloc(0,sizeof(struct ga_str));
	ret->values=gar_alloc(0,value_size);
	ret->value_size=value_size;
	ret->oom=gar_oom(ret->keys)||gar_oom(ret->values)||gah_oom(ret->table);

	return ret;
}
struct ga_string_hash_table* gahs_push(struct ga_string_hash_table *h,struct ga_str key,void *value)
{


	ga_hash_t hash;
	size_t j,ht;

	if(!h || h->oom)
		return h;

	hash=gas_hash(key);
	ht=h->table->number_of_slots;
	for(j=0;gah_slot_is_taken(h->table,hash,j) && !gas_seq(key,h->keys[gah_geti(h->table,hash,j)]) ;++j)
		if(gah_slot_is_tombstone(h->table,hash,j))
			ht=j;

	if(gah_slot_is_taken(h->table,hash,j))
	{
		ht=gah_geti(h->table,hash,j);
		memcpy(h->values+h->value_size*ht,value,h->value_size);
	}else
	{
		if(ht==h->table->number_of_slots)
			ht=j;

		h->keys=gar_expand(h->keys,1);
		h->values=gar_expand(h->values,1);
		memcpy(h->values+h->value_size*gar_last_index(h->values),value,h->value_size);
		h->keys[gar_last_index(h->keys)]=gas_copy(key);

		h->table=gah_push(h->table,hash,ht,gar_last_index(h->values));

		h->oom=gar_oom(h->keys)||gar_oom(h->values)||gah_oom(h->table);
	}

	return h;
}
struct ga_string_hash_table* gahs_remove(struct ga_string_hash_table *h,struct ga_str key)
{
	ga_hash_t hash;
	size_t j;

	if(!h || h->oom)
		return h;

	hash=gas_hash(key);
	for(j=0;gah_slot_is_taken(h->table,hash,j) && !gas_seq(key,h->keys[gah_geti(h->table,hash,j)]) ;++j);
	
	if(gah_slot_is_taken(h->table,hash,j))
	{
		size_t i;
		i=gah_geti(h->table,hash,j);

		gas_delete(h->keys+i);
		gar_delete_elements(h->keys,i,1);
		gar_delete_elements(h->values,i,1);
		h->table=gah_remove(h->table,hash,j);
		/*it's a bit slow :-(*/
		for(j=0;j<h->table->number_of_slots;++j)
			if(h->table->slots[j].is_alive && h->table->slots[j].where_in_array>i)
				h->table->slots[j].where_in_array--; //shift the indices after the deleted element
	}
	return h;
}
void* gahs_get(struct ga_string_hash_table *h,struct ga_str key)
{
	ga_hash_t hash;
	size_t j;

	if(!h || h->oom)
		return NULL; //technically we can be oom and still extract stuff from the ht, but this is more noticable

	hash=gas_hash(key);
	for(j=0;gah_slot_is_taken(h->table,hash,j) && !gas_seq(key,h->keys[gah_geti(h->table,hash,j)]) ;++j);

	if(gah_slot_is_taken(h->table,hash,j) && gas_seq(key,h->keys[gah_geti(h->table,hash,j)]))
		return h->values+gah_geti(h->table,hash,j)*h->value_size;
	else
		return NULL;
}
_Bool gahs_oom(struct ga_string_hash_table *h)
{
	return h==NULL || h->oom;
}
void gahs_delete(struct ga_string_hash_table *h)
{
	if(h==NULL)
		return;
	if(h->keys)
	{
		for(size_t i=0;i<gar_size(h->keys);++i)
			gas_delete(h->keys+i);
		gar_delete(h->keys);
	}

	if(h->values)
	{
		gar_delete(h->values);
	}

	if(h->table)
	{
		gah_delete(h->table);
	}
	ga_free(h);
}

struct ga_memory_hash_table* gahm_make(size_t key_size,size_t value_size)
{
	struct ga_memory_hash_table *ret;

	if(value_size==0)
		return NULL;

	ret=ga_malloc(sizeof(struct ga_memory_hash_table));
	if(ret==NULL)
		return ret;
	ret->table=gah_make();
	ret->keys=gar_alloc(0,key_size);
	ret->values=gar_alloc(0,value_size);
	ret->key_size=key_size;
	ret->value_size=value_size;
	ret->oom=gar_oom(ret->keys)||gar_oom(ret->values)||gah_oom(ret->table);

	return ret;
}
struct ga_memory_hash_table* gahm_push(struct ga_memory_hash_table *h,void* key,void* value)
{
	ga_hash_t hash;
	size_t j,ht;

	if(!h || h->oom)
		return h;

	hash=ga_hash_bytes(key,h->key_size);
	ht=h->table->number_of_slots;
	for(j=0;gah_slot_is_taken(h->table,hash,j) && memcmp(key,h->keys+h->key_size*gah_geti(h->table,hash,j),h->key_size) ;++j)
		if(gah_slot_is_tombstone(h->table,hash,j))
			ht=j;

	if(gah_slot_is_taken(h->table,hash,j))
	{
		ht=gah_geti(h->table,hash,j);
		memcpy(h->values+h->value_size*ht,value,h->value_size);
	}else
	{
		if(ht==h->table->number_of_slots)
			ht=j;

		h->keys=gar_expand(h->keys,1);
		h->values=gar_expand(h->values,1);
		memcpy(h->values+h->value_size*gar_last_index(h->values),value,h->value_size);
		memcpy(h->keys+h->key_size*gar_last_index(h->keys),key,h->key_size);

		h->table=gah_push(h->table,hash,ht,gar_last_index(h->values));

		h->oom=gar_oom(h->keys)||gar_oom(h->values)||gah_oom(h->table);
	}

	return h;
}
struct ga_memory_hash_table* gahm_remove(struct ga_memory_hash_table *h,void* key)
{

	ga_hash_t hash;
	size_t j;

	if(!h || h->oom)
		return h;

	hash=ga_hash_bytes(key,h->key_size);
	for(j=0;gah_slot_is_taken(h->table,hash,j) && memcmp(key,h->keys+h->key_size*gah_geti(h->table,hash,j),h->key_size) ;++j);
	
	if(gah_slot_is_taken(h->table,hash,j))
	{
		size_t i;
		i=gah_geti(h->table,hash,j);

		gar_delete_elements(h->keys,i,1);
		gar_delete_elements(h->values,i,1);
		h->table=gah_remove(h->table,hash,j);
		/*it's a bit slow :-(*/
		for(j=0;j<h->table->number_of_slots;++j)
			if(h->table->slots[j].is_alive && h->table->slots[j].where_in_array>i)
				h->table->slots[j].where_in_array--; //shift the indices after the deleted element
	}
	return h;
}
void* gahm_get(struct ga_memory_hash_table *h,void* key)
{
	ga_hash_t hash;
	size_t j;

	if(!h || h->oom)
		return NULL; //technically we can be oom and still extract stuff from the ht, but this is more noticable

	hash=ga_hash_bytes(key,h->key_size);
	for(j=0;gah_slot_is_taken(h->table,hash,j) && memcmp(key,h->keys+h->key_size*gah_geti(h->table,hash,j),h->key_size) ;++j);

	if(gah_slot_is_taken(h->table,hash,j) && !memcmp(key,h->keys+h->key_size*gah_geti(h->table,hash,j),h->key_size))
		return h->values+gah_geti(h->table,hash,j)*h->value_size;
	else
		return NULL;
}
_Bool gahm_oom(struct ga_memory_hash_table *h)
{
	return h==NULL || h->oom;
}
void gahm_delete(struct ga_memory_hash_table *h)
{
	if(h==NULL)
		return;
	if(h->keys)
		gar_delete(h->keys);

	if(h->values)
		gar_delete(h->values);

	if(h->table)
		gah_delete(h->table);
	ga_free(h);
}
void ga_list_head_init(struct ga_list_head *node)
{
	node->next=node->previous=NULL;
}
struct ga_list_head* ga_list_find_first(struct ga_list_head *node)
{
	while(node->previous!=NULL)
		node=node->previous;

	return node;
}
struct ga_list_head* ga_list_find_last(struct ga_list_head *node)
{
	while(node->next!=NULL)
		node=node->next;
	return node;
}
void ga_list_push_front(struct ga_list_head *list_node,struct ga_list_head *new_node)
{
	list_node=ga_list_find_first(list_node);
	new_node->next=list_node;
	list_node->previous=new_node;
	new_node->previous=NULL;
}
void ga_list_delete_node(struct ga_list_head *list_node)
{
	if(list_node->previous)
	{
		list_node->previous->next=list_node->next;
		list_node->previous=NULL;
	}
	if(list_node->next)
	{
		list_node->next->previous=list_node->previous;
		list_node->previous=NULL;
	}
}
void ga_list_push_after(struct ga_list_head *list_node,struct ga_list_head *new_node)
{
	new_node->previous=list_node;
	new_node->next=list_node->next;
	if(list_node->next)
		list_node->next->previous=new_node;
	list_node->next=new_node;
}
void ga_list_push_before(struct ga_list_head *list_node,struct ga_list_head *new_node)
{
	new_node->next=list_node;
	new_node->previous=list_node->previous;
	if(list_node->previous)
		list_node->previous->next=new_node;
	list_node->previous=new_node;
}

void ga_stack_init(struct ga_stack_head *node)
{
	node->next=NULL;
}
void ga_stack_push(struct ga_stack_head *top,struct ga_stack_head *new)
{
	new->next=top;
}
struct ga_stack_head* ga_stack_pop(struct ga_stack_head *top)
{
	struct ga_stack_head *ret=top->next;
	top->next=NULL;
	return ret;
}


#ifdef GALIB_CURL

size_t ga__curl_get_read_callback(void *data,size_t size,size_t nmemb,void *closure)
{
	struct ga_curl_http_get *curl=closure;
	size_t hold_last_index=gar_last_index(curl->buffer);
	curl->buffer=gar_expand(curl->buffer,size*nmemb);
	memmove(curl->buffer+hold_last_index,data,nmemb*size);
	return size*nmemb;
}
ssize_t ga__curl_read(void *state,void *dst,size_t num_bytes)
{
	int running_handles;
	struct ga_curl_http_get *curl=state;
	if(curl->still_running)
	{
		
		if(num_bytes<gar_size(curl->buffer))
		{
			memmove(dst,curl->buffer,gar_size(curl->buffer));
			memcpy(curl->buffer,curl->buffer+num_bytes,gar_size(curl->buffer)-num_bytes);
			gar_shrink(curl->buffer,num_bytes);
			return num_bytes;
		}else
		{
			do {
				CURLMcode mc;
				mc=curl_multi_perform(curl->mhandle,&running_handles);
				curl->still_running=running_handles;
				if(!mc && running_handles)
				{
					mc=curl_multi_poll(curl->mhandle,NULL,0,1000,NULL);
				}else
				{
					break;
				}
				if(gar_size(curl->buffer)>=num_bytes)
					break;
			}while(1);

			memmove(dst,curl->buffer,num_bytes);
			memcpy(curl->buffer,curl->buffer+num_bytes,gar_size(curl->buffer)-num_bytes);
			gar_shrink(curl->buffer,num_bytes);

			return num_bytes;
		}


	}else
	{
		if(gar_size(curl->buffer))
		{
			if(num_bytes<=gar_size(curl->buffer))
			{
				memmove(dst,curl->buffer,num_bytes);
				memcpy(curl->buffer,curl->buffer+num_bytes,gar_size(curl->buffer)-num_bytes);
				gar_shrink(curl->buffer,num_bytes);
				return num_bytes;
			}else
			{
				ssize_t hold_size=gar_size(curl->buffer);
				memmove(dst,curl->buffer,hold_size);
				curl->buffer=gar_resize(curl->buffer,0);
				return hold_size;
				
			}
		}else
		{
			return -1;
		}
	}
}
ssize_t ga__curl_write(void *state,void *src,size_t num_bytes);
_Bool ga__curl_fseek(void *state,size_t where,int whence);
_Bool ga__curl_get_eof(void *state)
{
	struct ga_curl_http_get *curl=state;
	return !curl->still_running && !gar_size(curl->buffer);
}

struct ga_curl_http_get* ga_curl_make_http_get_request(const char *url)
{
	struct ga_curl_http_get *ret;

	ret=ga_malloc(sizeof(struct ga_curl_http_get));

	if(ret==NULL)
		return NULL;

	ret->type=GA_CURL_TYPE_HTTP_GET;
	ret->url=gas_cstr(url);
	if(gas_oom(ret->url))
	{
		ga_free(ret);
		return NULL;
	}
	ret->out.read=ga__curl_read;
	ret->out.write=ga__fail_write;
	ret->out.fseek=ga__fail_fseek;
	ret->out.eof=ga__curl_get_eof;
	ret->out.type=GA_STREAM_TYPE_OOM;
	ret->out.state=ret;
	ret->handle=curl_easy_init();
	ret->mhandle=curl_multi_init();
	ret->sent_headers=NULL;
	ret->returned_header_names=gar_alloc(0,sizeof(struct ga_str));
	ret->returned_header_values=gar_alloc(0,sizeof(struct ga_str));
	ret->still_running=1;
	ret->buffer=gar_alloc(0,1);

	if(ret->handle==NULL)
	{
		gas_delete(&ret->url);
		ga_free(ret);
		return NULL;
	}

	curl_multi_add_handle(ret->mhandle,ret->handle);
	curl_easy_setopt(ret->handle,CURLOPT_URL,url);
	curl_easy_setopt(ret->handle,CURLOPT_WRITEFUNCTION,ga__curl_get_read_callback);
	curl_easy_setopt(ret->handle,CURLOPT_WRITEDATA,(void*)ret);

	return ret;
}

_Bool ga_curl_set_add_http_header(struct ga_curl *curl,const char *header_name,const char *header_value)
{
	struct ga_str temp;
	if(!curl || !header_name || (curl->type!=GA_CURL_TYPE_HTTP_POST && curl->type!=GA_CURL_TYPE_HTTP_GET))
		return 0;

	temp=gas_cstr(header_name);
	if(gas_oom(temp))
		return 0;
	gas_append(&temp,header_value);
	if(curl->type==GA_CURL_TYPE_HTTP_GET)
		((struct ga_curl_http_get*)curl)->sent_headers = curl_slist_append(((struct ga_curl_http_get*)curl)->sent_headers, temp.cs);
	gas_delete(&temp);

	return 1;
}
_Bool ga_curl_set_credentials(struct ga_curl *curl,const char *username,const char *password)
{
	if(!curl || !username)
		return 0;
	curl_easy_setopt(curl->handle, CURLOPT_USERNAME, username);
	if(password)
		curl_easy_setopt(curl->handle, CURLOPT_PASSWORD, password);
	return 1;
}

void ga_curl_delete(struct ga_curl *curl)
{

}

#endif

#ifdef GALIB_TIDY
struct ga_html_node* _garray ga__tidy_node_to_ga_node_internal(TidyNode node,TidyDoc doc,struct ga_html_node * _garray * where_to_push,struct ga_html_node *parent)
{
	struct ga_html_node *ret;
	TidyNodeType type;
	type=tidyNodeGetType(node);

	*where_to_push=gar_expand(*where_to_push,1);
	(*where_to_push)[gar_last_index(*where_to_push)].name=gas_make();
	(*where_to_push)[gar_last_index(*where_to_push)].text=gas_make();
	(*where_to_push)[gar_last_index(*where_to_push)].attribute_names=gar_alloc(0,sizeof(struct ga_str));
	(*where_to_push)[gar_last_index(*where_to_push)].attribute_values=gar_alloc(0,sizeof(struct ga_str));
	(*where_to_push)[gar_last_index(*where_to_push)].children=gar_alloc(0,sizeof(struct ga_html_node*));
	ret=*where_to_push+gar_last_index(*where_to_push);
	ret->parent=parent;


	if(type==TidyNode_Text)
	{
		TidyBuffer txt={0};
		tidyNodeGetText(doc,node,&txt);
		struct ga_stream ss;
		ss=gas_stream(&ret->text);
		ga_write(&ss,txt.bp,txt.size-1);

		tidyBufFree(&txt);
		ga_stream_delete(&ss);
		ret->type=GA_HTML_NODE_TYPE_TEXT;
	}else
	{
		TidyAttr it;

		ret->type=GA_HTML_NODE_TYPE_TAG;

		{
			struct ga_stream ss;
			ss=gas_stream(&ret->name);
			ga_fprintf(&ss,"%s",tidyNodeGetName(node));
			ga_stream_delete(&ss);
		}

		for(it=tidyAttrFirst(node);it!=NULL;it=tidyAttrNext(it))
		{
			ret->attribute_names=gar_expand(ret->attribute_names,1);
			ret->attribute_values=gar_expand(ret->attribute_values,1);

			ret->attribute_names[gar_last_index(ret->attribute_names)]=gas_cstr(tidyAttrName(it));
			ret->attribute_values[gar_last_index(ret->attribute_values)]=gas_cstr(tidyAttrValue(it));
		}

		for(TidyNode child=tidyGetChild(node);child!=NULL;child=tidyGetNext(child))
		{
			ret->children=gar_expand(ret->children,1);
			ret->children[gar_last_index(ret->children)]=ga__tidy_node_to_ga_node_internal(child,doc,where_to_push,ret);
		}

	}

	return ret;
}
Bool ga__html_parse_eof_helper(void *closure)
{
	struct ga__html_parse_info *i=closure;
	return gar_size(i->buff)==0 && ga_eof(i->input);
}
int ga__html_parse_get_byte_helper(void *closure)
{
	struct ga__html_parse_info *i=closure;
	if(gar_size(i->buff))
	{
		int ret;
		ret=(int)i->buff[0];
		gar_delete_elements(i->buff,0,1);
		return ret;
	}else
	{
		if(ga_eof(i->input))
			return EOF;
		else
		{
			char ret;
			ga_read(i->input,&ret,1);
			return (int)ret;
		}
	}	
}
void ga__html_parse_unget_byte_helper(void *closure,byte bt)
{
	struct ga__html_parse_info *i=closure;
	i->buff=gar_expand(i->buff,1);
	i->buff[gar_last_index(i->buff)]=bt;
}
size_t ga__get_number_of_html_nodes(TidyNode node,TidyDoc doc)
{
	size_t ret=1;
	for(TidyNode child=tidyGetChild(node);child!=NULL;child=tidyGetNext(child))
	{
		ret+=ga__get_number_of_html_nodes(child,doc);
	}
	return ret;
}
struct ga_html_document* ga_html_parse(struct ga_stream *in)
{
	TidyDoc doc;
	TidyInputSource src;
	struct ga__html_parse_info info;
	struct ga_html_node * _garray ret_nodes=NULL;
	struct ga_html_document *ret;
	ssize_t buff_read_size;
	
	if(!in)
		return NULL;


	if(!in || ga_eof(in))
		return NULL;

	ret_nodes=gar_alloc(0,sizeof(struct ga_html_node));
	if(gar_oom(ret_nodes))
	{
		gar_delete(ret_nodes);
		return NULL;
	}

	doc=tidyCreate();
	tidyOptSetBool(doc,TidyShowWarnings,no);
	tidyOptSetInt(doc,TidyShowErrors,0);
	tidyOptSetBool(doc,TidyShowInfo,no);
	tidyOptSetBool(doc, TidyForceOutput, yes);
	//tidyOptSetBool(doc, TidyXhtmlOut, yes);
	
	info.buff=gar_alloc(0,1);
	info.input=in;

	if(gar_oom(info.buff))
	{
		gar_delete(info.buff);
		gar_delete(ret_nodes);
		return NULL;
	}

	src.eof=ga__html_parse_eof_helper;
	src.getByte=ga__html_parse_get_byte_helper; 
	src.sourceData=&info;
	src.ungetByte=ga__html_parse_unget_byte_helper;

	tidyParseSource(doc,&src);

	gar_delete(info.buff);

	//tidyCleanAndRepair(doc);


	ret_nodes=gar_resize(ret_nodes,ga__get_number_of_html_nodes(tidyGetRoot(doc),doc));
	ret_nodes=gar_resize(ret_nodes,0);
	ga__tidy_node_to_ga_node_internal(tidyGetRoot(doc),doc,&ret_nodes,NULL);

	//tidyBufFree(&buffer); the attached html is freed with ga_delete_array
	//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	tidyRelease(doc);

	ret_nodes->parent=ret_nodes;
	
	ret=ga_malloc(sizeof(*ret));
	if(ret==NULL)
		gar_delete(ret_nodes);
	else
		ret->nodes=ret_nodes;
	return ret;
}
#endif
ssize_t ga_html_attribute_index(struct ga_html_node *node,char *attr_name)
{
	size_t attr_len;
	attr_len=strlen(attr_name);

	if(node)
	{
		for(size_t i=0;i<gar_size(node->attribute_names);++i)
		{
			if(!memcmp(node->attribute_names[i].cs,attr_name,attr_len))
			{
				if(i<gar_size(node->attribute_values))
					return (ssize_t)i;
				else
					return (ssize_t)-1;
			}

		}
	}

	return (ssize_t)-1;
}

void ga_print_html_node(struct ga_stream *out,struct ga_html_node *node)
{
	ga__print_html_node_inner(out,node,0);
}
void ga_print_html_document(struct ga_stream *out,struct ga_html_document *doc)
{
	ga__print_html_node_inner(out,doc->nodes,0);
}
void ga__print_html_node_inner(struct ga_stream *out,struct ga_html_node *node,size_t indent)
{
	if(node)
	{
		if(node->type==GA_HTML_NODE_TYPE_TAG)
		{
			for(size_t i=0;i<indent;++i)
				ga_fprintf(out,"\t");
			ga_fprintf(out,"<%s ",node->name.cs);
			for(size_t i=0;i<gar_size(node->attribute_names);++i)
				ga_fprintf(out,"%s=\"%s\" ",node->attribute_names[i].cs,node->attribute_values[i].cs);
			ga_fprintf(out,">\n");
			for(size_t i=0;i<gar_size(node->children);++i)
				ga__print_html_node_inner(out,node->children[i],indent+1);

			for(size_t i=0;i<indent;++i)
				ga_fprintf(out,"\t");
			ga_fprintf(out,"</%s>\n",node->name.cs);
		}else
		{
			for(size_t i=0;i<indent;++i)
				ga_fprintf(out,"\t");
			ga_fprintf(out,"%s\n",node->text.cs);
		}
	}
}
struct ga_html_node* ga_html_get_path(struct ga_html_node *node,int depth,...)
{
	va_list args;
	int hold_arg;
	va_start(args,depth);

	if(depth>100)/*this prob isn't going to happen*/
		return NULL;

	for(int i=0;i<depth && node;++i)
	{
		hold_arg=va_arg(args,int);

		if(hold_arg<gar_size(node->children))
		{
			node=node->children[hold_arg];
		}else
		{
			node=NULL;
			break;
		}
	}
	
	va_end(args);

	return node;

}
void ga_delete_html_nodes(struct ga_html_node **nodes)
{
}

#ifdef GALIB_XML
static struct ga_html_node* ga__parse_xml_condense(xmlNode *node,struct ga_html_node **where_to_push,struct ga_html_node *parent)
{
	struct ga_html_node *ret;
	ga_push_element(where_to_push,((struct ga_html_node){.name=NULL,.text=NULL,.attribute_names=NULL,.attribute_values=NULL,.children=NULL,.parent=NULL}));
	ret=&ga_last_element(*where_to_push);
	ret->parent=parent;

	if(node->type==XML_TEXT_NODE || node->type==XML_CDATA_SECTION_NODE)
	{
		ret->text=ga_cstr_to_str(node->content);
	}else
	{
		ret->name=ga_cstr_to_str(node->name);
		for(xmlAttr *it=node->properties;it!=NULL;it=it->next)
		{
			ga_push_element(&(ret->attribute_names),ga_cstr_to_str(it->name));
			ga_push_element(&(ret->attribute_values),ga_cstr_to_str(it->children->content));
		}
		for(xmlNode *it=node->children;it!=NULL;it=it->next)
		{
			ga_push_element(&(ret->children),ga__parse_xml_condense(it,where_to_push,ret));
		}
	}
	return ret;

}
static size_t ga__parse_xml_get_node_count(xmlNode *node)
{
	xmlNode *it=NULL;
	size_t ret=0;

	for(it=node;it!=NULL;it=it->next) 
	{
		if(it->type==XML_ELEMENT_NODE) 
			ret+=1+ga__parse_xml_get_node_count(it->children);/*children+parent*/
		else
			ret+=1;
	}

	return ret;

}
struct ga_html_node* ga_parse_xml(ga_str_t xml)
{
	xmlDocPtr doc=NULL;
	xmlNode *root=NULL;
	struct ga_html_node *ret=NULL;
	size_t number_of_nodes;

	LIBXML_TEST_VERSION
	doc=xmlReadMemory(xml,ga_str_size(xml), "noname.xml", NULL, 0);

	if(doc)
	{
		root=xmlDocGetRootElement(doc);
		number_of_nodes=ga__parse_xml_get_node_count(root);

		ga_resize_array(&ret,number_of_nodes);
		ga_resize_array(&ret,0);
		ga__parse_xml_condense(root,&ret,ret);
	}


	xmlFreeDoc(doc);
	xmlCleanupParser();

	return ret;
}
static ga_str_t ga__parse_rss_children_to_str(struct ga_html_node *parent)
{
	ga_str_t ret=NULL;
	for(size_t i=0;i<ga_array_size(parent->children);++i)
	{
		ga_str_append_str(&ret,ga_html_node_to_str(parent->children[i]));
	}
	return ret;

}
static void ga__parse_rss_channel_item(struct ga_html_node *item_node,struct ga_rss_channel_item **where_to_push)
{
	struct ga_rss_channel_item hold_item={.title=NULL,.link=NULL,.description=NULL,.author=NULL};
	for(size_t i=0;i<ga_array_size(item_node->children);++i)
	{
		if(ga_str_eq(item_node->children[i]->name,"title"))
		{
			if(hold_item.title)
				ga_delete_str(&hold_item.title);
			hold_item.title=ga__parse_rss_children_to_str(item_node->children[i]);
		}
		else if(ga_str_eq(item_node->children[i]->name,"author"))
		{
			if(hold_item.author)
				ga_delete_str(&hold_item.author);
			hold_item.author=ga__parse_rss_children_to_str(item_node->children[i]);
		}else if(ga_str_eq(item_node->children[i]->name,"description"))
		{
			if(hold_item.description)
				ga_delete_str(&hold_item.description);
			hold_item.description=ga__parse_rss_children_to_str(item_node->children[i]);
		}else if(ga_str_eq(item_node->children[i]->name,"link"))
		{
			if(hold_item.link)
				ga_delete_str(&hold_item.link);
			hold_item.link=ga__parse_rss_children_to_str(item_node->children[i]);
		}else if(ga_str_eq(item_node->children[i]->name,"group"))/*youtube stuff*/
		{
			for(size_t j=0;j<ga_array_size(item_node->children[i]->children);++j)
			{

				if(ga_str_eq(item_node->children[i]->children[j]->name,"title"))
				{
					if(hold_item.title)
						ga_delete_str(&hold_item.title);
					hold_item.title=ga__parse_rss_children_to_str(item_node->children[i]);
				}
				else if(ga_str_eq(item_node->children[i]->children[j]->name,"content"))
				{
					if(hold_item.link)
						ga_delete_str(&hold_item.link);
					hold_item.link=ga_get_value_of_attribute(item_node->children[i]->children[j],"url");

				}else if(ga_str_eq(item_node->children[i]->children[j]->name,"description"))
				{
					if(hold_item.description)
						ga_delete_str(&hold_item.description);
					hold_item.description=ga__parse_rss_children_to_str(item_node->children[i]->children[j]);
				}
			}
		}
	}
	ga_push_element(where_to_push,hold_item);
}
static void ga__parse_rss_channel(struct ga_html_node *channel_node,struct ga_rss_channel **where_to_push)
{
	struct ga_rss_channel hold_channel={.title=NULL,.description=NULL,.author=NULL,.items=NULL};
	
	for(size_t i=0;i<ga_array_size(channel_node->children);++i)
	{
		if(ga_str_eq(channel_node->children[i]->name,"title"))
		{
			if(hold_channel.title)
				ga_delete_str(&hold_channel.title);
			hold_channel.title=ga__parse_rss_children_to_str(channel_node->children[i]);
		}
		else if(ga_str_eq(channel_node->children[i]->name,"author"))
		{
			if(hold_channel.author)
				ga_delete_str(&hold_channel.author);
			hold_channel.author=ga__parse_rss_children_to_str(channel_node->children[i]);
		}else if(ga_str_eq(channel_node->children[i]->name,"description"))
		{
			if(hold_channel.description)
				ga_delete_str(&hold_channel.description);
			hold_channel.description=ga__parse_rss_children_to_str(channel_node->children[i]);
		}else if(
			ga_str_eq(channel_node->children[i]->name,"item")
			|| ga_str_eq(channel_node->children[i]->name,"entry")
			)
		{
			ga__parse_rss_channel_item(channel_node->children[i],&(hold_channel.items));
		}
	}
	ga_push_element(where_to_push,hold_channel);
}
struct ga_rss_channel* ga_parse_rss_feed(ga_str_t rss)
{
	struct ga_html_node *hold_nodes;
	struct ga_html_node *rss_node=NULL;
	struct ga_html_node *feed_node=NULL;
	struct ga_rss_channel *ret=NULL;
	size_t i;

	hold_nodes=ga_parse_xml(rss);

	for(i=0;i<ga_array_size(hold_nodes);++i)
	{
		if(ga_str_eq(hold_nodes[i].name,"rss"))
		{
			rss_node=hold_nodes+i;
			break;
		}else if(ga_str_eq(hold_nodes[i].name,"feed"))
		{
			feed_node=hold_nodes+i;
			break;
		}

	}

	if(rss_node)
	{
		for(i=0;i<ga_array_size(rss_node->children);++i)
		{
			if(ga_str_eq(rss_node->children[i]->name,"channel"))
				ga__parse_rss_channel(rss_node->children[i],&ret);
		}
	}else if(feed_node)
	{
		ga__parse_rss_channel(feed_node,&ret);
	}


	ga_delete_html_nodes(&hold_nodes);

	return ret;
}
void ga_delete_rss_feed(struct ga_rss_channel **feed)
{
	for(size_t i=0;i<ga_array_size(*feed);++i)
	{
		ga_delete_array(&((*feed)[i].items));
	}

	ga_delete_array(feed);
}

#endif


#ifdef GALIB_MYSQL

struct ga_mysql* ga_mysql_connect(const char *url,const char *user,const char *pass)
{
	struct ga_mysql *ret=NULL;

	ret=ga_malloc(sizeof(struct ga_mysql));

	if(ret==NULL)
		return NULL;
	ret->connection=mysql_init(NULL);
	ret->state=GA_MYSQL_STATE_START;
	ret->statement=NULL;
	ret->bind=NULL;
	ret->result_bind=NULL;
	ret->has_remaining_rows=0;

	if(ret->connection==NULL)
	{
		ga_free(ret);
		return NULL;
	}

	if(mysql_real_connect(ret->connection,url,user,pass,NULL,0,NULL,0)==NULL) 
	{
		mysql_close(ret->connection);
		ga_free(ret);
		return NULL;
	}

	return ret;
}
_Bool ga_mysql_use(struct ga_mysql *mysql,const char *database)
{
	if(mysql && mysql->state!=GA_MYSQL_STATE_ERROR)
	{
		if(mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS)
		{
			if(!ga__mysql_drop_prepared_statement(mysql))
			{
				mysql->state=GA_MYSQL_STATE_ERROR;
				mysql->has_remaining_rows=0;
				return 0;
			}
		}

		if(mysql_select_db(mysql->connection,database))
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}else
		{
			mysql->state=GA_MYSQL_STATE_USING_DATABASE;
			return 1;
		}
	}else
	{
		return 0;
	}

}
_Bool ga_mysql_prepare(struct ga_mysql *mysql,const char *statement)
{
	if(statement && mysql && (mysql->state==GA_MYSQL_STATE_USING_DATABASE || mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS))
	{
		if(mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS)
		{
			if(!ga__mysql_drop_prepared_statement(mysql))
			{
				mysql->state=GA_MYSQL_STATE_ERROR;
				mysql->has_remaining_rows=0;
				return 0;
			}
		}

		mysql->statement=mysql_stmt_init(mysql->connection);
		if(mysql->statement==NULL)
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}else
		{
			size_t statement_len;
			size_t num_param=0;
			statement_len=strnlen(statement,1000);

			if(mysql_stmt_prepare(mysql->statement,statement,statement_len))
			{
				mysql->state=GA_MYSQL_STATE_ERROR;
				mysql->has_remaining_rows=0;
				return 0;
			}else
			{
				mysql->state=GA_MYSQL_STATE_LOADING_ARGUMENTS;
				mysql_free_result(mysql_stmt_result_metadata(mysql->statement));
				
				for(size_t i=0;i<statement_len;++i)
					if(statement[i]=='?')
						++num_param;

				ga_resize_array(&(mysql->bind),num_param);
				ga_resize_array(&(mysql->result_bind),mysql_stmt_field_count(mysql->statement));
				if(mysql->bind==NULL || mysql->result_bind==NULL)
				{
					mysql->state=GA_MYSQL_STATE_ERROR;
					mysql->has_remaining_rows=0;
					return 0;
				}


				memset(mysql->bind,0,num_param*sizeof(mysql->bind[0]));
				memset(mysql->result_bind,0,ga_array_size(mysql->result_bind)*sizeof(mysql->bind[0]));
				return 1;
			}
		}

	}else
	{
		if(mysql)
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
		}
		return 0;
	}

}
_Bool ga__mysql_drop_prepared_statement(struct ga_mysql *mysql)
{
	if(mysql)
	{
		ga__mysql_reset_args(mysql);
		if(mysql->statement)
			mysql_stmt_close(mysql->statement);
		ga_delete_array(&mysql->bind);
		ga_delete_array(&mysql->result_bind);
		mysql->bind=NULL;
		mysql->result_bind=NULL;
		mysql->has_remaining_rows=0;
		return 1;
	}else
	{
		return 0;
	}
}
void ga_mysql_disconnect(struct ga_mysql *mysql)
{
	ga__mysql_drop_prepared_statement(mysql);
	mysql_close(mysql->connection);
	mysql->state=GA_MYSQL_STATE_END;
}
_Bool ga_mysql_bind_int(struct ga_mysql *mysql,size_t index,int data)
{
	int *gimp;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && mysql->bind && index<ga_array_size(mysql->bind) )
	{
		ga__mysql_reset_arg(mysql->bind,index);/*reset data if there was any in*/
		gimp=ga_malloc(sizeof(int));
		if(gimp)
		{
			*gimp=data;
			mysql->bind[index].buffer=gimp;
			mysql->bind[index].buffer_type=MYSQL_TYPE_LONG;
			mysql->bind[index].buffer_length=sizeof(int);
			mysql->bind[index].length=&(mysql->bind[index].buffer_length);
			mysql->bind[index].is_unsigned=0;
			return 1;
		}else
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}
	}else
	{
		mysql->state=GA_MYSQL_STATE_ERROR;
		mysql->has_remaining_rows=0;
		return 0;
	}
}
_Bool ga_mysql_bind_double(struct ga_mysql *mysql,size_t index,double data)
{
	double *gimp;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && mysql->bind && index<ga_array_size(mysql->bind) )
	{
		ga__mysql_reset_arg(mysql->bind,index);/*reset data if there was any in*/
		gimp=ga_malloc(sizeof(double));
		if(gimp)
		{
			*gimp=data;
			mysql->bind[index].buffer=gimp;
			mysql->bind[index].buffer_type=MYSQL_TYPE_DOUBLE;
			mysql->bind[index].buffer_length=sizeof(double);
			mysql->bind[index].length=&(mysql->bind[index].buffer_length);
			mysql->bind[index].is_unsigned=0;
			return 1;
		}else
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}
	}else
	{
		mysql->state=GA_MYSQL_STATE_ERROR;
		mysql->has_remaining_rows=0;
		return 0;
	}
}

_Bool ga_mysql_bind_string(struct ga_mysql *mysql,size_t index,char *data)
{
	ga_str_t gimp;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && mysql->bind && index<ga_array_size(mysql->bind) )
	{
		ga__mysql_reset_arg(mysql->bind,index);/*reset data if there was any in*/
		gimp=ga_cstr_to_str(data);
		if(gimp)
		{
			mysql->bind[index].buffer=gimp;
			mysql->bind[index].buffer_type=MYSQL_TYPE_STRING;
			mysql->bind[index].buffer_length=ga_str_size(gimp);
			mysql->bind[index].length=&(mysql->bind[index].buffer_length);
			mysql->bind[index].is_unsigned=0;
			return 1;
		}else
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}
	}else
	{
		mysql->state=GA_MYSQL_STATE_ERROR;
		mysql->has_remaining_rows=0;
		return 0;
	}
}
_Bool ga_mysql_bind_blob(struct ga_mysql *mysql,size_t index,void *data,size_t data_size)
{
	ga_str_t gimp=NULL;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && mysql->bind && index<ga_array_size(mysql->bind) )
	{
		ga__mysql_reset_arg(mysql->bind,index);/*reset data if there was any in*/
		ga_resize_str(&gimp,data_size);
		if(gimp)
		{
			memcpy(gimp,data,data_size);
			mysql->bind[index].buffer=gimp;
			mysql->bind[index].buffer_type=MYSQL_TYPE_STRING;
			mysql->bind[index].buffer_length=ga_str_size(gimp);
			mysql->bind[index].length=&(mysql->bind[index].buffer_length);
			mysql->bind[index].is_unsigned=0;
			return 1;
		}else
		{
			mysql->state=GA_MYSQL_STATE_ERROR;
			mysql->has_remaining_rows=0;
			return 0;
		}
	}else
	{
		mysql->state=GA_MYSQL_STATE_ERROR;
		mysql->has_remaining_rows=0;
		return 0;
	}
}
_Bool ga_mysql_execute(struct ga_mysql *mysql)
{
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS)
	{
		_Bool ret=1;

		ret=!mysql_stmt_bind_param(mysql->statement,mysql->bind);
		ret=!mysql_stmt_bind_result(mysql->statement,mysql->result_bind) && ret;

		ret=!mysql_stmt_execute(mysql->statement);
		if(ret && mysql_stmt_fetch(mysql->statement)!=1)
		{
			mysql->has_remaining_rows=mysql_stmt_num_rows(mysql->statement);
			ret=ga__mysql_reset_args(mysql) && ret;
			return ret;
		}else
		{
			return 0;
		}
	}else
	{
		return 0;
	}
}
_Bool ga_mysql_has_remaining_rows(struct ga_mysql *mysql)
{
	return mysql->has_remaining_rows;
}
_Bool ga_mysql_next_row(struct ga_mysql *mysql)
{
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && mysql->result_bind)
	{
		switch(mysql_stmt_fetch(mysql->statement))
		{
			case 0:
			case MYSQL_DATA_TRUNCATED:
				mysql->has_remaining_rows=1;
				return 1;
			case 1:
				mysql->has_remaining_rows=0;
				return 0;
			case MYSQL_NO_DATA:
				mysql->has_remaining_rows=0;
				return 1;
		}
		return 0;
	}else
	{
		mysql->state=GA_MYSQL_STATE_ERROR;
		mysql->has_remaining_rows=0;
		return 0;
	}
}
_Bool ga__mysql_reset_args(struct ga_mysql *mysql)
{
	if(mysql && mysql->state!=GA_MYSQL_STATE_ERROR)
	{
		for(size_t i=0;i<ga_array_size(mysql->bind);++i)
			if(!ga__mysql_reset_arg(mysql->bind,i))
				return 0;

		return 1;
	}else
	{
		return 0;
	}
}
_Bool ga__mysql_reset_arg(MYSQL_BIND *bind,size_t arg_index)
{
	if(bind[arg_index].buffer)
	{
		switch(bind[arg_index].buffer_type)
		{
			case MYSQL_TYPE_SHORT:
			case MYSQL_TYPE_LONG:
			case MYSQL_TYPE_LONGLONG:
			case MYSQL_TYPE_FLOAT:
			case MYSQL_TYPE_DOUBLE:
			case MYSQL_TYPE_TINY:
				ga_free(bind[arg_index].buffer);
				break;
			case MYSQL_TYPE_STRING:
			case MYSQL_TYPE_BLOB:
				ga_delete_str((ga_str_t *)&(bind[arg_index].buffer));
				break;
			case MYSQL_TYPE_NULL:
				break;
			default:
				return 0;
		}
	}
	memset(bind+arg_index,0,sizeof(bind[arg_index]));
	return 1;
}
_Bool ga_mysql_get_int(struct ga_mysql *mysql,size_t index,int *destination)
{
	_Bool ret;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && index<ga_array_size(mysql->result_bind) )
	{
		mysql->result_bind[index].buffer_type=MYSQL_TYPE_LONG;
		mysql->result_bind[index].buffer=destination;
		mysql->result_bind[index].buffer_length=sizeof(int);
		ret=!mysql_stmt_fetch_column(mysql->statement,mysql->result_bind+index,index,0);
		mysql->result_bind[index].buffer=NULL;
		return ret;
	}else
	{
		return 0;
	}
}
_Bool ga_mysql_get_double(struct ga_mysql *mysql,size_t index,double *destination)
{
	_Bool ret;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && index<ga_array_size(mysql->result_bind) )
	{
		mysql->result_bind[index].buffer_type=MYSQL_TYPE_DOUBLE;
		mysql->result_bind[index].buffer=destination;
		mysql->result_bind[index].buffer_length=sizeof(double);
		ret=!mysql_stmt_fetch_column(mysql->statement,mysql->result_bind+index,index,0);
		mysql->result_bind[index].buffer=NULL;
		return ret;
	}else
	{
		return 0;
	}
}
ga_str_t ga__mysql_get_str_blob(struct ga_mysql *mysql,size_t index,int type)
{
	ga_str_t ret=NULL;
	if(mysql && mysql->state==GA_MYSQL_STATE_LOADING_ARGUMENTS && index<ga_array_size(mysql->result_bind) )
	{
		unsigned long int real_number_of_bytes=0;
		unsigned char byte;


		mysql->result_bind[index].buffer_type=type;
		mysql->result_bind[index].buffer=&byte;
		/*horribly inefficient? Prob buffered in lib*/
		mysql->result_bind[index].buffer_length=1;
		mysql->result_bind[index].length=&real_number_of_bytes;
		/*hacky loop*/
		for(unsigned long offset=0;!mysql_stmt_fetch_column(mysql->statement,mysql->result_bind+index,index,offset) && offset<real_number_of_bytes;++offset)
		{
			ga_str_push_char(&ret,byte);
		}
		mysql->result_bind[index].buffer=NULL;
		mysql->result_bind[index].buffer_length=0;
		mysql->result_bind[index].length=NULL;
		return ret;
	}else
	{
		return NULL;
	}
}
ga_str_t ga_mysql_get_string(struct ga_mysql *mysql,size_t index)
{
	return ga__mysql_get_str_blob(mysql,index,MYSQL_TYPE_STRING);
}
ga_str_t ga_mysql_get_blob(struct ga_mysql *mysql,size_t index)
{
	return ga__mysql_get_str_blob(mysql,index,MYSQL_TYPE_BLOB);
}
_Bool ga_mysql_start_transaction(struct ga_mysql *mysql)
{
	if(mysql && mysql->connection && mysql->state!=GA_MYSQL_STATE_ERROR)
	{
		if(!mysql_query(mysql->connection,"START TRANSACTION"))
			return 1;
		else
			return 0;
	}else
	{
		return 0;
	}
}
_Bool ga_mysql_end_transaction(struct ga_mysql *mysql)
{
	if(mysql && mysql->connection && mysql->state!=GA_MYSQL_STATE_ERROR)
	{
		if(!mysql_query(mysql->connection,"COMMIT"))
			return 1;
		else
			return 0;
	}else
	{
		return 0;
	}
}
_Bool ga_mysql_has_errors(struct ga_mysql *mysql)
{
	return mysql==NULL || mysql->state==GA_MYSQL_STATE_ERROR || mysql->state>=GA_MYSQL_STATE_END;
}
#endif

#ifdef GALIB_OPENSSL
ga_bytes_t ga_openssl_hash(void *bytes,size_t num_bytes,char *hash_name)
{

	EVP_MD_CTX *mdctx;
	const EVP_MD *md;
	ga_bytes_t ret=NULL;
	unsigned int ret_len=0;


	md=EVP_get_digestbyname(hash_name);
	if(md==NULL)
		return NULL;

	ga_resize_array(&ret,EVP_MAX_MD_SIZE);

	if(ret)
	{
		mdctx=EVP_MD_CTX_new();
		EVP_DigestInit_ex(mdctx, md, NULL);
		EVP_DigestUpdate(mdctx, bytes, num_bytes);
		EVP_DigestFinal_ex(mdctx, ret, &ret_len);
		EVP_MD_CTX_free(mdctx);

		ga_resize_array(&ret,ret_len);
	}

	return ret;

}
ga_bytes_t ga_openssl_get_random_bytes(size_t num_bytes)
{
	ga_bytes_t ret=NULL;
	ga_resize_array(&ret,num_bytes);

	if(ret)
	{
		RAND_priv_bytes(ret,ga_array_size(ret));
	}

	return ret;
}
void ga_openssl_put_random_bytes(void *where,size_t num_bytes)
{
	RAND_priv_bytes(where,num_bytes);
}
#endif

#if 0
/*json must not be NULL*/
size_t ga__parse_skip_white_space(char *json,size_t json_size)
{
	size_t i;
	for(i=0;i<json_size && (json[i]==' ' || json[i]=='\t' || json[i]=='\n' || json[i]=='\r');++i);
	
	return i;
}
struct ga_json* ga_parse_json(char *json,size_t json_size)
{
	if(json)
	{
		if(json[0]=='\0')
		{
			return NULL;
		}else if(json[0]=='{')
		{
			return ga__parse_object_inner(
		}else if(json[0]=='[')
		{

		}else if(json[0]
	}else
	{
		return NULL;
	}
}
#endif
#endif

/*uint32_t takes care of endianness*/
_Bool ga_float_is_negative(float f)
{
	return (*((uint32_t*)(void*)&f))&0x80000000u;
}

int16_t ga_float_get_exponent(float f)
{
	return (((*((uint32_t*)(void*)&f))&0x7F800000u)>>23);
}
uint32_t ga_float_get_mantissa(float f)
{
	return (*((uint32_t*)(void*)&f))&0x007FFFFFu;
}
float ga_float_set_sign(float f,_Bool is_negative)
{
	uint32_t *as_uint=(void*)&f;
	if(is_negative)
		*as_uint=((*as_uint)|0x80000000u);
	else
		*as_uint=((*as_uint)&0x7FFFFFFFu);
	return *(float*)(void*)as_uint;
}
float ga_float_set_exponent(float f,int16_t exponent)
{
	uint32_t *as_uint=(void*)&f;
	*as_uint=((*as_uint)&0x807FFFFFu)|((0xFu&(uint32_t)(exponent+0x7F))<<23);
	return *(float*)(void*)as_uint;
}
float ga_float_set_base(float f,uint32_t base)
{
	uint32_t *as_uint=(void*)&f;
	*as_uint=((*as_uint)&0xFF800000u)|((0x007FFFFFu)&base);
	return *(float*)(void*)as_uint;
}
/*uint64_t takes care of endianness*/
_Bool ga_double_is_negative(double d)
{
	return (*((uint64_t*)(void*)&d))&0x8000000000000000ull;
}
int16_t ga_double_get_exponent(double d)
{
	return ((*((uint64_t*)(void*)&d))&0x7FF0000000000000ull)>>52;
}
uint64_t ga_double_get_mantissa(double d)
{
	return (*((uint64_t*)(void*)&d))&0x000FFFFFFFFFFFFFull;
}
double ga_double_set_sign(double d,_Bool is_negative)
{
	uint64_t *as_uint=(void*)&d;
	if(is_negative)
		*as_uint=((*as_uint)|0x8000000000000000ull);
	else
		*as_uint=((*as_uint)&0x7FFFFFFFFFFFFFFFull);
	return *(double*)(void*)as_uint;
}
double ga_double_set_exponent(double d,int16_t exponent)
{
	uint64_t *as_uint=(void*)&d;
	*as_uint=((*as_uint)&0x800FFFFFFFFFFFFFull)|((0x7Fu&(uint64_t)exponent)<<52);
	return *(double*)(void*)as_uint;
}
double ga_double_set_mantissa(double d,uint64_t mantissa)
{
	uint64_t *as_uint=(void*)&d;
	*as_uint=((*as_uint)&0xFFF0000000000000ull)|((0x000FFFFFFFFFFFFFull)&mantissa);
	return *(double*)(void*)as_uint;
}


#ifdef GALIB_POSIX
struct ga_child* ga_system(const char *format,...)
{
	struct ga_str * _garray args;
	char * _garray * _garray arguments_raw;
	struct ga__scanformat fmt;
	pid_t pid;
	int pipe_in[2],pipe_out[2];
	va_list vargs;
	struct ga_child *ret;

	va_start(vargs,format);

	ret=ga_malloc(sizeof(struct ga_child));
	if(ret==NULL)
	{
		va_end(vargs);
		return NULL;
	}
	ret->stdio.state=ga_malloc(sizeof(struct ga__system_stream_state));
	args=gar_alloc(1,sizeof(struct ga_str));
	if(gar_oom(args) || ret->stdio.state==NULL)
	{
		ga_free(ret);
		va_end(vargs);
		return NULL;
	}
	args[0]=gas_make();

	for(size_t i=0;format[i]!='\0';)
	{
		if(format[i]=='%')
		{
			ga__parse_scan_format(format+i,&fmt);
			gas__from_scanformat(&fmt,vargs,args+gar_last_index(args));
			i+=fmt.forward_crawl;
		}else if(format[i]==' ' || format[i]=='\t' || format[i]=='\n' || format[i]=='\r')
		{
			if(gas_length(args[gar_last_index(args)])!=1) // null is one glyph
			{
				args=gar_expand(args,1);
				args[gar_last_index(args)]=gas_make();
			}
			++i;
		}else
		{
			gas_push_codepoint(args+gar_last_index(args),(uint32_t)format[i]);
			++i;
		}
	}
	
	va_end(vargs);

	arguments_raw=gar_alloc(gar_size(args)+1,sizeof(char *));

	if(gas_ceq(args[0],"") || gar_oom(args) || gar_oom(arguments_raw))
	{
		for(size_t i=0;i<gar_size(args);++i)
			gas_delete(args+i);
		gar_delete(arguments_raw);
		ga_free(ret->stdio.state);
		ga_free(ret);
		return NULL;
	}
	
	for(size_t i=0;i<gar_size(args);++i)
		arguments_raw[i]=(char*)args[i].cs;
	arguments_raw[gar_last_index(arguments_raw)]=NULL;

	pipe(pipe_in);
	pipe(pipe_out);

	pid=fork();
	if(pid==0)
	{
		close(pipe_out[0]);
		close(pipe_in[1]);
		close(0);
		dup(pipe_in[0]);
		close(1);
		dup(pipe_out[1]);
		close(2);
		dup(pipe_out[1]);
		execvp(arguments_raw[0],arguments_raw);
		exit(1);
	}
	close(pipe_in[0]);
	close(pipe_out[1]);

	ret->pid=pid;
	ret->args=args;

	ret->stdio.write=ga__system_stream_write;
	ret->stdio.read=ga__system_stream_read;
	ret->stdio.fseek=ga__system_stream_fseek;
	ret->stdio.eof=ga__system_stream_eof;
	ret->stdio.type=GA_STREAM_TYPE_SYSTEM;
	struct ga__system_stream_state *s=(struct ga__system_stream_state*)ret->stdio.state;
	s->pipe_in=pipe_out[0];
	s->pipe_out=pipe_in[1];
	s->has_cached_byte=0;
	s->cached_byte=0;


	gar_delete(arguments_raw);
	return ret;
}
void ga__system_stream_delete(struct ga_stream *s)
{

	struct ga__system_stream_state *ss=(struct ga__system_stream_state*)s->state;
	close(ss->pipe_in);
	close(ss->pipe_out);
	ga_free(s->state);
}
ssize_t ga__system_stream_read(void *state,void *dst,size_t num_bytes)
{
	struct ga__system_stream_state *s=(struct ga__system_stream_state*)state;
	if(num_bytes && s->has_cached_byte)
	{
		((unsigned char*)dst)[0]=s->cached_byte;
		s->has_cached_byte=0;
		--num_bytes;
	}
	return 1+read(s->pipe_in,dst+1,num_bytes);
}
ssize_t ga__system_stream_write(void *state,void *src,size_t num_bytes)
{
	struct ga__system_stream_state *s=(struct ga__system_stream_state*)state;
	return write(s->pipe_out,src,num_bytes);
}
_Bool ga__system_stream_fseek(void *state,size_t where,int whence)
{
	return 0;
}
_Bool ga__system_stream_eof(void *state)
{
	struct ga__system_stream_state *s=(struct ga__system_stream_state*)state;
	if(s->has_cached_byte)
		return 1;
	else
		return !(s->has_cached_byte=read(s->pipe_in,&s->cached_byte,1));
}
_Bool ga_is_executable_present(const char *exe)
{
	struct ga_child *hold;
	int ret;
	hold=ga_system("test %s",exe);
	ret=ga_wait_child(hold);
	ga_delete_child(hold);

	return !ret;
}
int ga_wait_child(struct ga_child *child)
{
	int ret=0;
	if(child->pid!=0)
	       waitpid(child->pid,&ret,0);
	return ret;
}
_Bool ga_wait_child_timed(struct ga_child *child,ga_usek_t usek,int *ret)
{

}
void ga_delete_child(struct ga_child *child)
{
	ga_stream_delete(&child->stdio);
	for(size_t i=0;i<gar_size(child->args);++i)
		gas_delete(child->args+i);
	gar_delete(child->args);
	ga_free(child);
}
#endif