Skip to content

Commit 7f1c790

Browse files
committed
Move find_bin_for_hash() to repository_lib
Move repository_tool._find_bin_for_hash() and helper functions it uses to non-protected functions in repository_lib. These functions will be useful to adopters using the WIP low-level API for updating metadata files (see theupdateframework#1048) Signed-off-by: Joshua Lock <[email protected]>
1 parent b18eb34 commit 7f1c790

File tree

2 files changed

+113
-88
lines changed

2 files changed

+113
-88
lines changed

tuf/repository_lib.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,114 @@ def get_metadata_versioninfo(rolename, repository_name):
980980

981981

982982

983+
def create_bin_name(low, high, prefix_len):
984+
"""
985+
<Purpose>
986+
Create a string name of a delegated hash bin, where name will be a range of
987+
zero-padded (up to prefix_len) strings i.e. for low=00, high=07,
988+
prefix_len=3 the returned name would be '000-007'.
989+
990+
<Arguments>
991+
low:
992+
The low end of the prefix range to be binned
993+
994+
high:
995+
The high end of the prefix range to be binned
996+
997+
prefix_len:
998+
The length of the prefix range components
999+
1000+
<Returns>
1001+
A string bin name, with each end of the range zero-padded up to prefix_len
1002+
"""
1003+
if low == high:
1004+
return "{low:0{len}x}".format(low=low, len=prefix_len)
1005+
1006+
return "{low:0{len}x}-{high:0{len}x}".format(low=low, high=high,
1007+
len=prefix_len)
1008+
1009+
1010+
1011+
1012+
1013+
def get_bin_numbers(number_of_bins):
1014+
"""
1015+
<Purpose>
1016+
Given the desired number of bins (number_of_bins) calculate the prefix
1017+
length (prefix_length), total number of prefixes (prefix_count) and the
1018+
number of prefixes to be stored in each bin (bin_size).
1019+
Example: number_of_bins = 32
1020+
prefix_length = 2
1021+
prefix_count = 256
1022+
bin_size = 8
1023+
That is, each of the 32 hashed bins are responsible for 8 hash prefixes,
1024+
i.e. 00-07, 08-0f, ..., f8-ff.
1025+
1026+
<Arguments>
1027+
number_of_bins:
1028+
The number of hashed bins in use
1029+
1030+
<Returns>
1031+
A tuple of three values:
1032+
1. prefix_length: the length of each prefix
1033+
2. prefix_count: the total number of prefixes in use
1034+
3. bin_size: the number of hash prefixes to be stored in each bin
1035+
"""
1036+
# Convert 'number_of_bins' to hexadecimal and determine the number of
1037+
# hexadecimal digits needed by each hash prefix
1038+
prefix_length = len("{:x}".format(number_of_bins - 1))
1039+
# Calculate the total number of hash prefixes (e.g., 000 - FFF total values)
1040+
prefix_count = 16 ** prefix_length
1041+
# Determine how many prefixes to assign to each bin
1042+
bin_size = prefix_count // number_of_bins
1043+
1044+
# For simplicity, ensure that 'prefix_count' (16 ^ n) can be evenly
1045+
# distributed over 'number_of_bins' (must be 2 ^ n). Each bin will contain
1046+
# (prefix_count / number_of_bins) hash prefixes.
1047+
if prefix_count % number_of_bins != 0:
1048+
# Note: x % y != 0 does not guarantee that y is not a power of 2 for
1049+
# arbitrary x and y values. However, due to the relationship between
1050+
# number_of_bins and prefix_count, it is true for them.
1051+
raise securesystemslib.exceptions.Error('The "number_of_bins" argument'
1052+
' must be a power of 2.')
1053+
1054+
return prefix_length, prefix_count, bin_size
1055+
1056+
1057+
1058+
1059+
1060+
def find_bin_for_hash(path_hash, number_of_bins):
1061+
"""
1062+
<Purpose>
1063+
For a given hashed filename, path_hash, calculate the name of a hashed bin
1064+
into which this file would be delegated given number_of_bins bins are in
1065+
use.
1066+
1067+
<Arguments>
1068+
path_hash:
1069+
The hash of the target file's path
1070+
1071+
number_of_bins:
1072+
The number of hashed_bins in use
1073+
1074+
<Returns>
1075+
The name of the hashed bin path_hash would be binned into.
1076+
"""
1077+
1078+
prefix_length, _, bin_size = get_bin_numbers(number_of_bins)
1079+
1080+
prefix = int(path_hash[:prefix_length], 16)
1081+
1082+
low = prefix - (prefix % bin_size)
1083+
high = (low + bin_size - 1)
1084+
1085+
return create_bin_name(low, high, prefix_length)
1086+
1087+
1088+
1089+
1090+
9831091
def get_target_hash(target_filepath):
9841092
"""
9851093
<Purpose>

tuf/repository_tool.py

Lines changed: 5 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
#!/usr/bin/env python
23

34
# Copyright 2013 - 2017, New York University and the TUF contributors
@@ -2529,7 +2530,7 @@ def delegate_hashed_bins(self, list_of_targets, keys_of_hashed_bins,
25292530
securesystemslib.formats.ANYKEYLIST_SCHEMA.check_match(keys_of_hashed_bins)
25302531
tuf.formats.NUMBINS_SCHEMA.check_match(number_of_bins)
25312532

2532-
prefix_length, prefix_count, bin_size = _get_bin_numbers(number_of_bins)
2533+
prefix_length, prefix_count, bin_size = repo_lib.get_bin_numbers(number_of_bins)
25332534

25342535
logger.info('Creating hashed bin delegations.\n' +
25352536
repr(len(list_of_targets)) + ' total targets.\n' +
@@ -2543,7 +2544,7 @@ def delegate_hashed_bins(self, list_of_targets, keys_of_hashed_bins,
25432544
ordered_roles = []
25442545
for idx in range(0, prefix_count, bin_size):
25452546
high = idx + bin_size - 1
2546-
name = _create_bin_name(idx, high, prefix_length)
2547+
name = repo_lib.create_bin_name(idx, high, prefix_length)
25472548
if bin_size == 1:
25482549
target_hash_prefixes = [name]
25492550
else:
@@ -2675,7 +2676,7 @@ def add_target_to_bin(self, target_filepath, number_of_bins=DEFAULT_NUM_BINS,
26752676
# TODO: check target_filepath is sane
26762677

26772678
path_hash = repo_lib.get_target_hash(target_filepath)
2678-
bin_name = _find_bin_for_hash(path_hash, number_of_bins)
2679+
bin_name = repo_lib.find_bin_for_hash(path_hash, number_of_bins)
26792680

26802681
# Ensure the Targets object has delegated to hashed bins
26812682
if not self._delegated_roles.get(bin_name, None):
@@ -2737,7 +2738,7 @@ def remove_target_from_bin(self, target_filepath,
27372738
# TODO: check target_filepath is sane?
27382739

27392740
path_hash = repo_lib.get_target_hash(target_filepath)
2740-
bin_name = _find_bin_for_hash(path_hash, number_of_bins)
2741+
bin_name = repo_lib.find_bin_for_hash(path_hash, number_of_bins)
27412742

27422743
# Ensure the Targets object has delegated to hashed bins
27432744
if not self._delegated_roles.get(bin_name, None):
@@ -2839,90 +2840,6 @@ def _keys_to_keydict(keys):
28392840

28402841

28412842

2842-
2843-
def _create_bin_name(low, high, prefix_len):
2844-
"""
2845-
<Purpose>
2846-
Create a string name of a delegated hash bin, where name will be a range of
2847-
zero-padded (up to prefix_len) strings i.e. for low=00, high=07,
2848-
prefix_len=3 the returned name would be '000-007'.
2849-
"""
2850-
if low == high:
2851-
return "{low:0{len}x}".format(low=low, len=prefix_len)
2852-
2853-
return "{low:0{len}x}-{high:0{len}x}".format(low=low, high=high,
2854-
len=prefix_len)
2855-
2856-
2857-
2858-
2859-
2860-
def _get_bin_numbers(number_of_bins):
2861-
"""
2862-
Given the desired number of bins (number_of_bins) calculate the prefix length
2863-
(prefix_length), total number of prefixes (prefix_count) and the number of
2864-
prefixes to be stored in each bin (bin_size).
2865-
Example: number_of_bins = 32
2866-
prefix_length = 2
2867-
prefix_count = 256
2868-
bin_size = 8
2869-
That is, each of the 32 hashed bins are responsible for 8 hash prefixes, i.e.
2870-
00-07, 08-0f, ..., f8-ff.
2871-
"""
2872-
# Convert 'number_of_bins' to hexadecimal and determine the number of
2873-
# hexadecimal digits needed by each hash prefix
2874-
prefix_length = len("{:x}".format(number_of_bins - 1))
2875-
# Calculate the total number of hash prefixes (e.g., 000 - FFF total values)
2876-
prefix_count = 16 ** prefix_length
2877-
# Determine how many prefixes to assign to each bin
2878-
bin_size = prefix_count // number_of_bins
2879-
2880-
# For simplicity, ensure that 'prefix_count' (16 ^ n) can be evenly
2881-
# distributed over 'number_of_bins' (must be 2 ^ n). Each bin will contain
2882-
# (prefix_count / number_of_bins) hash prefixes.
2883-
if prefix_count % number_of_bins != 0:
2884-
# Note: x % y != 0 does not guarantee that y is not a power of 2 for
2885-
# arbitrary x and y values. However, due to the relationship between
2886-
# number_of_bins and prefix_count, it is true for them.
2887-
raise securesystemslib.exceptions.Error('The "number_of_bins" argument'
2888-
' must be a power of 2.')
2889-
2890-
return prefix_length, prefix_count, bin_size
2891-
2892-
2893-
2894-
2895-
def _find_bin_for_hash(path_hash, number_of_bins):
2896-
"""
2897-
<Purpose>
2898-
For a given hashed filename, path_hash, calculate the name of a hashed bin
2899-
into which this file would be delegated given number_of_bins bins are in
2900-
use.
2901-
2902-
<Arguments>
2903-
path_hash:
2904-
The hash of the target file's path
2905-
2906-
number_of_bins:
2907-
The number of hashed_bins in use
2908-
2909-
<Returns>
2910-
The name of the hashed bin path_hash would be binned into.
2911-
"""
2912-
2913-
prefix_length, _, bin_size = _get_bin_numbers(number_of_bins)
2914-
2915-
prefix = int(path_hash[:prefix_length], 16)
2916-
2917-
low = prefix - (prefix % bin_size)
2918-
high = (low + bin_size - 1)
2919-
2920-
return _create_bin_name(low, high, prefix_length)
2921-
2922-
2923-
2924-
2925-
29262843
def create_new_repository(repository_directory, repository_name='default',
29272844
storage_backend=None):
29282845
"""

0 commit comments

Comments
 (0)