diff --git a/hostnames_allocator/README.md b/hostnames_allocator/README.md new file mode 100644 index 0000000..d5aa561 --- /dev/null +++ b/hostnames_allocator/README.md @@ -0,0 +1,81 @@ +## Task + +(Task description is vaguely reproduced from memory.) + +Implement a hostname allocator supporting two commands, `A hosttype` and `D hostname`, such that: + +* `A hosttype` allocates and returns unique hostname of the form `hosttype123` with the smallest possible integer number (starting with 1) currently free for this host type; +* `D hostname` (where `hostname` is always of the form `hosttype123`) frees this hostname (or does nothing if it wasn't allocated before), allowing to reuse it later. + +For example: + +``` +A gateway +A gateway +A gateway +A proxy +A proxy +D gateway2 +D gateway5 +A proxy +A gateway +A gateway +A gateway +``` +should result in the following allocations made +``` +gateway1 +gateway2 +gateway3 +proxy1 +proxy2 +proxy3 +gateway2 +gateway4 +gateway5 +``` + +No error handling is necessary; panicking if any of the input values is not in the expected format is acceptable. + +## Goals + +The solution should be reasonably fast for both types of commands; it should be implemented within 1-2 hours; readability and maintainability can suffer. + +## Algorithm + +Store hashmap of allocators by host types. + +For every host type, store a self-balancing tree (with access and modify operations taking `O(tree depth) = O(log(tree length))`) with non-adjacent ranges of reserved numbers; +"non-adjacent" as in at least one unallocated number should separate any two ranges. +This way, for every "free" operation, finding a matching range (if any) and updating the tree should take `O(log(number of ranges for host type))`; +and for every "allocate" operation, only the first or the first two ranges need to be checked and updated, +which might mean constant time in best case (depending on the tree implementation) and `O(log(number of ranges for host type))` in worst case +(plus `O(log(number of host types))`). + +See [src/ranged_number_allocator.rs](src/ranged_number_allocator.rs) for more details. + +The asymptotic complexity above is theoretical; in practice, ideal trees don't actually work like that. +In this implementation, Rust's [BTreeMap](https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html) is used +which is supposed to provide the same logarithmic asymptotic complexity in practice. + +Memory requirements are `O(log(number of ranges))` plus combined length of all host types. + +Constant factors were not considered in this implementation. + +## Usage + +Nightly Rust is needed (because `btree_cursors` unstable feature is used). +This implementation can probably be rewritten from `btree_cursors` feature to [`range_mut`](https://doc.rust-lang.org/beta/std/collections/struct.BTreeMap.html#method.range_mut) method, +but then it will be even more complex and even more difficult to reason about. + +Tests are in [`tests/`](tests) and can be run with: + +``` +cargo test +``` + +Sample input commands are in [`sample.in`](sample.in) and can be run with: + +``` +cargo run < sample.in +```