animehunter-mongo 0.9

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 (79) hide show
  1. data/README.rdoc +311 -0
  2. data/Rakefile +62 -0
  3. data/bin/bson_benchmark.rb +59 -0
  4. data/bin/mongo_console +21 -0
  5. data/bin/run_test_script +19 -0
  6. data/bin/standard_benchmark +109 -0
  7. data/examples/admin.rb +41 -0
  8. data/examples/benchmarks.rb +42 -0
  9. data/examples/blog.rb +76 -0
  10. data/examples/capped.rb +23 -0
  11. data/examples/cursor.rb +47 -0
  12. data/examples/gridfs.rb +87 -0
  13. data/examples/index_test.rb +125 -0
  14. data/examples/info.rb +30 -0
  15. data/examples/queries.rb +69 -0
  16. data/examples/simple.rb +23 -0
  17. data/examples/strict.rb +34 -0
  18. data/examples/types.rb +40 -0
  19. data/lib/mongo.rb +19 -0
  20. data/lib/mongo/admin.rb +87 -0
  21. data/lib/mongo/collection.rb +235 -0
  22. data/lib/mongo/cursor.rb +227 -0
  23. data/lib/mongo/db.rb +538 -0
  24. data/lib/mongo/gridfs.rb +16 -0
  25. data/lib/mongo/gridfs/chunk.rb +96 -0
  26. data/lib/mongo/gridfs/grid_store.rb +468 -0
  27. data/lib/mongo/message.rb +20 -0
  28. data/lib/mongo/message/get_more_message.rb +37 -0
  29. data/lib/mongo/message/insert_message.rb +35 -0
  30. data/lib/mongo/message/kill_cursors_message.rb +36 -0
  31. data/lib/mongo/message/message.rb +84 -0
  32. data/lib/mongo/message/message_header.rb +50 -0
  33. data/lib/mongo/message/msg_message.rb +33 -0
  34. data/lib/mongo/message/opcodes.rb +32 -0
  35. data/lib/mongo/message/query_message.rb +77 -0
  36. data/lib/mongo/message/remove_message.rb +36 -0
  37. data/lib/mongo/message/update_message.rb +37 -0
  38. data/lib/mongo/mongo.rb +164 -0
  39. data/lib/mongo/query.rb +119 -0
  40. data/lib/mongo/types/binary.rb +42 -0
  41. data/lib/mongo/types/code.rb +34 -0
  42. data/lib/mongo/types/dbref.rb +37 -0
  43. data/lib/mongo/types/objectid.rb +137 -0
  44. data/lib/mongo/types/regexp_of_holding.rb +44 -0
  45. data/lib/mongo/types/undefined.rb +31 -0
  46. data/lib/mongo/util/bson.rb +534 -0
  47. data/lib/mongo/util/byte_buffer.rb +167 -0
  48. data/lib/mongo/util/ordered_hash.rb +96 -0
  49. data/lib/mongo/util/xml_to_ruby.rb +107 -0
  50. data/mongo-ruby-driver.gemspec +99 -0
  51. data/tests/mongo-qa/_common.rb +8 -0
  52. data/tests/mongo-qa/admin +26 -0
  53. data/tests/mongo-qa/capped +22 -0
  54. data/tests/mongo-qa/count1 +18 -0
  55. data/tests/mongo-qa/dbs +22 -0
  56. data/tests/mongo-qa/find +10 -0
  57. data/tests/mongo-qa/find1 +15 -0
  58. data/tests/mongo-qa/gridfs_in +16 -0
  59. data/tests/mongo-qa/gridfs_out +17 -0
  60. data/tests/mongo-qa/indices +49 -0
  61. data/tests/mongo-qa/remove +25 -0
  62. data/tests/mongo-qa/stress1 +35 -0
  63. data/tests/mongo-qa/test1 +11 -0
  64. data/tests/mongo-qa/update +18 -0
  65. data/tests/test_admin.rb +69 -0
  66. data/tests/test_bson.rb +246 -0
  67. data/tests/test_byte_buffer.rb +69 -0
  68. data/tests/test_chunk.rb +84 -0
  69. data/tests/test_cursor.rb +121 -0
  70. data/tests/test_db.rb +160 -0
  71. data/tests/test_db_api.rb +701 -0
  72. data/tests/test_db_connection.rb +18 -0
  73. data/tests/test_grid_store.rb +284 -0
  74. data/tests/test_message.rb +35 -0
  75. data/tests/test_mongo.rb +78 -0
  76. data/tests/test_objectid.rb +98 -0
  77. data/tests/test_ordered_hash.rb +129 -0
  78. data/tests/test_round_trip.rb +116 -0
  79. metadata +133 -0
