/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007-2009 Joshua Simmons * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "goban.h" #include "sgf_storage.h" #include "sgf.h" #include "util.h" #define ALIGNMENT_VAL (sizeof (union storage_t)) union storage_t *storage_buffer[] = { NULL, NULL }; size_t storage_buffer_size[] = { 0, 0 }; uint8_t *storage_free_list[] = { NULL, NULL }; size_t storage_free_list_size[] = { 0, 0 }; bool storage_initialized[] = { false, false }; size_t total_storage_size = 0; /* the next handle to check */ int next_free_handle_buffer; int next_free_handle; static bool setup_storage_buffer (char *temp_buffer, size_t size); static void clear_storage_buffer (int index); static bool find_free (int *ret_buffer, int *ret_handle); static bool is_free (int buffer_num, int handle); static void set_free (int buffer_num, int handle, bool free); #if 0 static void debugf_current_node (void); static void debugf_current_node (void) { int temp_prop = NO_PROP; if (current_node < 0) { DEBUGF ("CURRENT_NODE < 0 ON DEBUGF_CURRENT_NODE!!!!\n"); return; } DEBUGF ("-----------------------------------------\n"); DEBUGF ("current_node: %d\n", current_node); DEBUGF ("start_node %d %d\n", start_node, tree_head); DEBUGF ("prev/next: %d/%d\n", get_node (current_node)->prev, get_node (current_node)->next); DEBUGF ("num variations: %d\n", num_variations_sgf ()); DEBUGF ("props:\n"); if (!get_node (current_node) || (temp_prop = get_node (current_node)->props) < 0) { DEBUGF ("none\n"); } while (1) { if (temp_prop < 0) { break; } DEBUGF (" handle: %d\n", temp_prop); DEBUGF (" type: %d ", get_prop (temp_prop)->type); if (get_prop (temp_prop)->type < PROP_NAMES_SIZE) { DEBUGF ("(%s)", prop_names[get_prop (temp_prop)->type]); } DEBUGF ("\n"); if (get_prop (temp_prop)->type == PROP_BLACK_MOVE || get_prop (temp_prop)->type == PROP_WHITE_MOVE || get_prop (temp_prop)->type == PROP_ADD_BLACK || get_prop (temp_prop)->type == PROP_ADD_WHITE || get_prop (temp_prop)->type == PROP_ADD_EMPTY) { DEBUGF (" i: %d j: %d\n", I (get_prop (temp_prop)->data.position), J (get_prop (temp_prop)->data.position)); } else { DEBUGF (" data: %d\n", get_prop (temp_prop)->data.number); } DEBUGF (" next: %d\n", get_prop (temp_prop)->next); temp_prop = get_prop (temp_prop)->next; if (temp_prop >= 0) { DEBUGF ("\n"); } } DEBUGF ("-----------------------------------------\n"); } #endif static void clear_storage_buffer (int index) { int temp; /* everything starts free */ rb->memset (storage_free_list[index], (unsigned char) 0xFF, storage_free_list_size[index]); /* if there are extra bits at the end of the free list (because storage_buffer_size is not divisible by 8) then we set them not free, so we won't end up using those ever by accident (shouldn't be possible anyways, but makes calculation easier later) */ temp = storage_free_list_size[index] * 8 - storage_buffer_size[index]; storage_free_list[index][storage_free_list_size[index] - 1] ^= (1 << temp) - 1; } void free_tree_sgf (void) { unsigned int i; for (i = 0; i < sizeof (storage_initialized) / sizeof (storage_initialized[0]); ++i) { if (storage_initialized[i]) { clear_storage_buffer (i); } } tree_head = start_node = current_node = alloc_storage_sgf (); if (tree_head < 0) { rb->splash (5 * HZ, "Error allocating first node! Please exit immediately."); } get_node (tree_head)->props = NO_PROP; get_node (tree_head)->next = NO_NODE; get_node (tree_head)->prev = NO_NODE; } bool audio_stolen_sgf (void) { return storage_initialized[1]; } int alloc_storage_sgf (void) { int buffer_num; int handle; int temp_buffer; int ret_val; char *new_storage_buffer; size_t size; if (!find_free (&buffer_num, &handle)) { if (!storage_initialized[1]) { rb->splash (2 * HZ, "Stopping music playback to get more space"); DEBUGF ("stealing audio buffer: %d\n", (int) total_storage_size); new_storage_buffer = rb->plugin_get_audio_buffer (&size); setup_storage_buffer (new_storage_buffer, size); DEBUGF ("after stealing: %d\n", (int) total_storage_size); } else { return -1; } /* try again */ if (!find_free (&buffer_num, &handle)) { return -1; } } set_free (buffer_num, handle, false); temp_buffer = 0; ret_val = handle; while (temp_buffer != buffer_num) { if (storage_initialized[temp_buffer]) { ret_val += storage_buffer_size[temp_buffer]; } ++temp_buffer; } return ret_val; } void free_storage_sgf (int handle) { int index; if (handle < 0 || (unsigned int) handle >= total_storage_size) { DEBUGF ("tried to free an out of bounds handle!!\n"); } else { index = 0; while ((unsigned int) handle >= storage_buffer_size[index]) { handle -= storage_buffer_size[index++]; } rb->memset (&storage_buffer[index][handle], 0xFF, sizeof (union storage_t)); set_free (index, handle, true); } } static bool find_free (int *ret_buffer, int *ret_handle) { unsigned int handle = next_free_handle; unsigned int buffer_index = next_free_handle_buffer; /* so we know where we started, to prevent infinite loop */ unsigned int start_handle = handle; unsigned int start_buffer = buffer_index; do { ++handle; if (handle >= storage_buffer_size[buffer_index]) { handle = 0; do { ++buffer_index; if (buffer_index >= sizeof (storage_initialized) / sizeof (storage_initialized[0])) { buffer_index = 0; } } while (!storage_initialized[buffer_index]); } if (is_free (buffer_index, handle)) { next_free_handle_buffer = buffer_index; next_free_handle = handle; *ret_buffer = buffer_index; *ret_handle = handle; return true; } } while (handle != start_handle || buffer_index != start_buffer); return false; } static bool is_free (int buffer_num, int handle) { return storage_free_list[buffer_num][handle / 8] & (1 << (7 - (handle % 8))); } static void set_free (int buffer_num, int handle, bool free) { if (free) { /* simple, just 'or' the byte with the specific bit switched on */ storage_free_list[buffer_num][handle / 8] |= 1 << (7 - (handle % 8)); } else { /* start with a byte with all bits turned on and turn off the one we're trying to set to zero. then take that result and 'and' it with the current value */ storage_free_list[buffer_num][handle / 8] &= 0xFF ^ (1 << (7 - (handle % 8))); } } bool setup_sgf (void) { size_t size; char *temp_buffer; temp_buffer = rb->plugin_get_buffer (&size); setup_storage_buffer (temp_buffer, size); if (total_storage_size < MIN_STORAGE_BUFFER_SIZE) { rb->splash (1 * HZ, "Stopping music playback to get more space"); DEBUGF ("storage_buffer_size < MIN!!: %d\n", (int) total_storage_size); temp_buffer = rb->plugin_get_audio_buffer (&size); setup_storage_buffer (temp_buffer, size); } if (total_storage_size < MIN_STORAGE_BUFFER_SIZE) { rb->splash (1 * HZ, "Low memory. Large files may not load."); DEBUGF ("storage_buffer_size < MIN!!!!: %d\n", (int) total_storage_size); } DEBUGF ("storage_buffer_size: %d\n", (int) total_storage_size); unhandled_fd = create_or_open_file (UNHANDLED_PROP_LIST_FILE); if (unhandled_fd < 0) { return false; } rb->lseek (unhandled_fd, 0, SEEK_SET); rb->ftruncate (unhandled_fd, 0); empty_stack (&parse_stack); return true; } void clear_caches_sgf (void) { empty_stack (&parse_stack); rb->lseek (unhandled_fd, 0, SEEK_SET); rb->ftruncate (unhandled_fd, 0); } void cleanup_sgf (void) { empty_stack (&parse_stack); rb->lseek (unhandled_fd, 0, SEEK_SET); rb->ftruncate (unhandled_fd, 0); close_file (&unhandled_fd); close_file (&sgf_fd); } static bool setup_storage_buffer (char *temp_buffer, size_t size) { unsigned int index = 0; int temp; #if PLUGIN_BUFFER_SIZE < 0x10000 && !defined(SIMULATOR) /* loaded as an overlay plugin, protect from overwriting ourselves */ if (plugin_start_addr >= (unsigned char *) temp_buffer && plugin_start_addr < (unsigned char *) temp_buffer + size) { size = plugin_start_addr - (unsigned char *) temp_buffer; } #endif while (1) { if (index >= sizeof (storage_initialized) / sizeof (storage_initialized[0])) { return false; } if (!storage_initialized[index]) { break; } ++index; } temp_buffer = align_buffer (temp_buffer, &size); if (!temp_buffer || !size) { return false; } /* same as temp = size / (sizeof(union storage_t) + 1/8) (we need 1 bit extra for each union storage_t, for the free list) */ temp = (8 * (size - ALIGNMENT_VAL - 1)) / (8 * sizeof (union storage_t) + 1); /* the - ALIGNMENT_VAL - 1 is for possible wasted space in alignment and possible extra byte needed in the free list */ storage_buffer[index] = (void *) temp_buffer; storage_buffer_size[index] = temp; storage_free_list_size[index] = storage_buffer_size[index] / 8; if (storage_free_list_size[index] * 8 < storage_buffer_size[index]) { ++(storage_free_list_size[index]); } temp_buffer += sizeof (union storage_t) * temp; size -= sizeof (union storage_t) * temp; temp_buffer = align_buffer (temp_buffer, &size); if (!temp_buffer || !size) { return false; } if (size < storage_free_list_size[index]) { DEBUGF ("Big problem on line %d in sgf.c\n", __LINE__); rb->splashf (5 * HZ, "Error in allocating storage buffer! Exit and report this!\n"); return false; } storage_free_list[index] = temp_buffer; total_storage_size += storage_buffer_size[index]; storage_initialized[index] = true; clear_storage_buffer (index); return true; } struct node_t * get_node (int handle) { if (handle < 0) { return NULL; } else if ((unsigned int) handle >= total_storage_size) { return NULL; } int index = 0; while ((unsigned int) handle >= storage_buffer_size[index]) { handle -= storage_buffer_size[index++]; } return &(storage_buffer[index][handle].node); } struct prop_t * get_prop (int handle) { if (handle < 0) { return NULL; } else if ((unsigned int) handle >= total_storage_size) { return NULL; } int index = 0; while ((unsigned int) handle >= storage_buffer_size[index]) { handle -= storage_buffer_size[index++]; } return &(storage_buffer[index][handle].prop); }