bucket.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # COPYRIGHT: Openmoko Inc. 2010
  4. # LICENSE: GPL Version 3 or later
  5. # DESCRIPTION: Article Rendering
  6. # AUTHORS: Christopher Hall <hsw@openmoko.com>
  7. import sys
  8. import os
  9. class Bucket(object):
  10. """maintain a set of containers"""
  11. def __init__(self, max_buckets = 10, bucket_size = 10240, max_items_per_bucket = 30):
  12. """set up the instance"""
  13. self.max_buckets = max_buckets
  14. self.bucket_size = bucket_size
  15. self.max_items_per_bucket = max_items_per_bucket
  16. self.buckets = []
  17. for i in range(max_buckets):
  18. self.buckets += [[]]
  19. self.bucket_remaining = [bucket_size] * max_buckets
  20. self.bucket_counts = [0] * max_buckets
  21. def write(self, data):
  22. """user supplied callback to output the list: [(size, data),...]"""
  23. raise AttributeError('Virtual function Bucket.write called')
  24. def find_bucket(self, size):
  25. """find a bucket with some space in it"""
  26. # bucket with maximum items
  27. for i in range(self.max_buckets):
  28. if self.bucket_counts[i] >= self.max_items_per_bucket:
  29. self.empty(i)
  30. return i
  31. # bucket with sufficient free space
  32. for i in range(self.max_buckets):
  33. if self.bucket_remaining[i] >= size:
  34. return i
  35. # empty the fullest bucket, but try to top it up first
  36. bucket = 0
  37. size = self.bucket_size
  38. for i in range(self.max_buckets):
  39. if self.bucket_remaining[i] < size:
  40. size = self.bucket_remaining[i]
  41. bucket = i
  42. #print('fullest bucket: {0:d}'.format(i))
  43. self.top_up(bucket)
  44. self.empty(bucket)
  45. return bucket
  46. def top_up(self, bucket):
  47. """try to top a bucket from others"""
  48. for i in range(self.max_buckets):
  49. if self.bucket_counts[bucket] >= self.max_items_per_bucket:
  50. break
  51. if i != bucket:
  52. #print('bucket: {0:d}'.format(i))
  53. for j in range(len(self.buckets[i]) - 1, -1, -1):
  54. item = self.buckets[i][j]
  55. #print('bucket: {0:d} {1:d} = {2!r:s}'.format(i, j, item))
  56. size = item[0]
  57. #print('bucket: {0:d} s = {1:d} remaining = {2:d}'.format(i, size, self.bucket_remaining[bucket]))
  58. if [] != size and size <= self.bucket_remaining[bucket]:
  59. self.buckets[bucket] += [(size, item[1])]
  60. self.bucket_remaining[bucket] -= size
  61. del self.buckets[i][j]
  62. self.bucket_counts[i] -= 1
  63. self.bucket_remaining[i] += size
  64. if self.bucket_counts[bucket] >= self.max_items_per_bucket:
  65. break
  66. self.empty(bucket)
  67. return bucket
  68. def empty(self, bucket):
  69. """empty a specific bucket"""
  70. if self.bucket_remaining[bucket] != self.bucket_size:
  71. self.write(self.buckets[bucket])
  72. self.bucket_remaining[bucket] = self.bucket_size
  73. self.bucket_counts[bucket] = 0
  74. self.buckets[bucket] = []
  75. def flush(self):
  76. """empty all buckets"""
  77. for bucket in range(self.max_buckets):
  78. self.top_up(bucket)
  79. self.empty(bucket)
  80. def add(self, data, size):
  81. """add a data item to the first bucket with space"""
  82. if size > self.bucket_size:
  83. self.write([(size, data)])
  84. else:
  85. bucket = self.find_bucket(size)
  86. #print('bucket {0!d}'.format(bucket))
  87. self.buckets[bucket] += [(size, data)]
  88. self.bucket_remaining[bucket] -= size
  89. self.bucket_counts[bucket] += 1
  90. def main():
  91. """simple test"""
  92. class MyBucket(Bucket):
  93. def __init__(self, *args, **kw):
  94. super(MyBucket, self).__init__(*args, **kw)
  95. def write(self, data):
  96. print('write: {0!r:s}'.format(data))
  97. b = MyBucket(max_buckets = 5, bucket_size = 17, max_items_per_bucket = 10)
  98. b.add('big_item', 1000)
  99. b.add('A_init', 1)
  100. b.add('A_one', 3)
  101. b.add('A_two', 3)
  102. b.add('A_three', 5)
  103. b.add('A_four', 4)
  104. b.add('A_five', 4)
  105. b.add('A_six', 3)
  106. b.add('A_seven', 5)
  107. b.add('A_eight', 5)
  108. b.add('A_nine', 4)
  109. b.add('A_ten', 3)
  110. b.add('A_eleven', 6)
  111. b.add('A_twelve', 6)
  112. b.add('A_thirteen', 8)
  113. b.add('B_init', 1)
  114. b.add('B_one', 3)
  115. b.add('B_two', 3)
  116. b.add('B_three', 5)
  117. b.add('B_four', 4)
  118. b.add('B_five', 4)
  119. b.add('B_six', 3)
  120. b.add('B_seven', 5)
  121. b.add('B_eight', 5)
  122. b.add('B_nine', 4)
  123. b.add('B_ten', 3)
  124. b.add('B_eleven', 6)
  125. b.add('B_twelve', 6)
  126. b.add('B_thirteen', 8)
  127. b.add('B_end1', 2)
  128. b.add('B_end2', 2)
  129. b.add('B_end3', 2)
  130. b.flush()
  131. # run the program
  132. if __name__ == "__main__":
  133. main()