fifo-cache 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (11) hide show
  1. data/.document +5 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +20 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +77 -0
  6. data/Rakefile +37 -0
  7. data/VERSION +1 -0
  8. data/lib/fifocache.rb +284 -0
  9. data/qrpc.gemspec +56 -0
  10. data/test +37 -0
  11. metadata +112 -0
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem "depq", ">= 0.4"
5
+ gem "hash-utils", ">= 0.7.0"
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.2"
12
+ end
@@ -0,0 +1,20 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ depq (0.4)
5
+ git (1.2.5)
6
+ hash-utils (0.7.0)
7
+ jeweler (1.5.2)
8
+ bundler (~> 1.0.0)
9
+ git (>= 1.2.5)
10
+ rake
11
+ rake (0.8.7)
12
+
13
+ PLATFORMS
14
+ ruby
15
+
16
+ DEPENDENCIES
17
+ bundler (~> 1.0.0)
18
+ depq (>= 0.4)
19
+ hash-utils (>= 0.7.0)
20
+ jeweler (~> 1.5.2)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 - 2011 Martin Kozák (martinkozak@martinkozak.net)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ FIFO Cache
2
+ ==========
3
+
4
+ **FIFO Cache** is fast hash-like fixed size cache class with FIFO
5
+ functionality which removes oldest or less accessed records based on
6
+ [implicit heap][1].
7
+
8
+ ### Examples and Tracking & Handicap Factor
9
+
10
+ Cache allows track both hits and puts and remove items from cache
11
+ according these statistics. They are turned off by default, but can be
12
+ turned on by setting `#factor` (or `:factor` in costructor) to another
13
+ value than `0`.
14
+
15
+ *Handicap factor* is multiplier of the minimal hits count of all items
16
+ in the cache. It's important set it in some cases.
17
+
18
+ If tracking is turned on and no handicap factor is explicitly set,
19
+ handicap 1 is assigned to new items. It's safe, but not very acceptable
20
+ because cache will become static after filling. So it's necessary (or at
21
+ least higly reasonable) to set priority weighting factor to number
22
+ higher than 1 according dynamics of your application.
23
+
24
+ Usage is simple (examples here are for demonstration purposes written
25
+ without factor set):
26
+
27
+ require "fifocache"
28
+
29
+ cache = Fifocache::new(3, :puts => true) # or 300000, od sure :-)
30
+ cache[:alfa] = 'alfa'
31
+ cache[:beta] = 'beta'
32
+ cache[:gama] = 'gama'
33
+ cache[:delta] = 'delta' # in this moment, :alfa is removed
34
+
35
+ But multiple addings are tracked, so subsequent call:
36
+
37
+ cache[:beta] = 'beta' # :beta, :gama, :delta in cache
38
+ cache[:alfa] = 'alfa' # :beta, :delta, :alfa in cache
39
+
40
+ …will cause `:gama` will be removed, not `:beta` because `:beta` is
41
+ fresher now. If hits tracking is turned on:
42
+
43
+ cache.hits = true # you can do it in costructor too
44
+
45
+ puts cache[:delta] # cache hit
46
+ cache[:gama] = 'gama' # :beta, :delta, :gama in cache
47
+
48
+ …because `:beta` has been put-in two times, `:delta` has been hit
49
+ recently, so `:alfa` is less accessed row and has been removed. In case
50
+ of hits tracking turned off, `:delta` would be removed of sure and
51
+ `:alfa` kept.
52
+
53
+ Changing size of existing cache is possible although reducing the size
54
+ is generally rather slow because of necessity to remove all redundant
55
+ "oldest" rows.
56
+
57
+
58
+ Contributing
59
+ ------------
60
+
61
+ 1. Fork it.
62
+ 2. Create a branch (`git checkout -b 20101220-my-change`).
63
+ 3. Commit your changes (`git commit -am "Added something"`).
64
+ 4. Push to the branch (`git push origin 20101220-my-change`).
65
+ 5. Create an [Issue][2] with a link to your branch.
66
+ 6. Enjoy a refreshing Diet Coke and wait.
67
+
68
+
69
+ Copyright
70
+ ---------
71
+
72
+ Copyright © 2011 [Martin Kozák][3]. See `LICENSE.txt` for
73
+ further details.
74
+
75
+ [1]: http://www.cs.princeton.edu/courses/archive/spr09/cos423/Lectures/i-heaps.pdf
76
+ [2]: http://github.com/martinkozak/qrpc/issues
77
+ [3]: http://www.martinkozak.net/
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "fifo-cache"
17
+ gem.homepage = "http://github.com/martinkozak/fifo-cache"
18
+ gem.license = "MIT"
19
+ gem.summary = 'Fast hash-like fixed size cache class with FIFO functionality which removes oldest or less accessed records based on implicit heap.'
20
+ gem.email = "martinkozak@martinkozak.net"
21
+ gem.authors = ["Martin Kozák"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/rdoctask'
30
+ Rake::RDocTask.new do |rdoc|
31
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
32
+
33
+ rdoc.rdoc_dir = 'rdoc'
34
+ rdoc.title = "qrpc #{version}"
35
+ rdoc.rdoc_files.include('README*')
36
+ rdoc.rdoc_files.include('lib/**/*.rb')
37
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,284 @@
1
+ # encoding: utf-8
2
+ # (c) 2010-2011 Martin Kozák (martinkozak@martinkozak.net)
3
+
4
+ require "hash-utils/object"
5
+ require "depq"
6
+
7
+ ##
8
+ # Represents cache object accessible by similar way as hash,
9
+ # but with fixed capacity with FIFO funcionality.
10
+ #
11
+ # It's useful for limited size caches. Oldest cache records are removed.
12
+ # Also can be used by dynamic mode, so the less putted or less accessed
13
+ # (or both) cache records are removed instead of the oldest records in
14
+ # that cases.
15
+ #
16
+ # For touches tracking utilizes implicit heap queue.
17
+ #
18
+
19
+ class Fifocache
20
+
21
+ @data
22
+ @queue
23
+ @counts
24
+
25
+ ##
26
+ # Integer overflow protection.
27
+ #
28
+
29
+ INFINITY = 1.0/0
30
+
31
+ ##
32
+ # Contains maximal size of the cache.
33
+ # @return [Integer] maximal size of the cache
34
+ #
35
+
36
+ attr_reader :size
37
+ @size
38
+
39
+ ##
40
+ # Indicates puts should be tracked.
41
+ # @return [Boolean]
42
+ #
43
+
44
+ attr_accessor :puts
45
+ @puts
46
+
47
+ ##
48
+ # Indicates hits should be tracked.
49
+ # @return [Boolean]
50
+ #
51
+
52
+ attr_accessor :hits
53
+ @hits
54
+
55
+ ##
56
+ # Indicates new items handicap factor.
57
+ #
58
+ # Handicap factor is multiplier of the min hits count of all items in
59
+ # the cache. It's important set it in some cases. See {#[]=}.
60
+ #
61
+ # @return [Float]
62
+ # @see #[]=
63
+ #
64
+
65
+ attr_accessor :factor
66
+ @factor
67
+
68
+ ##
69
+ # Constructor. Initializes cache to appropriate size.
70
+ #
71
+ # @param [Integer] site size of the cache
72
+ # @param [Hash] opts tracking options
73
+ # @option opts [Boolean] :puts indicates, puts should be tracked
74
+ # @option opts [Boolean] :hits indicates, hits should be tracked
75
+ # @option opts [Float, Integer] :factor indicates new items priority correction factor
76
+ #
77
+
78
+ def initialize(size, opts = { })
79
+ @data = { }
80
+ @queue = Depq::new
81
+ @counts = { }
82
+
83
+ @size = size
84
+ @puts = opts[:puts].to_b
85
+ @hits = opts[:hits].to_b
86
+ @factor = opts[:factor].to_f
87
+ end
88
+
89
+ ##
90
+ # Puts item with key to cache.
91
+ #
92
+ # If tracking is turned on and no {#factor} explicitly set, handicap
93
+ # 1 is assigned to new items. It's safe, but not very acceptable
94
+ # because cache will become static after filling. So it's necessary
95
+ # (or at least higly reasonable) to set priority weighting factor to
96
+ # number higher than 1 according dynamics of your application.
97
+ #
98
+ # @param [Object] key item key
99
+ # @param [Object] item item value
100
+ # @see #factor
101
+ #
102
+
103
+ def []=(key, item)
104
+
105
+ # Adds to cache
106
+
107
+ if not self.has_key? key
108
+
109
+ # Inserts to tracking structures
110
+ @data[key] = item
111
+ locator = @queue.insert(key, __new_priority)
112
+ @counts[key] = locator
113
+
114
+ # Eventually removes first (last)
115
+ if self.length > @size
116
+ self.clean!
117
+ end
118
+
119
+ elsif @puts
120
+ self.touch(key)
121
+ end
122
+
123
+ end
124
+
125
+ ##
126
+ # Returns item from cache.
127
+ #
128
+ # @param [Object] key item key
129
+ # @return [Object] item value
130
+ #
131
+
132
+ def [](key)
133
+ if @hits
134
+ self.touch(key)
135
+ end
136
+
137
+ @data[key]
138
+ end
139
+
140
+ ##
141
+ # Indicates key is in cache.
142
+ #
143
+ # @param [Object] key item key
144
+ # @return [Boolean] +true+ it it is, +false+ otherwise
145
+ #
146
+
147
+ def has_key?(key)
148
+ @data.has_key? key
149
+ end
150
+
151
+ alias :"include?" :"has_key?"
152
+
153
+ ##
154
+ # Touches key in cache.
155
+ # @param [Object] key item key
156
+ #
157
+
158
+ def touch(key)
159
+ locator = @counts[key]
160
+ priority = locator.priority + 1
161
+
162
+ if priority != self.class::INFINITY
163
+ locator.update(key, locator.priority + 1)
164
+ end
165
+ end
166
+
167
+ ##
168
+ # Indicates current size of the cache.
169
+ # @return [Integer] current size of the cache
170
+ #
171
+
172
+ def length
173
+ @data.length
174
+ end
175
+
176
+ ##
177
+ # Sets new size.
178
+ # @param [Integer] size new size
179
+ #
180
+
181
+ def size=(size)
182
+ if size < self.length
183
+ self.clean(self.length - size)
184
+ end
185
+ end
186
+
187
+ alias :resize :"size="
188
+
189
+ ##
190
+ # Removes key.
191
+ #
192
+ # @param [Object] key item key
193
+ # @return [Object] removed item value
194
+ #
195
+
196
+ def remove(key)
197
+ if self.has_key? key
198
+ # Heap queue
199
+ locator = @counts[key]
200
+ @queue.delete_locator(locator)
201
+
202
+ # Data holders
203
+ result = __purge(key)
204
+ else
205
+ result = nil
206
+ end
207
+
208
+ return result
209
+ end
210
+
211
+ ##
212
+ # Cleans specified number of slots.
213
+ # @return [Hash] removed pairs
214
+ #
215
+
216
+ def clean!(count = 1)
217
+ result = { }
218
+ count.times do
219
+ dkey = @queue.delete_min
220
+ result[dkey] = __purge(dkey)
221
+
222
+ if @data.empty?
223
+ break
224
+ end
225
+ end
226
+
227
+ return result
228
+ end
229
+
230
+ alias :clean :"clean!"
231
+
232
+ ##
233
+ # Clear whole cache.
234
+ #
235
+
236
+ def clear!
237
+ @data.replace({ })
238
+ @counts.replace({ })
239
+ @queue.clear
240
+ end
241
+
242
+ alias :clear :"clear!"
243
+
244
+ ##
245
+ # Converts to hash.
246
+ # @return [Hash] cache data
247
+ #
248
+
249
+ def to_h
250
+ @data.dup
251
+ end
252
+
253
+
254
+ private
255
+
256
+ ##
257
+ # Purges item from data holders.
258
+ #
259
+
260
+ def __purge(key)
261
+ result = @data[key]
262
+ @data.delete(key)
263
+ @counts.delete(key)
264
+
265
+ return result
266
+ end
267
+
268
+ ##
269
+ # Returns new priority.
270
+ #
271
+
272
+ def __new_priority
273
+ if (@puts or @hits) and (@factor != 0)
274
+ min = @queue.find_min_locator
275
+ priority = min.nil? ? 1 : (min.priority * @factor)
276
+ else
277
+ priority = 1
278
+ end
279
+
280
+ return priority
281
+ end
282
+
283
+ end
284
+
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{qrpc}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Martin Kozák"]
12
+ s.date = %q{2011-02-10}
13
+ s.email = %q{martinkozak@martinkozak.net}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE.txt",
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/fifocache.rb",
27
+ "test"
28
+ ]
29
+ s.homepage = %q{http://github.com/martinkozak/fifo-cache}
30
+ s.licenses = ["MIT"]
31
+ s.require_paths = ["lib"]
32
+ s.rubygems_version = %q{1.5.1}
33
+ s.summary = %q{Fast hash-like fixed size cache class with FIFO functionality which removes oldest or less accessed records based on implicit heap.}
34
+
35
+ if s.respond_to? :specification_version then
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
39
+ s.add_runtime_dependency(%q<depq>, [">= 0.4"])
40
+ s.add_runtime_dependency(%q<hash-utils>, [">= 0.7.0"])
41
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
42
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
43
+ else
44
+ s.add_dependency(%q<depq>, [">= 0.4"])
45
+ s.add_dependency(%q<hash-utils>, [">= 0.7.0"])
46
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
47
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
48
+ end
49
+ else
50
+ s.add_dependency(%q<depq>, [">= 0.4"])
51
+ s.add_dependency(%q<hash-utils>, [">= 0.7.0"])
52
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
54
+ end
55
+ end
56
+
data/test ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $:.push("./lib")
5
+ require "fifocache"
6
+
7
+
8
+ cache = Fifocache::new(100000, :puts => false, :factor => 0)
9
+ 200000.times do |i|
10
+ cache[i] = i
11
+ end
12
+
13
+ exit
14
+
15
+ cache = Fifocache::new(3, :puts => true)
16
+ cache[:alfa] = 'alfa'
17
+ cache[:beta] = 'beta'
18
+ cache[:gama] = 'gama'
19
+ cache[:delta] = 'delta' # in this moment, :alfa is removed
20
+
21
+ puts cache.to_h.inspect
22
+
23
+ cache[:beta] = 'beta' # :beta, :gama, :delta in cache
24
+ cache[:alfa] = 'alfa' # :beta, :delta, :alfa in cache
25
+
26
+ puts cache.to_h.inspect
27
+
28
+ cache.hits = true # you can do it in costructor too
29
+ puts cache[:delta] # cache hit
30
+ cache[:gama] = 'gama' # :beta, :delta, :gama in cache
31
+
32
+ puts cache.to_h.inspect
33
+
34
+ cache.resize(0)
35
+
36
+ puts cache.to_h.inspect
37
+
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fifo-cache
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - "Martin Koz\xC3\xA1k"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-10 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: depq
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0.4"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: hash-utils
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.7.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: bundler
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ type: :development
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: jeweler
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 1.5.2
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ description:
61
+ email: martinkozak@martinkozak.net
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - LICENSE.txt
68
+ - README.md
69
+ files:
70
+ - .document
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - VERSION
77
+ - lib/fifocache.rb
78
+ - qrpc.gemspec
79
+ - test
80
+ has_rdoc: true
81
+ homepage: http://github.com/martinkozak/fifo-cache
82
+ licenses:
83
+ - MIT
84
+ post_install_message:
85
+ rdoc_options: []
86
+
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 2127215493640234633
95
+ segments:
96
+ - 0
97
+ version: "0"
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project:
107
+ rubygems_version: 1.5.1
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Fast hash-like fixed size cache class with FIFO functionality which removes oldest or less accessed records based on implicit heap.
111
+ test_files: []
112
+