woolen_common 0.0.1
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.
- checksums.yaml +15 -0
- data/README.md +31 -0
- data/ext/woolen_common/extconf.rb +26 -0
- data/ext/woolen_common/linux.h +3 -0
- data/ext/woolen_common/win.c +18 -0
- data/ext/woolen_common/win.h +4 -0
- data/ext/woolen_common/win/puts_color.c +139 -0
- data/ext/woolen_common/win/puts_color.h +5 -0
- data/ext/woolen_common/woolen_common.c +20 -0
- data/ext/woolen_common/woolen_common.h +7 -0
- data/lib/woolen_common.rb +39 -0
- data/lib/woolen_common/abstract_middleware/builder.rb +138 -0
- data/lib/woolen_common/abstract_middleware/map_cfg_manager.rb +52 -0
- data/lib/woolen_common/abstract_middleware/runner.rb +72 -0
- data/lib/woolen_common/action_pool_proxy.rb +28 -0
- data/lib/woolen_common/actionpool.rb +10 -0
- data/lib/woolen_common/actionpool/pool.rb +295 -0
- data/lib/woolen_common/actionpool/queue.rb +41 -0
- data/lib/woolen_common/actionpool/thread.rb +181 -0
- data/lib/woolen_common/addr_helper.rb +93 -0
- data/lib/woolen_common/cache.rb +305 -0
- data/lib/woolen_common/common_helper.rb +42 -0
- data/lib/woolen_common/config_manager.rb +36 -0
- data/lib/woolen_common/drb_helper.rb +125 -0
- data/lib/woolen_common/ffi/win32_kernel32.rb +86 -0
- data/lib/woolen_common/logger.rb +419 -0
- data/lib/woolen_common/pcap/mu/fixnum_ext.rb +8 -0
- data/lib/woolen_common/pcap/mu/pcap/ethernet.rb +164 -0
- data/lib/woolen_common/pcap/mu/pcap/header.rb +76 -0
- data/lib/woolen_common/pcap/mu/pcap/io_pair.rb +68 -0
- data/lib/woolen_common/pcap/mu/pcap/io_wrapper.rb +77 -0
- data/lib/woolen_common/pcap/mu/pcap/ip.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv4.rb +274 -0
- data/lib/woolen_common/pcap/mu/pcap/ipv6.rb +149 -0
- data/lib/woolen_common/pcap/mu/pcap/packet.rb +106 -0
- data/lib/woolen_common/pcap/mu/pcap/pkthdr.rb +162 -0
- data/lib/woolen_common/pcap/mu/pcap/reader.rb +62 -0
- data/lib/woolen_common/pcap/mu/pcap/reader/http_family.rb +175 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp.rb +369 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk.rb +124 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/data.rb +135 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init.rb +101 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init_ack.rb +69 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter.rb +111 -0
- data/lib/woolen_common/pcap/mu/pcap/sctp/parameter/ip_address.rb +49 -0
- data/lib/woolen_common/pcap/mu/pcap/stream_packetizer.rb +74 -0
- data/lib/woolen_common/pcap/mu/pcap/tcp.rb +522 -0
- data/lib/woolen_common/pcap/mu/pcap/udp.rb +81 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap.rb +175 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/fields.rb +51 -0
- data/lib/woolen_common/pcap/mu/scenario/pcap/rtp.rb +72 -0
- data/lib/woolen_common/pcap/pcap.rb +115 -0
- data/lib/woolen_common/pcap/readme.md +72 -0
- data/lib/woolen_common/ruby_ext/blank.rb +126 -0
- data/lib/woolen_common/ruby_ext/drb_ext.rb +7 -0
- data/lib/woolen_common/ruby_ext/string.rb +116 -0
- data/lib/woolen_common/ruby_ext/win32_ole.rb +4 -0
- data/lib/woolen_common/ruby_proxy.rb +5 -0
- data/lib/woolen_common/ruby_proxy/client.rb +305 -0
- data/lib/woolen_common/ruby_proxy/config.rb +44 -0
- data/lib/woolen_common/ruby_proxy/exceptions.rb +17 -0
- data/lib/woolen_common/ruby_proxy/proxy.rb +157 -0
- data/lib/woolen_common/ruby_proxy/proxy_global_set.rb +44 -0
- data/lib/woolen_common/ruby_proxy/proxy_load.rb +34 -0
- data/lib/woolen_common/ruby_proxy/server.rb +53 -0
- data/lib/woolen_common/splib.rb +36 -0
- data/lib/woolen_common/splib/Array.rb +33 -0
- data/lib/woolen_common/splib/CodeReloader.rb +59 -0
- data/lib/woolen_common/splib/Constants.rb +44 -0
- data/lib/woolen_common/splib/Conversions.rb +47 -0
- data/lib/woolen_common/splib/Exec.rb +132 -0
- data/lib/woolen_common/splib/Float.rb +13 -0
- data/lib/woolen_common/splib/HumanIdealRandomIterator.rb +40 -0
- data/lib/woolen_common/splib/Monitor.rb +214 -0
- data/lib/woolen_common/splib/PriorityQueue.rb +110 -0
- data/lib/woolen_common/splib/Sleep.rb +10 -0
- data/lib/woolen_common/splib/UrlShorteners.rb +48 -0
- data/lib/woolen_common/ssh_proxy.rb +146 -0
- data/lib/woolen_common/system_helper.rb +123 -0
- data/lib/woolen_common/system_monitor.rb +23 -0
- data/lib/woolen_common/system_monitor/linux_monitor.rb +250 -0
- data/lib/woolen_common/system_monitor/windows_monitor.rb +145 -0
- data/lib/woolen_common/type_helper.rb +42 -0
- data/lib/woolen_common/ver_ctrl_middle_ware.rb +92 -0
- data/lib/woolen_common/version.rb +3 -0
- metadata +210 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ActionPool
|
4
|
+
# Adds a little bit extra functionality to the Queue class
|
5
|
+
class Queue < ::Queue
|
6
|
+
# Create a new Queue for the ActionPool::Pool
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
@wait = false
|
10
|
+
@pause_guard = Splib::Monitor.new
|
11
|
+
@empty_guard = Splib::Monitor.new
|
12
|
+
end
|
13
|
+
# Stop the queue from returning results to requesting
|
14
|
+
# threads. Threads will wait for results until signalled
|
15
|
+
def pause
|
16
|
+
@wait = true
|
17
|
+
end
|
18
|
+
# Allow the queue to return results. Any threads waiting
|
19
|
+
# will have results given to them.
|
20
|
+
def unpause
|
21
|
+
@wait = false
|
22
|
+
@pause_guard.broadcast
|
23
|
+
end
|
24
|
+
# Check if queue needs to wait before returning
|
25
|
+
def pop
|
26
|
+
@pause_guard.wait_while{ @wait }
|
27
|
+
o = super
|
28
|
+
@empty_guard.broadcast if empty?
|
29
|
+
return o
|
30
|
+
end
|
31
|
+
# Clear queue
|
32
|
+
def clear
|
33
|
+
super
|
34
|
+
@empty_guard.broadcast
|
35
|
+
end
|
36
|
+
# Park a thread here until queue is empty
|
37
|
+
def wait_empty
|
38
|
+
@empty_guard.wait_while{ size > 0 }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module ActionPool
|
4
|
+
# Exception class used for waking up a thread
|
5
|
+
class Wakeup < StandardError
|
6
|
+
end
|
7
|
+
# Raised within a thread when the timeout is changed
|
8
|
+
class Retimeout < StandardError
|
9
|
+
end
|
10
|
+
class Thread
|
11
|
+
# :pool:: pool thread is associated with
|
12
|
+
# :t_timeout:: max time a thread is allowed to wait for action
|
13
|
+
# :a_timeout:: max time thread is allowed to work
|
14
|
+
# :respond_thread:: thread to send execptions to
|
15
|
+
# :logger:: LogHelper for logging messages
|
16
|
+
# :autostart:: Automatically start the thread
|
17
|
+
# Create a new thread
|
18
|
+
def initialize(args)
|
19
|
+
raise ArgumentError.new('Hash required for initialization') unless args.is_a?(Hash)
|
20
|
+
raise ArgumentError.new('ActionPool::Thread requires a pool') unless args[:pool]
|
21
|
+
raise ArgumentError.new('ActionPool::Thread requries thread to respond') unless args[:respond_thread]
|
22
|
+
@pool = args[:pool]
|
23
|
+
@respond_to = args[:respond_thread]
|
24
|
+
@thread_timeout = args[:t_timeout] ? args[:t_timeout].to_f : 0
|
25
|
+
@action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
|
26
|
+
args[:autostart] = true unless args.has_key?(:autostart)
|
27
|
+
@kill = false
|
28
|
+
@logger = args[:logger].is_a?(Logger) ? args[:logger] : Logger.new(nil)
|
29
|
+
@lock = Splib::Monitor.new
|
30
|
+
@action = nil
|
31
|
+
@thread = args[:autostart] ? ::Thread.new{ start_thread } : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def start
|
35
|
+
@thread = ::Thread.new{ start_thread } if @thread.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
# :force:: force the thread to stop
|
39
|
+
# :wait:: wait for the thread to stop
|
40
|
+
# Stop the thread
|
41
|
+
def stop(*args)
|
42
|
+
@kill = true
|
43
|
+
if(args.include?(:force) || waiting?)
|
44
|
+
begin
|
45
|
+
@thread.raise Wakeup.new
|
46
|
+
rescue Wakeup
|
47
|
+
#ignore since we are the caller
|
48
|
+
end
|
49
|
+
sleep(0.01)
|
50
|
+
@thread.kill if @thread.alive?
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Currently waiting
|
56
|
+
def waiting?
|
57
|
+
@action.nil?
|
58
|
+
# @status == :wait
|
59
|
+
end
|
60
|
+
|
61
|
+
# Currently running
|
62
|
+
def running?
|
63
|
+
!@action.nil?
|
64
|
+
# @status == :run
|
65
|
+
end
|
66
|
+
|
67
|
+
# Is the thread still alive
|
68
|
+
def alive?
|
69
|
+
@thread.alive?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Current thread status
|
73
|
+
def status
|
74
|
+
@action
|
75
|
+
end
|
76
|
+
|
77
|
+
# Join internal thread
|
78
|
+
def join
|
79
|
+
@thread.join(@action_timeout)
|
80
|
+
if(@thread.alive?)
|
81
|
+
@thread.kill
|
82
|
+
@thread.join
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Kill internal thread
|
87
|
+
def kill
|
88
|
+
@thread.kill
|
89
|
+
end
|
90
|
+
|
91
|
+
# Seconds thread will wait for input
|
92
|
+
def thread_timeout
|
93
|
+
@thread_timeout
|
94
|
+
end
|
95
|
+
|
96
|
+
# Seconds thread will spend working on a given task
|
97
|
+
def action_timeout
|
98
|
+
@action_timeout
|
99
|
+
end
|
100
|
+
|
101
|
+
# t:: seconds to wait for input (floats allow for values 0 < t < 1)
|
102
|
+
# Set the maximum amount of time to wait for a task
|
103
|
+
def thread_timeout=(t)
|
104
|
+
t = t.to_f
|
105
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
106
|
+
@thread_timeout = t
|
107
|
+
@thread.raise Retimeout.new if waiting?
|
108
|
+
t
|
109
|
+
end
|
110
|
+
|
111
|
+
# t:: seconds to work on a task (floats allow for values 0 < t < 1)
|
112
|
+
# Set the maximum amount of time to work on a given task
|
113
|
+
# Note: Modification of this will not affect actions already in process
|
114
|
+
def action_timeout=(t)
|
115
|
+
t = t.to_f
|
116
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
117
|
+
@action_timeout = t
|
118
|
+
t
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Start our thread
|
124
|
+
def start_thread
|
125
|
+
begin
|
126
|
+
@logger.info("New pool thread is starting (#{self})")
|
127
|
+
until(@kill) do
|
128
|
+
begin
|
129
|
+
@action = nil
|
130
|
+
if(@pool.size > @pool.min && !@thread_timeout.zero?)
|
131
|
+
Timeout::timeout(@thread_timeout) do
|
132
|
+
@action = @pool.action
|
133
|
+
end
|
134
|
+
else
|
135
|
+
@action = @pool.action
|
136
|
+
end
|
137
|
+
run(@action[0], @action[1]) unless @action.nil?
|
138
|
+
rescue Timeout::Error
|
139
|
+
@kill = true
|
140
|
+
rescue Wakeup
|
141
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
142
|
+
rescue Retimeout
|
143
|
+
@logger.warn('Thread was woken up to reset thread timeout')
|
144
|
+
rescue Exception => boom
|
145
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
146
|
+
@respond_to.raise boom
|
147
|
+
end
|
148
|
+
end
|
149
|
+
rescue Retimeout
|
150
|
+
@logger.warn('Thread was woken up to reset thread timeout')
|
151
|
+
retry
|
152
|
+
rescue Wakeup
|
153
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
154
|
+
rescue Exception => boom
|
155
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
156
|
+
@respond_to.raise boom
|
157
|
+
ensure
|
158
|
+
@logger.info("Pool thread is shutting down (#{self})")
|
159
|
+
@pool.remove(self)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# action:: task to be run
|
164
|
+
# args:: arguments to be passed to task
|
165
|
+
# Run the task
|
166
|
+
def run(action, args)
|
167
|
+
args = args.respond_to?(:fixed_flatten) ? args.fixed_flatten(1) : args.flatten(1)
|
168
|
+
begin
|
169
|
+
unless(@action_timeout.zero?)
|
170
|
+
Timeout::timeout(@action_timeout) do
|
171
|
+
action.call(*args)
|
172
|
+
end
|
173
|
+
else
|
174
|
+
action.call(*args)
|
175
|
+
end
|
176
|
+
rescue Timeout::Error => boom
|
177
|
+
@logger.warn("Pool thread reached max execution time for action: #{boom}")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require "#{File.join(File.dirname(__FILE__), 'logger')}"
|
3
|
+
require "#{File.join(File.dirname(__FILE__), 'common_helper')}"
|
4
|
+
require "#{File.join(File.dirname(__FILE__), 'type_helper')}"
|
5
|
+
require 'ipaddr'
|
6
|
+
module WoolenCommon
|
7
|
+
class AddrHelper
|
8
|
+
class << self
|
9
|
+
include WoolenCommon::ToolLogger
|
10
|
+
# IP地址字符串转做数值(无符号的)
|
11
|
+
def ip_str_to_unsigned(ip_addr_str)
|
12
|
+
IPAddr.new(ip_addr_str).to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
# IP地址字符串转做数值(有符号的)
|
16
|
+
def ip_str_to_signed(ip_addr_str,byte_length=32)
|
17
|
+
begin
|
18
|
+
return TypeHelper.to_signed(IPAddr.new(ip_addr_str).to_i, byte_length)
|
19
|
+
rescue Exception=>e
|
20
|
+
debug "对IP地址[#{ip_addr_str}],进行转换到有符号数字出错::#{e.message}"
|
21
|
+
return 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# 有符号地址值转成ip字符串
|
26
|
+
def signed_to_ip_str(signed,byte_length=32)
|
27
|
+
unsigned_32 = TypeHelper.to_unsigned(signed,byte_length)
|
28
|
+
IPAddr.new(unsigned_32,Socket::AF_INET).to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# 无符号地址值转成ip字符串
|
32
|
+
def unsigned_to_ip_str(unsigned_32)
|
33
|
+
IPAddr.new(unsigned_32,Socket::AF_INET).to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
# mac地址字符串转成无符号数字数组
|
37
|
+
def mac_str_to_array(mac_str)
|
38
|
+
if mac_str.kind_of? String
|
39
|
+
if mac_str.include? ':'
|
40
|
+
split_arry = mac_str.split ':'
|
41
|
+
elsif mac_str.include? '-'
|
42
|
+
split_arry = mac_str.split '-'
|
43
|
+
elsif mac_str.length == 12
|
44
|
+
split_arry = []
|
45
|
+
6.times do |cnt|
|
46
|
+
split_arry << mac_str[(2 * cnt)... (2 * (cnt+1))]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
debug "not support mac format:[#{mac_str}],please use : or - format"
|
50
|
+
return []
|
51
|
+
end
|
52
|
+
if split_arry.length != 6
|
53
|
+
debug "to long format:[#{mac_str}]~"
|
54
|
+
return []
|
55
|
+
end
|
56
|
+
return_array=[]
|
57
|
+
split_arry.each do |split_str|
|
58
|
+
return_array << split_str.to_i(16)
|
59
|
+
end
|
60
|
+
return_array
|
61
|
+
else
|
62
|
+
error "not support not string format:[#{mac_str}],please give me a mac string"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# mac地址无符号数字数组转字符串
|
67
|
+
def array_to_mac_str(mac_array,str_gap=':')
|
68
|
+
debug "start to get mac str from mac array :: #{mac_array}"
|
69
|
+
if mac_array.kind_of? Array
|
70
|
+
if mac_array.length != 6
|
71
|
+
error "wrong long arry :[#{mac_array}]~"
|
72
|
+
return ''
|
73
|
+
else
|
74
|
+
ret_str_array = []
|
75
|
+
mac_array.each do |mac_int_x16|
|
76
|
+
mac_value = TypeHelper.get_low_bit_num(mac_int_x16,8)
|
77
|
+
if mac_value >= 16
|
78
|
+
ret_str_array << mac_value.to_s(16)
|
79
|
+
elsif mac_value >= 0 && mac_int_x16 < 16
|
80
|
+
ret_str_array << "0#{mac_value.to_s(16)}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
debug "success get mac str ::#{ret_str_array.join str_gap}"
|
84
|
+
return ret_str_array.join str_gap
|
85
|
+
end
|
86
|
+
else
|
87
|
+
error "not a array mac please give me a array::#{mac_array}~"
|
88
|
+
''
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
#! /usr/bin/env ruby
|
3
|
+
#
|
4
|
+
# Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
|
5
|
+
#
|
6
|
+
# You may redistribute it and/or modify it under the same term as Ruby.
|
7
|
+
|
8
|
+
# Cache manager based on the LRU algorithm.
|
9
|
+
require "#{File.join(File.dirname(__FILE__), 'logger')}"
|
10
|
+
module WoolenCommon
|
11
|
+
class Cache
|
12
|
+
include ToolLogger
|
13
|
+
|
14
|
+
CACHE_OBJECT = Struct.new('CacheObject', :content, :size, :atime)
|
15
|
+
CACHE_VERSION = '0.3'
|
16
|
+
|
17
|
+
include Enumerable
|
18
|
+
|
19
|
+
def self.version
|
20
|
+
CACHE_VERSION
|
21
|
+
end
|
22
|
+
|
23
|
+
# initialize(max_obj_size = nil, max_size = nil, max_num = nil,
|
24
|
+
# expiration = nil, &hook)
|
25
|
+
# initialize(hash, &hook)
|
26
|
+
def initialize(*args, &hook)
|
27
|
+
if args.size == 1 and args[0].kind_of?(Hash)
|
28
|
+
@max_obj_size = @max_size = @max_num = @expiration = nil
|
29
|
+
args[0].each do |k, v|
|
30
|
+
k = k.intern if k.respond_to?(:intern)
|
31
|
+
case k
|
32
|
+
when :max_obj_size
|
33
|
+
@max_obj_size = v
|
34
|
+
when :max_size
|
35
|
+
@max_size = v
|
36
|
+
when :max_num
|
37
|
+
@max_num = v
|
38
|
+
when :expiration
|
39
|
+
@expiration = v
|
40
|
+
else
|
41
|
+
warn "cache unknown key :#{k}=>#{v}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@max_obj_size, @max_size, @max_num, @expiration = args
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sanity checks.
|
49
|
+
if @max_obj_size and @max_size and @max_obj_size > @max_size
|
50
|
+
raise ArgumentError, "max_obj_size exceeds max_size (#{@max_obj_size} > #{@max_size})"
|
51
|
+
end
|
52
|
+
if @max_obj_size and @max_obj_size <= 0
|
53
|
+
raise ArgumentError, "invalid max_obj_size `#{@max_obj_size}'"
|
54
|
+
end
|
55
|
+
if @max_size and @max_size <= 0
|
56
|
+
raise ArgumentError, "invalid max_size `#{@max_size}'"
|
57
|
+
end
|
58
|
+
if @max_num and @max_num <= 0
|
59
|
+
raise ArgumentError, "invalid max_num `#{@max_num}'"
|
60
|
+
end
|
61
|
+
if @expiration and @expiration <= 0
|
62
|
+
raise ArgumentError, "invalid expiration `#{@expiration}'"
|
63
|
+
end
|
64
|
+
|
65
|
+
@hook = hook
|
66
|
+
|
67
|
+
@objs = {}
|
68
|
+
@size = 0
|
69
|
+
@list = []
|
70
|
+
|
71
|
+
@hits = 0
|
72
|
+
@misses = 0
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_reader :max_obj_size, :max_size, :max_num, :expiration
|
76
|
+
|
77
|
+
def cached?(key)
|
78
|
+
@objs.include?(key)
|
79
|
+
end
|
80
|
+
|
81
|
+
alias :include? :cached?
|
82
|
+
alias :member? :cached?
|
83
|
+
alias :key? :cached?
|
84
|
+
alias :has_key? :cached?
|
85
|
+
|
86
|
+
def cached_value?(val)
|
87
|
+
self.each_value do |v|
|
88
|
+
return true if v == val
|
89
|
+
end
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
alias :has_value? :cached_value?
|
94
|
+
alias :value? :cached_value?
|
95
|
+
|
96
|
+
def index(val)
|
97
|
+
self.each_pair do |k, v|
|
98
|
+
return k if v == val
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def keys
|
104
|
+
@objs.keys
|
105
|
+
end
|
106
|
+
|
107
|
+
def length
|
108
|
+
@objs.length
|
109
|
+
end
|
110
|
+
|
111
|
+
alias :size :length
|
112
|
+
|
113
|
+
def to_hash
|
114
|
+
@objs.dup
|
115
|
+
end
|
116
|
+
|
117
|
+
def values
|
118
|
+
@objs.collect { |key, obj| obj.content }
|
119
|
+
end
|
120
|
+
|
121
|
+
def invalidate(key)
|
122
|
+
obj = @objs[key]
|
123
|
+
if obj
|
124
|
+
if @hook
|
125
|
+
@hook.call(key, obj.content)
|
126
|
+
end
|
127
|
+
@size -= obj.size
|
128
|
+
@objs.delete(key)
|
129
|
+
@list.each_index do |i|
|
130
|
+
if @list[i] == key
|
131
|
+
@list.delete_at(i)
|
132
|
+
break
|
133
|
+
end
|
134
|
+
end
|
135
|
+
elsif block_given?
|
136
|
+
return yield(key)
|
137
|
+
end
|
138
|
+
obj.content
|
139
|
+
end
|
140
|
+
|
141
|
+
alias :delete :invalidate
|
142
|
+
|
143
|
+
def invalidate_all
|
144
|
+
if @hook
|
145
|
+
@objs.each do |key, obj|
|
146
|
+
@hook.call(key, obj)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
@objs.clear
|
151
|
+
@list.clear
|
152
|
+
@size = 0
|
153
|
+
end
|
154
|
+
|
155
|
+
alias :clear :invalidate_all
|
156
|
+
|
157
|
+
def expire
|
158
|
+
if @expiration
|
159
|
+
now = Time.now.to_i
|
160
|
+
@list.each_index do |i|
|
161
|
+
key = @list[i]
|
162
|
+
|
163
|
+
break unless @objs[key].atime + @expiration <= now
|
164
|
+
self.invalidate(key)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
# GC.start
|
168
|
+
end
|
169
|
+
|
170
|
+
def [](key)
|
171
|
+
self.expire
|
172
|
+
|
173
|
+
unless @objs.include?(key)
|
174
|
+
@misses += 1
|
175
|
+
return nil
|
176
|
+
end
|
177
|
+
|
178
|
+
obj = @objs[key]
|
179
|
+
obj.atime = Time.now.to_i
|
180
|
+
|
181
|
+
@list.each_index do |i|
|
182
|
+
if @list[i] == key
|
183
|
+
@list.delete_at(i)
|
184
|
+
break
|
185
|
+
end
|
186
|
+
end
|
187
|
+
@list.push(key)
|
188
|
+
|
189
|
+
@hits += 1
|
190
|
+
obj.content
|
191
|
+
end
|
192
|
+
|
193
|
+
def []=(key, obj)
|
194
|
+
self.expire
|
195
|
+
|
196
|
+
if self.cached?(key)
|
197
|
+
self.invalidate(key)
|
198
|
+
end
|
199
|
+
|
200
|
+
size = obj.to_s.size
|
201
|
+
if @max_obj_size and @max_obj_size < size
|
202
|
+
debug("warning: `#{obj.inspect}' isn't cached because its size exceeds #{@max_obj_size}")
|
203
|
+
return obj
|
204
|
+
end
|
205
|
+
if @max_obj_size.nil? and @max_size and @max_size < size
|
206
|
+
debug("warning: `#{obj.inspect}' isn't cached because its size exceeds #{@max_size}")
|
207
|
+
return obj
|
208
|
+
end
|
209
|
+
|
210
|
+
if @max_num and @max_num == @list.size
|
211
|
+
self.invalidate(@list.first)
|
212
|
+
end
|
213
|
+
|
214
|
+
@size += size
|
215
|
+
if @max_size
|
216
|
+
while @max_size < @size
|
217
|
+
self.invalidate(@list.first)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
@objs[key] = CACHE_OBJECT.new(obj, size, Time.now.to_i)
|
222
|
+
@list.push(key)
|
223
|
+
|
224
|
+
obj
|
225
|
+
end
|
226
|
+
|
227
|
+
def store(key, value)
|
228
|
+
self[key] = value
|
229
|
+
end
|
230
|
+
|
231
|
+
def each_pair
|
232
|
+
@objs.each do |key, obj|
|
233
|
+
yield key, obj.content
|
234
|
+
end
|
235
|
+
self
|
236
|
+
end
|
237
|
+
|
238
|
+
alias :each :each_pair
|
239
|
+
|
240
|
+
def each_key
|
241
|
+
@objs.each_key do |key|
|
242
|
+
yield key
|
243
|
+
end
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
def each_value
|
248
|
+
@objs.each_value do |obj|
|
249
|
+
yield obj.content
|
250
|
+
end
|
251
|
+
self
|
252
|
+
end
|
253
|
+
|
254
|
+
def empty?
|
255
|
+
@objs.empty?
|
256
|
+
end
|
257
|
+
|
258
|
+
def fetch(key, default = nil)
|
259
|
+
val = self[key]
|
260
|
+
if val.nil?
|
261
|
+
if default
|
262
|
+
val = self[key] = default
|
263
|
+
elsif block_given?
|
264
|
+
val = self[key] = yield(key)
|
265
|
+
else
|
266
|
+
raise IndexError, "invalid key `#{key}'"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
val
|
270
|
+
end
|
271
|
+
|
272
|
+
# The total size of cached objects, the number of cached objects,
|
273
|
+
# the number of cache hits, and the number of cache misses.
|
274
|
+
def state_statics
|
275
|
+
[@size, @list.size, @hits, @misses]
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Run a test, if executed.
|
280
|
+
if __FILE__ == $0
|
281
|
+
cache = Cache.new(100 * 1024, 100 * 1024 * 1024, 256, 13)
|
282
|
+
1000.times do
|
283
|
+
key = rand(1000)
|
284
|
+
cache[key] = key.to_s
|
285
|
+
end
|
286
|
+
1000.times do
|
287
|
+
key = rand(1000)
|
288
|
+
puts cache[key]
|
289
|
+
end
|
290
|
+
sleep 1
|
291
|
+
1000.times do
|
292
|
+
key = rand(1000)
|
293
|
+
puts cache[key]
|
294
|
+
end
|
295
|
+
|
296
|
+
stat = cache.state_statics
|
297
|
+
hits = stat[2]
|
298
|
+
misses = stat[3]
|
299
|
+
ratio = hits.to_f / (hits + misses)
|
300
|
+
|
301
|
+
puts "Total size:\t#{stat[0]}"
|
302
|
+
puts "Number:\t\t#{stat[1]}"
|
303
|
+
puts "Hit ratio:\t#{ratio * 100}% (#{hits} / #{hits + misses})"
|
304
|
+
end
|
305
|
+
end
|