/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "writer.h" #include #include #include #include #include #define ERROR0(msg) fprintf(stderr, "%s:%d: " msg, __FILE__, __LINE__) #define ERROR(fmt, ...) \ fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__) #define INITIAL_OUTPUT_BUFFER_CAPACITY (64 * 1024) static WasmResult write_data_to_file(size_t offset, const void* data, size_t size, void* user_data) { if (size == 0) return WASM_OK; WasmFileWriter* writer = user_data; if (offset != writer->offset) { if (fseek(writer->file, offset, SEEK_SET) != 0) { ERROR("fseek offset=%" PRIzd " failed, errno=%d\n", size, errno); return WASM_ERROR; } writer->offset = offset; } if (fwrite(data, size, 1, writer->file) != 1) { ERROR("fwrite size=%" PRIzd " failed, errno=%d\n", size, errno); return WASM_ERROR; } writer->offset += size; return WASM_OK; } static WasmResult move_data_in_file(size_t dst_offset, size_t src_offset, size_t size, void* user_data) { if (size == 0) return WASM_OK; /* TODO(binji): implement if needed. */ ERROR0("move_data_in_file not implemented!\n"); return WASM_ERROR; } void wasm_init_file_writer_existing(WasmFileWriter* writer, FILE* file) { WASM_ZERO_MEMORY(*writer); writer->file = file; writer->offset = 0; writer->base.user_data = writer; writer->base.write_data = write_data_to_file; writer->base.move_data = move_data_in_file; } WasmResult wasm_init_file_writer(WasmFileWriter* writer, const char* filename) { FILE* file = fopen(filename, "wb"); if (!file) { ERROR("fopen name=\"%s\" failed, errno=%d\n", filename, errno); return WASM_ERROR; } wasm_init_file_writer_existing(writer, file); return WASM_OK; } void wasm_close_file_writer(WasmFileWriter* writer) { fclose(writer->file); } void wasm_init_output_buffer(WasmAllocator* allocator, WasmOutputBuffer* buf, size_t initial_capacity) { assert(initial_capacity != 0); buf->allocator = allocator; buf->start = wasm_alloc(allocator, initial_capacity, WASM_DEFAULT_ALIGN); buf->size = 0; buf->capacity = initial_capacity; } static void ensure_output_buffer_capacity(WasmOutputBuffer* buf, size_t ensure_capacity) { if (ensure_capacity > buf->capacity) { assert(buf->capacity != 0); size_t new_capacity = buf->capacity * 2; while (new_capacity < ensure_capacity) new_capacity *= 2; buf->start = wasm_realloc(buf->allocator, buf->start, new_capacity, WASM_DEFAULT_ALIGN); buf->capacity = new_capacity; } } static WasmResult write_data_to_output_buffer(size_t offset, const void* data, size_t size, void* user_data) { WasmMemoryWriter* writer = user_data; size_t end = offset + size; ensure_output_buffer_capacity(&writer->buf, end); memcpy((void*)((size_t)writer->buf.start + offset), data, size); if (end > writer->buf.size) writer->buf.size = end; return WASM_OK; } static WasmResult move_data_in_output_buffer(size_t dst_offset, size_t src_offset, size_t size, void* user_data) { WasmMemoryWriter* writer = user_data; size_t src_end = src_offset + size; size_t dst_end = dst_offset + size; size_t end = src_end > dst_end ? src_end : dst_end; ensure_output_buffer_capacity(&writer->buf, end); void* dst = (void*)((size_t)writer->buf.start + dst_offset); void* src = (void*)((size_t)writer->buf.start + src_offset); memmove(dst, src, size); if (end > writer->buf.size) writer->buf.size = end; return WASM_OK; } WasmResult wasm_init_mem_writer(WasmAllocator* allocator, WasmMemoryWriter* writer) { WASM_ZERO_MEMORY(*writer); writer->base.user_data = writer; writer->base.write_data = write_data_to_output_buffer; writer->base.move_data = move_data_in_output_buffer; wasm_init_output_buffer(allocator, &writer->buf, INITIAL_OUTPUT_BUFFER_CAPACITY); return WASM_OK; } WasmResult wasm_init_mem_writer_existing(WasmMemoryWriter* writer, WasmOutputBuffer* buf) { WASM_ZERO_MEMORY(*writer); writer->base.user_data = writer; writer->base.write_data = write_data_to_output_buffer; writer->base.move_data = move_data_in_output_buffer; writer->buf = *buf; /* Clear buffer, since ownership has passed to the writer. */ WASM_ZERO_MEMORY(*buf); return WASM_OK; } void wasm_steal_mem_writer_output_buffer(WasmMemoryWriter* writer, WasmOutputBuffer* out_buf) { *out_buf= writer->buf; writer->buf.start = NULL; writer->buf.size = 0; writer->buf.capacity = 0; } void wasm_close_mem_writer(WasmMemoryWriter* writer) { wasm_destroy_output_buffer(&writer->buf); } WasmResult wasm_write_output_buffer_to_file(WasmOutputBuffer* buf, const char* filename) { FILE* file = fopen(filename, "wb"); if (!file) { ERROR("unable to open %s for writing\n", filename); return WASM_ERROR; } ssize_t bytes = fwrite(buf->start, 1, buf->size, file); if (bytes < 0 || (size_t)bytes != buf->size) { ERROR("failed to write %" PRIzd " bytes to %s\n", buf->size, filename); return WASM_ERROR; } fclose(file); return WASM_OK; } void wasm_destroy_output_buffer(WasmOutputBuffer* buf) { if (buf->allocator) wasm_free(buf->allocator, buf->start); }