diff options
author | LiChen <akaineko@google.com> | 2016-11-28 12:15:33 +0800 |
---|---|---|
committer | SzuWei Lin <szuweilin@google.com> | 2016-12-05 23:30:16 +0000 |
commit | 3084ce7cbdff84093286459758f99c15082e6556 (patch) | |
tree | a77b76e8f4f75bddc23ccc0ec52c00f2baf94b3d /ufdt_node_dict.c | |
parent | b53a69fa5218e6a5069d8ce35148ff882b448ac8 (diff) | |
download | libufdt-3084ce7cbdff84093286459758f99c15082e6556.tar.gz |
libufdt: device tree overlay via unflattening FDT
The original version of libdtoverlay is slow in searching for
particular nodes and adding subnodes/properties due to the operations
on flattened device tree (FDT).
`libufdt` builds a real tree structure (named ufdt -- unflattned
device tree) from FDT. In the real tree, we can perform certain
operations (e.g., merge 2 subtrees, search for a node by path) in
almost optimal time complexity with acceptable additional memory usage.
With libufdt, we improve the merging of two dtb files from O(N^2) to
O(N), where N is the number of nodes in the tree.
Bug: 30800619
Test: run test scripts (see libufdt/tests/README)
Test: manually ported libufdt into a bootloader,
checked it can merge a base dtb and a dtbo to generate a final dtb
to boot the device
Change-Id: I6a282cc99129d5280ecbf40852723f83735fa523
Diffstat (limited to 'ufdt_node_dict.c')
-rw-r--r-- | ufdt_node_dict.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/ufdt_node_dict.c b/ufdt_node_dict.c new file mode 100644 index 0000000..448bc49 --- /dev/null +++ b/ufdt_node_dict.c @@ -0,0 +1,178 @@ + +#include "libufdt.h" +#include "ufdt_util.h" + +/* + * BEGIN of Hash table internal implementations. + */ + +#define DICT_LIMIT_NUM 2 +#define DICT_LIMIT_DEN 3 + +static bool _ufdt_node_dict_is_too_full(struct ufdt_node_dict *dict) { + /* + * We say a dict is too full if it's DICT_LIMIT_NUM / DICT_LIMIT_DEN full. + */ + if (dict->num_used * DICT_LIMIT_DEN > dict->mem_size * DICT_LIMIT_NUM) + return true; + return false; +} + +/* + * If collision happened, use linear probing to find idx in the hash table. + */ +static int _ufdt_node_dict_find_index_by_name_len(struct ufdt_node **hash_table, + int size, const char *name, + int len) { + if (!name || !size) return -1; + /* + * All size should be 2^k for some k + */ + int hsh = get_hash_len(name, len); + int idx = hsh & (size - 1); + for (int cnt = 0; cnt < size; ++cnt) { + if (hash_table[idx] == NULL) return idx; + if (node_name_eq(hash_table[idx], name, len) == 1) return idx; + ++idx; + idx &= (size - 1); + } + return -1; +} + +static int _ufdt_node_dict_find_index_by_name(struct ufdt_node **hash_table, + int size, const char *name) { + return _ufdt_node_dict_find_index_by_name_len(hash_table, size, name, + dto_strlen(name)); +} + +static int _ufdt_node_dict_find_index_in_ht(struct ufdt_node **hash_table, + int size, struct ufdt_node *x) { + if (x == NULL) return -1; + return _ufdt_node_dict_find_index_by_name(hash_table, size, name_of(x)); +} + +/* + * END of Hash table internal implementations. + */ + +/* + * ufdt_node_dict methods. + */ + +struct ufdt_node_dict ufdt_node_dict_construct() { + struct ufdt_node_dict res; + res.mem_size = DTNL_INIT_SZ; + res.num_used = 0; + res.nodes = dto_malloc(DTNL_INIT_SZ * sizeof(struct ufdt_node *)); + if (res.nodes == NULL) { + res.mem_size = 0; + return res; + } + dto_memset(res.nodes, 0, DTNL_INIT_SZ * sizeof(struct ufdt_node *)); + return res; +} + +void ufdt_node_dict_destruct(struct ufdt_node_dict *dict) { + if (dict == NULL) return; + dto_free(dict->nodes); + dict->mem_size = dict->num_used = 0; +} + +static int ufdt_node_dict_resize(struct ufdt_node_dict *dict) { + if (dict == NULL) return -1; + + int new_size = dict->mem_size << 1; + + struct ufdt_node **new_nodes = + dto_malloc(new_size * sizeof(struct ufdt_node *)); + + dto_memset(new_nodes, 0, new_size * sizeof(struct ufdt_node *)); + + for (int i = 0; i < dict->mem_size; i++) { + struct ufdt_node *node = dict->nodes[i]; + if (node == NULL) continue; + int idx = _ufdt_node_dict_find_index_in_ht(new_nodes, new_size, node); + if (idx < 0) { + dto_error( + "failed to find new index in ufdt_node_dict resize for entry :%s:\n", + name_of(node)); + dto_free(new_nodes); + return -1; + } + new_nodes[idx] = node; + } + + dto_free(dict->nodes); + + dict->mem_size = new_size; + dict->nodes = new_nodes; + return 0; +} + +int ufdt_node_dict_add(struct ufdt_node_dict *dict, struct ufdt_node *node) { + if (node == NULL) return -1; + if (dict == NULL) return -1; + + int idx = _ufdt_node_dict_find_index_in_ht(dict->nodes, dict->mem_size, node); + if (idx < 0) { + dto_error("failed to find new index in ufdt_node_dict add for entry :%s:\n", + name_of(node)); + return -1; + } + + if (dict->nodes[idx] == NULL) ++dict->num_used; + dict->nodes[idx] = node; + + /* + * When the hash table is too full, double the size and rehashing. + */ + int err = 0; + if (_ufdt_node_dict_is_too_full(dict)) { + err = ufdt_node_dict_resize(dict); + } + + return err; +} + +/* + * BEGIN of ufdt_dict searching related methods. + */ + +/* + * return a pointer to the hash table entry + */ +struct ufdt_node **ufdt_node_dict_find_len(struct ufdt_node_dict *dict, + const char *name, int len) { + if (dict == NULL) return NULL; + int idx = _ufdt_node_dict_find_index_by_name_len(dict->nodes, dict->mem_size, + name, len); + if (idx < 0) return NULL; + if (dict->nodes[idx] == NULL) return NULL; + return dict->nodes + idx; +} + +struct ufdt_node **ufdt_node_dict_find(struct ufdt_node_dict *dict, + const char *name) { + return ufdt_node_dict_find_len(dict, name, dto_strlen(name)); +} + +struct ufdt_node *ufdt_node_dict_find_node_len(struct ufdt_node_dict *dict, + const char *name, int len) { + struct ufdt_node **res = ufdt_node_dict_find_len(dict, name, len); + if (res == NULL) return NULL; + return *res; +} + +struct ufdt_node *ufdt_node_dict_find_node(struct ufdt_node_dict *dict, + const char *name) { + return ufdt_node_dict_find_node_len(dict, name, dto_strlen(name)); +} + +/* + * END of ufdt_dict searching related methods. + */ + +void ufdt_node_dict_print(struct ufdt_node_dict *dict) { + struct ufdt_node **it; + for_each(it, dict) dto_print("%ld -> %s\n", it - dict->nodes, name_of(*it)); +} |