├── LICENSE ├── SConscript ├── _config.yml ├── examples └── hash_match_demo.c ├── hash-match.c ├── hash-match.h ├── murmurhash3.c ├── murmurhash3.h ├── readme.md ├── simplehash.c └── simplehash.h /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | import rtconfig 3 | 4 | cwd = GetCurrentDir() 5 | 6 | # init src 7 | src = [] 8 | src += Glob('*.c') 9 | 10 | if GetDepend(['PKG_USING_HASH_MATCH_DEMO']): 11 | src += Glob("examples/hash_match_demo.c") 12 | 13 | CPPPATH = [cwd] 14 | 15 | group = DefineGroup('hash-match', src, depend = ['PKG_USING_HASH_MATCH'], CPPPATH = CPPPATH) 16 | 17 | Return('group') 18 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: hash-match 2 | description: Using Hashmap algorithm on MCUs. 3 | show_downloads: true 4 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /examples/hash_match_demo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-04-03 smartmx the first version 9 | * 10 | */ 11 | #include "hash-match.h" 12 | #include 13 | 14 | /* test1 */ 15 | void hash_match_test1func(void *t) 16 | { 17 | rt_kprintf("test1\n"); 18 | } 19 | const uint8_t hash_match_test1key[] = {'a', 'b', 'c', 'd', 'e', 'f', 232, 'g', 168, 192}; 20 | /* test upper and lower compare. */ 21 | const uint8_t hash_match_test1keyu[] = {'A', 'B', 'C', 'd', 'e', 'f', 232, 'g', 168, 192}; 22 | HASH_MATCH_EXPORT(hash_match_test, hash_match_test1, hash_match_test1key, sizeof(hash_match_test1key), hash_match_test1func, "this is test 1"); 23 | 24 | /* different section group with same key, for test. */ 25 | void hash_match_test1func1(void *t) 26 | { 27 | rt_kprintf("test11\n"); 28 | } 29 | HASH_MATCH_EXPORT(hash_match_test1, hash_match_test11, hash_match_test1key, sizeof(hash_match_test1key), hash_match_test1func1, "this is test 11"); 30 | 31 | /* test2 */ 32 | void hash_match_test2func(void *t) 33 | { 34 | rt_kprintf("test2\n"); 35 | } 36 | const uint8_t hash_match_test2key[] = {51, 135, 10, 0, 33, 67, 45, 123, 172, 8, 0}; 37 | HASH_MATCH_EXPORT(hash_match_test, hash_match_test2, hash_match_test2key, sizeof(hash_match_test2key), hash_match_test2func, "this is test 2"); 38 | 39 | /* different section group with same key, for test. */ 40 | void hash_match_test2func1(void *t) 41 | { 42 | rt_kprintf("test21\n"); 43 | } 44 | HASH_MATCH_EXPORT(hash_match_test1, hash_match_test21, hash_match_test2key, sizeof(hash_match_test2key), hash_match_test2func1, "this is test 21"); 45 | 46 | /* test3 */ 47 | void hash_match_test3func(void *t) 48 | { 49 | rt_kprintf("test3\n"); 50 | } 51 | const uint8_t hash_match_test3key[] = {8, 99, 23, 170, 234, 7, 212, 65, 20, 88, 19, 122}; 52 | HASH_MATCH_EXPORT(hash_match_test, hash_match_test3, hash_match_test3key, sizeof(hash_match_test3key), hash_match_test3func, "this is test 3"); 53 | 54 | /* different section group with same key, for test. */ 55 | void hash_match_test3func1(void *t) 56 | { 57 | rt_kprintf("test31\n"); 58 | } 59 | HASH_MATCH_EXPORT(hash_match_test1, hash_match_test31, hash_match_test3key, sizeof(hash_match_test3key), hash_match_test3func1, "this is test 31"); 60 | 61 | static void hash_match_test_task(void *arg) 62 | { 63 | /* init hash_match_test section group. */ 64 | HASH_MATCH_INIT(hash_match_test); 65 | HASH_MATCH_LIST(hash_match_test); 66 | 67 | /* init hash_match_test1 section group. */ 68 | HASH_MATCH_INIT(hash_match_test1); 69 | HASH_MATCH_LIST(hash_match_test1); 70 | while (1) 71 | { 72 | HASH_MATCH(hash_match_test, hash_match_test1key, sizeof(hash_match_test1key), NULL); 73 | HASH_MATCH(hash_match_test, hash_match_test2key, sizeof(hash_match_test2key), NULL); 74 | HASH_MATCH(hash_match_test, hash_match_test3key, sizeof(hash_match_test3key), NULL); 75 | /* test upper and lower compare. */ 76 | HASH_MATCH(hash_match_test, hash_match_test1keyu, sizeof(hash_match_test1keyu), NULL); 77 | /* mix up the length, to try. */ 78 | HASH_MATCH(hash_match_test, hash_match_test1key, sizeof(hash_match_test2key), NULL); 79 | HASH_MATCH(hash_match_test, hash_match_test2key, sizeof(hash_match_test3key), NULL); 80 | HASH_MATCH(hash_match_test, hash_match_test3key, sizeof(hash_match_test1key), NULL); 81 | /* try with different section group. */ 82 | HASH_MATCH(hash_match_test1, hash_match_test1key, sizeof(hash_match_test1key), NULL); 83 | HASH_MATCH(hash_match_test1, hash_match_test2key, sizeof(hash_match_test2key), NULL); 84 | HASH_MATCH(hash_match_test1, hash_match_test3key, sizeof(hash_match_test3key), NULL); 85 | rt_thread_mdelay(1000); 86 | } 87 | } 88 | 89 | int hash_match_test_main(void) 90 | { 91 | rt_thread_t tid = RT_NULL; 92 | 93 | /* Create background ticks thread */ 94 | tid = rt_thread_create("hash-match", hash_match_test_task, RT_NULL, 1024, 10, 10); 95 | if (tid != RT_NULL) 96 | { 97 | rt_thread_startup(tid); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | INIT_APP_EXPORT(hash_match_test_main); 104 | -------------------------------------------------------------------------------- /hash-match.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2023, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-04-03 smartmx the first version 9 | * 2023-03-02 smartmx hash_match_group function will return the pointer to index 10 | * 11 | */ 12 | #include "hash-match.h" 13 | 14 | /** 15 | * check all calculated hash code in the group, compare with hash_code. 16 | * 17 | * @param start the hash match group start address. 18 | * @param end the hash match group end address. 19 | * @param hash_code the hash code which is need be checked. 20 | * 21 | * @return None. 22 | */ 23 | void hash_match_check(const hash_match_t *start, const hash_match_t *end, uint32_t hash_code, uint32_t hash_key_len) 24 | { 25 | const hash_match_t *check_start = (const hash_match_t *)start; 26 | HASH_MATCH_PRINTF("check hash_code:'%08x'\n", hash_code); 27 | while (1) 28 | { 29 | if (end <= check_start) 30 | { 31 | break; 32 | } 33 | if (*(check_start->hash_code) == hash_code) 34 | { 35 | HASH_MATCH_PRINTF("hash_code is duplicated with hash_key:\n"); 36 | HASH_MATCH_PRINTF("1st:"); 37 | for (uint32_t i = 0; i < check_start->hash_key_len; i++) 38 | { 39 | HASH_MATCH_PRINTF(" %02x", check_start->hash_key_src[i]); 40 | } 41 | HASH_MATCH_PRINTF("\n"); 42 | HASH_MATCH_PRINTF("2nd:"); 43 | for (uint32_t i = 0; i < end->hash_key_len; i++) 44 | { 45 | HASH_MATCH_PRINTF(" %02x", end->hash_key_src[i]); 46 | } 47 | HASH_MATCH_PRINTF("\n"); 48 | if (check_start->hash_key_len == hash_key_len) 49 | { 50 | HASH_MATCH_PRINTF("hash_code and hash_key_len %d are all duplicated, you must fix it!\n", hash_key_len); 51 | break; 52 | } 53 | } 54 | check_start++; 55 | } 56 | } 57 | 58 | /** 59 | * init a hash code group. 60 | * 61 | * @param start the hash match group start address. 62 | * @param end the hash match group end address. 63 | * 64 | * @return None. 65 | */ 66 | void hash_match_group_init(const hash_match_t *start, const hash_match_t *end) 67 | { 68 | const hash_match_t *init_start = (const hash_match_t *)start; 69 | while (1) 70 | { 71 | if (end <= init_start) 72 | { 73 | break; 74 | } 75 | *(init_start->hash_code) = hash_match_caculate(init_start->hash_key_src, init_start->hash_key_len); 76 | #if HASH_MATCH_INIT_CHECK 77 | hash_match_check(start, init_start, *(init_start->hash_code), init_start->hash_key_len); 78 | #endif 79 | init_start++; 80 | } 81 | 82 | } 83 | 84 | /** 85 | * check all calculated hash code in the group, compare with hash_code. 86 | * 87 | * @param start the hash match group start address. 88 | * @param end the hash match group end address. 89 | * @param src the hash key source pointer. 90 | * @param len the length of hash key source. 91 | * @param (void*) parameter when calling handler. 92 | * 93 | * @return None. 94 | */ 95 | void *hash_match_group(const hash_match_t *start, const hash_match_t *end, const void *src, uint32_t len, void *param) 96 | { 97 | const hash_match_t *find_start = (const hash_match_t *)start; 98 | uint32_t hash_code; 99 | hash_code = hash_match_caculate(src, len); 100 | while (1) 101 | { 102 | if (end <= find_start) 103 | { 104 | break; 105 | } 106 | if ((*(find_start->hash_code) == hash_code) && (len == find_start->hash_key_len)) 107 | { 108 | #if HASH_MATCH_COMPARE_KEY 109 | if (hash_match_memcmp(src, find_start->hash_key_src, len) == HASH_MATCH_MEMCMP_SAME) 110 | { 111 | #endif 112 | if (find_start->handler != NULL) 113 | { 114 | find_start->handler(param); 115 | } 116 | return (void *)find_start; 117 | #if HASH_MATCH_COMPARE_KEY 118 | } 119 | #endif 120 | } 121 | find_start++; 122 | } 123 | return NULL; 124 | } 125 | 126 | /** 127 | * list all hash key info in a hash code group. 128 | * 129 | * @param start the hash match group start address. 130 | * @param end the hash match group end address. 131 | * 132 | * @return None. 133 | */ 134 | void hash_match_group_list(const hash_match_t *start, const hash_match_t *end) 135 | { 136 | const hash_match_t *list_start = (const hash_match_t *)start; 137 | uint32_t len; 138 | while (1) 139 | { 140 | if (end <= list_start) 141 | { 142 | break; 143 | } 144 | len = 0; 145 | HASH_MATCH_PRINTF("hash match key:%02x", list_start->hash_key_src[len]); 146 | len++; 147 | while (len < list_start->hash_key_len) 148 | { 149 | HASH_MATCH_PRINTF(" %02x", list_start->hash_key_src[len]); 150 | len++; 151 | } 152 | HASH_MATCH_PRINTF("\nhash match key len:%d\n", list_start->hash_key_len); 153 | HASH_MATCH_PRINTF("hash match hash code:%08x\n", *(list_start->hash_code)); 154 | #if HASH_MATCH_SAVE_DESC 155 | HASH_MATCH_PRINTF("hash match description: %s\n", list_start->hash_desc); 156 | #endif 157 | list_start++; 158 | } 159 | } 160 | 161 | -------------------------------------------------------------------------------- /hash-match.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2023, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-04-03 smartmx the first version 9 | * 2023-03-02 smartmx add HASH_MATCH_NO_FOUND_ACTION api 10 | * 11 | */ 12 | #ifndef _HASH_MACTH_H_ 13 | #define _HASH_MACTH_H_ 14 | 15 | #include "stdio.h" 16 | #include "stdint.h" 17 | #include "murmurhash3.h" 18 | #include "simplehash.h" 19 | 20 | /* 21 | * @Note: hash-match use murmur3 hash algorithm in default: https://github.com/aappleby/smhasher. 22 | * you can use your own hash algorithm by change the definition "hash_match_caculate". 23 | */ 24 | #define hash_match_caculate murmurhash3_caculate32 25 | 26 | /* whether save description of hash source or not, set 0 will not save description. */ 27 | #define HASH_MATCH_SAVE_DESC 0 28 | 29 | /* set HASH_MATCH_INIT_CHECK to 1 will check all hash values in one group during init a group, report if some hash value is duplicated. */ 30 | #define HASH_MATCH_INIT_CHECK 0 31 | 32 | /* change to your own printf function, or don't use it. */ 33 | #define HASH_MATCH_PRINTF printf 34 | 35 | /* whether compare key when hash_code is same. */ 36 | #define HASH_MATCH_COMPARE_KEY 1 37 | 38 | /* use string.h or self functions to compare key. */ 39 | #define HASH_MATCH_USE_STRING_H 1 40 | 41 | #if HASH_MATCH_USE_STRING_H 42 | #include "string.h" 43 | #define hash_match_memcmp memcmp 44 | #define HASH_MATCH_MEMCMP_SAME 0 45 | #else 46 | #define hash_match_memcmp murmurhash3_lower_char_upper_memcmp 47 | #define HASH_MATCH_MEMCMP_SAME 0 48 | #endif 49 | 50 | typedef void (*hash_match_handler)(void *); 51 | 52 | typedef struct _hash_match_struct 53 | { 54 | const uint8_t *hash_key_src; 55 | uint32_t hash_key_len; 56 | uint32_t *hash_code; 57 | hash_match_handler handler; 58 | #if HASH_MATCH_SAVE_DESC 59 | const char *hash_desc; 60 | #endif 61 | } hash_match_t; 62 | 63 | #if HASH_MATCH_SAVE_DESC 64 | #define HASH_MATCH_EXPORT(GROUP, NAME, hash_key_src, hash_key_len, handler, hash_desc) \ 65 | static const char NAME##_hash_desc[] = hash_desc; \ 66 | static uint32_t NAME##_hash_code = 0; \ 67 | HASH_MATCH_USED const hash_match_t NAME HASH_MATCH_SECTION(#GROUP)= \ 68 | { \ 69 | hash_key_src, \ 70 | hash_key_len, \ 71 | &NAME##_hash_code, \ 72 | (hash_match_handler)&handler, \ 73 | NAME##_hash_desc, \ 74 | } 75 | #else 76 | /* use va_args to adapt from codes which has HASH_MATCH_SAVE_DESC enabled. */ 77 | #define HASH_MATCH_EXPORT(GROUP, NAME, hash_key_src, hash_key_len, handler, ...) \ 78 | static uint32_t NAME##_hash_code = 0; \ 79 | HASH_MATCH_USED const hash_match_t NAME HASH_MATCH_SECTION(#GROUP)= \ 80 | { \ 81 | hash_key_src, \ 82 | hash_key_len, \ 83 | &NAME##_hash_code, \ 84 | (hash_match_handler)&handler, \ 85 | } 86 | #endif 87 | 88 | /* we do not use these functions directly, but use macro definitions functions instead. */ 89 | extern void hash_match_group_init(const hash_match_t *start, const hash_match_t *end); 90 | 91 | extern void *hash_match_group(const hash_match_t *start, const hash_match_t *end, const void *src, uint32_t len, void *param); 92 | 93 | extern void hash_match_group_list(const hash_match_t *start, const hash_match_t *end); 94 | 95 | /* Compiler Related Definitions */ 96 | #if defined(__CC_ARM) || defined(__CLANG_ARM) /* ARM Compiler */ 97 | 98 | #define HASH_MATCH_SECTION(x) __attribute__((section(x))) 99 | #define HASH_MATCH_USED __attribute__((used)) 100 | 101 | #define HASH_MATCH_INIT(GROUP) do \ 102 | { \ 103 | extern const int GROUP##$$Base; \ 104 | extern const int GROUP##$$Limit; \ 105 | hash_match_group_init((const hash_match_t*)&GROUP##$$Base, (const hash_match_t*)&GROUP##$$Limit); \ 106 | } while(0) 107 | 108 | #define HASH_MATCH(GROUP, SRC, LEN, PARAMS) do \ 109 | { \ 110 | extern const int GROUP##$$Base; \ 111 | extern const int GROUP##$$Limit; \ 112 | hash_match_group((const hash_match_t*)&GROUP##$$Base, (const hash_match_t*)&GROUP##$$Limit, SRC, LEN, PARAMS); \ 113 | } while(0) 114 | 115 | #define HASH_MATCH_NO_FOUND_ACTION(GROUP, SRC, LEN, PARAMS, NO_FOUND_ACTION) do \ 116 | { \ 117 | extern const int GROUP##$$Base; \ 118 | extern const int GROUP##$$Limit; \ 119 | if(hash_match_group((const hash_match_t*)&GROUP##$$Base, (const hash_match_t*)&GROUP##$$Limit, SRC, LEN, PARAMS) == NULL) \ 120 | { \ 121 | NO_FOUND_ACTION; \ 122 | } \ 123 | } while(0) 124 | 125 | #define HASH_MATCH_LIST(GROUP) do \ 126 | { \ 127 | extern const int GROUP##$$Base; \ 128 | extern const int GROUP##$$Limit; \ 129 | hash_match_group_list((const hash_match_t*)&GROUP##$$Base, (const hash_match_t*)&GROUP##$$Limit); \ 130 | } while(0) 131 | 132 | #elif defined (__IAR_SYSTEMS_ICC__) /* for IAR Compiler */ 133 | 134 | #define HASH_MATCH_SECTION(x) @ x 135 | #define HASH_MATCH_USED __root 136 | 137 | #define HASH_MATCH_INIT(GROUP) do \ 138 | { \ 139 | hash_match_group_init((const hash_match_t*)(__section_begin(#GROUP)), (const hash_match_t*)(__section_end(#GROUP))); \ 140 | } while(0) 141 | 142 | #define HASH_MATCH(GROUP, SRC, LEN, PARAMS) do \ 143 | { \ 144 | hash_match_group((const hash_match_t*)(__section_begin(#GROUP)), (const hash_match_t*)(__section_end(#GROUP)), SRC, LEN, PARAMS); \ 145 | } while(0) 146 | 147 | #define HASH_MATCH_NO_FOUND_ACTION(GROUP, SRC, LEN, PARAMS, NO_FOUND_ACTION) do \ 148 | { \ 149 | if(hash_match_group((const hash_match_t*)(__section_begin(#GROUP)), (const hash_match_t*)(__section_end(#GROUP)), SRC, LEN, PARAMS) == NULL) \ 150 | { \ 151 | NO_FOUND_ACTION; \ 152 | } \ 153 | } while(0) 154 | 155 | #define HASH_MATCH_LIST(GROUP) do \ 156 | { \ 157 | hash_match_group_list((const hash_match_t*)(__section_begin(#GROUP)), (const hash_match_t*)(__section_end(#GROUP))); \ 158 | } while(0) 159 | 160 | #elif defined (__GNUC__) /* GNU GCC Compiler */ 161 | 162 | #define HASH_MATCH_SECTION(x) __attribute__((section(x))) 163 | #define HASH_MATCH_USED __attribute__((used)) 164 | 165 | #define HASH_MATCH_INIT(GROUP) do \ 166 | { \ 167 | extern const int GROUP##_start; \ 168 | extern const int GROUP##_end; \ 169 | hash_match_group_init((const hash_match_t*)&GROUP##_start, (const hash_match_t*)&GROUP##_end); \ 170 | } while(0) 171 | 172 | #define HASH_MATCH(GROUP, SRC, LEN, PARAMS) do \ 173 | { \ 174 | extern const int GROUP##_start; \ 175 | extern const int GROUP##_end; \ 176 | hash_match_group((const hash_match_t*)&GROUP##_start, (const hash_match_t*)&GROUP##_end, SRC, LEN, PARAMS); \ 177 | } while(0) 178 | 179 | #define HASH_MATCH_NO_FOUND_ACTION(GROUP, SRC, LEN, PARAMS, NO_FOUND_ACTION) do \ 180 | { \ 181 | extern const int GROUP##_start; \ 182 | extern const int GROUP##_end; \ 183 | if(hash_match_group((const hash_match_t*)&GROUP##_start, (const hash_match_t*)&GROUP##_end, SRC, LEN, PARAMS)== NULL) \ 184 | { \ 185 | NO_FOUND_ACTION; \ 186 | } \ 187 | } while(0) 188 | 189 | #define HASH_MATCH_LIST(GROUP) do \ 190 | { \ 191 | extern const int GROUP##_start; \ 192 | extern const int GROUP##_end; \ 193 | hash_match_group_list((const hash_match_t*)&GROUP##_start, (const hash_match_t*)&GROUP##_end); \ 194 | } while(0) 195 | #else 196 | #error not supported tool chain 197 | #endif 198 | 199 | #endif 200 | -------------------------------------------------------------------------------- /murmurhash3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-04-03 smartmx the first version 9 | * 10 | */ 11 | /* 12 | * @Note: 13 | * This is murmur3 hash algorithm: https://github.com/aappleby/smhasher. 14 | * But coded with c instead. 15 | * This repository only accomplish 32bit hash algorithm. 16 | */ 17 | #include "murmurhash3.h" 18 | 19 | #define ROTL32(x,y) ((x << y) | (x >> (32 - y))) 20 | 21 | /** 22 | * calculate hash_code. 23 | * 24 | * @param hash_key the hash_key start address. 25 | * @param len the length of the hash_key. 26 | * 27 | * @return type uint32_t, the result of calculated value. 28 | */ 29 | uint32_t murmurhash3_caculate32(const void *hash_key, uint32_t len) 30 | { 31 | const uint8_t *hash_key_ptr = (const uint8_t *)hash_key; 32 | int nblocks = len / 4; 33 | uint32_t hash = MURMURHASH3_SEED_VALUE; 34 | uint32_t data; 35 | 36 | /* body */ 37 | while (nblocks > 0) 38 | { 39 | /* get 32bit data */ 40 | data = (hash_key_ptr[0] << 24) | (hash_key_ptr[1] << 16) | (hash_key_ptr[2] << 8) | (hash_key_ptr[3]); 41 | 42 | data *= MURMURHASH3_C1_VALUE; 43 | data = ROTL32(data, MURMURHASH3_R1_VALUE); 44 | data *= MURMURHASH3_C2_VALUE; 45 | 46 | hash ^= data; 47 | hash = ROTL32(hash, MURMURHASH3_R2_VALUE); 48 | hash = hash * MURMURHASH3_M_VALUE + MURMURHASH3_N_VALUE; 49 | 50 | hash_key_ptr += 4; 51 | nblocks--; 52 | } 53 | /* tail */ 54 | data = 0; 55 | switch (len & 3) 56 | { 57 | case 3: 58 | data ^= (hash_key_ptr[2] << 16);/* @suppress("No break at end of case") */ 59 | case 2: 60 | data ^= (hash_key_ptr[1] << 8); /* @suppress("No break at end of case") */ 61 | case 1: 62 | data ^= hash_key_ptr[0]; 63 | data *= MURMURHASH3_C1_VALUE; 64 | data = ROTL32(data, MURMURHASH3_R1_VALUE); 65 | data *= MURMURHASH3_C2_VALUE; 66 | hash ^= data; 67 | } 68 | 69 | /* finalization */ 70 | 71 | hash ^= len; 72 | 73 | /* Finalization mix - force all bits of a hash block to avalanche */ 74 | hash ^= hash >> 16; 75 | hash *= 0x85ebca6b; 76 | hash ^= hash >> 13; 77 | hash *= 0xc2b2ae35; 78 | hash ^= hash >> 16; 79 | 80 | return hash; 81 | } 82 | 83 | /** 84 | * upper all lower letters in hash key and calculate the hash_code. 85 | * 86 | * @param c the char value. 87 | * 88 | * @return type uint8_t, the upper char value. 89 | */ 90 | static uint8_t murmurhash3_lower_char_upper(uint8_t c) 91 | { 92 | if ((c >= 'a') && (c <= 'z')) 93 | return c + ('A' - 'a'); 94 | return c; 95 | } 96 | 97 | /** 98 | * upper all lower letters in hash key and calculate the hash_code. 99 | * 100 | * @param c the char value. 101 | * 102 | * @return type uint8_t, the upper char value. 103 | */ 104 | uint8_t murmurhash3_lower_char_upper_memcmp(const void *src1, const void *src2, uint32_t len) 105 | { 106 | uint8_t *src1_char = (uint8_t *)src1; 107 | uint8_t *src2_char = (uint8_t *)src2; 108 | uint32_t all_len = len; 109 | while (all_len) 110 | { 111 | if (murmurhash3_lower_char_upper(*src1_char) == murmurhash3_lower_char_upper(*src2_char)) 112 | { 113 | src1_char++; 114 | src2_char++; 115 | all_len--; 116 | } 117 | else 118 | { 119 | return (len - all_len); 120 | } 121 | 122 | } 123 | return 0; 124 | } 125 | 126 | /** 127 | * upper all lower letters in hash key and calculate the hash_code. 128 | * 129 | * @param hash_key the hash_key start address. 130 | * @param len the length of the hash_key. 131 | * 132 | * @return type uint32_t, the result of calculated value. 133 | */ 134 | uint32_t murmurhash3_upper_caculate32(const void *hash_key, uint32_t len) 135 | { 136 | const uint8_t *hash_key_ptr = (const uint8_t *)hash_key; 137 | int nblocks = len / 4; 138 | uint32_t hash = MURMURHASH3_SEED_VALUE; 139 | uint32_t data; 140 | 141 | /* body */ 142 | while (nblocks > 0) 143 | { 144 | /* get 32bit data */ 145 | data = (murmurhash3_lower_char_upper(hash_key_ptr[0]) << 24) | \ 146 | (murmurhash3_lower_char_upper(hash_key_ptr[1]) << 16) | \ 147 | (murmurhash3_lower_char_upper(hash_key_ptr[2]) << 8) | \ 148 | (murmurhash3_lower_char_upper(hash_key_ptr[3])); 149 | 150 | data *= MURMURHASH3_C1_VALUE; 151 | data = ROTL32(data, MURMURHASH3_R1_VALUE); 152 | data *= MURMURHASH3_C2_VALUE; 153 | 154 | hash ^= data; 155 | hash = ROTL32(hash, MURMURHASH3_R2_VALUE); 156 | hash = hash * MURMURHASH3_M_VALUE + MURMURHASH3_N_VALUE; 157 | 158 | hash_key_ptr += 4; 159 | nblocks--; 160 | } 161 | /* tail */ 162 | data = 0; 163 | switch (len & 3) 164 | { 165 | case 3: 166 | data ^= (murmurhash3_lower_char_upper(hash_key_ptr[2]) << 16);/* @suppress("No break at end of case") */ 167 | case 2: 168 | data ^= (murmurhash3_lower_char_upper(hash_key_ptr[1]) << 8); /* @suppress("No break at end of case") */ 169 | case 1: 170 | data ^= murmurhash3_lower_char_upper(hash_key_ptr[0]); 171 | data *= MURMURHASH3_C1_VALUE; 172 | data = ROTL32(data, MURMURHASH3_R1_VALUE); 173 | data *= MURMURHASH3_C2_VALUE; 174 | hash ^= data; 175 | } 176 | 177 | /* finalization */ 178 | 179 | hash ^= len; 180 | 181 | /* Finalization mix - force all bits of a hash block to avalanche */ 182 | hash ^= hash >> 16; 183 | hash *= 0x85ebca6b; 184 | hash ^= hash >> 13; 185 | hash *= 0xc2b2ae35; 186 | hash ^= hash >> 16; 187 | 188 | return hash; 189 | } 190 | -------------------------------------------------------------------------------- /murmurhash3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-04-03 smartmx the first version 9 | * 10 | */ 11 | /* 12 | * @Note: 13 | * This is murmur3 hash algorithm: https://github.com/aappleby/smhasher. 14 | * But coded with c instead. 15 | * This repository only accomplish 32bit hash algorithm. 16 | */ 17 | #ifndef _MURMURHASH3_H_ 18 | #define _MURMURHASH3_H_ 19 | 20 | #include "stdio.h" 21 | #include "stdint.h" 22 | 23 | /* The seed value, you can change as you want. */ 24 | #define MURMURHASH3_SEED_VALUE 0x31313137 25 | 26 | /* The hash calculate parameters value, don't change it unless it's necessary. */ 27 | #define MURMURHASH3_C1_VALUE 0xcc9e2d51 28 | #define MURMURHASH3_C2_VALUE 0x1b873593 29 | #define MURMURHASH3_R1_VALUE 15 30 | #define MURMURHASH3_R2_VALUE 13 31 | #define MURMURHASH3_M_VALUE 5 32 | #define MURMURHASH3_N_VALUE 0xe6546b64 33 | 34 | extern uint32_t murmurhash3_caculate32(const void *hash_key, uint32_t len); 35 | 36 | extern uint32_t murmurhash3_upper_caculate32(const void *hash_key, uint32_t len); 37 | 38 | extern uint8_t murmurhash3_lower_char_upper_memcmp(const void *src1, const void *src2, uint32_t len); 39 | 40 | #endif /* _MURMURHASH3_H_ */ 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # hash-match 2 | 3 | 哈希匹配算法在单片机上的应用 4 | 5 | ## hash-match设计前言 6 | 7 | 在单片机日常开发中,总会遇到根据收到的不同数据执行不同的程序,通常都会使用`memcmp`或者`strcmp`进行比较。 8 | 9 | 在匹配数据情况不多时,的确很合适。 10 | 11 | 但是一旦工程量变大,运行速度下降明显。比如有200个数据数组等待比较,每次都要比较很多次。 12 | 13 | 经常可以看到有的工程里`if(memcmp()) {}`后边有上百个`else if(memcmp()) {}`,极度影响代码运行效率。 14 | 15 | 此时,将key的数组特征化,计算出特有的hash值,来代替比较,就可以给运行速度带来很大的提升。 16 | 17 | ## HASH算法 18 | 19 | hash-match库中提供了两种哈希算法: 20 | 21 | ### [Murmurhash3](https://github.com/aappleby/smhasher) 22 | 23 | MurmurHash是一种非加密函数的哈希函数,它能够产生出32-bit或128-bit哈希值。自称超级快的hash算法,是FNV的4-5倍。 24 | 25 | MurmurHash算法由Austin Appleby创建于2008年,现已应用到Hadoop、libstdc 、nginx、libmemcached,Redis,Memcached,Cassandra,HBase,Lucene等开源系统。 26 | 27 | 用户可以移植其他匹配算法,通过更改`hash_match_caculate`宏定义决定使用哪一种。 28 | 29 | ### Simplehash 30 | 31 | Simplehash是一种非常简单的哈希函数,它能够产生出32-bit哈希值。运行速度很快,更适合低速单片机使用。 32 | 33 | Simplehash算法是参考[cmd-parser](https://github.com/jiejieTop/cmd-parser)。 34 | 35 | ## 使用方法 36 | 37 | 库中头文件参照了rt-thread中针对不同的编译器设置了不同的宏定义,目前支持MDK、IAR和GNU gcc。其他编译器暂未移植,一般来说已经满足使用。如果有其他编译器需求,请自行移植。 38 | 39 | 使用中只需要2个API函数和一个数据定义宏 40 | 41 | ```c 42 | HASH_MATCH_EXPORT(GROUP, NAME, hash_key_src, hash_key_len, handler, ...); 43 | ``` 44 | 45 | 使用`HASH_MATCH_EXPORT`定义数据参数定义到指定代码段 46 | 47 | ```c 48 | HASH_MATCH_INIT(GROUP); 49 | ``` 50 | 51 | 使用`HASH_MATCH_INIT`将初始化整个组内的哈希值 52 | 53 | ```c 54 | HASH_MATCH(GROUP, SRC, LEN, PARAMS); 55 | ``` 56 | 57 | 使用`HASH_MATCH`将对提供的key进行哈希匹配 58 | 59 | ### MDK和IAR编译器使用方法 60 | 61 | MDK和IAR中都在软件内部进行更改了代码编译后的链接操作,可以很方便的使用,无需修改工程文件。 62 | 63 | #### GROUP命名 64 | 65 | 选择一个GROUP名字,例如hash_match_test 66 | 67 | #### 注册命令 68 | 69 | 使用该名字定义该组下的所有命令,定义应当为全局变量定义,即放在函数外部 70 | 71 | ```c 72 | HASH_MATCH_EXPORT(hash_match_test, hash_match_test1, hash_match_test1key, sizeof(hash_match_test1key), hash_match_test1func, "this is test 1"); 73 | ``` 74 | 75 | #### 初始化GROUP 76 | 77 | ```c 78 | HASH_MATCH_INIT(hash_match_test); 79 | ``` 80 | 81 | #### 匹配key 82 | 83 | ```c 84 | HASH_MATCH(hash_match_test, hash_match_test1key, sizeof(hash_match_test1key), NULL); 85 | ``` 86 | 87 | ### GUN gcc编译器使用方法 88 | 89 | GUN gcc工程需要在工程的ld文件中找到非零变量的初始化链接代码。 90 | 91 | hash-match具有分组信息,即使用时的GROUP参数。 92 | 93 | 在gnu gcc编译器时,通过宏定义展开,每个GROUP定义的const常量都会被放到__attribute__((section("GROUP")))中 94 | 95 | 例如,应用代码中串口有命令需要处理,将GROUP参数传入为uart,则const常量会被放到__attribute__((section("uart")))中 96 | 97 | 而gnu gcc编译器并没有在链接代码操作中有自定义的操作 98 | 99 | 所以根据不同的GROUP,在ld文件的`.text`段中添加代码,本例子将以GROUP参数为hash_match_test为例子 100 | 101 | ```ld 102 | . = ALIGN(4); 103 | PROVIDE(hash_match_test_start = .); 104 | KEEP(*(hash_match_test)) 105 | PROVIDE(hash_match_test_end = .); 106 | . = ALIGN(4); 107 | ``` 108 | 109 | 例如,提供的例程中就可以将ld文件修改如下: 110 | 111 | ```ld 112 | .text : 113 | { 114 | . = ALIGN(4); 115 | KEEP(*(SORT_NONE(.handle_reset))) 116 | *(.text) 117 | *(.text.*) 118 | *(.rodata) 119 | *(.rodata*) 120 | *(.sdata2.*) 121 | *(.glue_7) 122 | *(.glue_7t) 123 | *(.gnu.linkonce.t.*) 124 | 125 | /* this is for GROUP hash_match_test of hash_match library. */ 126 | . = ALIGN(4); 127 | PROVIDE(hash_match_test_start = .); 128 | KEEP(*(hash_match_test)) 129 | . = ALIGN(4); 130 | PROVIDE(hash_match_test_end = .); 131 | 132 | /* this is for GROUP hash_match_test1 of hash_match library. */ 133 | . = ALIGN(4); 134 | PROVIDE(hash_match_test1_start = .); 135 | KEEP(*(hash_match_test)) 136 | . = ALIGN(4); 137 | PROVIDE(hash_match_test1_end = .); 138 | 139 | . = ALIGN(4); 140 | } >FLASH AT>FLASH 141 | ``` 142 | 143 | 代码中使用了几个GROUP,就需要在ld中添加几个相关代码,其他步骤和MDK和IAR编译器使用方法一致 144 | 145 | ## 配置选项 146 | 147 | 配置选项可以在`hash-match.h`中修改 148 | 149 | ```c 150 | #define hash_match_caculate murmurhash3_caculate32 151 | 152 | /* whether save description of hash source or not, set 0 will not save description. */ 153 | #define HASH_MATCH_SAVE_DESC 0 154 | 155 | /* set HASH_MATCH_INIT_CHECK to 1 will check all hash values in one group during init a group, report if some hash value is duplicated. */ 156 | #define HASH_MATCH_INIT_CHECK 0 157 | 158 | /* change to your own printf function, or don't use it. */ 159 | #define HASH_MATCH_PRINTF printf 160 | 161 | /* whether compare key when hash_code is same. */ 162 | #define HASH_MATCH_COMPARE_KEY 1 163 | 164 | /* use string.h or self functions to compare key. */ 165 | #define HASH_MATCH_USE_STRING_H 1 166 | 167 | #if HASH_MATCH_USE_STRING_H 168 | #include "string.h" 169 | #define hash_match_memcmp memcmp 170 | #define HASH_MATCH_MEMCMP_SAME 0 171 | #else 172 | #define hash_match_memcmp 173 | #define HASH_MATCH_MEMCMP_SAME 174 | #endif 175 | ``` 176 | 177 | `hash_match_caculate`宏定义决定了`hash-match`采用的哈希算法,移植如需要改为自己的算法,请按照`uint32_t murmurhash3_caculate32(const void *hash_key, uint32_t len);`函数接口实现。 178 | 179 | `HASH_MATCH_SAVE_DESC`宏定义决定了是否会保存描述,设置为非0就会启用。 180 | 181 | `HASH_MATCH_INIT_CHECK`宏定义决定在初始化时,是否和已经计算得到的其他哈希值进行比较,如果有相同的哈希值,则报错。 182 | 183 | `HASH_MATCH_PRINTF`宏定义是在有报错信息时候调用的打印函数。 184 | 185 | `HASH_MATCH_COMPARE_KEY`宏定义决定了在哈希值匹配成功后,是否继续匹配数据内容。一般来说,长度一样的数据内容,哈希值一样的概率很小,所以可以根据安全性要求来决定是否继续匹配数据内容。 186 | 187 | `HASH_MATCH_USE_STRING_H`宏定义决定了匹配数据内容使用的函数,如果不准备使用,可以改为其他函数。 188 | 189 | ## 大小写通用匹配 190 | 191 | `hash-match.c`中并未直接处理大小写可以通用的转换,需要在hash计算的函数中处理。 192 | 193 | 目前在`murmurhash3.c`中提供了转换大小写的处理函数,如需使用,需要把配置项改为如下: 194 | 195 | ```c 196 | #define hash_match_caculate murmurhash3_upper_caculate32 197 | 198 | /* whether save description of hash source or not, set 0 will not save description. */ 199 | #define HASH_MATCH_SAVE_DESC 0 200 | 201 | /* set HASH_MATCH_INIT_CHECK to 1 will check all hash values in one group during init a group, report if some hash value is duplicated. */ 202 | #define HASH_MATCH_INIT_CHECK 0 203 | 204 | /* change to your own printf function, or don't use it. */ 205 | #define HASH_MATCH_PRINTF printf 206 | 207 | /* whether compare key when hash_code is same. */ 208 | #define HASH_MATCH_COMPARE_KEY 1 209 | 210 | /* use string.h or self functions to compare key. */ 211 | #define HASH_MATCH_USE_STRING_H 0 212 | 213 | #if HASH_MATCH_USE_STRING_H 214 | #include "string.h" 215 | #define hash_match_memcmp memcmp 216 | #define HASH_MATCH_MEMCMP_SAME 0 217 | #else 218 | #define hash_match_memcmp murmurhash3_lower_char_upper_memcmp 219 | #define HASH_MATCH_MEMCMP_SAME 0 220 | #endif 221 | ``` 222 | 223 | `murmurhash3_upper_caculate32`函数会将小写字母转换为大写字母再进行哈希值计算 224 | 225 | `murmurhash3_lower_char_upper_memcmp`函数会将小写字母转换为大写字母后再比较是否相同 226 | 227 | ## 通过宏定义来简化定义过程 228 | 229 | 例如,你现在打算为每个AT指令创建函数,可以在头文件中增加如下宏定义: 230 | 231 | ```c 232 | #define _AT_HASH_MATCH_EXPORT(A, B) static void A##_handler(at_uart_data_t *p); \ 233 | HASH_MATCH_EXPORT(uart_at_cmd, A, B, (sizeof(B) - 1), A##_handler); \ 234 | static void A##_handler(at_uart_data_t *p) 235 | 236 | #define AT_HASH_MATCH_EXPORT(X) _AT_HASH_MATCH_EXPORT(at_##X, #X) 237 | ``` 238 | 239 | 那么就可以在c文件里很快的定义出函数: 240 | 241 | ```c 242 | /* 243 | * AT+CONNECT命令,启用连接 244 | */ 245 | AT_HASH_MATCH_EXPORT(connect) 246 | { 247 | printf("+CONNECT:1" "\r\nOK\r\n"); 248 | } 249 | ``` 250 | 251 | 上述函数展开后: 252 | 253 | ```c 254 | /* 255 | * AT+CONNECT命令,启用连接 256 | */ 257 | static void at_connect_handler(at_uart_data_t *p); 258 | static uint32_t at_connect_hash_code = 0; 259 | __attribute__((used)) const hash_match_t at_connect __attribute__((section("uart_at_cmd"))) = 260 | { 261 | "connect", 262 | (sizeof("connect") - 1), 263 | &at_connect_hash_code, 264 | (hash_match_handler) &at_connect_handler, 265 | }; 266 | 267 | static void at_connect_handler(at_uart_data_t *p) 268 | { 269 | printf("+CONNECT:1" "\r\nOK\r\n"); 270 | } 271 | ``` 272 | 273 | ## 注意事项 274 | 275 | 虽然头文件中提供了函数,但是一般不直接使用那些函数,因为需要根据不同编译器有不同的配置,所以此时需要使用宏定义的函数来对不同编译器产生适用性。 276 | 277 | 库中头文件参照了rt-thread中针对不同的编译器设置了不同的宏定义,目前支持MDK、IAR和GNU gcc。其他编译器暂未移植,一般来说已经满足使用。如果有其他编译器需求,请自行移植。 278 | 279 | hash-match没有使用可变参数传递,匹配回调函数只携带了一个参数`void *`。 280 | 281 | ## 示例程序 282 | 283 | rt-thread移植例程: 284 | 285 | [examples/hash_match_demo.c](https://github.com/smartmx/hash-match/tree/main/examples/hash_match_demo.c)。 286 | 287 | 可以通过`RT-Thread menuconfig`方式,将文件添加至工程: 288 | 289 | ```c 290 | RT-Thread online packages ---> 291 | tools packages ---> 292 | [*] hash-match: using hashmap on MCUs ---> 293 | [*] Enable hash match demo 294 | version (latest) ---> 295 | ``` 296 | 297 | ## 版权许可 298 | 299 | hash-match 遵循 Apache License v2.0 开源协议。 300 | 301 | 欢迎大家提交PR,并在change log中附上自己的修改日志。 302 | 303 | ## [博客主页](https://blog.maxiang.vip/) 304 | -------------------------------------------------------------------------------- /simplehash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2023-03-03 smartmx the first version 9 | * 10 | */ 11 | /* 12 | * @Note: 13 | * This simple hash algorithm comes from: https://github.com/jiejieTop/cmd-parser. 14 | */ 15 | #include "simplehash.h" 16 | 17 | /** 18 | * calculate hash_code. 19 | * 20 | * @param hash_key the hash_key start address. 21 | * @param len the length of the hash_key. 22 | * 23 | * @return type uint32_t, the result of calculated value. 24 | */ 25 | uint32_t simplehash_caculate32(const void *hash_key, uint32_t len) 26 | { 27 | const uint8_t *hash_key_ptr = (const uint8_t *)hash_key; 28 | uint32_t clen, data; 29 | uint32_t hash = 0; 30 | uint32_t seed = SIMPLEHASH_SEED_VALUE; 31 | 32 | /* body */ 33 | for (clen = 0; clen < len; clen++) 34 | { 35 | data = *hash_key_ptr; 36 | hash = (hash ^ seed) + data; 37 | hash_key_ptr++; 38 | } 39 | 40 | return hash; 41 | } 42 | 43 | /** 44 | * upper all lower letters in hash key and calculate the hash_code. 45 | * 46 | * @param c the char value. 47 | * 48 | * @return type uint8_t, the upper char value. 49 | */ 50 | static uint8_t simplehash_lower_char_upper(uint8_t c) 51 | { 52 | if ((c >= 'a') && (c <= 'z')) 53 | return c + ('A' - 'a'); 54 | return c; 55 | } 56 | 57 | /** 58 | * upper all lower letters in hash key and calculate the hash_code. 59 | * 60 | * @param c the char value. 61 | * 62 | * @return type uint8_t, the upper char value. 63 | */ 64 | uint8_t simplehash_lower_char_upper_memcmp(const void *src1, const void *src2, uint32_t len) 65 | { 66 | uint8_t *src1_char = (uint8_t *)src1; 67 | uint8_t *src2_char = (uint8_t *)src2; 68 | uint32_t all_len = len; 69 | while (all_len) 70 | { 71 | if (simplehash_lower_char_upper(*src1_char) == simplehash_lower_char_upper(*src2_char)) 72 | { 73 | src1_char++; 74 | src2_char++; 75 | all_len--; 76 | } 77 | else 78 | { 79 | return (len - all_len); 80 | } 81 | 82 | } 83 | return 0; 84 | } 85 | 86 | /** 87 | * upper all lower letters in hash key and calculate the hash_code. 88 | * 89 | * @param hash_key the hash_key start address. 90 | * @param len the length of the hash_key. 91 | * 92 | * @return type uint32_t, the result of calculated value. 93 | */ 94 | uint32_t simplehash_upper_caculate32(const void *hash_key, uint32_t len) 95 | { 96 | const uint8_t *hash_key_ptr = (const uint8_t *)hash_key; 97 | uint32_t clen, data; 98 | uint32_t hash = 0; 99 | uint32_t seed = SIMPLEHASH_SEED_VALUE; 100 | 101 | /* body */ 102 | for (clen = 0; clen < len; clen++) 103 | { 104 | data = simplehash_lower_char_upper(*hash_key_ptr); 105 | hash = (hash ^ seed) + data; 106 | hash_key_ptr++; 107 | } 108 | 109 | return hash; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /simplehash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023, smartmx 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2023-03-03 smartmx the first version 9 | * 10 | */ 11 | #ifndef _SIMPLEHASH_H_ 12 | #define _SIMPLEHASH_H_ 13 | 14 | #include "stdio.h" 15 | #include "stdint.h" 16 | 17 | /* The seed value, you can change as you want. */ 18 | #define SIMPLEHASH_SEED_VALUE 0x30393235 19 | 20 | extern uint32_t simplehash_caculate32(const void *hash_key, uint32_t len); 21 | 22 | extern uint32_t simplehash_upper_caculate32(const void *hash_key, uint32_t len); 23 | 24 | extern uint8_t simplehash_lower_char_upper_memcmp(const void *src1, const void *src2, uint32_t len); 25 | 26 | #endif /* _SIMPLEHASH_H_ */ 27 | --------------------------------------------------------------------------------