← Index
Performance Profile   « block view • line view • sub view »
For opac/opac-main.pl
  Run on Mon Aug 24 11:28:47 2009
Reported on Mon Aug 24 11:29:11 2009

File /usr/lib/perl5/ZOOM.pm
Statements Executed 24
Total Time 0.0070826 seconds
Subroutines — ordered by exclusive time
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
11121µs82µsZOOM::Log::::mask_str ZOOM::Log::mask_str
0000s0sZOOM::CCL::Error::::BAD_RELATION ZOOM::CCL::Error::BAD_RELATION
0000s0sZOOM::CCL::Error::::BAD_RP ZOOM::CCL::Error::BAD_RP
0000s0sZOOM::CCL::Error::::DOUBLE_QUAL ZOOM::CCL::Error::DOUBLE_QUAL
0000s0sZOOM::CCL::Error::::EQ_EXPECTED ZOOM::CCL::Error::EQ_EXPECTED
0000s0sZOOM::CCL::Error::::OK ZOOM::CCL::Error::OK
0000s0sZOOM::CCL::Error::::OP_EXPECTED ZOOM::CCL::Error::OP_EXPECTED
0000s0sZOOM::CCL::Error::::RP_EXPECTED ZOOM::CCL::Error::RP_EXPECTED
0000s0sZOOM::CCL::Error::::SETNAME_EXPECTED ZOOM::CCL::Error::SETNAME_EXPECTED
0000s0sZOOM::CCL::Error::::TERM_EXPECTED ZOOM::CCL::Error::TERM_EXPECTED
0000s0sZOOM::CCL::Error::::TRUNC_NOT_BOTH ZOOM::CCL::Error::TRUNC_NOT_BOTH
0000s0sZOOM::CCL::Error::::TRUNC_NOT_LEFT ZOOM::CCL::Error::TRUNC_NOT_LEFT
0000s0sZOOM::CCL::Error::::TRUNC_NOT_RIGHT ZOOM::CCL::Error::TRUNC_NOT_RIGHT
0000s0sZOOM::CCL::Error::::UNKNOWN_QUAL ZOOM::CCL::Error::UNKNOWN_QUAL
0000s0sZOOM::Connection::::_check ZOOM::Connection::_check
0000s0sZOOM::Connection::::_conn ZOOM::Connection::_conn
0000s0sZOOM::Connection::::addinfo ZOOM::Connection::addinfo
0000s0sZOOM::Connection::::check ZOOM::Connection::check
0000s0sZOOM::Connection::::connect ZOOM::Connection::connect
0000s0sZOOM::Connection::::create ZOOM::Connection::create
0000s0sZOOM::Connection::::destroy ZOOM::Connection::destroy
0000s0sZOOM::Connection::::diagset ZOOM::Connection::diagset
0000s0sZOOM::Connection::::errcode ZOOM::Connection::errcode
0000s0sZOOM::Connection::::errmsg ZOOM::Connection::errmsg
0000s0sZOOM::Connection::::error_x ZOOM::Connection::error_x
0000s0sZOOM::Connection::::exception ZOOM::Connection::exception
0000s0sZOOM::Connection::::is_idle ZOOM::Connection::is_idle
0000s0sZOOM::Connection::::last_event ZOOM::Connection::last_event
0000s0sZOOM::Connection::::new ZOOM::Connection::new
0000s0sZOOM::Connection::::option ZOOM::Connection::option
0000s0sZOOM::Connection::::option_binary ZOOM::Connection::option_binary
0000s0sZOOM::Connection::::package ZOOM::Connection::package
0000s0sZOOM::Connection::::peek_event ZOOM::Connection::peek_event
0000s0sZOOM::Connection::::scan ZOOM::Connection::scan
0000s0sZOOM::Connection::::scan_pqf ZOOM::Connection::scan_pqf
0000s0sZOOM::Connection::::search ZOOM::Connection::search
0000s0sZOOM::Connection::::search_pqf ZOOM::Connection::search_pqf
0000s0sZOOM::Error::::CCL_CONFIG ZOOM::Error::CCL_CONFIG
0000s0sZOOM::Error::::CCL_PARSE ZOOM::Error::CCL_PARSE
0000s0sZOOM::Error::::CLONE ZOOM::Error::CLONE
0000s0sZOOM::Error::::CONNECT ZOOM::Error::CONNECT
0000s0sZOOM::Error::::CONNECTION_LOST ZOOM::Error::CONNECTION_LOST
0000s0sZOOM::Error::::CQL_PARSE ZOOM::Error::CQL_PARSE
0000s0sZOOM::Error::::CQL_TRANSFORM ZOOM::Error::CQL_TRANSFORM
0000s0sZOOM::Error::::CREATE_QUERY ZOOM::Error::CREATE_QUERY
0000s0sZOOM::Error::::DECODE ZOOM::Error::DECODE
0000s0sZOOM::Error::::ENCODE ZOOM::Error::ENCODE
0000s0sZOOM::Error::::INTERNAL ZOOM::Error::INTERNAL
0000s0sZOOM::Error::::INVALID_QUERY ZOOM::Error::INVALID_QUERY
0000s0sZOOM::Error::::LOGLEVEL ZOOM::Error::LOGLEVEL
0000s0sZOOM::Error::::MEMORY ZOOM::Error::MEMORY
0000s0sZOOM::Error::::NONE ZOOM::Error::NONE
0000s0sZOOM::Error::::PACKAGE ZOOM::Error::PACKAGE
0000s0sZOOM::Error::::QUERY_CQL ZOOM::Error::QUERY_CQL
0000s0sZOOM::Error::::QUERY_PQF ZOOM::Error::QUERY_PQF
0000s0sZOOM::Error::::SCANTERM ZOOM::Error::SCANTERM
0000s0sZOOM::Error::::SORTBY ZOOM::Error::SORTBY
0000s0sZOOM::Error::::TIMEOUT ZOOM::Error::TIMEOUT
0000s0sZOOM::Error::::UNSUPPORTED_PROTOCOL ZOOM::Error::UNSUPPORTED_PROTOCOL
0000s0sZOOM::Error::::UNSUPPORTED_QUERY ZOOM::Error::UNSUPPORTED_QUERY
0000s0sZOOM::Error::::ZINIT ZOOM::Error::ZINIT
0000s0sZOOM::Event::::CONNECT ZOOM::Event::CONNECT
0000s0sZOOM::Event::::NONE ZOOM::Event::NONE
0000s0sZOOM::Event::::RECV_APDU ZOOM::Event::RECV_APDU
0000s0sZOOM::Event::::RECV_DATA ZOOM::Event::RECV_DATA
0000s0sZOOM::Event::::RECV_RECORD ZOOM::Event::RECV_RECORD
0000s0sZOOM::Event::::RECV_SEARCH ZOOM::Event::RECV_SEARCH
0000s0sZOOM::Event::::SEND_APDU ZOOM::Event::SEND_APDU
0000s0sZOOM::Event::::SEND_DATA ZOOM::Event::SEND_DATA
0000s0sZOOM::Event::::TIMEOUT ZOOM::Event::TIMEOUT
0000s0sZOOM::Event::::UNKNOWN ZOOM::Event::UNKNOWN
0000s0sZOOM::Event::::ZEND ZOOM::Event::ZEND
0000s0sZOOM::Exception::::BEGIN ZOOM::Exception::BEGIN
0000s0sZOOM::Exception::::addinfo ZOOM::Exception::addinfo
0000s0sZOOM::Exception::::code ZOOM::Exception::code
0000s0sZOOM::Exception::::diagset ZOOM::Exception::diagset
0000s0sZOOM::Exception::::message ZOOM::Exception::message
0000s0sZOOM::Exception::::new ZOOM::Exception::new
0000s0sZOOM::Exception::::render ZOOM::Exception::render
0000s0sZOOM::Log::::BEGIN ZOOM::Log::BEGIN
0000s0sZOOM::Log::::init ZOOM::Log::init
0000s0sZOOM::Log::::init_file ZOOM::Log::init_file
0000s0sZOOM::Log::::init_level ZOOM::Log::init_level
0000s0sZOOM::Log::::init_max_size ZOOM::Log::init_max_size
0000s0sZOOM::Log::::init_prefix ZOOM::Log::init_prefix
0000s0sZOOM::Log::::log ZOOM::Log::log
0000s0sZOOM::Log::::module_level ZOOM::Log::module_level
0000s0sZOOM::Log::::time_format ZOOM::Log::time_format
0000s0sZOOM::Options::::_opts ZOOM::Options::_opts
0000s0sZOOM::Options::::bool ZOOM::Options::bool
0000s0sZOOM::Options::::destroy ZOOM::Options::destroy
0000s0sZOOM::Options::::int ZOOM::Options::int
0000s0sZOOM::Options::::new ZOOM::Options::new
0000s0sZOOM::Options::::option ZOOM::Options::option
0000s0sZOOM::Options::::option_binary ZOOM::Options::option_binary
0000s0sZOOM::Options::::set_callback ZOOM::Options::set_callback
0000s0sZOOM::Options::::set_int ZOOM::Options::set_int
0000s0sZOOM::Package::::_new ZOOM::Package::_new
0000s0sZOOM::Package::::_p ZOOM::Package::_p
0000s0sZOOM::Package::::destroy ZOOM::Package::destroy
0000s0sZOOM::Package::::new ZOOM::Package::new
0000s0sZOOM::Package::::option ZOOM::Package::option
0000s0sZOOM::Package::::send ZOOM::Package::send
0000s0sZOOM::Query::CCL2RPN::::newZOOM::Query::CCL2RPN::new
0000s0sZOOM::Query::CQL2RPN::::newZOOM::Query::CQL2RPN::new
0000s0sZOOM::Query::CQL::::new ZOOM::Query::CQL::new
0000s0sZOOM::Query::PQF::::new ZOOM::Query::PQF::new
0000s0sZOOM::Query::::_query ZOOM::Query::_query
0000s0sZOOM::Query::::destroy ZOOM::Query::destroy
0000s0sZOOM::Query::::new ZOOM::Query::new
0000s0sZOOM::Query::::sortby ZOOM::Query::sortby
0000s0sZOOM::Record::::_new ZOOM::Record::_new
0000s0sZOOM::Record::::_rec ZOOM::Record::_rec
0000s0sZOOM::Record::::clone ZOOM::Record::clone
0000s0sZOOM::Record::::destroy ZOOM::Record::destroy
0000s0sZOOM::Record::::error ZOOM::Record::error
0000s0sZOOM::Record::::exception ZOOM::Record::exception
0000s0sZOOM::Record::::get ZOOM::Record::get
0000s0sZOOM::Record::::new ZOOM::Record::new
0000s0sZOOM::Record::::raw ZOOM::Record::raw
0000s0sZOOM::Record::::render ZOOM::Record::render
0000s0sZOOM::ResultSet::::_new ZOOM::ResultSet::_new
0000s0sZOOM::ResultSet::::_rs ZOOM::ResultSet::_rs
0000s0sZOOM::ResultSet::::cache_reset ZOOM::ResultSet::cache_reset
0000s0sZOOM::ResultSet::::destroy ZOOM::ResultSet::destroy
0000s0sZOOM::ResultSet::::new ZOOM::ResultSet::new
0000s0sZOOM::ResultSet::::option ZOOM::ResultSet::option
0000s0sZOOM::ResultSet::::record ZOOM::ResultSet::record
0000s0sZOOM::ResultSet::::record_immediate ZOOM::ResultSet::record_immediate
0000s0sZOOM::ResultSet::::records ZOOM::ResultSet::records
0000s0sZOOM::ResultSet::::size ZOOM::ResultSet::size
0000s0sZOOM::ResultSet::::sort ZOOM::ResultSet::sort
0000s0sZOOM::ScanSet::::_new ZOOM::ScanSet::_new
0000s0sZOOM::ScanSet::::_ss ZOOM::ScanSet::_ss
0000s0sZOOM::ScanSet::::destroy ZOOM::ScanSet::destroy
0000s0sZOOM::ScanSet::::display_term ZOOM::ScanSet::display_term
0000s0sZOOM::ScanSet::::new ZOOM::ScanSet::new
0000s0sZOOM::ScanSet::::option ZOOM::ScanSet::option
0000s0sZOOM::ScanSet::::size ZOOM::ScanSet::size
0000s0sZOOM::ScanSet::::term ZOOM::ScanSet::term
0000s0sZOOM::::_oops ZOOM::_oops
0000s0sZOOM::::diag_srw_str ZOOM::diag_srw_str
0000s0sZOOM::::diag_str ZOOM::diag_str
0000s0sZOOM::::event ZOOM::event
0000s0sZOOM::::event_str ZOOM::event_str
LineStmts.Exclusive
Time
Avg.Code
1# $Id: ZOOM.pm,v 1.50 2008-05-14 13:33:31 mike Exp $
2
3328µs9µsuse strict;
# spent 10µs making 1 call to strict::import
4331µs10µsuse warnings;
# spent 24µs making 1 call to warnings::import
53158µs53µsuse IO::File;
# spent 134µs making 1 call to Exporter::import
631.63ms545µsuse Net::Z3950::ZOOM;
# spent 4µs making 1 call to import
7
8
9package ZOOM;
10
11# Member naming convention: hash-element names which begin with an
12# underscore represent underlying ZOOM-C object descriptors; those
13# which lack them represent Perl's ZOOM objects. (The same convention
14# is used in naming local variables where appropriate.)
15#
16# So, for example, the ZOOM::Connection class has an {_conn} element,
17# which is a pointer to the ZOOM-C Connection object; but the
18# ZOOM::ResultSet class has a {conn} element, which is a reference to
19# the Perl-level Connection object by which it was created. (It may
20# be that we find we have no need for these references, but for now
21# they are retained.)
22#
23# To get at the underlying ZOOM-C connection object of a result-set
24# (if you ever needed to do such a thing, which you probably don't)
25# you'd use $rs->{conn}->_conn().
26
27# ----------------------------------------------------------------------------
28
29# The "Error" package contains constants returned as error-codes.
30package ZOOM::Error;
31sub NONE { Net::Z3950::ZOOM::ERROR_NONE }
32sub CONNECT { Net::Z3950::ZOOM::ERROR_CONNECT }
33sub MEMORY { Net::Z3950::ZOOM::ERROR_MEMORY }
34sub ENCODE { Net::Z3950::ZOOM::ERROR_ENCODE }
35sub DECODE { Net::Z3950::ZOOM::ERROR_DECODE }
36sub CONNECTION_LOST { Net::Z3950::ZOOM::ERROR_CONNECTION_LOST }
37sub ZINIT { Net::Z3950::ZOOM::ERROR_INIT }
38sub INTERNAL { Net::Z3950::ZOOM::ERROR_INTERNAL }
39sub TIMEOUT { Net::Z3950::ZOOM::ERROR_TIMEOUT }
40sub UNSUPPORTED_PROTOCOL { Net::Z3950::ZOOM::ERROR_UNSUPPORTED_PROTOCOL }
41sub UNSUPPORTED_QUERY { Net::Z3950::ZOOM::ERROR_UNSUPPORTED_QUERY }
42sub INVALID_QUERY { Net::Z3950::ZOOM::ERROR_INVALID_QUERY }
43sub CQL_PARSE { Net::Z3950::ZOOM::ERROR_CQL_PARSE }
44sub CQL_TRANSFORM { Net::Z3950::ZOOM::ERROR_CQL_TRANSFORM }
45sub CCL_CONFIG { Net::Z3950::ZOOM::ERROR_CCL_CONFIG }
46sub CCL_PARSE { Net::Z3950::ZOOM::ERROR_CCL_PARSE }
47# The following are added specifically for this OO interface
48sub CREATE_QUERY { 20001 }
49sub QUERY_CQL { 20002 }
50sub QUERY_PQF { 20003 }
51sub SORTBY { 20004 }
52sub CLONE { 20005 }
53sub PACKAGE { 20006 }
54sub SCANTERM { 20007 }
55sub LOGLEVEL { 20008 }
56
57# Separate space for CCL errors. Great.
58package ZOOM::CCL::Error;
59sub OK { Net::Z3950::ZOOM::CCL_ERR_OK }
60sub TERM_EXPECTED { Net::Z3950::ZOOM::CCL_ERR_TERM_EXPECTED }
61sub RP_EXPECTED { Net::Z3950::ZOOM::CCL_ERR_RP_EXPECTED }
62sub SETNAME_EXPECTED { Net::Z3950::ZOOM::CCL_ERR_SETNAME_EXPECTED }
63sub OP_EXPECTED { Net::Z3950::ZOOM::CCL_ERR_OP_EXPECTED }
64sub BAD_RP { Net::Z3950::ZOOM::CCL_ERR_BAD_RP }
65sub UNKNOWN_QUAL { Net::Z3950::ZOOM::CCL_ERR_UNKNOWN_QUAL }
66sub DOUBLE_QUAL { Net::Z3950::ZOOM::CCL_ERR_DOUBLE_QUAL }
67sub EQ_EXPECTED { Net::Z3950::ZOOM::CCL_ERR_EQ_EXPECTED }
68sub BAD_RELATION { Net::Z3950::ZOOM::CCL_ERR_BAD_RELATION }
69sub TRUNC_NOT_LEFT { Net::Z3950::ZOOM::CCL_ERR_TRUNC_NOT_LEFT }
70sub TRUNC_NOT_BOTH { Net::Z3950::ZOOM::CCL_ERR_TRUNC_NOT_BOTH }
71sub TRUNC_NOT_RIGHT { Net::Z3950::ZOOM::CCL_ERR_TRUNC_NOT_RIGHT }
72
73# The "Event" package contains constants returned by last_event()
74package ZOOM::Event;
75sub NONE { Net::Z3950::ZOOM::EVENT_NONE }
76sub CONNECT { Net::Z3950::ZOOM::EVENT_CONNECT }
77sub SEND_DATA { Net::Z3950::ZOOM::EVENT_SEND_DATA }
78sub RECV_DATA { Net::Z3950::ZOOM::EVENT_RECV_DATA }
79sub TIMEOUT { Net::Z3950::ZOOM::EVENT_TIMEOUT }
80sub UNKNOWN { Net::Z3950::ZOOM::EVENT_UNKNOWN }
81sub SEND_APDU { Net::Z3950::ZOOM::EVENT_SEND_APDU }
82sub RECV_APDU { Net::Z3950::ZOOM::EVENT_RECV_APDU }
83sub RECV_RECORD { Net::Z3950::ZOOM::EVENT_RECV_RECORD }
84sub RECV_SEARCH { Net::Z3950::ZOOM::EVENT_RECV_SEARCH }
85sub ZEND { Net::Z3950::ZOOM::EVENT_END }
86
87# ----------------------------------------------------------------------------
88
89package ZOOM;
90
91sub diag_str {
92 my($code) = @_;
93
94 # Special cases for error specific to the OO layer
95 if ($code == ZOOM::Error::CREATE_QUERY) {
96 return "can't create query object";
97 } elsif ($code == ZOOM::Error::QUERY_CQL) {
98 return "can't set CQL query";
99 } elsif ($code == ZOOM::Error::QUERY_PQF) {
100 return "can't set prefix query";
101 } elsif ($code == ZOOM::Error::SORTBY) {
102 return "can't set sort-specification";
103 } elsif ($code == ZOOM::Error::CLONE) {
104 return "can't clone record";
105 } elsif ($code == ZOOM::Error::PACKAGE) {
106 return "can't create package";
107 } elsif ($code == ZOOM::Error::SCANTERM) {
108 return "can't retrieve term from scan-set";
109 } elsif ($code == ZOOM::Error::LOGLEVEL) {
110 return "unregistered log-level";
111 }
112
113 return Net::Z3950::ZOOM::diag_str($code);
114}
115
116sub diag_srw_str {
117 my($code) = @_;
118
119 return Net::Z3950::ZOOM::diag_srw_str($code);
120}
121
122sub event_str {
123 return Net::Z3950::ZOOM::event_str(@_);
124}
125
126sub event {
127 my($connsref) = @_;
128
129 my @_connsref = map { $_->_conn() } @$connsref;
130 return Net::Z3950::ZOOM::event(\@_connsref);
131}
132
133sub _oops {
134 my($code, $addinfo, $diagset) = @_;
135
136 die new ZOOM::Exception($code, undef, $addinfo, $diagset);
137}
138
139# ----------------------------------------------------------------------------
140
141package ZOOM::Exception;
142
143sub new {
144 my $class = shift();
145 my($code, $message, $addinfo, $diagset) = @_;
146
147 $diagset ||= "ZOOM";
148 if (uc($diagset) eq "ZOOM" || uc($diagset) eq "BIB-1") {
149 $message ||= ZOOM::diag_str($code);
150 } elsif (lc($diagset) eq "info:srw/diagnostic/1") {
151 $message ||= ZOOM::diag_srw_str($code);
152 } else {
153 # Should fill in messages for any other known diagsets.
154 $message ||= "(unknown error)";
155 }
156
157 return bless {
158 code => $code,
159 message => $message,
160 addinfo => $addinfo,
161 diagset => $diagset,
162 }, $class;
163}
164
165sub code {
166 my $this = shift();
167 return $this->{code};
168}
169
170sub message {
171 my $this = shift();
172 return $this->{message};
173}
174
175sub addinfo {
176 my $this = shift();
177 return $this->{addinfo};
178}
179
180sub diagset {
181 my $this = shift();
182 return $this->{diagset};
183}
184
185sub render {
186 my $this = shift();
187
188 my $res = "ZOOM error " . $this->code();
189 $res .= ' "' . $this->message() . '"' if $this->message();
190 $res .= ' (addinfo: "' . $this->addinfo() . '")' if $this->addinfo();
191 $res .= " from diag-set '" . $this->diagset() . "'" if $this->diagset();
192 return $res;
193}
194
195# This means that untrapped exceptions render nicely.
19635.05ms1.68msuse overload '""' => \&render;
# spent 54µs making 1 call to overload::import
197
198# ----------------------------------------------------------------------------
199
200package ZOOM::Options;
201
202sub new {
203 my $class = shift();
204 my($p1, $p2) = @_;
205
206 my $opts;
207 if (@_ == 0) {
208 $opts = Net::Z3950::ZOOM::options_create();
209 } elsif (@_ == 1) {
210 $opts = Net::Z3950::ZOOM::options_create_with_parent($p1->_opts());
211 } elsif (@_ == 2) {
212 $opts = Net::Z3950::ZOOM::options_create_with_parent2($p1->_opts(),
213 $p2->_opts());
214 } else {
215 die "can't make $class object with more than 2 parents";
216 }
217
218 return bless {
219 _opts => $opts,
220 }, $class;
221}
222
223# PRIVATE to this class and ZOOM::Connection::create() and
224# ZOOM::Connection::package()
225#
226sub _opts {
227 my $this = shift();
228
229 my $_opts = $this->{_opts};
230 die "{_opts} undefined: has this Options block been destroy()ed?"
231 if !defined $_opts;
232
233 return $_opts;
234}
235
236sub option {
237 my $this = shift();
238 my($key, $value) = @_;
239
240 my $oldval = Net::Z3950::ZOOM::options_get($this->_opts(), $key);
241 Net::Z3950::ZOOM::options_set($this->_opts(), $key, $value)
242 if defined $value;
243
244 return $oldval;
245}
246
247sub option_binary {
248 my $this = shift();
249 my($key, $value) = @_;
250
251 my $dummylen = 0;
252 my $oldval = Net::Z3950::ZOOM::options_getl($this->_opts(),
253 $key, $dummylen);
254 Net::Z3950::ZOOM::options_setl($this->_opts(), $key,
255 $value, length($value))
256 if defined $value;
257
258 return $oldval;
259}
260
261# This is a bit stupid, since the scalar values that Perl returns from
262# option() can be used as a boolean; but it's just possible that some
263# applications will rely on ZOOM_options_get_bool()'s idiosyncratic
264# interpretation of what constitutes truth.
265#
266sub bool {
267 my $this = shift();
268 my($key, $default) = @_;
269
270 return Net::Z3950::ZOOM::options_get_bool($this->_opts(), $key, $default);
271}
272
273# .. and the next two are even more stupid
274sub int {
275 my $this = shift();
276 my($key, $default) = @_;
277
278 return Net::Z3950::ZOOM::options_get_int($this->_opts(), $key, $default);
279}
280
281sub set_int {
282 my $this = shift();
283 my($key, $value) = @_;
284
285 Net::Z3950::ZOOM::options_set_int($this->_opts(), $key, $value);
286}
287
288# ### Feel guilty. Feel very, very guilty. I've not been able to
289# get the callback memory-management right in "ZOOM.xs", with
290# the result that the values of $function and $udata passed into
291# this function, which are on the stack, have sometimes been
292# freed by the time they're used by __ZOOM_option_callback(),
293# with hilarious results. To avoid this, I copy the values into
294# module-scoped globals, and pass _those_ into the extension
295# function. To avoid overwriting those globals by subsequent
296# calls, I keep all the old ones, pushed onto the @_function and
297# @_udata arrays, which means that THIS FUNCTION LEAKS MEMORY
298# LIKE IT'S GOING OUT OF FASHION. Not nice. One day, I should
299# fix this, but for now there's more important fish to fry.
300#
3011900ns900nsmy(@_function, @_udata);
302sub set_callback {
303 my $o1 = shift();
304 my($function, $udata) = @_;
305
306 push @_function, $function;
307 push @_udata, $udata;
308 Net::Z3950::ZOOM::options_set_callback($o1->_opts(),
309 $_function[-1], $_udata[-1]);
310}
311
312sub destroy {
313 my $this = shift();
314
315 Net::Z3950::ZOOM::options_destroy($this->_opts());
316 $this->{_opts} = undef;
317}
318
319
320# ----------------------------------------------------------------------------
321
322package ZOOM::Connection;
323
324sub new {
325 my $class = shift();
326 my($host, $port, @options) = @_;
327
328 my $conn = $class->create(@options);
329 $conn->{host} = $host;
330 $conn->{port} = $port;
331
332 Net::Z3950::ZOOM::connection_connect($conn->_conn(), $host, $port || 0);
333 $conn->_check();
334
335 return $conn;
336}
337
338# PRIVATE to this class, to ZOOM::event() and to ZOOM::Query::CQL2RPN::new()
339sub _conn {
340 my $this = shift();
341
342 my $_conn = $this->{_conn};
343 die "{_conn} undefined: has this Connection been destroy()ed?"
344 if !defined $_conn;
345
346 return $_conn;
347}
348
349sub _check {
350 my $this = shift();
351 my($always_die_on_error) = @_;
352
353 my($errcode, $errmsg, $addinfo, $diagset) = (undef, "x", "x", "x");
354 $errcode = Net::Z3950::ZOOM::connection_error_x($this->_conn(), $errmsg,
355 $addinfo, $diagset);
356 if ($errcode) {
357 my $exception = new ZOOM::Exception($errcode, $errmsg, $addinfo,
358 $diagset);
359 if (!$this->option("async") || $always_die_on_error) {
360 ZOOM::Log::log("zoom_check", "throwing error $exception");
361 die $exception;
362 } else {
363 ZOOM::Log::log("zoom_check", "not reporting error $exception");
364 }
365 }
366}
367
368# This wrapper for _check() is called only from outside the ZOOM
369# module, and therefore only in situations where an asynchronous
370# application is actively asking for an exception to be thrown if an
371# error has been detected. So it passed always_die_on_error=1 to the
372# underlying _check() method.
373#
374sub check {
375 my $this = shift();
376 return $this->_check(1);
377}
378
379sub create {
380 my $class = shift();
381 my(@options) = @_;
382
383 my $_opts;
384 if (@_ == 1) {
385 $_opts = $_[0]->_opts();
386 } else {
387 $_opts = Net::Z3950::ZOOM::options_create();
388 while (@options >= 2) {
389 my $key = shift(@options);
390 my $val = shift(@options);
391 Net::Z3950::ZOOM::options_set($_opts, $key, $val);
392 }
393
394 die "Odd number of options specified"
395 if @options;
396 }
397
398 my $_conn = Net::Z3950::ZOOM::connection_create($_opts);
399 my $conn = bless {
400 host => undef,
401 port => undef,
402 _conn => $_conn,
403 }, $class;
404 return $conn;
405}
406
407sub error_x {
408 my $this = shift();
409
410 my($errcode, $errmsg, $addinfo, $diagset) = (undef, "dummy", "dummy", "d");
411 $errcode = Net::Z3950::ZOOM::connection_error_x($this->_conn(), $errmsg,
412 $addinfo, $diagset);
413 return wantarray() ? ($errcode, $errmsg, $addinfo, $diagset) : $errcode;
414}
415
416sub exception {
417 my $this = shift();
418
419 my($errcode, $errmsg, $addinfo, $diagset) = $this->error_x();
420 return undef if $errcode == 0;
421 return new ZOOM::Exception($errcode, $errmsg, $addinfo, $diagset);
422}
423
424sub errcode {
425 my $this = shift();
426 return Net::Z3950::ZOOM::connection_errcode($this->_conn());
427}
428
429sub errmsg {
430 my $this = shift();
431 return Net::Z3950::ZOOM::connection_errmsg($this->_conn());
432}
433
434sub addinfo {
435 my $this = shift();
436 return Net::Z3950::ZOOM::connection_addinfo($this->_conn());
437}
438
439sub diagset {
440 my $this = shift();
441 return Net::Z3950::ZOOM::connection_diagset($this->_conn());
442}
443
444sub connect {
445 my $this = shift();
446 my($host, $port) = @_;
447
448 $port = 0 if !defined $port;
449 Net::Z3950::ZOOM::connection_connect($this->_conn(), $host, $port);
450 $this->_check();
451 # No return value
452}
453
454sub option {
455 my $this = shift();
456 my($key, $value) = @_;
457
458 my $oldval = Net::Z3950::ZOOM::connection_option_get($this->_conn(), $key);
459 Net::Z3950::ZOOM::connection_option_set($this->_conn(), $key, $value)
460 if defined $value;
461
462 return $oldval;
463}
464
465sub option_binary {
466 my $this = shift();
467 my($key, $value) = @_;
468
469 my $dummylen = 0;
470 my $oldval = Net::Z3950::ZOOM::connection_option_getl($this->_conn(),
471 $key, $dummylen);
472 Net::Z3950::ZOOM::connection_option_setl($this->_conn(), $key,
473 $value, length($value))
474 if defined $value;
475
476 return $oldval;
477}
478
479sub search {
480 my $this = shift();
481 my($query) = @_;
482
483 my $_rs = Net::Z3950::ZOOM::connection_search($this->_conn(),
484 $query->_query());
485 $this->_check();
486 return _new ZOOM::ResultSet($this, $query, $_rs);
487}
488
489sub search_pqf {
490 my $this = shift();
491 my($pqf) = @_;
492
493 my $_rs = Net::Z3950::ZOOM::connection_search_pqf($this->_conn(), $pqf);
494 $this->_check();
495 return _new ZOOM::ResultSet($this, $pqf, $_rs);
496}
497
498sub scan_pqf {
499 my $this = shift();
500 my($startterm) = @_;
501
502 my $_ss = Net::Z3950::ZOOM::connection_scan($this->_conn(), $startterm);
503 $this->_check();
504 return _new ZOOM::ScanSet($this, $startterm, $_ss);
505}
506
507sub scan {
508 my $this = shift();
509 my($query) = @_;
510
511 my $_ss = Net::Z3950::ZOOM::connection_scan1($this->_conn(),
512 $query->_query());
513 $this->_check();
514 return _new ZOOM::ScanSet($this, $query, $_ss);
515}
516
517sub package {
518 my $this = shift();
519 my($options) = @_;
520
521 my $_o = defined $options ? $options->_opts() :
522 Net::Z3950::ZOOM::options_create();
523 my $_p = Net::Z3950::ZOOM::connection_package($this->_conn(), $_o)
524 or ZOOM::_oops(ZOOM::Error::PACKAGE);
525
526 return _new ZOOM::Package($this, $options, $_p);
527}
528
529sub last_event {
530 my $this = shift();
531
532 return Net::Z3950::ZOOM::connection_last_event($this->_conn());
533}
534
535sub is_idle {
536 my $this = shift();
537
538 return Net::Z3950::ZOOM::connection_is_idle($this->_conn());
539}
540
541sub peek_event {
542 my $this = shift();
543
544 return Net::Z3950::ZOOM::connection_peek_event($this->_conn());
545}
546
547sub destroy {
548 my $this = shift();
549
550 Net::Z3950::ZOOM::connection_destroy($this->_conn());
551 $this->{_conn} = undef;
552}
553
554
555# ----------------------------------------------------------------------------
556
557package ZOOM::Query;
558
559sub new {
560 my $class = shift();
561 die "You can't create $class objects: it's a virtual base class";
562}
563
564# PRIVATE to this class and ZOOM::Connection::search()
565sub _query {
566 my $this = shift();
567
568 my $_query = $this->{_query};
569 die "{_query} undefined: has this Query been destroy()ed?"
570 if !defined $_query;
571
572 return $_query;
573}
574
575sub sortby {
576 my $this = shift();
577 my($sortby) = @_;
578
579 Net::Z3950::ZOOM::query_sortby($this->_query(), $sortby) == 0
580 or ZOOM::_oops(ZOOM::Error::SORTBY, $sortby);
581}
582
583sub destroy {
584 my $this = shift();
585
586 Net::Z3950::ZOOM::query_destroy($this->_query());
587 $this->{_query} = undef;
588}
589
590
591package ZOOM::Query::CQL;
592111µs11µsour @ISA = qw(ZOOM::Query);
593
594sub new {
595 my $class = shift();
596 my($string) = @_;
597
598 my $q = Net::Z3950::ZOOM::query_create()
599 or ZOOM::_oops(ZOOM::Error::CREATE_QUERY);
600 Net::Z3950::ZOOM::query_cql($q, $string) == 0
601 or ZOOM::_oops(ZOOM::Error::QUERY_CQL, $string);
602
603 return bless {
604 _query => $q,
605 }, $class;
606}
607
608
609package ZOOM::Query::CQL2RPN;
61014µs4µsour @ISA = qw(ZOOM::Query);
611
612sub new {
613 my $class = shift();
614 my($string, $conn) = @_;
615
616 my $q = Net::Z3950::ZOOM::query_create()
617 or ZOOM::_oops(ZOOM::Error::CREATE_QUERY);
618 # check() throws the exception we want; but we only want it on failure!
619 Net::Z3950::ZOOM::query_cql2rpn($q, $string, $conn->_conn()) == 0
620 or $conn->_check();
621
622 return bless {
623 _query => $q,
624 }, $class;
625}
626
627
628# We have to work around the retarded ZOOM_query_ccl2rpn() API
629package ZOOM::Query::CCL2RPN;
63013µs3µsour @ISA = qw(ZOOM::Query);
631
632sub new {
633 my $class = shift();
634 my($string, $conn) = @_;
635
636 my $q = Net::Z3950::ZOOM::query_create()
637 or ZOOM::_oops(ZOOM::Error::CREATE_QUERY);
638
639 my $config = $conn->option("cclqual");
640 if (!defined $config) {
641 my $cclfile = $conn->option("cclfile")
642 or ZOOM::_oops(ZOOM::Error::CCL_CONFIG,
643 "no 'cclqual' or 'cclfile' specified");
644 my $fh = new IO::File("<$cclfile")
645 or ZOOM::_oops(ZOOM::Error::CCL_CONFIG,
646 "can't open cclfile '$cclfile': $!");
647 $config = join("", <$fh>);
648 $fh->close();
649 }
650
651 my($ccl_errcode, $ccl_errstr, $ccl_errpos) = (0, "", 0);
652 if (Net::Z3950::ZOOM::query_ccl2rpn($q, $string, $config,
653 $ccl_errcode, $ccl_errstr,
654 $ccl_errpos) < 0) {
655 # We have no use for $ccl_errcode or $ccl_errpos
656 ZOOM::_oops(ZOOM::Error::CCL_PARSE, $ccl_errstr);
657 }
658
659 return bless {
660 _query => $q,
661 }, $class;
662}
663
664
665package ZOOM::Query::PQF;
66613µs3µsour @ISA = qw(ZOOM::Query);
667
668sub new {
669 my $class = shift();
670 my($string) = @_;
671
672 my $q = Net::Z3950::ZOOM::query_create()
673 or ZOOM::_oops(ZOOM::Error::CREATE_QUERY);
674 Net::Z3950::ZOOM::query_prefix($q, $string) == 0
675 or ZOOM::_oops(ZOOM::Error::QUERY_PQF, $string);
676
677 return bless {
678 _query => $q,
679 }, $class;
680}
681
682
683# ----------------------------------------------------------------------------
684
685package ZOOM::ResultSet;
686
687sub new {
688 my $class = shift();
689 die "You can't create $class objects directly";
690}
691
692# PRIVATE to ZOOM::Connection::search() and ZOOM::Connection::search_pqf()
693sub _new {
694 my $class = shift();
695 my($conn, $query, $_rs) = @_;
696
697 return bless {
698 conn => $conn,
699 query => $query, # This is not currently used, which is
700 # just as well since it could be
701 # either a string (when the RS is
702 # created with search_pqf()) or a
703 # ZOOM::Query object (when it's
704 # created with search())
705 _rs => $_rs,
706 }, $class;
707}
708
709# PRIVATE to this class
710sub _rs {
711 my $this = shift();
712
713 my $_rs = $this->{_rs};
714 die "{_rs} undefined: has this ResultSet been destroy()ed?"
715 if !defined $_rs;
716
717 return $_rs;
718}
719
720sub option {
721 my $this = shift();
722 my($key, $value) = @_;
723
724 my $oldval = Net::Z3950::ZOOM::resultset_option_get($this->_rs(), $key);
725 Net::Z3950::ZOOM::resultset_option_set($this->_rs(), $key, $value)
726 if defined $value;
727
728 return $oldval;
729}
730
731sub size {
732 my $this = shift();
733
734 return Net::Z3950::ZOOM::resultset_size($this->_rs());
735}
736
737sub record {
738 my $this = shift();
739 my($which) = @_;
740
741 my $_rec = Net::Z3950::ZOOM::resultset_record($this->_rs(), $which);
742 $this->{conn}->_check();
743
744 # Even if no error has occurred, I think record() might
745 # legitimately return undef if we're running in asynchronous mode
746 # and the record just hasn't been retrieved yet. This goes double
747 # for record_immediate().
748 return undef if !defined $_rec;
749
750 # For some reason, I have to use the explicit "->" syntax in order
751 # to invoke the ZOOM::Record constructor here, even though I don't
752 # have to do the same for _new ZOOM::ResultSet above. Weird.
753 return ZOOM::Record->_new($this, $which, $_rec);
754}
755
756sub record_immediate {
757 my $this = shift();
758 my($which) = @_;
759
760 my $_rec = Net::Z3950::ZOOM::resultset_record_immediate($this->_rs(),
761 $which);
762 $this->{conn}->_check();
763 # The record might legitimately not be there yet
764 return undef if !defined $_rec;
765
766 return ZOOM::Record->_new($this, $which, $_rec);
767}
768
769sub cache_reset {
770 my $this = shift();
771
772 Net::Z3950::ZOOM::resultset_cache_reset($this->_rs());
773}
774
775sub records {
776 my $this = shift();
777 my($start, $count, $return_records) = @_;
778
779 # If the request is out of range, ZOOM-C will currently (as of YAZ
780 # 2.1.38) no-op: it understandably refuses to build and send a
781 # known-bad APDU, but it doesn't set a diagnostic as it ought. So
782 # for now, we do it here. It would be more polite to stash the
783 # error-code in the ZOOM-C connection object for subsequent
784 # discovery (which is what ZOOM-C will presumably do itself when
785 # it's fixed) but since there is no API that allows us to do that,
786 # we just have to throw the exception right now. That's probably
787 # OK for synchronous applications, but not really for
788 # multiplexers.
789 my $size = $this->size();
790 if ($start + $count-1 >= $size) {
791 # BIB-1 diagnostic 13 is "Present request out-of-range"
792 ZOOM::_oops(13, undef, "BIB-1");
793 }
794
795 my $raw = Net::Z3950::ZOOM::resultset_records($this->_rs(), $start, $count,
796 $return_records);
797 # By design, $raw may be undefined (if $return_records is true)
798 return undef if !defined $raw;
799
800 # We need to package up the returned records in ZOOM::Record objects
801 my @res = ();
802 for my $i (0 .. @$raw-1) {
803 my $_rec = $raw->[$i];
804 if (!defined $_rec) {
805 push @res, undef;
806 } else {
807 push @res, ZOOM::Record->_new($this, $start+$i, $_rec);
808 }
809 }
810
811 return \@res;
812}
813
814sub sort {
815 my $this = shift();
816 my($sort_type, $sort_spec) = @_;
817
818 return Net::Z3950::ZOOM::resultset_sort1($this->_rs(),
819 $sort_type, $sort_spec);
820}
821
822sub destroy {
823 my $this = shift();
824
825 Net::Z3950::ZOOM::resultset_destroy($this->_rs());
826 $this->{_rs} = undef;
827}
828
829
830# ----------------------------------------------------------------------------
831
832package ZOOM::Record;
833
834sub new {
835 my $class = shift();
836 die "You can't create $class objects directly";
837}
838
839# PRIVATE to ZOOM::ResultSet::record(),
840# ZOOM::ResultSet::record_immediate(), ZOOM::ResultSet::records() and
841# ZOOM::Record::clone()
842#
843sub _new {
844 my $class = shift();
845 my($rs, $which, $_rec) = @_;
846
847 return bless {
848 rs => $rs,
849 which => $which,
850 _rec => $_rec,
851 }, $class;
852}
853
854# PRIVATE to this class
855sub _rec {
856 my $this = shift();
857
858 my $_rec = $this->{_rec};
859 die "{_rec} undefined: has this Record been destroy()ed?"
860 if !defined $_rec;
861
862 return $_rec;
863}
864
865sub error {
866 my $this = shift();
867
868 my($errcode, $errmsg, $addinfo, $diagset) = (undef, "dummy", "dummy", "d");
869 $errcode = Net::Z3950::ZOOM::record_error($this->_rec(), $errmsg,
870 $addinfo, $diagset);
871
872 return wantarray() ? ($errcode, $errmsg, $addinfo, $diagset) : $errcode;
873}
874
875sub exception {
876 my $this = shift();
877
878 my($errcode, $errmsg, $addinfo, $diagset) = $this->error();
879 return undef if $errcode == 0;
880 return new ZOOM::Exception($errcode, $errmsg, $addinfo, $diagset);
881}
882
883
884sub render {
885 my $this = shift();
886
887 return $this->get("render", @_);
888}
889
890sub raw {
891 my $this = shift();
892
893 return $this->get("raw", @_);
894}
895
896sub get {
897 my $this = shift();
898 my($type, $args) = @_;
899
900 $type = "$type;$args" if defined $args;
901 return Net::Z3950::ZOOM::record_get($this->_rec(), $type);
902}
903
904sub clone {
905 my $this = shift();
906
907 my $raw = Net::Z3950::ZOOM::record_clone($this->_rec())
908 or ZOOM::_oops(ZOOM::Error::CLONE);
909
910 # Arg 1 (rs) is undefined as the new record doesn't belong to an RS
911 return _new ZOOM::Record(undef, undef, $raw);
912}
913
914sub destroy {
915 my $this = shift();
916
917 Net::Z3950::ZOOM::record_destroy($this->_rec());
918 $this->{_rec} = undef;
919}
920
921
922# ----------------------------------------------------------------------------
923
924package ZOOM::ScanSet;
925
926sub new {
927 my $class = shift();
928 die "You can't create $class objects directly";
929}
930
931# PRIVATE to ZOOM::Connection::scan(),
932sub _new {
933 my $class = shift();
934 my($conn, $startterm, $_ss) = @_;
935
936 return bless {
937 conn => $conn,
938 startterm => $startterm,# This is not currently used, which is
939 # just as well since it could be
940 # either a string (when the SS is
941 # created with scan()) or a
942 # ZOOM::Query object (when it's
943 # created with scan1())
944 _ss => $_ss,
945 }, $class;
946}
947
948# PRIVATE to this class
949sub _ss {
950 my $this = shift();
951
952 my $_ss = $this->{_ss};
953 die "{_ss} undefined: has this ScanSet been destroy()ed?"
954 if !defined $_ss;
955
956 return $_ss;
957}
958
959sub option {
960 my $this = shift();
961 my($key, $value) = @_;
962
963 my $oldval = Net::Z3950::ZOOM::scanset_option_get($this->_ss(), $key);
964 Net::Z3950::ZOOM::scanset_option_set($this->_ss(), $key, $value)
965 if defined $value;
966
967 return $oldval;
968}
969
970sub size {
971 my $this = shift();
972
973 return Net::Z3950::ZOOM::scanset_size($this->_ss());
974}
975
976sub term {
977 my $this = shift();
978 my($which) = @_;
979
980 my($occ, $len) = (0, 0);
981 my $term = Net::Z3950::ZOOM::scanset_term($this->_ss(), $which,
982 $occ, $len)
983 or ZOOM::_oops(ZOOM::Error::SCANTERM);
984
985 die "length of term '$term' differs from returned len=$len"
986 if length($term) != $len;
987
988 return ($term, $occ);
989}
990
991sub display_term {
992 my $this = shift();
993 my($which) = @_;
994
995 my($occ, $len) = (0, 0);
996 my $term = Net::Z3950::ZOOM::scanset_display_term($this->_ss(), $which,
997 $occ, $len)
998 or ZOOM::_oops(ZOOM::Error::SCANTERM);
999
1000 die "length of display term '$term' differs from returned len=$len"
1001 if length($term) != $len;
1002
1003 return ($term, $occ);
1004}
1005
1006sub destroy {
1007 my $this = shift();
1008
1009 Net::Z3950::ZOOM::scanset_destroy($this->_ss());
1010 $this->{_ss} = undef;
1011}
1012
1013
1014# ----------------------------------------------------------------------------
1015
1016package ZOOM::Package;
1017
1018sub new {
1019 my $class = shift();
1020 die "You can't create $class objects directly";
1021}
1022
1023# PRIVATE to ZOOM::Connection::package(),
1024sub _new {
1025 my $class = shift();
1026 my($conn, $options, $_p) = @_;
1027
1028 return bless {
1029 conn => $conn,
1030 options => $options,
1031 _p => $_p,
1032 }, $class;
1033}
1034
1035# PRIVATE to this class
1036sub _p {
1037 my $this = shift();
1038
1039 my $_p = $this->{_p};
1040 die "{_p} undefined: has this Package been destroy()ed?"
1041 if !defined $_p;
1042
1043 return $_p;
1044}
1045
1046sub option {
1047 my $this = shift();
1048 my($key, $value) = @_;
1049
1050 my $oldval = Net::Z3950::ZOOM::package_option_get($this->_p(), $key);
1051 Net::Z3950::ZOOM::package_option_set($this->_p(), $key, $value)
1052 if defined $value;
1053
1054 return $oldval;
1055}
1056
1057sub send {
1058 my $this = shift();
1059 my($type) = @_;
1060
1061 Net::Z3950::ZOOM::package_send($this->_p(), $type);
1062 $this->{conn}->_check();
1063}
1064
1065sub destroy {
1066 my $this = shift();
1067
1068 Net::Z3950::ZOOM::package_destroy($this->_p());
1069 $this->{_p} = undef;
1070}
1071
1072
1073# There follows trivial support for YAZ logging. This is wired out
1074# into the Net::Z3950::ZOOM package, and we here provide wrapper
1075# functions -- nothing more than aliases, really -- in the ZOOM::Log
1076# package. There really is no point in inventing an OO interface.
1077#
1078# Passing @_ directly to the underlying Net::Z3950::ZOOM::* functions
1079# doesn't work, for reasons that I can't begin to fathom, and that
1080# don't particularly interest me. Unpacking into scalars and passing
1081# those _does_ work, so that's what we do.
1082
1083package ZOOM::Log;
1084
1085268µs34µs
# spent 82µs (21+61) within ZOOM::Log::mask_str which was called # once (21µs+61µs) by ZOOM::Log::BEGIN at line 1109
sub mask_str { my($a) = @_; Net::Z3950::ZOOM::yaz_log_mask_str($a); }
# spent 61µs making 1 call to Net::Z3950::ZOOM::yaz_log_mask_str
1086sub module_level { my($a) = @_; Net::Z3950::ZOOM::yaz_log_module_level($a); }
1087sub init { my($a, $b, $c) = @_;
1088 Net::Z3950::ZOOM::yaz_log_init($a, $b, $c) }
1089sub init_file { my($a) = @_; Net::Z3950::ZOOM::yaz_log_init_file($a) }
1090sub init_level { my($a) = @_; Net::Z3950::ZOOM::yaz_log_init_level($a) }
1091sub init_prefix { my($a) = @_; Net::Z3950::ZOOM::yaz_log_init_prefix($a) }
1092sub time_format { my($a) = @_; Net::Z3950::ZOOM::yaz_log_time_format($a) }
1093sub init_max_size { my($a) = @_; Net::Z3950::ZOOM::yaz_log_init_max_size($a) }
1094
1095sub log {
1096 my($level, @message) = @_;
1097
1098 if ($level !~ /^(0x)?\d+$/) {
1099 # Assuming its log-level name, we look it up.
1100 my $num = module_level($level);
1101 ZOOM::_oops(ZOOM::Error::LOGLEVEL, $level)
1102 if $num == 0;
1103 $level = $num;
1104 }
1105
1106 Net::Z3950::ZOOM::yaz_log($level, join("", @message));
1107}
1108
1109186µs86µsBEGIN { ZOOM::Log::mask_str("zoom_check"); }
# spent 82µs making 1 call to ZOOM::Log::mask_str
1110
111118µs8µs1;