@@ -0,0 +1,37 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'mongo/message/message'
18
+ require 'mongo/message/opcodes'
19
+
20
+ module XGen
21
+ module Mongo
22
+ module Driver
23
+
24
+ class UpdateMessage < Message
25
+
26
+ def initialize(db_name, collection_name, sel, obj, repsert)
27
+ super(OP_UPDATE)
28
+ write_int(0)
29
+ write_string("#{db_name}.#{collection_name}")
30
+ write_int(repsert ? 1 : 0) # 1 if a repsert operation (upsert)
31
+ write_doc(sel)
32
+ write_doc(obj)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,164 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'mongo/db'
18
+
19
+ module XGen
20
+ module Mongo
21
+ module Driver
22
+
23
+ # Represents a Mongo database server.
24
+ class Mongo
25
+
26
+ DEFAULT_PORT = 27017
27
+
28
+ # Create a Mongo database server instance. You specify either one or a
29
+ # pair of servers. If one, you also say if connecting to a slave is
30
+ # OK. In either case, the host default is "localhost" and port default
31
+ # is DEFAULT_PORT.
32
+ #
33
+ # If you specify a pair, pair_or_host is a hash with two keys :left
34
+ # and :right. Each key maps to either
35
+ # * a server name, in which case port is DEFAULT_PORT
36
+ # * a port number, in which case server is "localhost"
37
+ # * an array containing a server name and a port number in that order
38
+ #
39
+ # +options+ are passed on to each DB instance:
40
+ #
41
+ # :slave_ok :: Only used if one host is specified. If false, when
42
+ # connecting to that host/port a DB object will check to
43
+ # see if the server is the master. If it is not, an error
44
+ # is thrown.
45
+ #
46
+ # :auto_reconnect :: If a DB connection gets closed (for example, we
47
+ # have a server pair and saw the "not master"
48
+ # error, which closes the connection), then
49
+ # automatically try to reconnect to the master or
50
+ # to the single server we have been given. Defaults
51
+ # to +false+.
52
+ #
53
+ # Since that's so confusing, here are a few examples:
54
+ #
55
+ # Mongo.new # localhost, DEFAULT_PORT, !slave
56
+ # Mongo.new("localhost") # localhost, DEFAULT_PORT, !slave
57
+ # Mongo.new("localhost", 3000) # localhost, 3000, slave not ok
58
+ # # localhost, 3000, slave ok
59
+ # Mongo.new("localhost", 3000, :slave_ok => true)
60
+ # # localhost, DEFAULT_PORT, auto reconnect
61
+ # Mongo.new(nil, nil, :auto_reconnect => true)
62
+ #
63
+ # # A pair of servers. DB will always talk to the master. On socket
64
+ # # error or "not master" error, we will auto-reconnect to the
65
+ # # current master.
66
+ # Mongo.new({:left => ["db1.example.com", 3000],
67
+ # :right => "db2.example.com"}, # DEFAULT_PORT
68
+ # nil, :auto_reconnect => true)
69
+ #
70
+ # # Here, :right is localhost/DEFAULT_PORT. No auto-reconnect.
71
+ # Mongo.new({:left => ["db1.example.com", 3000]})
72
+ #
73
+ # When a DB object first connects to a pair, it will find the master
74
+ # instance and connect to that one.
75
+ def initialize(pair_or_host=nil, port=nil, options={})
76
+ @pair = case pair_or_host
77
+ when String
78
+ [[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
79
+ when Hash
80
+ connections = []
81
+ connections << pair_val_to_connection(pair_or_host[:left])
82
+ connections << pair_val_to_connection(pair_or_host[:right])
83
+ connections
84
+ when nil
85
+ [['localhost', DEFAULT_PORT]]
86
+ end
87
+ @options = options
88
+ end
89
+
90
+ # Return the XGen::Mongo::Driver::DB named +db_name+. The slave_ok and
91
+ # auto_reconnect options passed in via #new may be overridden here.
92
+ # See DB#new for other options you can pass in.
93
+ def db(db_name, options={})
94
+ XGen::Mongo::Driver::DB.new(db_name, @pair, @options.merge(options))
95
+ end
96
+
97
+ # Returns a hash containing database names as keys and disk space for
98
+ # each as values.
99
+ def database_info
100
+ doc = single_db_command('admin', :listDatabases => 1)
101
+ h = {}
102
+ doc['databases'].each { |db|
103
+ h[db['name']] = db['sizeOnDisk'].to_i
104
+ }
105
+ h
106
+ end
107
+
108
+ # Returns an array of database names.
109
+ def database_names
110
+ database_info.keys
111
+ end
112
+
113
+ # Not implemented.
114
+ def clone_database(from)
115
+ raise "not implemented"
116
+ end
117
+
118
+ # Not implemented.
119
+ def copy_database(from_host, from_db, to_db)
120
+ raise "not implemented"
121
+ end
122
+
123
+ # Drops the database +name+.
124
+ def drop_database(name)
125
+ single_db_command(name, :dropDatabase => 1)
126
+ end
127
+
128
+ protected
129
+
130
+ # Turns an array containing a host name string and a
131
+ # port number integer into a [host, port] pair array.
132
+ def pair_val_to_connection(a)
133
+ case a
134
+ when nil
135
+ ['localhost', DEFAULT_PORT]
136
+ when String
137
+ [a, DEFAULT_PORT]
138
+ when Integer
139
+ ['localhost', a]
140
+ when Array
141
+ a
142
+ end
143
+ end
144
+
145
+ # Send cmd (a hash, possibly ordered) to the admin database and return
146
+ # the answer. Raises an error unless the return is "ok" (DB#ok?
147
+ # returns +true+).
148
+ def single_db_command(db_name, cmd)
149
+ db = nil
150
+ begin
151
+ db = db(db_name)
152
+ doc = db.db_command(cmd)
153
+ raise "error retrieving database info: #{doc.inspect}" unless db.ok?(doc)
154
+ doc
155
+ ensure
156
+ db.close if db
157
+ end
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
164
+
@@ -0,0 +1,119 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'mongo/collection'
18
+ require 'mongo/message'
19
+ require 'mongo/types/code'
20
+
21
+ module XGen
22
+ module Mongo
23
+ module Driver
24
+
25
+ # A query against a collection. A query's selector is a hash. See the
26
+ # Mongo documentation for query details.
27
+ class Query
28
+
29
+ attr_accessor :number_to_skip, :number_to_return, :order_by
30
+ # If true, $explain will be set in QueryMessage that uses this query.
31
+ attr_accessor :explain
32
+ # Either +nil+ or a hash (preferably an OrderedHash).
33
+ attr_accessor :hint
34
+ attr_reader :selector # writer defined below
35
+
36
+ # sel :: A hash describing the query. See the Mongo docs for details.
37
+ #
38
+ # return_fields :: If not +nil+, a single field name or an array of
39
+ # field names. Only those fields will be returned.
40
+ # (Called :fields in calls to Collection#find.)
41
+ #
42
+ # number_to_skip :: Number of records to skip before returning
43
+ # records. (Called :offset in calls to
44
+ # Collection#find.) Default is 0.
45
+ #
46
+ # number_to_return :: Max number of records to return. (Called :limit
47
+ # in calls to Collection#find.) Default is 0 (all
48
+ # records).
49
+ #
50
+ # order_by :: If not +nil+, specifies record sort order. May be a
51
+ # String, Hash, OrderedHash, or Array. If a string, the
52
+ # results will be ordered by that field in ascending
53
+ # order. If an array, it should be an array of field names
54
+ # which will all be sorted in ascending order. If a hash,
55
+ # it may be either a regular Hash or an OrderedHash. The
56
+ # keys should be field names, and the values should be 1
57
+ # (ascending) or -1 (descending). Note that if it is a
58
+ # regular Hash then sorting by more than one field
59
+ # probably will not be what you intend because key order
60
+ # is not preserved. (order_by is called :sort in calls to
61
+ # Collection#find.)
62
+ #
63
+ # hint :: If not +nil+, specifies query hint fields. Must be either
64
+ # +nil+ or a hash (preferably an OrderedHash). See
65
+ # Collection#hint.
66
+ def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil)
67
+ @number_to_skip, @number_to_return, @order_by, @hint =
68
+ number_to_skip, number_to_return, order_by, hint
69
+ @explain = nil
70
+ self.selector = sel
71
+ self.fields = return_fields
72
+ end
73
+
74
+ # Set query selector hash. If sel is Code/string, it will be used as a
75
+ # $where clause. (See Mongo docs for details.)
76
+ def selector=(sel)
77
+ @selector = case sel
78
+ when nil
79
+ {}
80
+ when Code
81
+ {"$where" => sel}
82
+ when String
83
+ {"$where" => Code.new(sel)}
84
+ when Hash
85
+ sel
86
+ end
87
+ end
88
+
89
+ # Set fields to return. If +val+ is +nil+ or empty, all fields will be
90
+ # returned.
91
+ def fields=(val)
92
+ @fields = val
93
+ @fields = nil if @fields && @fields.empty?
94
+ end
95
+
96
+ def fields
97
+ case @fields
98
+ when String
99
+ {@fields => 1}
100
+ when Array
101
+ if @fields.length == 0
102
+ nil
103
+ else
104
+ h = {}
105
+ @fields.each { |field| h[field] = 1 }
106
+ h
107
+ end
108
+ else # nil, anything else
109
+ nil
110
+ end
111
+ end
112
+
113
+ def contains_special_fields
114
+ (@order_by != nil && @order_by.length > 0) || @explain || @hint
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,42 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'mongo/util/byte_buffer'
18
+
19
+ module XGen
20
+ module Mongo
21
+ module Driver
22
+
23
+ # An array of binary bytes with a Mongo subtype value.
24
+ class Binary < ByteBuffer
25
+
26
+ SUBTYPE_BYTES = 0x02
27
+ SUBTYPE_UUID = 0x03
28
+ SUBTYPE_MD5 = 0x05
29
+ SUBTYPE_USER_DEFINED = 0x80
30
+
31
+ # One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
32
+ attr_accessor :subtype
33
+
34
+ def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
35
+ super(initial_data)
36
+ @subtype = subtype
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module XGen
18
+ module Mongo
19
+ module Driver
20
+
21
+ # JavaScript code to be evaluated by MongoDB
22
+ class Code < String
23
+ # Hash mapping identifiers to their values
24
+ attr_accessor :scope
25
+
26
+ def initialize(code, scope={})
27
+ super(code)
28
+ @scope = scope
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,37 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module XGen
18
+ module Mongo
19
+ module Driver
20
+
21
+ class DBRef
22
+
23
+ attr_reader :namespace, :object_id
24
+
25
+ def initialize(namespace, object_id)
26
+ @namespace, @object_id =
27
+ namespace, object_id
28
+ end
29
+
30
+ def to_s
31
+ "ns: #{namespace}, id: #{object_id}"
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,137 @@
1
+ # --
2
+ # Copyright (C) 2008-2009 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'mutex_m'
18
+ require 'mongo/util/byte_buffer'
19
+
20
+ module XGen
21
+ module Mongo
22
+ module Driver
23
+
24
+ # Implementation of the Babble OID. Object ids are not required by
25
+ # Mongo, but they make certain operations more efficient.
26
+ #
27
+ # The driver does not automatically assign ids to records that are
28
+ # inserted. (An upcoming feature will allow you to give an id "factory"
29
+ # to a database and/or a collection.)
30
+ #
31
+ # 12 bytes
32
+ # ---
33
+ # 0 time
34
+ # 1
35
+ # 2
36
+ # 3
37
+ # 4 machine
38
+ # 5
39
+ # 6
40
+ # 7 pid
41
+ # 8
42
+ # 9 inc
43
+ # 10
44
+ # 11
45
+ class ObjectID
46
+
47
+ MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] )
48
+ PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; )
49
+
50
+ # The string representation of an OID is different than its internal
51
+ # and BSON byte representations. The BYTE_ORDER here maps
52
+ # internal/BSON byte position (the index in BYTE_ORDER) to the
53
+ # position of the two hex characters representing that byte in the
54
+ # string representation. For example, the 0th BSON byte corresponds to
55
+ # the (0-based) 7th pair of hex chars in the string.
56
+ BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
57
+
58
+ LOCK = Object.new
59
+ LOCK.extend Mutex_m
60
+
61
+ @@index_time = Time.new.to_i
62
+ @@index = 0
63
+
64
+ # Given a string representation of an ObjectID, return a new ObjectID
65
+ # with that value.
66
+ def self.from_string(str)
67
+ raise "illegal ObjectID format" unless legal?(str)
68
+ data = []
69
+ BYTE_ORDER.each_with_index { |string_position, data_index|
70
+ data[data_index] = str[string_position * 2, 2].to_i(16)
71
+ }
72
+ self.new(data)
73
+ end
74
+
75
+ def self.legal?(str)
76
+ len = BYTE_ORDER.length * 2
77
+ str =~ /([0-9a-f]+)/i
78
+ match = $1
79
+ str && str.length == len && match == str
80
+ end
81
+
82
+ # +data+ is an array of bytes. If nil, a new id will be generated.
83
+ # The time +t+ is only used for testing; leave it nil.
84
+ def initialize(data=nil, t=nil)
85
+ @data = data || generate_id(t)
86
+ end
87
+
88
+ def eql?(other)
89
+ @data == other.instance_variable_get("@data")
90
+ end
91
+ alias_method :==, :eql?
92
+
93
+ def to_a
94
+ @data.dup
95
+ end
96
+
97
+ def to_s
98
+ str = ' ' * 24
99
+ BYTE_ORDER.each_with_index { |string_position, data_index|
100
+ str[string_position * 2, 2] = '%02x' % @data[data_index]
101
+ }
102
+ str
103
+ end
104
+
105
+ # (Would normally be private, but isn't so we can test it.)
106
+ def generate_id(t=nil)
107
+ t ||= Time.new.to_i
108
+ buf = ByteBuffer.new
109
+ buf.put_int(t & 0xffffffff)
110
+ buf.put_array(MACHINE)
111
+ buf.put_array(PID)
112
+ i = index_for_time(t)
113
+ buf.put(i & 0xff)
114
+ buf.put((i >> 8) & 0xff)
115
+ buf.put((i >> 16) & 0xff)
116
+
117
+ buf.rewind
118
+ buf.to_a.dup
119
+ end
120
+
121
+ # (Would normally be private, but isn't so we can test it.)
122
+ def index_for_time(t)
123
+ LOCK.mu_synchronize {
124
+ if t != @@index_time
125
+ @@index = 0
126
+ @@index_time = t
127
+ end
128
+ retval = @@index
129
+ @@index += 1
130
+ retval
131
+ }
132
+ end
133
+
134
+ end
135
+ end
136
+ end
137
+ end