mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 05:08:54 -07:00
Support for radio menu items
This commit is contained in:
13
v2/internal/ffenestri/LICENCES.md
Normal file
13
v2/internal/ffenestri/LICENCES.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 3rd Party Licenses
|
||||
|
||||
## vec
|
||||
Homepage: https://github.com/rxi/vec
|
||||
License: https://github.com/rxi/vec/blob/master/LICENSE
|
||||
|
||||
## json
|
||||
Homepage: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
||||
License: http://git.ozlabs.org/?p=ccan;a=blob;f=licenses/BSD-MIT;h=89de354795ec7a7cdab07c091029653d3618540d;hb=HEAD
|
||||
|
||||
## hashmap
|
||||
Homepage: https://github.com/sheredom/hashmap.h
|
||||
License: https://github.com/sheredom/hashmap.h/blob/master/LICENSE
|
||||
File diff suppressed because one or more lines are too long
@@ -73,7 +73,10 @@ extern const char *icon[];
|
||||
int debug;
|
||||
|
||||
// MenuItem map
|
||||
map_t menuItemMap;
|
||||
struct hashmap_s menuItemMap;
|
||||
|
||||
// RadioGroup map. Maps a menuitem id with its associated radio group items
|
||||
struct hashmap_s radioGroupMap;
|
||||
|
||||
// Dispatch Method
|
||||
typedef void (^dispatchMethod)(void);
|
||||
@@ -106,6 +109,20 @@ BOOL yes(id self, SEL cmd)
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Prints a hashmap entry
|
||||
int hashmap_log(void const *context, struct hashmap_element_s const *e) {
|
||||
printf("%s: %p ", (char*)e->key, e->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Utility function to visualise a hashmap
|
||||
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
||||
printf("%s = { ", name);
|
||||
if (0!=hashmap_iterate_pairs(hashmap, hashmap_log, NULL)) {
|
||||
fprintf(stderr, "Failed to dump hashmap entries\n");
|
||||
}
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
extern void messageFromWindowCallback(const char *);
|
||||
typedef void (*ffenestriCallback)(const char *);
|
||||
@@ -330,8 +347,7 @@ void checkboxMenuItemPressed(id self, SEL cmd, id sender) {
|
||||
const char *menuID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
|
||||
|
||||
// Get the menu item from the menu item map
|
||||
id menuItem;
|
||||
hashmap_get(menuItemMap, (char*)menuID, (void**)&menuItem);
|
||||
id menuItem = (id)hashmap_get(&menuItemMap, (char*)menuID, strlen(menuID));
|
||||
|
||||
// Get the current state
|
||||
bool state = msg(menuItem, s("state"));
|
||||
@@ -345,6 +361,42 @@ void checkboxMenuItemPressed(id self, SEL cmd, id sender) {
|
||||
free((void*)message);
|
||||
}
|
||||
|
||||
// radioMenuItemPressed
|
||||
void radioMenuItemPressed(id self, SEL cmd, id sender) {
|
||||
const char *menuID = (const char *)msg(msg(sender, s("representedObject")), s("pointerValue"));
|
||||
|
||||
// Get the menu item from the menu item map
|
||||
id menuItem = (id)hashmap_get(&menuItemMap, (char*)menuID, strlen(menuID));
|
||||
|
||||
// Check the menu items' current state
|
||||
bool selected = msg(menuItem, s("state"));
|
||||
|
||||
// If it's already selected, exit early
|
||||
if (selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get this item's radio group members and turn them off
|
||||
id *members = (id*)hashmap_get(&radioGroupMap, (char*)menuID, strlen(menuID));
|
||||
|
||||
// Uncheck all members of the group
|
||||
id thisMember = members[0];
|
||||
int count = 0;
|
||||
while(thisMember != NULL) {
|
||||
msg(thisMember, s("setState:"), NSControlStateValueOff);
|
||||
count = count + 1;
|
||||
thisMember = members[count];
|
||||
}
|
||||
|
||||
// check the selected menu item
|
||||
msg(menuItem, s("setState:"), NSControlStateValueOn);
|
||||
|
||||
// Notify the backend
|
||||
const char *message = concat("MC", menuID);
|
||||
messageFromWindowCallback(message);
|
||||
free((void*)message);
|
||||
}
|
||||
|
||||
// closeWindow is called when the close button is pressed
|
||||
void closeWindow(id self, SEL cmd, id sender) {
|
||||
printf("\n\n\ncloseWindow called!!!!\n\n\n");
|
||||
@@ -432,9 +484,30 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
||||
|
||||
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
||||
|
||||
|
||||
// Allocate new menuItem map
|
||||
if( 0 != hashmap_create((const unsigned)16, &menuItemMap)) {
|
||||
// Couldn't allocate map
|
||||
Fatal(result, "Not enough memory to allocate menuItemMap!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate the Radio Group Cache
|
||||
if( 0 != hashmap_create((const unsigned)4, &radioGroupMap)) {
|
||||
// Couldn't allocate map
|
||||
Fatal(result, "Not enough memory to allocate radioGroupMap!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (void*) result;
|
||||
}
|
||||
|
||||
int freeHashmapItem(void *context, struct hashmap_element_s const *e) {
|
||||
free(e->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void DestroyApplication(struct Application *app) {
|
||||
Debug(app, "Destroying Application");
|
||||
|
||||
@@ -455,7 +528,15 @@ void DestroyApplication(struct Application *app) {
|
||||
}
|
||||
|
||||
// Free menu item hashmap
|
||||
hashmap_free(menuItemMap);
|
||||
hashmap_destroy(&menuItemMap);
|
||||
|
||||
// Free radio group members
|
||||
if (0!=hashmap_iterate_pairs(&radioGroupMap, freeHashmapItem, NULL)) {
|
||||
Error(app, "failed to deallocate hashmap entries!");
|
||||
}
|
||||
|
||||
//Free radio groups hashmap
|
||||
hashmap_destroy(&radioGroupMap);
|
||||
|
||||
// Remove script handlers
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
||||
@@ -898,6 +979,8 @@ void createDelegate(struct Application *app) {
|
||||
// Menu Callbacks
|
||||
class_addMethod(delegateClass, s("menuCallback:"), (IMP)menuItemPressed, "v@:@");
|
||||
class_addMethod(delegateClass, s("checkboxMenuCallback:"), (IMP)checkboxMenuItemPressed, "v@:@");
|
||||
class_addMethod(delegateClass, s("radioMenuCallback:"), (IMP)radioMenuItemPressed, "v@:@");
|
||||
|
||||
|
||||
// Script handler
|
||||
class_addMethod(delegateClass, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
||||
@@ -1140,6 +1223,15 @@ bool getJSONBool(JsonNode *item, const char* key, bool *result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getJSONInt(JsonNode *item, const char* key, int *result) {
|
||||
JsonNode *node = json_find_member(item, key);
|
||||
if ( node != NULL && node->tag == JSON_NUMBER) {
|
||||
*result = (int) node->number_;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
id parseTextMenuItem(struct Application *app, id parentMenu, JsonNode *item, const char *label, const char *id, bool disabled) {
|
||||
|
||||
const char *accelerator = "";
|
||||
@@ -1150,14 +1242,30 @@ id parseCheckboxMenuItem(struct Application *app, id parentmenu, const char *tit
|
||||
id item = ALLOC("NSMenuItem");
|
||||
|
||||
// Store the item in the menu item map
|
||||
hashmap_put(menuItemMap, (char*)menuid, item);
|
||||
hashmap_put(&menuItemMap, (char*)menuid, strlen(menuid), item);
|
||||
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), menuid);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("checkboxMenuCallback:"), str(key));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
printf("\n\nSetting checked on menu: %s to %s\n\n", title, (checked? "true" : "false"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
|
||||
}
|
||||
|
||||
id parseRadioMenuItem(struct Application *app, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
|
||||
// Store the item in the menu item map
|
||||
hashmap_put(&menuItemMap, (char*)menuid, strlen(menuid), item);
|
||||
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), menuid);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("radioMenuCallback:"), str(key));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
@@ -1176,7 +1284,6 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
// Get the role
|
||||
JsonNode *role = json_find_member(item, "Role");
|
||||
if( role != NULL ) {
|
||||
printf("Parsing MENU ROLE %s!!!\n", role->string_);
|
||||
parseMenuRole(app, parentMenu, role);
|
||||
return;
|
||||
}
|
||||
@@ -1184,8 +1291,6 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
// Check if this is a submenu
|
||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||
if( submenu != NULL ) {
|
||||
printf("Parsing SUBMENU!!!\n");
|
||||
|
||||
// Get the label
|
||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||
const char *name = "";
|
||||
@@ -1204,7 +1309,6 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
json_foreach(item, submenu) {
|
||||
// Get item label
|
||||
parseMenuItem(app, thisMenu, item);
|
||||
printf("Parsing submenu item for '%s'!!!\n", name);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -1217,7 +1321,7 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
label = "(empty)";
|
||||
}
|
||||
|
||||
const char *menuid = getJSONString(item, "Id");
|
||||
const char *menuid = getJSONString(item, "ID");
|
||||
if ( menuid == NULL) {
|
||||
menuid = "";
|
||||
}
|
||||
@@ -1239,7 +1343,6 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
return;
|
||||
}
|
||||
if ( STREQ(type->string_, "Checkbox")) {
|
||||
printf("PARSING CHECKBOX!!!!!!!!!!!");
|
||||
// Get checked state
|
||||
bool checked = false;
|
||||
getJSONBool(item, "Checked", &checked);
|
||||
@@ -1247,6 +1350,14 @@ void parseMenuItem(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
parseCheckboxMenuItem(app, parentMenu, label, menuid, disabled, checked, "");
|
||||
return;
|
||||
}
|
||||
if ( STREQ(type->string_, "Radio")) {
|
||||
// Get checked state
|
||||
bool checked = false;
|
||||
getJSONBool(item, "Checked", &checked);
|
||||
|
||||
parseRadioMenuItem(app, parentMenu, label, menuid, disabled, checked, "");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1268,22 +1379,98 @@ void parseMenu(struct Application *app, id parentMenu, JsonNode *menu) {
|
||||
}
|
||||
}
|
||||
|
||||
void dumpMemberList(const char *name, id *memberList) {
|
||||
void *member = memberList[0];
|
||||
int count = 0;
|
||||
printf("%s = %p -> [ ", name, memberList);
|
||||
while( member != NULL ) {
|
||||
printf("%p ", member);
|
||||
count = count + 1;
|
||||
member = memberList[count];
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
void processRadioGroup(JsonNode *radioGroup) {
|
||||
|
||||
int groupLength;
|
||||
getJSONInt(radioGroup, "Length", &groupLength);
|
||||
JsonNode *members = json_find_member(radioGroup, "Members");
|
||||
JsonNode *member;
|
||||
|
||||
// Allocate array
|
||||
size_t arrayLength = sizeof(id)*(groupLength+1);
|
||||
id memberList[arrayLength];
|
||||
|
||||
// Build the radio group items
|
||||
int count=0;
|
||||
json_foreach(member, members) {
|
||||
// Get menu by id
|
||||
id menuItem = (id)hashmap_get(&menuItemMap, (char*)member->string_, strlen(member->string_));
|
||||
// Save Member
|
||||
memberList[count] = menuItem;
|
||||
count = count + 1;
|
||||
}
|
||||
// Null terminate array
|
||||
memberList[groupLength] = 0;
|
||||
|
||||
// dumpMemberList("memberList", memberList);
|
||||
|
||||
// Store the members
|
||||
json_foreach(member, members) {
|
||||
// Copy the memberList
|
||||
char *newMemberList = (char *)malloc(arrayLength);
|
||||
memcpy(newMemberList, memberList, arrayLength);
|
||||
// dumpMemberList("newMemberList", newMemberList);
|
||||
// printf("Address of newMemberList = %p\n", newMemberList);
|
||||
|
||||
// add group to each member of group
|
||||
hashmap_put(&radioGroupMap, member->string_, strlen(member->string_), newMemberList);
|
||||
}
|
||||
|
||||
// dumpHashmap("radioGroupMap", &radioGroupMap);
|
||||
|
||||
}
|
||||
|
||||
void parseMenuData(struct Application *app) {
|
||||
|
||||
// Create a new menu bar
|
||||
id menubar = createMenu(str(""));
|
||||
|
||||
// Parse the menu json
|
||||
JsonNode *menuData = json_decode(app->menuAsJSON);
|
||||
// Parse the processed menu json
|
||||
JsonNode *processedMenu = json_decode(app->menuAsJSON);
|
||||
|
||||
if( processedMenu == NULL ) {
|
||||
// Parse error!
|
||||
Fatal(app, "Unable to parse Menu JSON: %s", app->menuAsJSON);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pull out the Menu
|
||||
JsonNode *menuData = json_find_member(processedMenu, "Menu");
|
||||
if( menuData == NULL ) {
|
||||
// Parse error!
|
||||
Fatal(app, "Unable to parse Menu JSON:", app->menuAsJSON);
|
||||
Fatal(app, "Unable to find Menu data: %s", processedMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
parseMenu(app, menubar, menuData);
|
||||
|
||||
// Create the radiogroup cache
|
||||
JsonNode *radioGroups = json_find_member(processedMenu, "RadioGroups");
|
||||
if( radioGroups == NULL ) {
|
||||
// Parse error!
|
||||
Fatal(app, "Unable to find RadioGroups data: %s", processedMenu);
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate radio groups
|
||||
JsonNode *radioGroup;
|
||||
json_foreach(radioGroup, radioGroups) {
|
||||
// Get item label
|
||||
processRadioGroup(radioGroup);
|
||||
}
|
||||
|
||||
// Apply the menu bar
|
||||
msg(msg(c("NSApplication"), s("sharedApplication")), s("setMainMenu:"), menubar);
|
||||
}
|
||||
@@ -1291,9 +1478,6 @@ void parseMenuData(struct Application *app) {
|
||||
|
||||
void Run(struct Application *app, int argc, char **argv) {
|
||||
|
||||
// Allocate new hashmap
|
||||
menuItemMap = hashmap_new();
|
||||
|
||||
processDecorations(app);
|
||||
|
||||
createApplication(app);
|
||||
@@ -1338,10 +1522,6 @@ void Run(struct Application *app, int argc, char **argv) {
|
||||
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
||||
app->wkwebview = wkwebview;
|
||||
|
||||
// Only show content when fully rendered
|
||||
|
||||
// TODO: Fix "NSWindow warning: adding an unknown subview: <WKInspectorWKWebView: 0x465ed90>. Break on NSLog to debug." error
|
||||
|
||||
msg(wkwebview, s("initWithFrame:configuration:"), CGRectMake(0, 0, 0, 0), config);
|
||||
|
||||
msg(contentView, s("addSubview:"), wkwebview);
|
||||
|
||||
@@ -17,7 +17,11 @@ extern void SetWindowBackgroundIsTranslucent(void *);
|
||||
extern void SetMenu(void *, const char *);
|
||||
*/
|
||||
import "C"
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
func (a *Application) processPlatformSettings() error {
|
||||
|
||||
@@ -69,12 +73,113 @@ func (a *Application) processPlatformSettings() error {
|
||||
|
||||
// Process menu
|
||||
if mac.Menu != nil {
|
||||
menuJson, err := json.Marshal(mac.Menu)
|
||||
|
||||
/*
|
||||
As radio groups need to be manually managed on OSX,
|
||||
we preprocess the menu to determine the radio groups.
|
||||
This is defined as any adjacent menu item of type "RadioType".
|
||||
We keep a record of every radio group member we discover by saving
|
||||
a list of all members of the group and the number of members
|
||||
in the group (this last one is for optimisation at the C layer).
|
||||
|
||||
Example:
|
||||
{
|
||||
"RadioGroups": [
|
||||
{
|
||||
"Members": [
|
||||
"option-1",
|
||||
"option-2",
|
||||
"option-3"
|
||||
],
|
||||
"Length": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
processedMenu := NewProcessedMenu(mac.Menu)
|
||||
menuJSON, err := json.Marshal(processedMenu)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.SetMenu(a.app, a.string2CString(string(menuJson)))
|
||||
C.SetMenu(a.app, a.string2CString(string(menuJSON)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessedMenu is the original menu with the addition
|
||||
// of radio groups extracted from the menu data
|
||||
type ProcessedMenu struct {
|
||||
Menu *menu.Menu
|
||||
RadioGroups []*RadioGroup
|
||||
currentRadioGroup []string
|
||||
}
|
||||
|
||||
// RadioGroup holds all the members of the same radio group
|
||||
type RadioGroup struct {
|
||||
Members []string
|
||||
Length int
|
||||
}
|
||||
|
||||
// NewProcessedMenu processed the given menu and returns
|
||||
// the original menu with the extracted radio groups
|
||||
func NewProcessedMenu(menu *menu.Menu) *ProcessedMenu {
|
||||
result := &ProcessedMenu{
|
||||
Menu: menu,
|
||||
RadioGroups: []*RadioGroup{},
|
||||
currentRadioGroup: []string{},
|
||||
}
|
||||
|
||||
result.processMenu()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *ProcessedMenu) processMenu() {
|
||||
// Loop over top level menus
|
||||
for _, item := range p.Menu.Items {
|
||||
// Process MenuItem
|
||||
p.processMenuItem(item)
|
||||
}
|
||||
|
||||
p.finaliseRadioGroup()
|
||||
}
|
||||
|
||||
func (p *ProcessedMenu) processMenuItem(item *menu.MenuItem) {
|
||||
|
||||
switch item.Type {
|
||||
|
||||
// We need to recurse submenus
|
||||
case menu.SubmenuType:
|
||||
|
||||
// Finalise any current radio groups as they don't trickle down to submenus
|
||||
p.finaliseRadioGroup()
|
||||
|
||||
// Process each submenu item
|
||||
for _, subitem := range item.SubMenu {
|
||||
p.processMenuItem(subitem)
|
||||
}
|
||||
case menu.RadioType:
|
||||
// Add the item to the radio group
|
||||
p.currentRadioGroup = append(p.currentRadioGroup, item.ID)
|
||||
default:
|
||||
p.finaliseRadioGroup()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ProcessedMenu) finaliseRadioGroup() {
|
||||
|
||||
// If we were processing a radio group, fix up the references
|
||||
if len(p.currentRadioGroup) > 0 {
|
||||
|
||||
// Create new radiogroup
|
||||
group := &RadioGroup{
|
||||
Members: p.currentRadioGroup,
|
||||
Length: len(p.currentRadioGroup),
|
||||
}
|
||||
p.RadioGroups = append(p.RadioGroups, group)
|
||||
|
||||
// Empty the radio group
|
||||
p.currentRadioGroup = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
/*
|
||||
* Generic map implementation.
|
||||
*/
|
||||
#include "hashmap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define INITIAL_SIZE (256)
|
||||
#define MAX_CHAIN_LENGTH (8)
|
||||
|
||||
/* We need to keep keys and values */
|
||||
typedef struct _hashmap_element{
|
||||
char* key;
|
||||
int in_use;
|
||||
any_t data;
|
||||
} hashmap_element;
|
||||
|
||||
/* A hashmap has some maximum size and current size,
|
||||
* as well as the data to hold. */
|
||||
typedef struct _hashmap_map{
|
||||
int table_size;
|
||||
int size;
|
||||
hashmap_element *data;
|
||||
} hashmap_map;
|
||||
|
||||
/*
|
||||
* Return an empty hashmap, or NULL on failure.
|
||||
*/
|
||||
map_t hashmap_new() {
|
||||
hashmap_map* m = (hashmap_map*) malloc(sizeof(hashmap_map));
|
||||
if(!m) goto err;
|
||||
|
||||
m->data = (hashmap_element*) calloc(INITIAL_SIZE, sizeof(hashmap_element));
|
||||
if(!m->data) goto err;
|
||||
|
||||
m->table_size = INITIAL_SIZE;
|
||||
m->size = 0;
|
||||
|
||||
return m;
|
||||
err:
|
||||
if (m)
|
||||
hashmap_free(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The implementation here was originally done by Gary S. Brown. I have
|
||||
borrowed the tables directly, and made some minor changes to the
|
||||
crc32-function (including changing the interface). //ylo */
|
||||
|
||||
/* ============================================================= */
|
||||
/* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */
|
||||
/* code or tables extracted from it, as desired without restriction. */
|
||||
/* */
|
||||
/* First, the polynomial itself and its table of feedback terms. The */
|
||||
/* polynomial is */
|
||||
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
|
||||
/* */
|
||||
/* Note that we take it "backwards" and put the highest-order term in */
|
||||
/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
|
||||
/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
|
||||
/* the MSB being 1. */
|
||||
/* */
|
||||
/* Note that the usual hardware shift register implementation, which */
|
||||
/* is what we're using (we're merely optimizing it by doing eight-bit */
|
||||
/* chunks at a time) shifts bits into the lowest-order term. In our */
|
||||
/* implementation, that means shifting towards the right. Why do we */
|
||||
/* do it this way? Because the calculated CRC must be transmitted in */
|
||||
/* order from highest-order term to lowest-order term. UARTs transmit */
|
||||
/* characters in order from LSB to MSB. By storing the CRC this way, */
|
||||
/* we hand it to the UART in the order low-byte to high-byte; the UART */
|
||||
/* sends each low-bit to hight-bit; and the result is transmission bit */
|
||||
/* by bit from highest- to lowest-order term without requiring any bit */
|
||||
/* shuffling on our part. Reception works similarly. */
|
||||
/* */
|
||||
/* The feedback terms table consists of 256, 32-bit entries. Notes: */
|
||||
/* */
|
||||
/* The table can be generated at runtime if desired; code to do so */
|
||||
/* is shown later. It might not be obvious, but the feedback */
|
||||
/* terms simply represent the results of eight shift/xor opera- */
|
||||
/* tions for all combinations of data and CRC register values. */
|
||||
/* */
|
||||
/* The values must be right-shifted by eight bits by the "updcrc" */
|
||||
/* logic; the shift must be unsigned (bring in zeroes). On some */
|
||||
/* hardware you could probably optimize the shift in assembler by */
|
||||
/* using byte-swap instructions. */
|
||||
/* polynomial $edb88320 */
|
||||
/* */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
static unsigned long crc32_tab[] = {
|
||||
0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
|
||||
0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
|
||||
0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
|
||||
0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
|
||||
0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
|
||||
0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
|
||||
0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
|
||||
0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
|
||||
0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
|
||||
0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
|
||||
0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
|
||||
0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
|
||||
0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
|
||||
0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
|
||||
0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
|
||||
0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
|
||||
0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
|
||||
0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
|
||||
0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
|
||||
0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
|
||||
0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
|
||||
0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
|
||||
0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
|
||||
0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
|
||||
0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
|
||||
0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
|
||||
0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
|
||||
0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
|
||||
0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
|
||||
0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
|
||||
0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
|
||||
0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
|
||||
0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
|
||||
0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
|
||||
0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
|
||||
0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
|
||||
0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
|
||||
0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
|
||||
0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
|
||||
0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
|
||||
0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
|
||||
0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
|
||||
0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
|
||||
0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
|
||||
0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
|
||||
0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
|
||||
0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
|
||||
0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
|
||||
0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
|
||||
0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
|
||||
0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
|
||||
0x2d02ef8dL
|
||||
};
|
||||
|
||||
/* Return a 32-bit CRC of the contents of the buffer. */
|
||||
|
||||
unsigned long crc32(const unsigned char *s, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned long crc32val;
|
||||
|
||||
crc32val = 0;
|
||||
for (i = 0; i < len; i ++)
|
||||
{
|
||||
crc32val =
|
||||
crc32_tab[(crc32val ^ s[i]) & 0xff] ^
|
||||
(crc32val >> 8);
|
||||
}
|
||||
return crc32val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hashing function for a string
|
||||
*/
|
||||
unsigned int hashmap_hash_int(hashmap_map * m, char* keystring){
|
||||
|
||||
unsigned long key = crc32((unsigned char*)(keystring), strlen(keystring));
|
||||
|
||||
/* Robert Jenkins' 32 bit Mix Function */
|
||||
key += (key << 12);
|
||||
key ^= (key >> 22);
|
||||
key += (key << 4);
|
||||
key ^= (key >> 9);
|
||||
key += (key << 10);
|
||||
key ^= (key >> 2);
|
||||
key += (key << 7);
|
||||
key ^= (key >> 12);
|
||||
|
||||
/* Knuth's Multiplicative Method */
|
||||
key = (key >> 3) * 2654435761;
|
||||
|
||||
return key % m->table_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the integer of the location in data
|
||||
* to store the point to the item, or MAP_FULL.
|
||||
*/
|
||||
int hashmap_hash(map_t in, char* key){
|
||||
int curr;
|
||||
int i;
|
||||
|
||||
/* Cast the hashmap */
|
||||
hashmap_map* m = (hashmap_map *) in;
|
||||
|
||||
/* If full, return immediately */
|
||||
if(m->size >= (m->table_size/2)) return MAP_FULL;
|
||||
|
||||
/* Find the best index */
|
||||
curr = hashmap_hash_int(m, key);
|
||||
|
||||
/* Linear probing */
|
||||
for(i = 0; i< MAX_CHAIN_LENGTH; i++){
|
||||
if(m->data[curr].in_use == 0)
|
||||
return curr;
|
||||
|
||||
if(m->data[curr].in_use == 1 && (strcmp(m->data[curr].key,key)==0))
|
||||
return curr;
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
return MAP_FULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Doubles the size of the hashmap, and rehashes all the elements
|
||||
*/
|
||||
int hashmap_rehash(map_t in){
|
||||
int i;
|
||||
int old_size;
|
||||
hashmap_element* curr;
|
||||
|
||||
/* Setup the new elements */
|
||||
hashmap_map *m = (hashmap_map *) in;
|
||||
hashmap_element* temp = (hashmap_element *)
|
||||
calloc(2 * m->table_size, sizeof(hashmap_element));
|
||||
if(!temp) return MAP_OMEM;
|
||||
|
||||
/* Update the array */
|
||||
curr = m->data;
|
||||
m->data = temp;
|
||||
|
||||
/* Update the size */
|
||||
old_size = m->table_size;
|
||||
m->table_size = 2 * m->table_size;
|
||||
m->size = 0;
|
||||
|
||||
/* Rehash the elements */
|
||||
for(i = 0; i < old_size; i++){
|
||||
int status;
|
||||
|
||||
if (curr[i].in_use == 0)
|
||||
continue;
|
||||
|
||||
status = hashmap_put(m, curr[i].key, curr[i].data);
|
||||
if (status != MAP_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
free(curr);
|
||||
|
||||
return MAP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a pointer to the hashmap with some key
|
||||
*/
|
||||
int hashmap_put(map_t in, char* key, any_t value){
|
||||
int index;
|
||||
hashmap_map* m;
|
||||
|
||||
/* Cast the hashmap */
|
||||
m = (hashmap_map *) in;
|
||||
|
||||
/* Find a place to put our value */
|
||||
index = hashmap_hash(in, key);
|
||||
while(index == MAP_FULL){
|
||||
if (hashmap_rehash(in) == MAP_OMEM) {
|
||||
return MAP_OMEM;
|
||||
}
|
||||
index = hashmap_hash(in, key);
|
||||
}
|
||||
|
||||
/* Set the data */
|
||||
m->data[index].data = value;
|
||||
m->data[index].key = key;
|
||||
m->data[index].in_use = 1;
|
||||
m->size++;
|
||||
|
||||
return MAP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get your pointer out of the hashmap with a key
|
||||
*/
|
||||
int hashmap_get(map_t in, char* key, any_t *arg){
|
||||
int curr;
|
||||
int i;
|
||||
hashmap_map* m;
|
||||
|
||||
/* Cast the hashmap */
|
||||
m = (hashmap_map *) in;
|
||||
|
||||
/* Find data location */
|
||||
curr = hashmap_hash_int(m, key);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for(i = 0; i<MAX_CHAIN_LENGTH; i++){
|
||||
|
||||
int in_use = m->data[curr].in_use;
|
||||
if (in_use == 1){
|
||||
if (strcmp(m->data[curr].key,key)==0){
|
||||
*arg = (m->data[curr].data);
|
||||
return MAP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
*arg = NULL;
|
||||
|
||||
/* Not found */
|
||||
return MAP_MISSING;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate the function parameter over each element in the hashmap. The
|
||||
* additional any_t argument is passed to the function as its first
|
||||
* argument and the hashmap element is the second.
|
||||
*/
|
||||
int hashmap_iterate(map_t in, PFany f, any_t item) {
|
||||
int i;
|
||||
|
||||
/* Cast the hashmap */
|
||||
hashmap_map* m = (hashmap_map*) in;
|
||||
|
||||
/* On empty hashmap, return immediately */
|
||||
if (hashmap_length(m) <= 0)
|
||||
return MAP_MISSING;
|
||||
|
||||
/* Linear probing */
|
||||
for(i = 0; i< m->table_size; i++)
|
||||
if(m->data[i].in_use != 0) {
|
||||
any_t data = (any_t) (m->data[i].data);
|
||||
int status = f(item, data);
|
||||
if (status != MAP_OK) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return MAP_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an element with that key from the map
|
||||
*/
|
||||
int hashmap_remove(map_t in, char* key){
|
||||
int i;
|
||||
int curr;
|
||||
hashmap_map* m;
|
||||
|
||||
/* Cast the hashmap */
|
||||
m = (hashmap_map *) in;
|
||||
|
||||
/* Find key */
|
||||
curr = hashmap_hash_int(m, key);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for(i = 0; i<MAX_CHAIN_LENGTH; i++){
|
||||
|
||||
int in_use = m->data[curr].in_use;
|
||||
if (in_use == 1){
|
||||
if (strcmp(m->data[curr].key,key)==0){
|
||||
/* Blank out the fields */
|
||||
m->data[curr].in_use = 0;
|
||||
m->data[curr].data = NULL;
|
||||
m->data[curr].key = NULL;
|
||||
|
||||
/* Reduce the size */
|
||||
m->size--;
|
||||
return MAP_OK;
|
||||
}
|
||||
}
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
/* Data not found */
|
||||
return MAP_MISSING;
|
||||
}
|
||||
|
||||
/* Deallocate the hashmap */
|
||||
void hashmap_free(map_t in){
|
||||
hashmap_map* m = (hashmap_map*) in;
|
||||
free(m->data);
|
||||
free(m);
|
||||
}
|
||||
|
||||
/* Return the length of the hashmap */
|
||||
int hashmap_length(map_t in){
|
||||
hashmap_map* m = (hashmap_map *) in;
|
||||
if(m != NULL) return m->size;
|
||||
else return 0;
|
||||
}
|
||||
@@ -1,81 +1,518 @@
|
||||
/*
|
||||
* Generic hashmap manipulation functions
|
||||
*
|
||||
* Originally by Elliot C Back - http://elliottback.com/wp/hashmap-implementation-in-c/
|
||||
*
|
||||
* Modified by Pete Warden to fix a serious performance problem, support strings as keys
|
||||
* and removed thread synchronization - http://petewarden.typepad.com
|
||||
*/
|
||||
#ifndef __HASHMAP_H__
|
||||
#define __HASHMAP_H__
|
||||
|
||||
#define MAP_MISSING -3 /* No such element */
|
||||
#define MAP_FULL -2 /* Hashmap is full */
|
||||
#define MAP_OMEM -1 /* Out of Memory */
|
||||
#define MAP_OK 0 /* OK */
|
||||
|
||||
/*
|
||||
* any_t is a pointer. This allows you to put arbitrary structures in
|
||||
* the hashmap.
|
||||
*/
|
||||
typedef void *any_t;
|
||||
|
||||
/*
|
||||
* PFany is a pointer to a function that can take two any_t arguments
|
||||
* and return an integer. Returns status code..
|
||||
*/
|
||||
typedef int (*PFany)(any_t, any_t);
|
||||
|
||||
/*
|
||||
* map_t is a pointer to an internally maintained data structure.
|
||||
* Clients of this package do not need to know how hashmaps are
|
||||
* represented. They see and manipulate only map_t's.
|
||||
*/
|
||||
typedef any_t map_t;
|
||||
|
||||
/*
|
||||
* Return an empty hashmap. Returns NULL if empty.
|
||||
The latest version of this library is available on GitHub;
|
||||
https://github.com/sheredom/hashmap.h
|
||||
*/
|
||||
extern map_t hashmap_new();
|
||||
|
||||
/*
|
||||
* Iteratively call f with argument (item, data) for
|
||||
* each element data in the hashmap. The function must
|
||||
* return a map status code. If it returns anything other
|
||||
* than MAP_OK the traversal is terminated. f must
|
||||
* not reenter any hashmap functions, or deadlock may arise.
|
||||
*/
|
||||
extern int hashmap_iterate(map_t in, PFany f, any_t item);
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
/*
|
||||
* Add an element to the hashmap. Return MAP_OK or MAP_OMEM.
|
||||
*/
|
||||
extern int hashmap_put(map_t in, char* key, any_t value);
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
/*
|
||||
* Get an element from the hashmap. Return MAP_OK or MAP_MISSING.
|
||||
*/
|
||||
extern int hashmap_get(map_t in, char* key, any_t *arg);
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
/*
|
||||
* Remove an element from the hashmap. Return MAP_OK or MAP_MISSING.
|
||||
*/
|
||||
extern int hashmap_remove(map_t in, char* key);
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
/*
|
||||
* Get any element. Return MAP_OK or MAP_MISSING.
|
||||
* remove - should the element be removed from the hashmap
|
||||
*/
|
||||
extern int hashmap_get_one(map_t in, any_t *arg, int remove);
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
#ifndef SHEREDOM_HASHMAP_H_INCLUDED
|
||||
#define SHEREDOM_HASHMAP_H_INCLUDED
|
||||
|
||||
/*
|
||||
* Free the hashmap
|
||||
*/
|
||||
extern void hashmap_free(map_t in);
|
||||
#if defined(_MSC_VER)
|
||||
// Workaround a bug in the MSVC runtime where it uses __cplusplus when not
|
||||
// defined.
|
||||
#pragma warning(push, 0)
|
||||
#pragma warning(disable : 4668)
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (defined(_MSC_VER) && defined(__AVX__)) || \
|
||||
(!defined(_MSC_VER) && defined(__SSE4_2__))
|
||||
#define HASHMAP_SSE42
|
||||
#endif
|
||||
|
||||
#if defined(HASHMAP_SSE42)
|
||||
#include <nmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
// Stop MSVC complaining about not inlining functions.
|
||||
#pragma warning(disable : 4710)
|
||||
// Stop MSVC complaining about inlining functions!
|
||||
#pragma warning(disable : 4711)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define HASHMAP_USED
|
||||
#elif defined(__GNUC__)
|
||||
#define HASHMAP_USED __attribute__((used))
|
||||
#else
|
||||
#define HASHMAP_USED
|
||||
#endif
|
||||
|
||||
/* We need to keep keys and values. */
|
||||
struct hashmap_element_s {
|
||||
const char *key;
|
||||
unsigned key_len;
|
||||
int in_use;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/* A hashmap has some maximum size and current size, as well as the data to
|
||||
* hold. */
|
||||
struct hashmap_s {
|
||||
unsigned table_size;
|
||||
unsigned size;
|
||||
struct hashmap_element_s *data;
|
||||
};
|
||||
|
||||
#define HASHMAP_MAX_CHAIN_LENGTH (8)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief Create a hashmap.
|
||||
/// @param initial_size The initial size of the hashmap. Must be a power of two.
|
||||
/// @param out_hashmap The storage for the created hashmap.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// Note that the initial size of the hashmap must be a power of two, and
|
||||
/// creation of the hashmap will fail if this is not the case.
|
||||
static int hashmap_create(const unsigned initial_size,
|
||||
struct hashmap_s *const out_hashmap) HASHMAP_USED;
|
||||
|
||||
/// @brief Put an element into the hashmap.
|
||||
/// @param hashmap The hashmap to insert into.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @param value The value to insert.
|
||||
/// @return On success 0 is returned.
|
||||
///
|
||||
/// The key string slice is not copied when creating the hashmap entry, and thus
|
||||
/// must remain a valid pointer until the hashmap entry is removed or the
|
||||
/// hashmap is destroyed.
|
||||
static int hashmap_put(struct hashmap_s *const hashmap, const char *const key,
|
||||
const unsigned len, void *const value) HASHMAP_USED;
|
||||
|
||||
/// @brief Get an element from the hashmap.
|
||||
/// @param hashmap The hashmap to get from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return The previously set element, or NULL if none exists.
|
||||
static void *hashmap_get(const struct hashmap_s *const hashmap,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
|
||||
/// @brief Remove an element from the hashmap.
|
||||
/// @param hashmap The hashmap to remove from.
|
||||
/// @param key The string key to use.
|
||||
/// @param len The length of the string key.
|
||||
/// @return On success 0 is returned.
|
||||
static int hashmap_remove(struct hashmap_s *const hashmap,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param f The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned. Otherwise if
|
||||
/// the callback function f returned non-zero then non-zero is returned.
|
||||
static int hashmap_iterate(const struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const context, void *const value),
|
||||
void *const context) HASHMAP_USED;
|
||||
|
||||
/// @brief Iterate over all the elements in a hashmap.
|
||||
/// @param hashmap The hashmap to iterate over.
|
||||
/// @param f The function pointer to call on each element.
|
||||
/// @param context The context to pass as the first argument to f.
|
||||
/// @return If the entire hashmap was iterated then 0 is returned.
|
||||
/// Otherwise if the callback function f returned positive then the positive
|
||||
/// value is returned. If the callback function returns -1, the current item
|
||||
/// is removed and iteration continues.
|
||||
static int hashmap_iterate_pairs(struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const, struct hashmap_element_s *const),
|
||||
void *const context) HASHMAP_USED;
|
||||
|
||||
/// @brief Get the size of the hashmap.
|
||||
/// @param hashmap The hashmap to get the size of.
|
||||
/// @return The size of the hashmap.
|
||||
static unsigned
|
||||
hashmap_num_entries(const struct hashmap_s *const hashmap) HASHMAP_USED;
|
||||
|
||||
/// @brief Destroy the hashmap.
|
||||
/// @param hashmap The hashmap to destroy.
|
||||
static void hashmap_destroy(struct hashmap_s *const hashmap) HASHMAP_USED;
|
||||
|
||||
static unsigned hashmap_crc32_helper(const char *const s,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static unsigned
|
||||
hashmap_hash_helper_int_helper(const struct hashmap_s *const m,
|
||||
const char *const keystring,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static int hashmap_match_helper(const struct hashmap_element_s *const element,
|
||||
const char *const key,
|
||||
const unsigned len) HASHMAP_USED;
|
||||
static int hashmap_hash_helper(const struct hashmap_s *const m,
|
||||
const char *const key, const unsigned len,
|
||||
unsigned *const out_index) HASHMAP_USED;
|
||||
static int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e) HASHMAP_USED;
|
||||
static int hashmap_rehash_helper(struct hashmap_s *const m) HASHMAP_USED;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define HASHMAP_CAST(type, x) static_cast<type>(x)
|
||||
#define HASHMAP_PTR_CAST(type, x) reinterpret_cast<type>(x)
|
||||
#define HASHMAP_NULL NULL
|
||||
#else
|
||||
#define HASHMAP_CAST(type, x) ((type)x)
|
||||
#define HASHMAP_PTR_CAST(type, x) ((type)x)
|
||||
#define HASHMAP_NULL 0
|
||||
#endif
|
||||
|
||||
int hashmap_create(const unsigned initial_size,
|
||||
struct hashmap_s *const out_hashmap) {
|
||||
if (0 == initial_size || 0 != (initial_size & (initial_size - 1))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out_hashmap->data =
|
||||
HASHMAP_CAST(struct hashmap_element_s *,
|
||||
calloc(initial_size, sizeof(struct hashmap_element_s)));
|
||||
if (!out_hashmap->data) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out_hashmap->table_size = initial_size;
|
||||
out_hashmap->size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_put(struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len, void *const value) {
|
||||
unsigned int index;
|
||||
|
||||
/* Find a place to put our value. */
|
||||
while (!hashmap_hash_helper(m, key, len, &index)) {
|
||||
if (hashmap_rehash_helper(m)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the data. */
|
||||
m->data[index].data = value;
|
||||
m->data[index].key = key;
|
||||
m->data[index].key_len = len;
|
||||
m->data[index].in_use = 1;
|
||||
m->size++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashmap_get(const struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len) {
|
||||
unsigned int curr;
|
||||
unsigned int i;
|
||||
|
||||
/* Find data location */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (m->data[curr].in_use) {
|
||||
if (hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
return m->data[curr].data;
|
||||
}
|
||||
}
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
/* Not found */
|
||||
return HASHMAP_NULL;
|
||||
}
|
||||
|
||||
int hashmap_remove(struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len) {
|
||||
unsigned int i;
|
||||
unsigned int curr;
|
||||
|
||||
/* Find key */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing, if necessary */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (m->data[curr].in_use) {
|
||||
if (hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
/* Blank out the fields including in_use */
|
||||
memset(&m->data[curr], 0, sizeof(struct hashmap_element_s));
|
||||
|
||||
/* Reduce the size */
|
||||
m->size--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hashmap_iterate(const struct hashmap_s *const m,
|
||||
int (*f)(void *const, void *const), void *const context) {
|
||||
unsigned int i;
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < m->table_size; i++) {
|
||||
if (m->data[i].in_use) {
|
||||
if (!f(context, m->data[i].data)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_iterate_pairs(struct hashmap_s *const hashmap,
|
||||
int (*f)(void *const, struct hashmap_element_s *const),
|
||||
void *const context) {
|
||||
unsigned int i;
|
||||
struct hashmap_element_s *p;
|
||||
int r;
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < hashmap->table_size; i++) {
|
||||
p=&hashmap->data[i];
|
||||
if (p->in_use) {
|
||||
r=f(context, p);
|
||||
switch (r)
|
||||
{
|
||||
case -1: /* remove item */
|
||||
memset(p, 0, sizeof(struct hashmap_element_s));
|
||||
hashmap->size--;
|
||||
break;
|
||||
case 0: /* continue iterating */
|
||||
break;
|
||||
default: /* early exit */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hashmap_destroy(struct hashmap_s *const m) {
|
||||
free(m->data);
|
||||
memset(m, 0, sizeof(struct hashmap_s));
|
||||
}
|
||||
|
||||
unsigned hashmap_num_entries(const struct hashmap_s *const m) {
|
||||
return m->size;
|
||||
}
|
||||
|
||||
unsigned hashmap_crc32_helper(const char *const s, const unsigned len) {
|
||||
unsigned i;
|
||||
unsigned crc32val = 0;
|
||||
|
||||
#if defined(HASHMAP_SSE42)
|
||||
for (i = 0; i < len; i++) {
|
||||
crc32val = _mm_crc32_u8(crc32val, HASHMAP_CAST(unsigned char, s[i]));
|
||||
}
|
||||
|
||||
return crc32val;
|
||||
#else
|
||||
// Using polynomial 0x11EDC6F41 to match SSE 4.2's crc function.
|
||||
static const unsigned crc32_tab[] = {
|
||||
0x00000000U, 0xF26B8303U, 0xE13B70F7U, 0x1350F3F4U, 0xC79A971FU,
|
||||
0x35F1141CU, 0x26A1E7E8U, 0xD4CA64EBU, 0x8AD958CFU, 0x78B2DBCCU,
|
||||
0x6BE22838U, 0x9989AB3BU, 0x4D43CFD0U, 0xBF284CD3U, 0xAC78BF27U,
|
||||
0x5E133C24U, 0x105EC76FU, 0xE235446CU, 0xF165B798U, 0x030E349BU,
|
||||
0xD7C45070U, 0x25AFD373U, 0x36FF2087U, 0xC494A384U, 0x9A879FA0U,
|
||||
0x68EC1CA3U, 0x7BBCEF57U, 0x89D76C54U, 0x5D1D08BFU, 0xAF768BBCU,
|
||||
0xBC267848U, 0x4E4DFB4BU, 0x20BD8EDEU, 0xD2D60DDDU, 0xC186FE29U,
|
||||
0x33ED7D2AU, 0xE72719C1U, 0x154C9AC2U, 0x061C6936U, 0xF477EA35U,
|
||||
0xAA64D611U, 0x580F5512U, 0x4B5FA6E6U, 0xB93425E5U, 0x6DFE410EU,
|
||||
0x9F95C20DU, 0x8CC531F9U, 0x7EAEB2FAU, 0x30E349B1U, 0xC288CAB2U,
|
||||
0xD1D83946U, 0x23B3BA45U, 0xF779DEAEU, 0x05125DADU, 0x1642AE59U,
|
||||
0xE4292D5AU, 0xBA3A117EU, 0x4851927DU, 0x5B016189U, 0xA96AE28AU,
|
||||
0x7DA08661U, 0x8FCB0562U, 0x9C9BF696U, 0x6EF07595U, 0x417B1DBCU,
|
||||
0xB3109EBFU, 0xA0406D4BU, 0x522BEE48U, 0x86E18AA3U, 0x748A09A0U,
|
||||
0x67DAFA54U, 0x95B17957U, 0xCBA24573U, 0x39C9C670U, 0x2A993584U,
|
||||
0xD8F2B687U, 0x0C38D26CU, 0xFE53516FU, 0xED03A29BU, 0x1F682198U,
|
||||
0x5125DAD3U, 0xA34E59D0U, 0xB01EAA24U, 0x42752927U, 0x96BF4DCCU,
|
||||
0x64D4CECFU, 0x77843D3BU, 0x85EFBE38U, 0xDBFC821CU, 0x2997011FU,
|
||||
0x3AC7F2EBU, 0xC8AC71E8U, 0x1C661503U, 0xEE0D9600U, 0xFD5D65F4U,
|
||||
0x0F36E6F7U, 0x61C69362U, 0x93AD1061U, 0x80FDE395U, 0x72966096U,
|
||||
0xA65C047DU, 0x5437877EU, 0x4767748AU, 0xB50CF789U, 0xEB1FCBADU,
|
||||
0x197448AEU, 0x0A24BB5AU, 0xF84F3859U, 0x2C855CB2U, 0xDEEEDFB1U,
|
||||
0xCDBE2C45U, 0x3FD5AF46U, 0x7198540DU, 0x83F3D70EU, 0x90A324FAU,
|
||||
0x62C8A7F9U, 0xB602C312U, 0x44694011U, 0x5739B3E5U, 0xA55230E6U,
|
||||
0xFB410CC2U, 0x092A8FC1U, 0x1A7A7C35U, 0xE811FF36U, 0x3CDB9BDDU,
|
||||
0xCEB018DEU, 0xDDE0EB2AU, 0x2F8B6829U, 0x82F63B78U, 0x709DB87BU,
|
||||
0x63CD4B8FU, 0x91A6C88CU, 0x456CAC67U, 0xB7072F64U, 0xA457DC90U,
|
||||
0x563C5F93U, 0x082F63B7U, 0xFA44E0B4U, 0xE9141340U, 0x1B7F9043U,
|
||||
0xCFB5F4A8U, 0x3DDE77ABU, 0x2E8E845FU, 0xDCE5075CU, 0x92A8FC17U,
|
||||
0x60C37F14U, 0x73938CE0U, 0x81F80FE3U, 0x55326B08U, 0xA759E80BU,
|
||||
0xB4091BFFU, 0x466298FCU, 0x1871A4D8U, 0xEA1A27DBU, 0xF94AD42FU,
|
||||
0x0B21572CU, 0xDFEB33C7U, 0x2D80B0C4U, 0x3ED04330U, 0xCCBBC033U,
|
||||
0xA24BB5A6U, 0x502036A5U, 0x4370C551U, 0xB11B4652U, 0x65D122B9U,
|
||||
0x97BAA1BAU, 0x84EA524EU, 0x7681D14DU, 0x2892ED69U, 0xDAF96E6AU,
|
||||
0xC9A99D9EU, 0x3BC21E9DU, 0xEF087A76U, 0x1D63F975U, 0x0E330A81U,
|
||||
0xFC588982U, 0xB21572C9U, 0x407EF1CAU, 0x532E023EU, 0xA145813DU,
|
||||
0x758FE5D6U, 0x87E466D5U, 0x94B49521U, 0x66DF1622U, 0x38CC2A06U,
|
||||
0xCAA7A905U, 0xD9F75AF1U, 0x2B9CD9F2U, 0xFF56BD19U, 0x0D3D3E1AU,
|
||||
0x1E6DCDEEU, 0xEC064EEDU, 0xC38D26C4U, 0x31E6A5C7U, 0x22B65633U,
|
||||
0xD0DDD530U, 0x0417B1DBU, 0xF67C32D8U, 0xE52CC12CU, 0x1747422FU,
|
||||
0x49547E0BU, 0xBB3FFD08U, 0xA86F0EFCU, 0x5A048DFFU, 0x8ECEE914U,
|
||||
0x7CA56A17U, 0x6FF599E3U, 0x9D9E1AE0U, 0xD3D3E1ABU, 0x21B862A8U,
|
||||
0x32E8915CU, 0xC083125FU, 0x144976B4U, 0xE622F5B7U, 0xF5720643U,
|
||||
0x07198540U, 0x590AB964U, 0xAB613A67U, 0xB831C993U, 0x4A5A4A90U,
|
||||
0x9E902E7BU, 0x6CFBAD78U, 0x7FAB5E8CU, 0x8DC0DD8FU, 0xE330A81AU,
|
||||
0x115B2B19U, 0x020BD8EDU, 0xF0605BEEU, 0x24AA3F05U, 0xD6C1BC06U,
|
||||
0xC5914FF2U, 0x37FACCF1U, 0x69E9F0D5U, 0x9B8273D6U, 0x88D28022U,
|
||||
0x7AB90321U, 0xAE7367CAU, 0x5C18E4C9U, 0x4F48173DU, 0xBD23943EU,
|
||||
0xF36E6F75U, 0x0105EC76U, 0x12551F82U, 0xE03E9C81U, 0x34F4F86AU,
|
||||
0xC69F7B69U, 0xD5CF889DU, 0x27A40B9EU, 0x79B737BAU, 0x8BDCB4B9U,
|
||||
0x988C474DU, 0x6AE7C44EU, 0xBE2DA0A5U, 0x4C4623A6U, 0x5F16D052U,
|
||||
0xAD7D5351U};
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
crc32val = crc32_tab[(HASHMAP_CAST(unsigned char, crc32val) ^
|
||||
HASHMAP_CAST(unsigned char, s[i]))] ^
|
||||
(crc32val >> 8);
|
||||
}
|
||||
return crc32val;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned hashmap_hash_helper_int_helper(const struct hashmap_s *const m,
|
||||
const char *const keystring,
|
||||
const unsigned len) {
|
||||
unsigned key = hashmap_crc32_helper(keystring, len);
|
||||
|
||||
/* Robert Jenkins' 32 bit Mix Function */
|
||||
key += (key << 12);
|
||||
key ^= (key >> 22);
|
||||
key += (key << 4);
|
||||
key ^= (key >> 9);
|
||||
key += (key << 10);
|
||||
key ^= (key >> 2);
|
||||
key += (key << 7);
|
||||
key ^= (key >> 12);
|
||||
|
||||
/* Knuth's Multiplicative Method */
|
||||
key = (key >> 3) * 2654435761;
|
||||
|
||||
return key % m->table_size;
|
||||
}
|
||||
|
||||
int hashmap_match_helper(const struct hashmap_element_s *const element,
|
||||
const char *const key, const unsigned len) {
|
||||
return (element->key_len == len) && (0 == memcmp(element->key, key, len));
|
||||
}
|
||||
|
||||
int hashmap_hash_helper(const struct hashmap_s *const m, const char *const key,
|
||||
const unsigned len, unsigned *const out_index) {
|
||||
unsigned int curr;
|
||||
unsigned int i;
|
||||
|
||||
/* If full, return immediately */
|
||||
if (m->size >= m->table_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the best index */
|
||||
curr = hashmap_hash_helper_int_helper(m, key, len);
|
||||
|
||||
/* Linear probing */
|
||||
for (i = 0; i < HASHMAP_MAX_CHAIN_LENGTH; i++) {
|
||||
if (!m->data[curr].in_use) {
|
||||
*out_index = curr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (m->data[curr].in_use &&
|
||||
hashmap_match_helper(&m->data[curr], key, len)) {
|
||||
*out_index = curr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
curr = (curr + 1) % m->table_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hashmap_rehash_iterator(void *const new_hash,
|
||||
struct hashmap_element_s *const e) {
|
||||
int temp=hashmap_put(HASHMAP_PTR_CAST(struct hashmap_s *, new_hash),
|
||||
e->key, e->key_len, e->data);
|
||||
if (0<temp) {
|
||||
return 1;
|
||||
}
|
||||
/* clear old value to avoid stale pointers */
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Get the current size of a hashmap
|
||||
* Doubles the size of the hashmap, and rehashes all the elements
|
||||
*/
|
||||
extern int hashmap_length(map_t in);
|
||||
int hashmap_rehash_helper(struct hashmap_s *const m) {
|
||||
/* If this multiplication overflows hashmap_create will fail. */
|
||||
unsigned new_size = 2 * m->table_size;
|
||||
|
||||
struct hashmap_s new_hash;
|
||||
|
||||
int flag = hashmap_create(new_size, &new_hash);
|
||||
if (0!=flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
/* copy the old elements to the new table */
|
||||
flag = hashmap_iterate_pairs(m, hashmap_rehash_iterator, HASHMAP_PTR_CAST(void *, &new_hash));
|
||||
if (0!=flag) {
|
||||
return flag;
|
||||
}
|
||||
|
||||
hashmap_destroy(m);
|
||||
/* put new hash into old hash structure by copying */
|
||||
memcpy(m, &new_hash, sizeof(struct hashmap_s));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
||||
113
v2/internal/ffenestri/vec.c
Normal file
113
v2/internal/ffenestri/vec.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Copyright (c) 2014 rxi
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "vec.h"
|
||||
|
||||
|
||||
int vec_expand_(char **data, int *length, int *capacity, int memsz) {
|
||||
if (*length + 1 > *capacity) {
|
||||
void *ptr;
|
||||
int n = (*capacity == 0) ? 1 : *capacity << 1;
|
||||
ptr = realloc(*data, n * memsz);
|
||||
if (ptr == NULL) return -1;
|
||||
*data = ptr;
|
||||
*capacity = n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n) {
|
||||
(void) length;
|
||||
if (n > *capacity) {
|
||||
void *ptr = realloc(*data, n * memsz);
|
||||
if (ptr == NULL) return -1;
|
||||
*data = ptr;
|
||||
*capacity = n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vec_reserve_po2_(
|
||||
char **data, int *length, int *capacity, int memsz, int n
|
||||
) {
|
||||
int n2 = 1;
|
||||
if (n == 0) return 0;
|
||||
while (n2 < n) n2 <<= 1;
|
||||
return vec_reserve_(data, length, capacity, memsz, n2);
|
||||
}
|
||||
|
||||
|
||||
int vec_compact_(char **data, int *length, int *capacity, int memsz) {
|
||||
if (*length == 0) {
|
||||
free(*data);
|
||||
*data = NULL;
|
||||
*capacity = 0;
|
||||
return 0;
|
||||
} else {
|
||||
void *ptr;
|
||||
int n = *length;
|
||||
ptr = realloc(*data, n * memsz);
|
||||
if (ptr == NULL) return -1;
|
||||
*capacity = n;
|
||||
*data = ptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int vec_insert_(char **data, int *length, int *capacity, int memsz,
|
||||
int idx
|
||||
) {
|
||||
int err = vec_expand_(data, length, capacity, memsz);
|
||||
if (err) return err;
|
||||
memmove(*data + (idx + 1) * memsz,
|
||||
*data + idx * memsz,
|
||||
(*length - idx) * memsz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void vec_splice_(char **data, int *length, int *capacity, int memsz,
|
||||
int start, int count
|
||||
) {
|
||||
(void) capacity;
|
||||
memmove(*data + start * memsz,
|
||||
*data + (start + count) * memsz,
|
||||
(*length - start - count) * memsz);
|
||||
}
|
||||
|
||||
|
||||
void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
|
||||
int start, int count
|
||||
) {
|
||||
(void) capacity;
|
||||
memmove(*data + start * memsz,
|
||||
*data + (*length - count) * memsz,
|
||||
count * memsz);
|
||||
}
|
||||
|
||||
|
||||
void vec_swap_(char **data, int *length, int *capacity, int memsz,
|
||||
int idx1, int idx2
|
||||
) {
|
||||
unsigned char *a, *b, tmp;
|
||||
int count;
|
||||
(void) length;
|
||||
(void) capacity;
|
||||
if (idx1 == idx2) return;
|
||||
a = (unsigned char*) *data + idx1 * memsz;
|
||||
b = (unsigned char*) *data + idx2 * memsz;
|
||||
count = memsz;
|
||||
while (count--) {
|
||||
tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
a++, b++;
|
||||
}
|
||||
}
|
||||
181
v2/internal/ffenestri/vec.h
Normal file
181
v2/internal/ffenestri/vec.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Copyright (c) 2014 rxi
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef VEC_H
|
||||
#define VEC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define VEC_VERSION "0.2.1"
|
||||
|
||||
|
||||
#define vec_unpack_(v)\
|
||||
(char**)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data)
|
||||
|
||||
|
||||
#define vec_t(T)\
|
||||
struct { T *data; int length, capacity; }
|
||||
|
||||
|
||||
#define vec_init(v)\
|
||||
memset((v), 0, sizeof(*(v)))
|
||||
|
||||
|
||||
#define vec_deinit(v)\
|
||||
( free((v)->data),\
|
||||
vec_init(v) )
|
||||
|
||||
|
||||
#define vec_push(v, val)\
|
||||
( vec_expand_(vec_unpack_(v)) ? -1 :\
|
||||
((v)->data[(v)->length++] = (val), 0), 0 )
|
||||
|
||||
|
||||
#define vec_pop(v)\
|
||||
(v)->data[--(v)->length]
|
||||
|
||||
|
||||
#define vec_splice(v, start, count)\
|
||||
( vec_splice_(vec_unpack_(v), start, count),\
|
||||
(v)->length -= (count) )
|
||||
|
||||
|
||||
#define vec_swapsplice(v, start, count)\
|
||||
( vec_swapsplice_(vec_unpack_(v), start, count),\
|
||||
(v)->length -= (count) )
|
||||
|
||||
|
||||
#define vec_insert(v, idx, val)\
|
||||
( vec_insert_(vec_unpack_(v), idx) ? -1 :\
|
||||
((v)->data[idx] = (val), 0), (v)->length++, 0 )
|
||||
|
||||
|
||||
#define vec_sort(v, fn)\
|
||||
qsort((v)->data, (v)->length, sizeof(*(v)->data), fn)
|
||||
|
||||
|
||||
#define vec_swap(v, idx1, idx2)\
|
||||
vec_swap_(vec_unpack_(v), idx1, idx2)
|
||||
|
||||
|
||||
#define vec_truncate(v, len)\
|
||||
((v)->length = (len) < (v)->length ? (len) : (v)->length)
|
||||
|
||||
|
||||
#define vec_clear(v)\
|
||||
((v)->length = 0)
|
||||
|
||||
|
||||
#define vec_first(v)\
|
||||
(v)->data[0]
|
||||
|
||||
|
||||
#define vec_last(v)\
|
||||
(v)->data[(v)->length - 1]
|
||||
|
||||
|
||||
#define vec_reserve(v, n)\
|
||||
vec_reserve_(vec_unpack_(v), n)
|
||||
|
||||
|
||||
#define vec_compact(v)\
|
||||
vec_compact_(vec_unpack_(v))
|
||||
|
||||
|
||||
#define vec_pusharr(v, arr, count)\
|
||||
do {\
|
||||
int i__, n__ = (count);\
|
||||
if (vec_reserve_po2_(vec_unpack_(v), (v)->length + n__) != 0) break;\
|
||||
for (i__ = 0; i__ < n__; i__++) {\
|
||||
(v)->data[(v)->length++] = (arr)[i__];\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
|
||||
#define vec_extend(v, v2)\
|
||||
vec_pusharr((v), (v2)->data, (v2)->length)
|
||||
|
||||
|
||||
#define vec_find(v, val, idx)\
|
||||
do {\
|
||||
for ((idx) = 0; (idx) < (v)->length; (idx)++) {\
|
||||
if ((v)->data[(idx)] == (val)) break;\
|
||||
}\
|
||||
if ((idx) == (v)->length) (idx) = -1;\
|
||||
} while (0)
|
||||
|
||||
|
||||
#define vec_remove(v, val)\
|
||||
do {\
|
||||
int idx__;\
|
||||
vec_find(v, val, idx__);\
|
||||
if (idx__ != -1) vec_splice(v, idx__, 1);\
|
||||
} while (0)
|
||||
|
||||
|
||||
#define vec_reverse(v)\
|
||||
do {\
|
||||
int i__ = (v)->length / 2;\
|
||||
while (i__--) {\
|
||||
vec_swap((v), i__, (v)->length - (i__ + 1));\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
|
||||
#define vec_foreach(v, var, iter)\
|
||||
if ( (v)->length > 0 )\
|
||||
for ( (iter) = 0;\
|
||||
(iter) < (v)->length && (((var) = (v)->data[(iter)]), 1);\
|
||||
++(iter))
|
||||
|
||||
|
||||
#define vec_foreach_rev(v, var, iter)\
|
||||
if ( (v)->length > 0 )\
|
||||
for ( (iter) = (v)->length - 1;\
|
||||
(iter) >= 0 && (((var) = (v)->data[(iter)]), 1);\
|
||||
--(iter))
|
||||
|
||||
|
||||
#define vec_foreach_ptr(v, var, iter)\
|
||||
if ( (v)->length > 0 )\
|
||||
for ( (iter) = 0;\
|
||||
(iter) < (v)->length && (((var) = &(v)->data[(iter)]), 1);\
|
||||
++(iter))
|
||||
|
||||
|
||||
#define vec_foreach_ptr_rev(v, var, iter)\
|
||||
if ( (v)->length > 0 )\
|
||||
for ( (iter) = (v)->length - 1;\
|
||||
(iter) >= 0 && (((var) = &(v)->data[(iter)]), 1);\
|
||||
--(iter))
|
||||
|
||||
|
||||
|
||||
int vec_expand_(char **data, int *length, int *capacity, int memsz);
|
||||
int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n);
|
||||
int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz,
|
||||
int n);
|
||||
int vec_compact_(char **data, int *length, int *capacity, int memsz);
|
||||
int vec_insert_(char **data, int *length, int *capacity, int memsz,
|
||||
int idx);
|
||||
void vec_splice_(char **data, int *length, int *capacity, int memsz,
|
||||
int start, int count);
|
||||
void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
|
||||
int start, int count);
|
||||
void vec_swap_(char **data, int *length, int *capacity, int memsz,
|
||||
int idx1, int idx2);
|
||||
|
||||
|
||||
typedef vec_t(void*) vec_void_t;
|
||||
typedef vec_t(char*) vec_str_t;
|
||||
typedef vec_t(int) vec_int_t;
|
||||
typedef vec_t(char) vec_char_t;
|
||||
typedef vec_t(float) vec_float_t;
|
||||
typedef vec_t(double) vec_double_t;
|
||||
|
||||
#endif
|
||||
@@ -141,7 +141,7 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
||||
// Get list of event listeners
|
||||
listeners := e.listeners[eventName]
|
||||
if listeners == nil {
|
||||
e.logger.Trace("No listeners for %s", eventName)
|
||||
e.logger.Trace("No listeners for event '%s'", eventName)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -144,12 +144,12 @@ func (m *Menu) processMenuItem(item *menu.MenuItem) {
|
||||
return
|
||||
}
|
||||
|
||||
if item.Id != "" {
|
||||
if m.menuItems[item.Id] != nil {
|
||||
m.logger.Error("Menu id '%s' is used by multiple menu items: %s %s", m.menuItems[item.Id].Label, item.Label)
|
||||
if item.ID != "" {
|
||||
if m.menuItems[item.ID] != nil {
|
||||
m.logger.Error("Menu id '%s' is used by multiple menu items: %s %s", m.menuItems[item.ID].Label, item.Label)
|
||||
return
|
||||
}
|
||||
m.menuItems[item.Id] = item
|
||||
m.menuItems[item.ID] = item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ func (m *Menu) notifyListeners(menuid string, menuItem *menu.MenuItem) {
|
||||
// Get list of menu listeners
|
||||
listeners := m.listeners[menuid]
|
||||
if listeners == nil {
|
||||
m.logger.Trace("No listeners for %s", menuid)
|
||||
m.logger.Trace("No listeners for MenuItem with ID '%s'", menuid)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
package menu
|
||||
|
||||
// MenuItem represents a menuitem contained in a menu
|
||||
type MenuItem struct {
|
||||
Id string `json:"Id,omitempty"`
|
||||
Label string
|
||||
Role Role `json:"Role,omitempty"`
|
||||
// The unique identifier of this menu item
|
||||
ID string `json:"ID,omitempty"`
|
||||
// Label is what appears as the menu text
|
||||
Label string
|
||||
// Role is a predefined menu type
|
||||
Role Role `json:"Role,omitempty"`
|
||||
// Accelerator holds a representation of a key binding
|
||||
Accelerator string `json:"Accelerator,omitempty"`
|
||||
Type Type
|
||||
Disabled bool
|
||||
Hidden bool
|
||||
Checked bool
|
||||
SubMenu []*MenuItem `json:"SubMenu,omitempty"`
|
||||
// Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu
|
||||
Type Type
|
||||
// Disabled makes the item unselectable
|
||||
Disabled bool
|
||||
// Hidden ensures that the item is not shown in the menu
|
||||
Hidden bool
|
||||
// Checked indicates if the item is selected (used by Checkbox and Radio types only)
|
||||
Checked bool
|
||||
// Submenu contains a list of menu items that will be shown as a submenu
|
||||
SubMenu []*MenuItem `json:"SubMenu,omitempty"`
|
||||
}
|
||||
|
||||
// Text is a helper to create basic Text menu items
|
||||
func Text(label string, id string) *MenuItem {
|
||||
return &MenuItem{
|
||||
Id: id,
|
||||
ID: id,
|
||||
Label: label,
|
||||
Type: TextType,
|
||||
}
|
||||
@@ -26,3 +37,32 @@ func Separator() *MenuItem {
|
||||
Type: SeparatorType,
|
||||
}
|
||||
}
|
||||
|
||||
// Radio is a helper to create basic Radio menu items
|
||||
func Radio(label string, id string, selected bool) *MenuItem {
|
||||
return &MenuItem{
|
||||
ID: id,
|
||||
Label: label,
|
||||
Type: RadioType,
|
||||
Checked: selected,
|
||||
}
|
||||
}
|
||||
|
||||
// Checkbox is a helper to create basic Checkbox menu items
|
||||
func Checkbox(label string, id string, checked bool) *MenuItem {
|
||||
return &MenuItem{
|
||||
ID: id,
|
||||
Label: label,
|
||||
Type: CheckboxType,
|
||||
Checked: checked,
|
||||
}
|
||||
}
|
||||
|
||||
// SubMenu is a helper to create Submenus
|
||||
func SubMenu(label string, items []*MenuItem) *MenuItem {
|
||||
return &MenuItem{
|
||||
Label: label,
|
||||
SubMenu: items,
|
||||
Type: SubmenuType,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package menu
|
||||
|
||||
// SubMenu creates a new submenu which may be added to other
|
||||
// menus
|
||||
func SubMenu(label string, items []*MenuItem) *MenuItem {
|
||||
return &MenuItem{
|
||||
Label: label,
|
||||
SubMenu: items,
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,15 @@ func main() {
|
||||
Hidden: true,
|
||||
},
|
||||
&menu.MenuItem{
|
||||
Id: "checkbox-menu",
|
||||
ID: "checkbox-menu",
|
||||
Label: "Checkbox Menu",
|
||||
Type: menu.CheckboxType,
|
||||
Checked: true,
|
||||
},
|
||||
menu.Separator(),
|
||||
menu.Radio("Option 1", "option-1", true),
|
||||
menu.Radio("Option 2", "option-2", false),
|
||||
menu.Radio("Option 3", "option-3", false),
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user