char* sl_iconv(sl_vm_t* vm, char* input_string, size_t input_length, char* from_encoding, char* to_encoding, size_t* output_length) { iconv_t cd = iconv_open(to_encoding, from_encoding); if(cd == (iconv_t)(-1)) { sl_throw_message2(vm, vm->lib.EncodingError, "Unknown encoding"); } char* in_buff = input_string; size_t in_bytes_left = input_length; size_t out_bytes_left = in_bytes_left * 4 + 15; size_t out_cap = out_bytes_left; char* out_buff = sl_alloc_buffer(vm->arena, out_cap); char* retn_out_buff = out_buff; while(1) { size_t ret = iconv(cd, &in_buff, &in_bytes_left, &out_buff, &out_bytes_left); if(ret != (size_t)(-1)) { break; } if(errno == E2BIG) { out_bytes_left = input_length; out_cap += input_length; out_buff = sl_realloc(vm->arena, out_buff, out_cap); continue; } if(errno == EILSEQ || errno == EINVAL) { iconv_close(cd); sl_throw_message2(vm, vm->lib.EncodingError, "Invalid encoding in source buffer"); } break; } iconv_close(cd); *output_length = out_cap - out_bytes_left; return retn_out_buff; }
static void ensure_utf8(sl_vm_t* vm, uint8_t* buff, size_t buff_len) { if(!sl_is_valid_utf8(buff, buff_len)) { sl_throw_message2(vm, vm->lib.EncodingError, "Invalid UTF-8"); } }
static void sl_mysql_stmt_check_error(sl_vm_t* vm, MYSQL_STMT* stmt) { const char* cerr = mysql_stmt_error(stmt); if(!cerr[0]) { return; } sl_throw_message2(vm, vm->store[cMySQL_Error], (char*)cerr); }
static gcrypt_algorithm_t* get_algo_check(sl_vm_t* vm, SLVAL obj) { gcrypt_algorithm_t* ptr = get_algo(vm, obj); if(ptr->algo == 0) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid GCrypt::Algorithm"); } return ptr; }
static SLVAL method_apply(sl_vm_t* vm, SLVAL method, size_t argc, SLVAL* argv) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't apply uninitialized Method"); } return sl_apply_method(vm, argv[0], methp, argc - 1, argv + 1); }
static SLVAL method_apply(sl_vm_t* vm, SLVAL method, size_t argc, SLVAL* argv) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!methp->initialized) { sl_throw_message2(vm, vm->lib.TypeError, "Can't apply uninitialized Method"); } return sl_apply_method(vm, argv[0], methp, argc - 1, argv + 1); }
static sl_regexp_t* get_regexp_check(sl_vm_t* vm, SLVAL rev) { sl_regexp_t* re = get_regexp(vm, rev); if(!re->re) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid operation on uninitialized regexp"); } return re; }
static SLVAL bound_method_call(sl_vm_t* vm, SLVAL bmethod, size_t argc, SLVAL* argv) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(bmethod); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't call uninitialized BoundMethod"); } return sl_apply_method(vm, methp->extra->bound_self, methp, argc, argv); }
static mysql_t* get_mysql(sl_vm_t* vm, SLVAL obj) { mysql_t* mysql = sl_data_get_ptr(vm, &mysql_data_type, obj); if(!mysql->valid) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid MySQL instance"); } return mysql; }
static mysql_stmt_t* get_mysql_stmt(sl_vm_t* vm, SLVAL obj) { mysql_stmt_t* stmt = sl_data_get_ptr(vm, &mysql_stmt_data_type, obj); if(!stmt->stmt) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid operation on uninitialized MySQL::Statement instance"); } return stmt; }
static SLVAL bound_method_call(sl_vm_t* vm, SLVAL bmethod, size_t argc, SLVAL* argv) { sl_bound_method_t* bmethp = (sl_bound_method_t*)sl_get_ptr(bmethod); if(!bmethp->method.initialized) { sl_throw_message2(vm, vm->lib.TypeError, "Can't call uninitialized BoundMethod"); } return sl_apply_method(vm, bmethp->self, &bmethp->method, argc, argv); }
static void check_pcre_error(sl_vm_t* vm, int rc) { if(rc == PCRE_ERROR_BADUTF8) { sl_throw_message2(vm, vm->lib.EncodingError, "Invalid UTF-8 in regular expression or match text"); } if(rc < 0) { sl_error(vm, vm->lib.Error, "PCRE error (%d)", rc); } }
static SLVAL range_enumerator_current(sl_vm_t* vm, SLVAL self) { sl_range_enumerator_t* range_enum = get_range_enumerator(vm, self); check_range_enumerator(vm, range_enum); if(range_enum->state != ES_ITERATING) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid operation on Range::Enumerator"); } return range_enum->current; }
static void check_range_enumerator(sl_vm_t* vm, sl_range_enumerator_t* range_enum) { if(sl_responds_to2(vm, range_enum->current, vm->id.succ)) { if(sl_responds_to2(vm, range_enum->current, range_enum->method)) { return; } } sl_throw_message2(vm, vm->lib.TypeError, "Uniterable type in range"); }
static sl_regexp_match_t* get_regexp_match(sl_vm_t* vm, SLVAL matchv) { sl_regexp_match_t* match; sl_expect(vm, matchv, vm->lib.Regexp_Match); match = (sl_regexp_match_t*)sl_get_ptr(matchv); if(!match->re) { sl_throw_message2(vm, vm->lib.TypeError, "Invalid Regexp::Match"); } return match; }
SLVAL sl_method_bind(sl_vm_t* vm, SLVAL method, SLVAL receiver) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); if(!(methp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't bind uninitialized Method"); } sl_method_t* bmethp = method_dup(vm, methp); bmethp->extra->bound_self = sl_expect(vm, receiver, methp->extra->klass); bmethp->base.klass = vm->lib.BoundMethod; return sl_make_ptr((sl_object_t*)bmethp); }
static SLVAL bound_method_unbind(sl_vm_t* vm, SLVAL bmethod) { sl_method_t* bmethp = (sl_method_t*)sl_get_ptr(bmethod); if(!(bmethp->base.user_flags & SL_FLAG_METHOD_INITIALIZED)) { sl_throw_message2(vm, vm->lib.TypeError, "Can't unbind uninitalized BoundMethod"); } sl_method_t* methp = method_dup(vm, bmethp); methp->extra->bound_self = vm->lib.nil; methp->base.klass = vm->lib.Method; return sl_make_ptr((sl_object_t*)methp); }
static void sl_setup_regexp(sl_vm_t* vm, sl_regexp_t* re_ptr, uint8_t* re_buff, size_t re_len, uint8_t* opts_buff, size_t opts_len) { char buff[256]; const char* error; char* rez; int error_offset; int opts = DEFAULT_OPTIONS; pcre* re; size_t i; for(i = 0; i < opts_len; i++) { switch(opts_buff[i]) { case 'i': opts |= PCRE_CASELESS; break; case 'x': opts |= PCRE_EXTENDED; break; default: sprintf(buff, "Unknown regular expression option '%c'", opts_buff[i]); sl_throw_message2(vm, vm->lib.ArgumentError, buff); } } if(memchr(re_buff, 0, re_len)) { sl_throw_message2(vm, vm->lib.ArgumentError, "Regular expression contains null byte"); } rez = sl_alloc_buffer(vm->arena, re_len + 1); memcpy(rez, re_buff, re_len); rez[re_len] = 0; re = pcre_compile(rez, opts, &error, &error_offset, NULL); if(!re) { sl_throw_message2(vm, vm->lib.SyntaxError, (char*)error); } re_ptr->source = sl_make_string(vm, re_buff, re_len); re_ptr->options = opts; re_ptr->re = re; re_ptr->study = NULL; }
SLVAL sl_regexp_match(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { sl_regexp_t* re = get_regexp_check(vm, self); sl_string_t* str = sl_get_string(vm, argv[0]); int offset = 0, rc, ncaps; int* caps; char err_buff[256]; sl_regexp_match_t* match; if(argc > 1) { offset = sl_get_int(sl_expect(vm, argv[1], vm->lib.Int)); } offset = sl_string_byte_offset_for_index(vm, argv[0], offset); if(offset < 0) { return vm->lib.nil; } pcre_fullinfo(re->re, re->study, PCRE_INFO_CAPTURECOUNT, &ncaps); ncaps += 1; ncaps *= 3; caps = sl_alloc(vm->arena, sizeof(int) * ncaps); rc = pcre_exec(re->re, re->study, (char*)str->buff, str->buff_len, offset, PCRE_NEWLINE_LF, caps, ncaps); if(rc < 0) { if(rc == PCRE_ERROR_NOMATCH) { return vm->lib.nil; } if(rc == PCRE_ERROR_BADUTF8) { sl_throw_message2(vm, vm->lib.EncodingError, "Invalid UTF-8 in regular expression or match text"); } sprintf(err_buff, "PCRE error (%d)", rc); sl_throw_message2(vm, vm->lib.Error, err_buff); } match = (sl_regexp_match_t*)sl_get_ptr(sl_allocate(vm, vm->lib.Regexp_Match)); match->re = re; match->match_string = argv[0]; match->capture_count = ncaps / 3; match->captures = caps; return sl_make_ptr((sl_object_t*)match); }
SLVAL sl_string_times(sl_vm_t* vm, SLVAL self, SLVAL other) { sl_string_t* str = sl_get_string(vm, self); long mul = sl_get_int(sl_expect(vm, other, vm->lib.Int)); if(mul == 0) { return sl_make_cstring(vm, ""); } if(mul < 0) { sl_throw_message2(vm, vm->lib.ArgumentError, "String multiplier must be positive"); } if(mul > 0 && (size_t)LONG_MAX / mul < str->buff_len) { sl_throw_message2(vm, vm->lib.ArgumentError, "String multiplier is too big"); } sl_string_t* new_str = sl_get_string(vm, sl_make_string(vm, NULL, 0)); new_str->buff_len = str->buff_len * mul; new_str->buff = sl_alloc(vm->arena, new_str->buff_len); for(size_t i = 0; i < new_str->buff_len; i += str->buff_len) { memcpy(new_str->buff + i, str->buff, str->buff_len); } new_str->char_len = str->char_len * mul; return sl_make_ptr((sl_object_t*)new_str); }
static SLVAL sl_regexp_init(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { sl_regexp_t* re_ptr = get_regexp(vm, self); sl_string_t* re = sl_get_string(vm, argv[0]); sl_string_t* opts; if(argc > 1) { opts = sl_get_string(vm, argv[1]); } else { opts = sl_cstring(vm, ""); } if(re_ptr->re) { sl_throw_message2(vm, vm->lib.TypeError, "Cannot reinitialize already initialized Regexp"); } sl_setup_regexp(vm, re_ptr, re->buff, re->buff_len, opts->buff, opts->buff_len); return self; }
static SLVAL string_range_index(sl_vm_t* vm, SLVAL self, SLVAL range) { sl_string_t* str = sl_get_string(vm, self); SLVAL lowerv = sl_range_lower(vm, range); SLVAL upperv = sl_range_upper(vm, range); if(!sl_is_a(vm, lowerv, vm->lib.Int) || !sl_is_a(vm, upperv, vm->lib.Int)) { sl_throw_message2(vm, vm->lib.TypeError, "Expected range of integers"); } long lower = sl_get_int(lowerv), upper = sl_get_int(upperv); if(lower < 0) { lower += str->char_len; } if(lower < 0 || (size_t)lower >= str->char_len) { return sl_make_cstring(vm, ""); } if(upper < 0) { upper += str->char_len; } if(upper < 0) { return sl_make_cstring(vm, ""); } if(sl_range_is_exclusive(vm, range)) { upper--; } if(upper < lower) { return sl_make_cstring(vm, ""); } uint8_t* begin_ptr = str->buff; uint8_t* end_ptr; size_t len = str->buff_len; long idx = 0; while(idx < lower && len) { idx++; sl_utf8_each_char(vm, &begin_ptr, &len); } end_ptr = begin_ptr; while(lower <= upper) { lower++; sl_utf8_each_char(vm, &end_ptr, &len); } return sl_make_string(vm, begin_ptr, (size_t)end_ptr - (size_t)begin_ptr); }
static void sl_json_parse_check_error(sl_vm_t* vm, sl_string_t* str, json_parse_t* json, yajl_status status) { uint8_t* err_str; SLVAL err; if(status == yajl_status_client_canceled) { /* the only reason we'd cancel the parse is if the data structre is too deep */ yajl_free(json->yajl); sl_throw_message2(vm, vm->store[cJSON_ParseError], "JSON structure recurses too deep"); } if(status == yajl_status_error) { err_str = yajl_get_error(json->yajl, 0, (const uint8_t*)str->buff, (size_t)str->buff_len); err = sl_make_cstring(vm, (char*)err_str); yajl_free_error(json->yajl, err_str); yajl_free(json->yajl); sl_throw(vm, sl_make_error2(vm, vm->store[cJSON_ParseError], err)); } }
SLVAL sl_method_bind(sl_vm_t* vm, SLVAL method, SLVAL receiver) { sl_method_t* methp = (sl_method_t*)sl_get_ptr(method); sl_bound_method_t* bmethp = (sl_bound_method_t*)sl_get_ptr(sl_allocate(vm, vm->lib.BoundMethod)); if(!methp->initialized) { sl_throw_message2(vm, vm->lib.TypeError, "Can't bind uninitialized Method"); } bmethp->method.initialized = 1; bmethp->method.name = methp->name; bmethp->method.klass = methp->klass; bmethp->method.is_c_func = methp->is_c_func; bmethp->method.arity = methp->arity; bmethp->method.as = methp->as; bmethp->self = sl_expect(vm, receiver, methp->klass); return sl_make_ptr((sl_object_t*)bmethp); }
static SLVAL enumerable_reduce(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { SLVAL enumerator = sl_send(vm, self, "enumerate", 0); SLVAL acc, val, f; if(argc == 1) { if(!sl_is_truthy(sl_send(vm, enumerator, "next", 0))) { sl_throw_message2(vm, vm->lib.ArgumentError, "Can't reduce empty array without initializer"); } else { acc = sl_send(vm, enumerator, "current", 0); } f = argv[0]; } else { acc = argv[0]; f = argv[1]; } while(sl_is_truthy(sl_send(vm, enumerator, "next", 0))) { val = sl_send(vm, enumerator, "current", 0); acc = sl_send(vm, f, "call", 2, acc, val); } return acc; }
static SLVAL sl_mysql_stmt_execute(sl_vm_t* vm, SLVAL self, size_t argc, SLVAL* argv) { mysql_stmt_t* stmt = get_mysql_stmt(vm, self); size_t req = mysql_stmt_param_count(stmt->stmt); if(argc < req) { char buff[100]; sprintf(buff, "Prepared statement has %lu parameter markers, but only %lu parameters were given", req, argc); sl_throw_message2(vm, vm->lib.ArgumentError, buff); } if(!stmt->bind) { stmt->bind = sl_alloc(vm->arena, sizeof(MYSQL_BIND) * req); } for(size_t i = 0; i < req; i++) { stmt->bind[i].buffer_type = MYSQL_TYPE_STRING; sl_string_t* str = sl_get_string(vm, sl_to_s(vm, argv[i])); stmt->bind[i].buffer = str->buff; stmt->bind[i].buffer_length = str->buff_len; stmt->bind[i].length = NULL; stmt->bind[i].is_null = NULL; stmt->bind[i].is_unsigned = 1; stmt->bind[i].error = NULL; } if(mysql_stmt_bind_param(stmt->stmt, stmt->bind)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } if(mysql_stmt_execute(stmt->stmt)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } MYSQL_RES* res = mysql_stmt_result_metadata(stmt->stmt); if(!res) { /* query did not produce a result set */ return sl_make_int(vm, mysql_stmt_affected_rows(stmt->stmt)); } int field_count = mysql_stmt_field_count(stmt->stmt); MYSQL_FIELD* field; SLVAL field_names[field_count]; enum enum_field_types field_types[field_count]; size_t field_i = 0; while((field = mysql_fetch_field(res))) { field_names[field_i] = sl_make_cstring(vm, field->name); if(field->type == MYSQL_TYPE_LONG || field->type == MYSQL_TYPE_SHORT || field->type == MYSQL_TYPE_TINY) { field_types[field_i] = MYSQL_TYPE_LONG; } else { field_types[field_i] = MYSQL_TYPE_STRING; } field_i++; } MYSQL_BIND output_binds[field_count]; my_bool output_errors[field_count]; my_bool output_is_nulls[field_count]; unsigned long output_lengths[field_count]; for(int i = 0; i < field_count; i++) { output_binds[i].buffer_type = MYSQL_TYPE_STRING; output_binds[i].buffer = NULL; output_binds[i].buffer_length = 0; output_binds[i].length = &output_lengths[i]; output_binds[i].is_null = &output_is_nulls[i]; output_binds[i].error = &output_errors[i]; } if(mysql_stmt_bind_result(stmt->stmt, output_binds)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } SLVAL result_rows = sl_make_array(vm, 0, NULL); while(1) { int code = mysql_stmt_fetch(stmt->stmt); if(code == MYSQL_NO_DATA) { break; } if(code == 1) { sl_mysql_stmt_check_error(vm, stmt->stmt); } SLVAL row = sl_make_dict(vm, 0, NULL); for(int i = 0; i < field_count; i++) { MYSQL_BIND cell; cell.length = &output_lengths[i]; cell.is_null = &output_is_nulls[i]; cell.error = &output_errors[i]; cell.buffer_type = field_types[i]; int buffer_long; switch(field_types[i]) { case MYSQL_TYPE_LONG: cell.buffer = &buffer_long; cell.buffer_length = sizeof(buffer_long); break; default: /* MYSQL_TYPE_STRING */ cell.buffer = sl_alloc_buffer(vm->arena, output_lengths[i] + 1); cell.buffer_length = output_lengths[i]; break; } if(mysql_stmt_fetch_column(stmt->stmt, &cell, i, 0)) { sl_mysql_stmt_check_error(vm, stmt->stmt); } switch(field_types[i]) { case MYSQL_TYPE_LONG: sl_dict_set(vm, row, field_names[i], sl_make_int(vm, buffer_long)); break; default: /* MYSQL_TYPE_STRING */ sl_dict_set(vm, row, field_names[i], sl_make_string(vm, cell.buffer, output_lengths[i])); break; } } sl_array_push(vm, result_rows, 1, &row); } return result_rows; }