Filename | /home/hejohns/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Text/CSV_XS.pm |
Statements | Executed 831084 statements in 12.7s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
133035 | 1 | 1 | 3.76s | 53705s | __ANON__[:1460] | Text::CSV_XS::
105 | 1 | 1 | 3.70s | 53714s | getline_all (xsub) | Text::CSV_XS::
153405 | 2 | 1 | 3.23s | 5.31s | CORE:readline (opcode) | Text::CSV_XS::
133035 | 1 | 1 | 109ms | 109ms | CORE:sort (opcode) | Text::CSV_XS::
105 | 1 | 1 | 31.9ms | 53714s | getline_hr_all | Text::CSV_XS::
105 | 1 | 1 | 24.1ms | 53715s | csv | Text::CSV_XS::
105 | 1 | 1 | 10.0ms | 12.5ms | new | Text::CSV_XS::
105 | 1 | 1 | 8.90ms | 35.1ms | _csv_attr | Text::CSV_XS::
105 | 1 | 1 | 4.44ms | 12.7ms | CORE:open (opcode) | Text::CSV_XS::
2520 | 9 | 1 | 2.12ms | 2.12ms | CORE:match (opcode) | Text::CSV_XS::
105 | 1 | 1 | 2.12ms | 2.25ms | error_diag | Text::CSV_XS::
105 | 1 | 1 | 1.88ms | 1.88ms | CORE:close (opcode) | Text::CSV_XS::
105 | 1 | 1 | 1.69ms | 3.33ms | callbacks | Text::CSV_XS::
105 | 1 | 1 | 1.36ms | 3.76ms | getline (xsub) | Text::CSV_XS::
105 | 1 | 1 | 1.11ms | 1.57ms | _check_sanity | Text::CSV_XS::
105 | 1 | 1 | 1.07ms | 1.07ms | column_names | Text::CSV_XS::
105 | 1 | 1 | 906µs | 997µs | _set_attr_X | Text::CSV_XS::
105 | 1 | 1 | 589µs | 589µs | CORE:subst (opcode) | Text::CSV_XS::
211 | 3 | 1 | 223µs | 223µs | SetDiag (xsub) | Text::CSV_XS::
105 | 1 | 1 | 182µs | 182µs | _unhealthy_whitespace | Text::CSV_XS::
105 | 1 | 1 | 90µs | 90µs | _cache_set (xsub) | Text::CSV_XS::
1 | 1 | 1 | 12µs | 12µs | BEGIN@25 | Text::CSV_XS::
1 | 1 | 1 | 8µs | 9µs | BEGIN@21 | Text::CSV_XS::
1 | 1 | 1 | 4µs | 23µs | BEGIN@22 | Text::CSV_XS::
1 | 1 | 1 | 4µs | 19µs | BEGIN@57 | Text::CSV_XS::
1 | 1 | 1 | 4µs | 21µs | BEGIN@26 | Text::CSV_XS::
1 | 1 | 1 | 4µs | 4µs | BEGIN@112 | DynaLoader::
1 | 1 | 1 | 3µs | 26µs | BEGIN@28 | Text::CSV_XS::
1 | 1 | 1 | 300ns | 300ns | __ANON__ (xsub) | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_FLAGS_ERROR_IN_FIELD | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_FLAGS_IS_BINARY | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_FLAGS_IS_MISSING | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_FLAGS_IS_QUOTED | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_TYPE_IV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_TYPE_NV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | CSV_TYPE_PV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | IV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | NV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | PV | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | _SetDiagInfo | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | __ANON__[:1280] | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | __ANON__[:1281] | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | __ANON__[:1282] | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | __ANON__[:58] | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | _set_attr_C | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | _set_attr_N | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | _supported_formula | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | allow_loose_escapes | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | allow_loose_quotes | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | allow_unquoted_escape | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | allow_whitespace | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | always_quote | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | auto_diag | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | binary | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | bind_columns | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | blank_is_undef | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | combine | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | comment_str | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | decode_utf8 | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | diag_verbose | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | empty_is_undef | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | eof | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | eol | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | escape_char | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | escape_null | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | fields | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | formula | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | formula_handling | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | fragment | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | getline_hr | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | header | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | is_binary | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | is_missing | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | is_quoted | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | keep_meta_info | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | known_attributes | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | meta_info | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | parse | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | print_hr | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote_binary | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote_char | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote_empty | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote_null | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | quote_space | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | record_number | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | say | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | sep | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | sep_char | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | skip_empty_rows | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | status | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | strict | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | string | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | types | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | undef_str | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | verbatim | Text::CSV_XS::
0 | 0 | 0 | 0s | 0s | version | Text::CSV_XS::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
0 | 1 | 4µs | Profile data that couldn't be associated with a specific line: # spent 4µs making 1 call to DynaLoader::BEGIN@112 | ||
1 | 1 | 8µs | package Text::CSV_XS; | ||
2 | |||||
3 | # Copyright (c) 2007-2022 H.Merijn Brand. All rights reserved. | ||||
4 | # Copyright (c) 1998-2001 Jochen Wiedmann. All rights reserved. | ||||
5 | # Copyright (c) 1997 Alan Citterman. All rights reserved. | ||||
6 | # | ||||
7 | # This program is free software; you can redistribute it and/or | ||||
8 | # modify it under the same terms as Perl itself. | ||||
9 | |||||
10 | # HISTORY | ||||
11 | # | ||||
12 | # 0.24 - | ||||
13 | # H.Merijn Brand (h.m.brand@xs4all.nl) | ||||
14 | # 0.10 - 0.23 | ||||
15 | # Jochen Wiedmann <joe@ispsoft.de> | ||||
16 | # Based on (the original) Text::CSV by: | ||||
17 | # Alan Citterman <alan@mfgrtl.com> | ||||
18 | |||||
19 | 1 | 8µs | require 5.006001; | ||
20 | |||||
21 | 2 | 20µs | 2 | 10µs | # spent 9µs (8+1) within Text::CSV_XS::BEGIN@21 which was called:
# once (8µs+1µs) by Text::CSV::BEGIN@1 at line 21 # spent 9µs making 1 call to Text::CSV_XS::BEGIN@21
# spent 1µs making 1 call to strict::import |
22 | 2 | 16µs | 2 | 41µs | # spent 23µs (4+18) within Text::CSV_XS::BEGIN@22 which was called:
# once (4µs+18µs) by Text::CSV::BEGIN@1 at line 22 # spent 23µs making 1 call to Text::CSV_XS::BEGIN@22
# spent 18µs making 1 call to warnings::import |
23 | |||||
24 | 1 | 800ns | require Exporter; | ||
25 | 2 | 21µs | 2 | 12µs | # spent 12µs (12+300ns) within Text::CSV_XS::BEGIN@25 which was called:
# once (12µs+300ns) by Text::CSV::BEGIN@1 at line 25 # spent 12µs making 1 call to Text::CSV_XS::BEGIN@25
# spent 300ns making 1 call to Text::CSV_XS::__ANON__ |
26 | 2 | 20µs | 2 | 39µs | # spent 21µs (4+18) within Text::CSV_XS::BEGIN@26 which was called:
# once (4µs+18µs) by Text::CSV::BEGIN@1 at line 26 # spent 21µs making 1 call to Text::CSV_XS::BEGIN@26
# spent 18µs making 1 call to Exporter::import |
27 | |||||
28 | 2 | 118µs | 2 | 48µs | # spent 26µs (3+22) within Text::CSV_XS::BEGIN@28 which was called:
# once (3µs+22µs) by Text::CSV::BEGIN@1 at line 28 # spent 26µs making 1 call to Text::CSV_XS::BEGIN@28
# spent 22µs making 1 call to vars::import |
29 | 1 | 200ns | $VERSION = "1.48"; | ||
30 | 1 | 7µs | @ISA = qw( Exporter ); | ||
31 | 1 | 164µs | 1 | 173µs | XSLoader::load ("Text::CSV_XS", $VERSION); # spent 173µs making 1 call to XSLoader::load |
32 | |||||
33 | sub PV { 0 } sub CSV_TYPE_PV { PV } | ||||
34 | sub IV { 1 } sub CSV_TYPE_IV { IV } | ||||
35 | sub NV { 2 } sub CSV_TYPE_NV { NV } | ||||
36 | |||||
37 | sub CSV_FLAGS_IS_QUOTED { 0x0001 } | ||||
38 | sub CSV_FLAGS_IS_BINARY { 0x0002 } | ||||
39 | sub CSV_FLAGS_ERROR_IN_FIELD { 0x0004 } | ||||
40 | sub CSV_FLAGS_IS_MISSING { 0x0010 } | ||||
41 | |||||
42 | 1 | 2µs | %EXPORT_TAGS = ( | ||
43 | CONSTANTS => [qw( | ||||
44 | CSV_FLAGS_IS_QUOTED | ||||
45 | CSV_FLAGS_IS_BINARY | ||||
46 | CSV_FLAGS_ERROR_IN_FIELD | ||||
47 | CSV_FLAGS_IS_MISSING | ||||
48 | |||||
49 | CSV_TYPE_PV | ||||
50 | CSV_TYPE_IV | ||||
51 | CSV_TYPE_NV | ||||
52 | )], | ||||
53 | ); | ||||
54 | 1 | 1µs | @EXPORT_OK = (qw( csv PV IV NV ), @{$EXPORT_TAGS{CONSTANTS}}); | ||
55 | |||||
56 | 1 | 300ns | if ($] < 5.008002) { | ||
57 | 2 | 5.92ms | 2 | 34µs | # spent 19µs (4+15) within Text::CSV_XS::BEGIN@57 which was called:
# once (4µs+15µs) by Text::CSV::BEGIN@1 at line 57 # spent 19µs making 1 call to Text::CSV_XS::BEGIN@57
# spent 15µs making 1 call to warnings::unimport |
58 | *utf8::decode = sub {}; | ||||
59 | } | ||||
60 | |||||
61 | # version | ||||
62 | # | ||||
63 | # class/object method expecting no arguments and returning the version | ||||
64 | # number of Text::CSV. there are no side-effects. | ||||
65 | |||||
66 | sub version { | ||||
67 | return $VERSION; | ||||
68 | } # version | ||||
69 | |||||
70 | # new | ||||
71 | # | ||||
72 | # class/object method expecting no arguments and returning a reference to | ||||
73 | # a newly created Text::CSV object. | ||||
74 | |||||
75 | 1 | 8µs | my %def_attr = ( | ||
76 | 'eol' => '', | ||||
77 | 'sep_char' => ',', | ||||
78 | 'quote_char' => '"', | ||||
79 | 'escape_char' => '"', | ||||
80 | 'binary' => 0, | ||||
81 | 'decode_utf8' => 1, | ||||
82 | 'auto_diag' => 0, | ||||
83 | 'diag_verbose' => 0, | ||||
84 | 'strict' => 0, | ||||
85 | 'blank_is_undef' => 0, | ||||
86 | 'empty_is_undef' => 0, | ||||
87 | 'allow_whitespace' => 0, | ||||
88 | 'allow_loose_quotes' => 0, | ||||
89 | 'allow_loose_escapes' => 0, | ||||
90 | 'allow_unquoted_escape' => 0, | ||||
91 | 'always_quote' => 0, | ||||
92 | 'quote_empty' => 0, | ||||
93 | 'quote_space' => 1, | ||||
94 | 'quote_binary' => 1, | ||||
95 | 'escape_null' => 1, | ||||
96 | 'keep_meta_info' => 0, | ||||
97 | 'verbatim' => 0, | ||||
98 | 'formula' => 0, | ||||
99 | 'skip_empty_rows' => 0, | ||||
100 | 'undef_str' => undef, | ||||
101 | 'comment_str' => undef, | ||||
102 | 'types' => undef, | ||||
103 | 'callbacks' => undef, | ||||
104 | |||||
105 | '_EOF' => "", | ||||
106 | '_RECNO' => 0, | ||||
107 | '_STATUS' => undef, | ||||
108 | '_FIELDS' => undef, | ||||
109 | '_FFLAGS' => undef, | ||||
110 | '_STRING' => undef, | ||||
111 | '_ERROR_INPUT' => undef, | ||||
112 | # spent 4µs within DynaLoader::BEGIN@112 which was called:
# once (4µs+0s) by XSLoader::load at line 0 | ||||
113 | '_BOUND_COLUMNS' => undef, | ||||
114 | '_AHEAD' => undef, | ||||
115 | '_FORMULA_CB' => undef, | ||||
116 | |||||
117 | 'ENCODING' => undef, | ||||
118 | ); | ||||
119 | 1 | 2µs | my %attr_alias = ( | ||
120 | 'quote_always' => "always_quote", | ||||
121 | 'verbose_diag' => "diag_verbose", | ||||
122 | 'quote_null' => "escape_null", | ||||
123 | 'escape' => "escape_char", | ||||
124 | 'comment' => "comment_str", | ||||
125 | ); | ||||
126 | 1 | 6µs | 1 | 2µs | my $last_new_err = Text::CSV_XS->SetDiag (0); # spent 2µs making 1 call to Text::CSV_XS::SetDiag |
127 | 1 | 300ns | my $ebcdic = ord ("A") == 0xC1; # Faster than $Config{'ebcdic'} | ||
128 | 1 | 100ns | my @internal_kh; | ||
129 | |||||
130 | # NOT a method: is also used before bless | ||||
131 | # spent 182µs within Text::CSV_XS::_unhealthy_whitespace which was called 105 times, avg 2µs/call:
# 105 times (182µs+0s) by Text::CSV_XS::_check_sanity at line 177, avg 2µs/call | ||||
132 | 105 | 39µs | my ($self, $aw) = @_; | ||
133 | 105 | 149µs | $aw or return 0; # no checks needed without allow_whitespace | ||
134 | |||||
135 | my $quo = $self->{'quote'}; | ||||
136 | defined $quo && length ($quo) or $quo = $self->{'quote_char'}; | ||||
137 | my $esc = $self->{'escape_char'}; | ||||
138 | |||||
139 | defined $quo && $quo =~ m/^[ \t]/ and return 1002; | ||||
140 | defined $esc && $esc =~ m/^[ \t]/ and return 1002; | ||||
141 | |||||
142 | return 0; | ||||
143 | } # _unhealty_whitespace | ||||
144 | |||||
145 | # spent 1.57ms (1.11+459µs) within Text::CSV_XS::_check_sanity which was called 105 times, avg 15µs/call:
# 105 times (1.11ms+459µs) by Text::CSV_XS::new at line 245, avg 15µs/call | ||||
146 | 105 | 22µs | my $self = shift; | ||
147 | |||||
148 | 105 | 52µs | my $eol = $self->{'eol'}; | ||
149 | 105 | 42µs | my $sep = $self->{'sep'}; | ||
150 | 105 | 64µs | defined $sep && length ($sep) or $sep = $self->{'sep_char'}; | ||
151 | 105 | 30µs | my $quo = $self->{'quote'}; | ||
152 | 105 | 30µs | defined $quo && length ($quo) or $quo = $self->{'quote_char'}; | ||
153 | 105 | 24µs | my $esc = $self->{'escape_char'}; | ||
154 | |||||
155 | # use DP;::diag ("SEP: '", DPeek ($sep), | ||||
156 | # "', QUO: '", DPeek ($quo), | ||||
157 | # "', ESC: '", DPeek ($esc),"'"); | ||||
158 | |||||
159 | # sep_char should not be undefined | ||||
160 | 105 | 24µs | $sep ne "" or return 1008; | ||
161 | 105 | 31µs | length ($sep) > 16 and return 1006; | ||
162 | 105 | 363µs | 105 | 233µs | $sep =~ m/[\r\n]/ and return 1003; # spent 233µs making 105 calls to Text::CSV_XS::CORE:match, avg 2µs/call |
163 | |||||
164 | 105 | 49µs | if (defined $quo) { | ||
165 | 105 | 26µs | $quo eq $sep and return 1001; | ||
166 | 105 | 17µs | length ($quo) > 16 and return 1007; | ||
167 | 105 | 125µs | 105 | 23µs | $quo =~ m/[\r\n]/ and return 1003; # spent 23µs making 105 calls to Text::CSV_XS::CORE:match, avg 221ns/call |
168 | } | ||||
169 | 105 | 46µs | if (defined $esc) { | ||
170 | 105 | 22µs | $esc eq $sep and return 1001; | ||
171 | 105 | 110µs | 105 | 20µs | $esc =~ m/[\r\n]/ and return 1003; # spent 20µs making 105 calls to Text::CSV_XS::CORE:match, avg 190ns/call |
172 | } | ||||
173 | 105 | 42µs | if (defined $eol) { | ||
174 | length ($eol) > 16 and return 1005; | ||||
175 | } | ||||
176 | |||||
177 | 105 | 305µs | 105 | 182µs | return _unhealthy_whitespace ($self, $self->{'allow_whitespace'}); # spent 182µs making 105 calls to Text::CSV_XS::_unhealthy_whitespace, avg 2µs/call |
178 | } # _check_sanity | ||||
179 | |||||
180 | sub known_attributes { | ||||
181 | sort grep !m/^_/ => "sep", "quote", keys %def_attr; | ||||
182 | } # known_attributes | ||||
183 | |||||
184 | # spent 12.5ms (10.0+2.49) within Text::CSV_XS::new which was called 105 times, avg 119µs/call:
# 105 times (10.0ms+2.49ms) by Text::CSV_XS::_csv_attr at line 1293, avg 119µs/call | ||||
185 | 105 | 470µs | 105 | 145µs | $last_new_err = Text::CSV_XS->SetDiag (1000, # spent 145µs making 105 calls to Text::CSV_XS::SetDiag, avg 1µs/call |
186 | "usage: my \$csv = Text::CSV_XS->new ([{ option => value, ... }]);"); | ||||
187 | |||||
188 | 105 | 30µs | my $proto = shift; | ||
189 | 105 | 51µs | my $class = ref $proto || $proto or return; | ||
190 | 105 | 75µs | @_ > 0 && ref $_[0] ne "HASH" and return; | ||
191 | 105 | 24µs | my $attr = shift || {}; | ||
192 | my %attr = map { | ||||
193 | 630 | 1.10ms | 630 | 476µs | my $k = m/^[a-zA-Z]\w+$/ ? lc $_ : $_; # spent 476µs making 630 calls to Text::CSV_XS::CORE:match, avg 756ns/call |
194 | 630 | 123µs | exists $attr_alias{$k} and $k = $attr_alias{$k}; | ||
195 | 630 | 229µs | ($k => $attr->{$_}); | ||
196 | 105 | 700µs | } keys %{$attr}; | ||
197 | |||||
198 | 105 | 182µs | my $sep_aliased = 0; | ||
199 | 105 | 51µs | if (exists $attr{'sep'}) { | ||
200 | $attr{'sep_char'} = delete $attr{'sep'}; | ||||
201 | $sep_aliased = 1; | ||||
202 | } | ||||
203 | 105 | 20µs | my $quote_aliased = 0; | ||
204 | 105 | 28µs | if (exists $attr{'quote'}) { | ||
205 | $attr{'quote_char'} = delete $attr{'quote'}; | ||||
206 | $quote_aliased = 1; | ||||
207 | } | ||||
208 | exists $attr{'formula_handling'} and | ||||
209 | 105 | 30µs | $attr{'formula'} = delete $attr{'formula_handling'}; | ||
210 | 105 | 45µs | my $attr_formula = delete $attr{'formula'}; | ||
211 | |||||
212 | 105 | 206µs | for (keys %attr) { | ||
213 | 630 | 744µs | 630 | 136µs | if (m/^[a-z]/ && exists $def_attr{$_}) { # spent 136µs making 630 calls to Text::CSV_XS::CORE:match, avg 216ns/call |
214 | # uncoverable condition false | ||||
215 | 630 | 623µs | 630 | 85µs | defined $attr{$_} && m/_char$/ and utf8::decode ($attr{$_}); # spent 85µs making 630 calls to Text::CSV_XS::CORE:match, avg 135ns/call |
216 | 630 | 128µs | next; | ||
217 | } | ||||
218 | # croak? | ||||
219 | $last_new_err = Text::CSV_XS->SetDiag (1000, "INI - Unknown attribute '$_'"); | ||||
220 | $attr{'auto_diag'} and error_diag (); | ||||
221 | return; | ||||
222 | } | ||||
223 | 105 | 31µs | if ($sep_aliased) { | ||
224 | my @b = unpack "U0C*", $attr{'sep_char'}; | ||||
225 | if (@b > 1) { | ||||
226 | $attr{'sep'} = $attr{'sep_char'}; | ||||
227 | $attr{'sep_char'} = "\0"; | ||||
228 | } | ||||
229 | else { | ||||
230 | $attr{'sep'} = undef; | ||||
231 | } | ||||
232 | } | ||||
233 | 105 | 19µs | if ($quote_aliased and defined $attr{'quote_char'}) { | ||
234 | my @b = unpack "U0C*", $attr{'quote_char'}; | ||||
235 | if (@b > 1) { | ||||
236 | $attr{'quote'} = $attr{'quote_char'}; | ||||
237 | $attr{'quote_char'} = "\0"; | ||||
238 | } | ||||
239 | else { | ||||
240 | $attr{'quote'} = undef; | ||||
241 | } | ||||
242 | } | ||||
243 | |||||
244 | 105 | 4.36ms | my $self = { %def_attr, %attr }; | ||
245 | 105 | 160µs | 105 | 1.57ms | if (my $ec = _check_sanity ($self)) { # spent 1.57ms making 105 calls to Text::CSV_XS::_check_sanity, avg 15µs/call |
246 | $last_new_err = Text::CSV_XS->SetDiag ($ec); | ||||
247 | $attr{'auto_diag'} and error_diag (); | ||||
248 | return; | ||||
249 | } | ||||
250 | 105 | 89µs | if (defined $self->{'callbacks'} && ref $self->{'callbacks'} ne "HASH") { | ||
251 | carp ("The 'callbacks' attribute is set but is not a hash: ignored\n"); | ||||
252 | $self->{'callbacks'} = undef; | ||||
253 | } | ||||
254 | |||||
255 | 105 | 251µs | 105 | 76µs | $last_new_err = Text::CSV_XS->SetDiag (0); # spent 76µs making 105 calls to Text::CSV_XS::SetDiag, avg 725ns/call |
256 | 105 | 328µs | defined $\ && !exists $attr{'eol'} and $self->{'eol'} = $\; | ||
257 | 105 | 41µs | bless $self, $class; | ||
258 | 105 | 48µs | defined $self->{'types'} and $self->types ($self->{'types'}); | ||
259 | 105 | 22µs | defined $attr_formula and $self->{'formula'} = _supported_formula ($self, $attr_formula); | ||
260 | 105 | 227µs | $self; | ||
261 | } # new | ||||
262 | |||||
263 | # Keep in sync with XS! | ||||
264 | 1 | 5µs | my %_cache_id = ( # Only expose what is accessed from within PM | ||
265 | 'quote_char' => 0, | ||||
266 | 'escape_char' => 1, | ||||
267 | 'sep_char' => 2, | ||||
268 | 'sep' => 39, # 39 .. 55 | ||||
269 | 'binary' => 3, | ||||
270 | 'keep_meta_info' => 4, | ||||
271 | 'always_quote' => 5, | ||||
272 | 'allow_loose_quotes' => 6, | ||||
273 | 'allow_loose_escapes' => 7, | ||||
274 | 'allow_unquoted_escape' => 8, | ||||
275 | 'allow_whitespace' => 9, | ||||
276 | 'blank_is_undef' => 10, | ||||
277 | 'eol' => 11, | ||||
278 | 'quote' => 15, | ||||
279 | 'verbatim' => 22, | ||||
280 | 'empty_is_undef' => 23, | ||||
281 | 'auto_diag' => 24, | ||||
282 | 'diag_verbose' => 33, | ||||
283 | 'quote_space' => 25, | ||||
284 | 'quote_empty' => 37, | ||||
285 | 'quote_binary' => 32, | ||||
286 | 'escape_null' => 31, | ||||
287 | 'decode_utf8' => 35, | ||||
288 | '_has_ahead' => 30, | ||||
289 | '_has_hooks' => 36, | ||||
290 | '_is_bound' => 26, # 26 .. 29 | ||||
291 | 'formula' => 38, | ||||
292 | 'strict' => 42, | ||||
293 | 'skip_empty_rows' => 43, | ||||
294 | 'undef_str' => 46, | ||||
295 | 'comment_str' => 54, | ||||
296 | 'types' => 62, | ||||
297 | ); | ||||
298 | |||||
299 | # A `character' | ||||
300 | sub _set_attr_C { | ||||
301 | my ($self, $name, $val, $ec) = @_; | ||||
302 | defined $val and utf8::decode ($val); | ||||
303 | $self->{$name} = $val; | ||||
304 | $ec = _check_sanity ($self) and croak ($self->SetDiag ($ec)); | ||||
305 | $self->_cache_set ($_cache_id{$name}, $val); | ||||
306 | } # _set_attr_C | ||||
307 | |||||
308 | # A flag | ||||
309 | # spent 997µs (906+90) within Text::CSV_XS::_set_attr_X which was called 105 times, avg 9µs/call:
# 105 times (906µs+90µs) by Text::CSV_XS::callbacks at line 670, avg 9µs/call | ||||
310 | 105 | 299µs | my ($self, $name, $val) = @_; | ||
311 | 105 | 34µs | defined $val or $val = 0; | ||
312 | 105 | 83µs | $self->{$name} = $val; | ||
313 | 105 | 437µs | 105 | 90µs | $self->_cache_set ($_cache_id{$name}, 0 + $val); # spent 90µs making 105 calls to Text::CSV_XS::_cache_set, avg 862ns/call |
314 | } # _set_attr_X | ||||
315 | |||||
316 | # A number | ||||
317 | sub _set_attr_N { | ||||
318 | my ($self, $name, $val) = @_; | ||||
319 | $self->{$name} = $val; | ||||
320 | $self->_cache_set ($_cache_id{$name}, 0 + $val); | ||||
321 | } # _set_attr_N | ||||
322 | |||||
323 | # Accessor methods. | ||||
324 | # It is unwise to change them halfway through a single file! | ||||
325 | sub quote_char { | ||||
326 | my $self = shift; | ||||
327 | if (@_) { | ||||
328 | $self->_set_attr_C ("quote_char", shift); | ||||
329 | $self->_cache_set ($_cache_id{'quote'}, ""); | ||||
330 | } | ||||
331 | $self->{'quote_char'}; | ||||
332 | } # quote_char | ||||
333 | |||||
334 | sub quote { | ||||
335 | my $self = shift; | ||||
336 | if (@_) { | ||||
337 | my $quote = shift; | ||||
338 | defined $quote or $quote = ""; | ||||
339 | utf8::decode ($quote); | ||||
340 | my @b = unpack "U0C*", $quote; | ||||
341 | if (@b > 1) { | ||||
342 | @b > 16 and croak ($self->SetDiag (1007)); | ||||
343 | $self->quote_char ("\0"); | ||||
344 | } | ||||
345 | else { | ||||
346 | $self->quote_char ($quote); | ||||
347 | $quote = ""; | ||||
348 | } | ||||
349 | $self->{'quote'} = $quote; | ||||
350 | |||||
351 | my $ec = _check_sanity ($self); | ||||
352 | $ec and croak ($self->SetDiag ($ec)); | ||||
353 | |||||
354 | $self->_cache_set ($_cache_id{'quote'}, $quote); | ||||
355 | } | ||||
356 | my $quote = $self->{'quote'}; | ||||
357 | defined $quote && length ($quote) ? $quote : $self->{'quote_char'}; | ||||
358 | } # quote | ||||
359 | |||||
360 | sub escape_char { | ||||
361 | my $self = shift; | ||||
362 | if (@_) { | ||||
363 | my $ec = shift; | ||||
364 | $self->_set_attr_C ("escape_char", $ec); | ||||
365 | $ec or $self->_set_attr_X ("escape_null", 0); | ||||
366 | } | ||||
367 | $self->{'escape_char'}; | ||||
368 | } # escape_char | ||||
369 | |||||
370 | sub sep_char { | ||||
371 | my $self = shift; | ||||
372 | if (@_) { | ||||
373 | $self->_set_attr_C ("sep_char", shift); | ||||
374 | $self->_cache_set ($_cache_id{'sep'}, ""); | ||||
375 | } | ||||
376 | $self->{'sep_char'}; | ||||
377 | } # sep_char | ||||
378 | |||||
379 | sub sep { | ||||
380 | my $self = shift; | ||||
381 | if (@_) { | ||||
382 | my $sep = shift; | ||||
383 | defined $sep or $sep = ""; | ||||
384 | utf8::decode ($sep); | ||||
385 | my @b = unpack "U0C*", $sep; | ||||
386 | if (@b > 1) { | ||||
387 | @b > 16 and croak ($self->SetDiag (1006)); | ||||
388 | $self->sep_char ("\0"); | ||||
389 | } | ||||
390 | else { | ||||
391 | $self->sep_char ($sep); | ||||
392 | $sep = ""; | ||||
393 | } | ||||
394 | $self->{'sep'} = $sep; | ||||
395 | |||||
396 | my $ec = _check_sanity ($self); | ||||
397 | $ec and croak ($self->SetDiag ($ec)); | ||||
398 | |||||
399 | $self->_cache_set ($_cache_id{'sep'}, $sep); | ||||
400 | } | ||||
401 | my $sep = $self->{'sep'}; | ||||
402 | defined $sep && length ($sep) ? $sep : $self->{'sep_char'}; | ||||
403 | } # sep | ||||
404 | |||||
405 | sub eol { | ||||
406 | my $self = shift; | ||||
407 | if (@_) { | ||||
408 | my $eol = shift; | ||||
409 | defined $eol or $eol = ""; | ||||
410 | length ($eol) > 16 and croak ($self->SetDiag (1005)); | ||||
411 | $self->{'eol'} = $eol; | ||||
412 | $self->_cache_set ($_cache_id{'eol'}, $eol); | ||||
413 | } | ||||
414 | $self->{'eol'}; | ||||
415 | } # eol | ||||
416 | |||||
417 | sub always_quote { | ||||
418 | my $self = shift; | ||||
419 | @_ and $self->_set_attr_X ("always_quote", shift); | ||||
420 | $self->{'always_quote'}; | ||||
421 | } # always_quote | ||||
422 | |||||
423 | sub quote_space { | ||||
424 | my $self = shift; | ||||
425 | @_ and $self->_set_attr_X ("quote_space", shift); | ||||
426 | $self->{'quote_space'}; | ||||
427 | } # quote_space | ||||
428 | |||||
429 | sub quote_empty { | ||||
430 | my $self = shift; | ||||
431 | @_ and $self->_set_attr_X ("quote_empty", shift); | ||||
432 | $self->{'quote_empty'}; | ||||
433 | } # quote_empty | ||||
434 | |||||
435 | sub escape_null { | ||||
436 | my $self = shift; | ||||
437 | @_ and $self->_set_attr_X ("escape_null", shift); | ||||
438 | $self->{'escape_null'}; | ||||
439 | } # escape_null | ||||
440 | sub quote_null { goto &escape_null; } | ||||
441 | |||||
442 | sub quote_binary { | ||||
443 | my $self = shift; | ||||
444 | @_ and $self->_set_attr_X ("quote_binary", shift); | ||||
445 | $self->{'quote_binary'}; | ||||
446 | } # quote_binary | ||||
447 | |||||
448 | sub binary { | ||||
449 | my $self = shift; | ||||
450 | @_ and $self->_set_attr_X ("binary", shift); | ||||
451 | $self->{'binary'}; | ||||
452 | } # binary | ||||
453 | |||||
454 | sub strict { | ||||
455 | my $self = shift; | ||||
456 | @_ and $self->_set_attr_X ("strict", shift); | ||||
457 | $self->{'strict'}; | ||||
458 | } # always_quote | ||||
459 | |||||
460 | sub skip_empty_rows { | ||||
461 | my $self = shift; | ||||
462 | @_ and $self->_set_attr_X ("skip_empty_rows", shift); | ||||
463 | $self->{'skip_empty_rows'}; | ||||
464 | } # always_quote | ||||
465 | |||||
466 | sub _SetDiagInfo { | ||||
467 | my ($self, $err, $msg) = @_; | ||||
468 | $self->SetDiag ($err); | ||||
469 | my $em = $self->error_diag (); | ||||
470 | $em =~ s/^\d+$// and $msg =~ s/^/# /; | ||||
471 | my $sep = $em =~ m/[;\n]$/ ? "\n\t" : ": "; | ||||
472 | join $sep => grep m/\S\S\S/ => $em, $msg; | ||||
473 | } # _SetDiagInfo | ||||
474 | |||||
475 | sub _supported_formula { | ||||
476 | my ($self, $f) = @_; | ||||
477 | defined $f or return 5; | ||||
478 | if ($self && $f && ref $f && ref $f eq "CODE") { | ||||
479 | $self->{'_FORMULA_CB'} = $f; | ||||
480 | return 6; | ||||
481 | } | ||||
482 | $f =~ m/^(?: 0 | none )$/xi ? 0 : | ||||
483 | $f =~ m/^(?: 1 | die )$/xi ? 1 : | ||||
484 | $f =~ m/^(?: 2 | croak )$/xi ? 2 : | ||||
485 | $f =~ m/^(?: 3 | diag )$/xi ? 3 : | ||||
486 | $f =~ m/^(?: 4 | empty | )$/xi ? 4 : | ||||
487 | $f =~ m/^(?: 5 | undef )$/xi ? 5 : | ||||
488 | $f =~ m/^(?: 6 | cb )$/xi ? 6 : do { | ||||
489 | $self ||= "Text::CSV_XS"; | ||||
490 | croak ($self->_SetDiagInfo (1500, "formula-handling '$f' is not supported")); | ||||
491 | }; | ||||
492 | } # _supported_formula | ||||
493 | |||||
494 | sub formula { | ||||
495 | my $self = shift; | ||||
496 | @_ and $self->_set_attr_N ("formula", _supported_formula ($self, shift)); | ||||
497 | $self->{'formula'} == 6 or $self->{'_FORMULA_CB'} = undef; | ||||
498 | [qw( none die croak diag empty undef cb )]->[_supported_formula ($self, $self->{'formula'})]; | ||||
499 | } # always_quote | ||||
500 | sub formula_handling { | ||||
501 | my $self = shift; | ||||
502 | $self->formula (@_); | ||||
503 | } # formula_handling | ||||
504 | |||||
505 | sub decode_utf8 { | ||||
506 | my $self = shift; | ||||
507 | @_ and $self->_set_attr_X ("decode_utf8", shift); | ||||
508 | $self->{'decode_utf8'}; | ||||
509 | } # decode_utf8 | ||||
510 | |||||
511 | sub keep_meta_info { | ||||
512 | my $self = shift; | ||||
513 | if (@_) { | ||||
514 | my $v = shift; | ||||
515 | !defined $v || $v eq "" and $v = 0; | ||||
516 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | ||||
517 | $self->_set_attr_X ("keep_meta_info", $v); | ||||
518 | } | ||||
519 | $self->{'keep_meta_info'}; | ||||
520 | } # keep_meta_info | ||||
521 | |||||
522 | sub allow_loose_quotes { | ||||
523 | my $self = shift; | ||||
524 | @_ and $self->_set_attr_X ("allow_loose_quotes", shift); | ||||
525 | $self->{'allow_loose_quotes'}; | ||||
526 | } # allow_loose_quotes | ||||
527 | |||||
528 | sub allow_loose_escapes { | ||||
529 | my $self = shift; | ||||
530 | @_ and $self->_set_attr_X ("allow_loose_escapes", shift); | ||||
531 | $self->{'allow_loose_escapes'}; | ||||
532 | } # allow_loose_escapes | ||||
533 | |||||
534 | sub allow_whitespace { | ||||
535 | my $self = shift; | ||||
536 | if (@_) { | ||||
537 | my $aw = shift; | ||||
538 | _unhealthy_whitespace ($self, $aw) and | ||||
539 | croak ($self->SetDiag (1002)); | ||||
540 | $self->_set_attr_X ("allow_whitespace", $aw); | ||||
541 | } | ||||
542 | $self->{'allow_whitespace'}; | ||||
543 | } # allow_whitespace | ||||
544 | |||||
545 | sub allow_unquoted_escape { | ||||
546 | my $self = shift; | ||||
547 | @_ and $self->_set_attr_X ("allow_unquoted_escape", shift); | ||||
548 | $self->{'allow_unquoted_escape'}; | ||||
549 | } # allow_unquoted_escape | ||||
550 | |||||
551 | sub blank_is_undef { | ||||
552 | my $self = shift; | ||||
553 | @_ and $self->_set_attr_X ("blank_is_undef", shift); | ||||
554 | $self->{'blank_is_undef'}; | ||||
555 | } # blank_is_undef | ||||
556 | |||||
557 | sub empty_is_undef { | ||||
558 | my $self = shift; | ||||
559 | @_ and $self->_set_attr_X ("empty_is_undef", shift); | ||||
560 | $self->{'empty_is_undef'}; | ||||
561 | } # empty_is_undef | ||||
562 | |||||
563 | sub verbatim { | ||||
564 | my $self = shift; | ||||
565 | @_ and $self->_set_attr_X ("verbatim", shift); | ||||
566 | $self->{'verbatim'}; | ||||
567 | } # verbatim | ||||
568 | |||||
569 | sub undef_str { | ||||
570 | my $self = shift; | ||||
571 | if (@_) { | ||||
572 | my $v = shift; | ||||
573 | $self->{'undef_str'} = defined $v ? "$v" : undef; | ||||
574 | $self->_cache_set ($_cache_id{'undef_str'}, $self->{'undef_str'}); | ||||
575 | } | ||||
576 | $self->{'undef_str'}; | ||||
577 | } # undef_str | ||||
578 | |||||
579 | sub comment_str { | ||||
580 | my $self = shift; | ||||
581 | if (@_) { | ||||
582 | my $v = shift; | ||||
583 | $self->{'comment_str'} = defined $v ? "$v" : undef; | ||||
584 | $self->_cache_set ($_cache_id{'comment_str'}, $self->{'comment_str'}); | ||||
585 | } | ||||
586 | $self->{'comment_str'}; | ||||
587 | } # comment_str | ||||
588 | |||||
589 | sub auto_diag { | ||||
590 | my $self = shift; | ||||
591 | if (@_) { | ||||
592 | my $v = shift; | ||||
593 | !defined $v || $v eq "" and $v = 0; | ||||
594 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | ||||
595 | $self->_set_attr_X ("auto_diag", $v); | ||||
596 | } | ||||
597 | $self->{'auto_diag'}; | ||||
598 | } # auto_diag | ||||
599 | |||||
600 | sub diag_verbose { | ||||
601 | my $self = shift; | ||||
602 | if (@_) { | ||||
603 | my $v = shift; | ||||
604 | !defined $v || $v eq "" and $v = 0; | ||||
605 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | ||||
606 | $self->_set_attr_X ("diag_verbose", $v); | ||||
607 | } | ||||
608 | $self->{'diag_verbose'}; | ||||
609 | } # diag_verbose | ||||
610 | |||||
611 | # status | ||||
612 | # | ||||
613 | # object method returning the success or failure of the most recent | ||||
614 | # combine () or parse (). there are no side-effects. | ||||
615 | |||||
616 | sub status { | ||||
617 | my $self = shift; | ||||
618 | return $self->{'_STATUS'}; | ||||
619 | } # status | ||||
620 | |||||
621 | sub eof { | ||||
622 | my $self = shift; | ||||
623 | return $self->{'_EOF'}; | ||||
624 | } # status | ||||
625 | |||||
626 | sub types { | ||||
627 | my $self = shift; | ||||
628 | if (@_) { | ||||
629 | if (my $types = shift) { | ||||
630 | $self->{'_types'} = join "", map { chr } @{$types}; | ||||
631 | $self->{'types'} = $types; | ||||
632 | $self->_cache_set ($_cache_id{'types'}, $self->{'_types'}); | ||||
633 | } | ||||
634 | else { | ||||
635 | delete $self->{'types'}; | ||||
636 | delete $self->{'_types'}; | ||||
637 | $self->_cache_set ($_cache_id{'types'}, undef); | ||||
638 | undef; | ||||
639 | } | ||||
640 | } | ||||
641 | else { | ||||
642 | $self->{'types'}; | ||||
643 | } | ||||
644 | } # types | ||||
645 | |||||
646 | # spent 3.33ms (1.69+1.64) within Text::CSV_XS::callbacks which was called 105 times, avg 32µs/call:
# 105 times (1.69ms+1.64ms) by Text::CSV_XS::csv at line 1460, avg 32µs/call | ||||
647 | 105 | 32µs | my $self = shift; | ||
648 | 105 | 50µs | if (@_) { | ||
649 | 105 | 17µs | my $cb; | ||
650 | 105 | 27µs | my $hf = 0x00; | ||
651 | 105 | 38µs | if (defined $_[0]) { | ||
652 | 105 | 91µs | grep { !defined } @_ and croak ($self->SetDiag (1004)); | ||
653 | 105 | 177µs | $cb = @_ == 1 && ref $_[0] eq "HASH" ? shift | ||
654 | : @_ % 2 == 0 ? { @_ } | ||||
655 | : croak ($self->SetDiag (1004)); | ||||
656 | 105 | 271µs | foreach my $cbk (keys %{$cb}) { | ||
657 | # A key cannot be a ref. That would be stored as the *string | ||||
658 | # 'SCALAR(0x1f3e710)' or 'ARRAY(0x1a5ae18)' | ||||
659 | 105 | 881µs | 105 | 641µs | $cbk =~ m/^[\w.]+$/ && ref $cb->{$cbk} eq "CODE" or # spent 641µs making 105 calls to Text::CSV_XS::CORE:match, avg 6µs/call |
660 | croak ($self->SetDiag (1004)); | ||||
661 | } | ||||
662 | 105 | 34µs | exists $cb->{'error'} and $hf |= 0x01; | ||
663 | 105 | 57µs | exists $cb->{'after_parse'} and $hf |= 0x02; | ||
664 | 105 | 24µs | exists $cb->{'before_print'} and $hf |= 0x04; | ||
665 | } | ||||
666 | elsif (@_ > 1) { | ||||
667 | # (undef, whatever) | ||||
668 | croak ($self->SetDiag (1004)); | ||||
669 | } | ||||
670 | 105 | 312µs | 105 | 997µs | $self->_set_attr_X ("_has_hooks", $hf); # spent 997µs making 105 calls to Text::CSV_XS::_set_attr_X, avg 9µs/call |
671 | 105 | 44µs | $self->{'callbacks'} = $cb; | ||
672 | } | ||||
673 | 105 | 123µs | $self->{'callbacks'}; | ||
674 | } # callbacks | ||||
675 | |||||
676 | # error_diag | ||||
677 | # | ||||
678 | # If (and only if) an error occurred, this function returns a code that | ||||
679 | # indicates the reason of failure | ||||
680 | |||||
681 | # spent 2.25ms (2.12+130µs) within Text::CSV_XS::error_diag which was called 105 times, avg 21µs/call:
# 105 times (2.12ms+130µs) by Text::CSV_XS::getline_all at line 1047, avg 21µs/call | ||||
682 | 105 | 34µs | my $self = shift; | ||
683 | 105 | 161µs | my @diag = (0 + $last_new_err, $last_new_err, 0, 0, 0); | ||
684 | |||||
685 | # Docs state to NEVER use UNIVERSAL::isa, because it will *never* call an | ||||
686 | # overridden isa method in any class. Well, that is exacly what I want here | ||||
687 | 105 | 502µs | 105 | 130µs | if ($self && ref $self and # Not a class method or direct call # spent 130µs making 105 calls to UNIVERSAL::isa, avg 1µs/call |
688 | UNIVERSAL::isa ($self, __PACKAGE__) && exists $self->{'_ERROR_DIAG'}) { | ||||
689 | 105 | 50µs | $diag[0] = 0 + $self->{'_ERROR_DIAG'}; | ||
690 | 105 | 45µs | $diag[1] = $self->{'_ERROR_DIAG'}; | ||
691 | 105 | 42µs | $diag[2] = 1 + $self->{'_ERROR_POS'} if exists $self->{'_ERROR_POS'}; | ||
692 | 105 | 40µs | $diag[3] = $self->{'_RECNO'}; | ||
693 | 105 | 21µs | $diag[4] = $self->{'_ERROR_FLD'} if exists $self->{'_ERROR_FLD'}; | ||
694 | |||||
695 | $diag[0] && $self->{'callbacks'} && $self->{'callbacks'}{'error'} and | ||||
696 | 105 | 87µs | return $self->{'callbacks'}{'error'}->(@diag); | ||
697 | } | ||||
698 | |||||
699 | 105 | 201µs | my $context = wantarray; | ||
700 | 105 | 32µs | unless (defined $context) { # Void context, auto-diag | ||
701 | 105 | 49µs | if ($diag[0] && $diag[0] != 2012) { | ||
702 | my $msg = "# CSV_XS ERROR: $diag[0] - $diag[1] \@ rec $diag[3] pos $diag[2]\n"; | ||||
703 | $diag[4] and $msg =~ s/$/ field $diag[4]/; | ||||
704 | |||||
705 | unless ($self && ref $self) { # auto_diag | ||||
706 | # called without args in void context | ||||
707 | warn $msg; | ||||
708 | return; | ||||
709 | } | ||||
710 | |||||
711 | $self->{'diag_verbose'} && $self->{'_ERROR_INPUT'} and | ||||
712 | $msg .= $self->{'_ERROR_INPUT'}."\n". | ||||
713 | (" " x ($diag[2] - 1))."^\n"; | ||||
714 | |||||
715 | my $lvl = $self->{'auto_diag'}; | ||||
716 | if ($lvl < 2) { | ||||
717 | my @c = caller (2); | ||||
718 | if (@c >= 11 && $c[10] && ref $c[10] eq "HASH") { | ||||
719 | my $hints = $c[10]; | ||||
720 | (exists $hints->{'autodie'} && $hints->{'autodie'} or | ||||
721 | exists $hints->{'guard Fatal'} && | ||||
722 | !exists $hints->{'no Fatal'}) and | ||||
723 | $lvl++; | ||||
724 | # Future releases of autodie will probably set $^H{autodie} | ||||
725 | # to "autodie @args", like "autodie :all" or "autodie open" | ||||
726 | # so we can/should check for "open" or "new" | ||||
727 | } | ||||
728 | } | ||||
729 | $lvl > 1 ? die $msg : warn $msg; | ||||
730 | } | ||||
731 | 105 | 221µs | return; | ||
732 | } | ||||
733 | return $context ? @diag : $diag[1]; | ||||
734 | } # error_diag | ||||
735 | |||||
736 | sub record_number { | ||||
737 | my $self = shift; | ||||
738 | return $self->{'_RECNO'}; | ||||
739 | } # record_number | ||||
740 | |||||
741 | # string | ||||
742 | # | ||||
743 | # object method returning the result of the most recent combine () or the | ||||
744 | # input to the most recent parse (), whichever is more recent. there are | ||||
745 | # no side-effects. | ||||
746 | |||||
747 | sub string { | ||||
748 | my $self = shift; | ||||
749 | return ref $self->{'_STRING'} ? ${$self->{'_STRING'}} : undef; | ||||
750 | } # string | ||||
751 | |||||
752 | # fields | ||||
753 | # | ||||
754 | # object method returning the result of the most recent parse () or the | ||||
755 | # input to the most recent combine (), whichever is more recent. there | ||||
756 | # are no side-effects. | ||||
757 | |||||
758 | sub fields { | ||||
759 | my $self = shift; | ||||
760 | return ref $self->{'_FIELDS'} ? @{$self->{'_FIELDS'}} : undef; | ||||
761 | } # fields | ||||
762 | |||||
763 | # meta_info | ||||
764 | # | ||||
765 | # object method returning the result of the most recent parse () or the | ||||
766 | # input to the most recent combine (), whichever is more recent. there | ||||
767 | # are no side-effects. meta_info () returns (if available) some of the | ||||
768 | # field's properties | ||||
769 | |||||
770 | sub meta_info { | ||||
771 | my $self = shift; | ||||
772 | return ref $self->{'_FFLAGS'} ? @{$self->{'_FFLAGS'}} : undef; | ||||
773 | } # meta_info | ||||
774 | |||||
775 | sub is_quoted { | ||||
776 | my ($self, $idx) = @_; | ||||
777 | ref $self->{'_FFLAGS'} && | ||||
778 | $idx >= 0 && $idx < @{$self->{'_FFLAGS'}} or return; | ||||
779 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_QUOTED () ? 1 : 0; | ||||
780 | } # is_quoted | ||||
781 | |||||
782 | sub is_binary { | ||||
783 | my ($self, $idx) = @_; | ||||
784 | ref $self->{'_FFLAGS'} && | ||||
785 | $idx >= 0 && $idx < @{$self->{'_FFLAGS'}} or return; | ||||
786 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_BINARY () ? 1 : 0; | ||||
787 | } # is_binary | ||||
788 | |||||
789 | sub is_missing { | ||||
790 | my ($self, $idx) = @_; | ||||
791 | $idx < 0 || !ref $self->{'_FFLAGS'} and return; | ||||
792 | $idx >= @{$self->{'_FFLAGS'}} and return 1; | ||||
793 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_MISSING () ? 1 : 0; | ||||
794 | } # is_missing | ||||
795 | |||||
796 | # combine | ||||
797 | # | ||||
798 | # Object method returning success or failure. The given arguments are | ||||
799 | # combined into a single comma-separated value. Failure can be the | ||||
800 | # result of no arguments or an argument containing an invalid character. | ||||
801 | # side-effects include: | ||||
802 | # setting status () | ||||
803 | # setting fields () | ||||
804 | # setting string () | ||||
805 | # setting error_input () | ||||
806 | |||||
807 | sub combine { | ||||
808 | my $self = shift; | ||||
809 | my $str = ""; | ||||
810 | $self->{'_FIELDS'} = \@_; | ||||
811 | $self->{'_STATUS'} = (@_ > 0) && $self->Combine (\$str, \@_, 0); | ||||
812 | $self->{'_STRING'} = \$str; | ||||
813 | $self->{'_STATUS'}; | ||||
814 | } # combine | ||||
815 | |||||
816 | # parse | ||||
817 | # | ||||
818 | # Object method returning success or failure. The given argument is | ||||
819 | # expected to be a valid comma-separated value. Failure can be the | ||||
820 | # result of no arguments or an argument containing an invalid sequence | ||||
821 | # of characters. Side-effects include: | ||||
822 | # setting status () | ||||
823 | # setting fields () | ||||
824 | # setting meta_info () | ||||
825 | # setting string () | ||||
826 | # setting error_input () | ||||
827 | |||||
828 | sub parse { | ||||
829 | my ($self, $str) = @_; | ||||
830 | |||||
831 | ref $str and croak ($self->SetDiag (1500)); | ||||
832 | |||||
833 | my $fields = []; | ||||
834 | my $fflags = []; | ||||
835 | $self->{'_STRING'} = \$str; | ||||
836 | if (defined $str && $self->Parse ($str, $fields, $fflags)) { | ||||
837 | $self->{'_FIELDS'} = $fields; | ||||
838 | $self->{'_FFLAGS'} = $fflags; | ||||
839 | $self->{'_STATUS'} = 1; | ||||
840 | } | ||||
841 | else { | ||||
842 | $self->{'_FIELDS'} = undef; | ||||
843 | $self->{'_FFLAGS'} = undef; | ||||
844 | $self->{'_STATUS'} = 0; | ||||
845 | } | ||||
846 | $self->{'_STATUS'}; | ||||
847 | } # parse | ||||
848 | |||||
849 | # spent 1.07ms within Text::CSV_XS::column_names which was called 105 times, avg 10µs/call:
# 105 times (1.07ms+0s) by Text::CSV_XS::csv at line 1467, avg 10µs/call | ||||
850 | 105 | 179µs | my ($self, @keys) = @_; | ||
851 | @keys or | ||||
852 | 105 | 28µs | return defined $self->{'_COLUMN_NAMES'} ? @{$self->{'_COLUMN_NAMES'}} : (); | ||
853 | |||||
854 | @keys == 1 && ! defined $keys[0] and | ||||
855 | 105 | 68µs | return $self->{'_COLUMN_NAMES'} = undef; | ||
856 | |||||
857 | 105 | 160µs | if (@keys == 1 && ref $keys[0] eq "ARRAY") { | ||
858 | @keys = @{$keys[0]}; | ||||
859 | } | ||||
860 | elsif (join "", map { defined $_ ? ref $_ : "" } @keys) { | ||||
861 | croak ($self->SetDiag (3001)); | ||||
862 | } | ||||
863 | |||||
864 | 105 | 49µs | $self->{'_BOUND_COLUMNS'} && @keys != @{$self->{'_BOUND_COLUMNS'}} and | ||
865 | croak ($self->SetDiag (3003)); | ||||
866 | |||||
867 | 105 | 246µs | $self->{'_COLUMN_NAMES'} = [ map { defined $_ ? $_ : "\cAUNDEF\cA" } @keys ]; | ||
868 | 105 | 224µs | @{$self->{'_COLUMN_NAMES'}}; | ||
869 | } # column_names | ||||
870 | |||||
871 | sub header { | ||||
872 | my ($self, $fh, @args) = @_; | ||||
873 | |||||
874 | $fh or croak ($self->SetDiag (1014)); | ||||
875 | |||||
876 | my (@seps, %args); | ||||
877 | for (@args) { | ||||
878 | if (ref $_ eq "ARRAY") { | ||||
879 | push @seps, @{$_}; | ||||
880 | next; | ||||
881 | } | ||||
882 | if (ref $_ eq "HASH") { | ||||
883 | %args = %{$_}; | ||||
884 | next; | ||||
885 | } | ||||
886 | croak ('usage: $csv->header ($fh, [ seps ], { options })'); | ||||
887 | } | ||||
888 | |||||
889 | defined $args{'munge'} && !defined $args{'munge_column_names'} and | ||||
890 | $args{'munge_column_names'} = $args{'munge'}; # munge as alias | ||||
891 | defined $args{'detect_bom'} or $args{'detect_bom'} = 1; | ||||
892 | defined $args{'set_column_names'} or $args{'set_column_names'} = 1; | ||||
893 | defined $args{'munge_column_names'} or $args{'munge_column_names'} = "lc"; | ||||
894 | |||||
895 | # Reset any previous leftovers | ||||
896 | $self->{'_RECNO'} = 0; | ||||
897 | $self->{'_AHEAD'} = undef; | ||||
898 | $self->{'_COLUMN_NAMES'} = undef if $args{'set_column_names'}; | ||||
899 | $self->{'_BOUND_COLUMNS'} = undef if $args{'set_column_names'}; | ||||
900 | |||||
901 | if (defined $args{'sep_set'}) { | ||||
902 | ref $args{'sep_set'} eq "ARRAY" or | ||||
903 | croak ($self->_SetDiagInfo (1500, "sep_set should be an array ref")); | ||||
904 | @seps = @{$args{'sep_set'}}; | ||||
905 | } | ||||
906 | |||||
907 | $^O eq "MSWin32" and binmode $fh; | ||||
908 | my $hdr = <$fh>; | ||||
909 | # check if $hdr can be empty here, I don't think so | ||||
910 | defined $hdr && $hdr ne "" or croak ($self->SetDiag (1010)); | ||||
911 | |||||
912 | my %sep; | ||||
913 | @seps or @seps = (",", ";"); | ||||
914 | foreach my $sep (@seps) { | ||||
915 | index ($hdr, $sep) >= 0 and $sep{$sep}++; | ||||
916 | } | ||||
917 | |||||
918 | keys %sep >= 2 and croak ($self->SetDiag (1011)); | ||||
919 | |||||
920 | $self->sep (keys %sep); | ||||
921 | my $enc = ""; | ||||
922 | if ($args{'detect_bom'}) { # UTF-7 is not supported | ||||
923 | if ($hdr =~ s/^\x00\x00\xfe\xff//) { $enc = "utf-32be" } | ||||
924 | elsif ($hdr =~ s/^\xff\xfe\x00\x00//) { $enc = "utf-32le" } | ||||
925 | elsif ($hdr =~ s/^\xfe\xff//) { $enc = "utf-16be" } | ||||
926 | elsif ($hdr =~ s/^\xff\xfe//) { $enc = "utf-16le" } | ||||
927 | elsif ($hdr =~ s/^\xef\xbb\xbf//) { $enc = "utf-8" } | ||||
928 | elsif ($hdr =~ s/^\xf7\x64\x4c//) { $enc = "utf-1" } | ||||
929 | elsif ($hdr =~ s/^\xdd\x73\x66\x73//) { $enc = "utf-ebcdic" } | ||||
930 | elsif ($hdr =~ s/^\x0e\xfe\xff//) { $enc = "scsu" } | ||||
931 | elsif ($hdr =~ s/^\xfb\xee\x28//) { $enc = "bocu-1" } | ||||
932 | elsif ($hdr =~ s/^\x84\x31\x95\x33//) { $enc = "gb-18030" } | ||||
933 | elsif ($hdr =~ s/^\x{feff}//) { $enc = "" } | ||||
934 | |||||
935 | $self->{'ENCODING'} = $enc ? uc $enc : undef; | ||||
936 | |||||
937 | $hdr eq "" and croak ($self->SetDiag (1010)); | ||||
938 | |||||
939 | if ($enc) { | ||||
940 | $ebcdic && $enc eq "utf-ebcdic" and $enc = ""; | ||||
941 | if ($enc =~ m/([13]).le$/) { | ||||
942 | my $l = 0 + $1; | ||||
943 | my $x; | ||||
944 | $hdr .= "\0" x $l; | ||||
945 | read $fh, $x, $l; | ||||
946 | } | ||||
947 | if ($enc) { | ||||
948 | if ($enc ne "utf-8") { | ||||
949 | require Encode; | ||||
950 | $hdr = Encode::decode ($enc, $hdr); | ||||
951 | } | ||||
952 | binmode $fh, ":encoding($enc)"; | ||||
953 | } | ||||
954 | } | ||||
955 | } | ||||
956 | |||||
957 | my ($ahead, $eol); | ||||
958 | if ($hdr and $hdr =~ s/\Asep=(\S)([\r\n]+)//i) { # Also look in xs:Parse | ||||
959 | $self->sep ($1); | ||||
960 | length $hdr or $hdr = <$fh>; | ||||
961 | } | ||||
962 | if ($hdr =~ s/^([^\r\n]+)([\r\n]+)([^\r\n].+)\z/$1/s) { | ||||
963 | $eol = $2; | ||||
964 | $ahead = $3; | ||||
965 | } | ||||
966 | |||||
967 | my $hr = \$hdr; # Will cause croak on perl-5.6.x | ||||
968 | open my $h, "<", $hr or croak ($self->SetDiag (1010)); | ||||
969 | |||||
970 | my $row = $self->getline ($h) or croak (); | ||||
971 | close $h; | ||||
972 | |||||
973 | if ( $args{'munge_column_names'} eq "lc") { | ||||
974 | $_ = lc for @{$row}; | ||||
975 | } | ||||
976 | elsif ($args{'munge_column_names'} eq "uc") { | ||||
977 | $_ = uc for @{$row}; | ||||
978 | } | ||||
979 | elsif ($args{'munge_column_names'} eq "db") { | ||||
980 | for (@{$row}) { | ||||
981 | s/\W+/_/g; | ||||
982 | s/^_+//; | ||||
983 | $_ = lc; | ||||
984 | } | ||||
985 | } | ||||
986 | |||||
987 | if ($ahead) { # Must be after getline, which creates the cache | ||||
988 | $self->_cache_set ($_cache_id{'_has_ahead'}, 1); | ||||
989 | $self->{'_AHEAD'} = $ahead; | ||||
990 | $eol =~ m/^\r([^\n]|\z)/ and $self->eol ($eol); | ||||
991 | } | ||||
992 | |||||
993 | my @hdr = @{$row}; | ||||
994 | ref $args{'munge_column_names'} eq "CODE" and | ||||
995 | @hdr = map { $args{'munge_column_names'}->($_) } @hdr; | ||||
996 | ref $args{'munge_column_names'} eq "HASH" and | ||||
997 | @hdr = map { $args{'munge_column_names'}->{$_} || $_ } @hdr; | ||||
998 | my %hdr; $hdr{$_}++ for @hdr; | ||||
999 | exists $hdr{""} and croak ($self->SetDiag (1012)); | ||||
1000 | unless (keys %hdr == @hdr) { | ||||
1001 | croak ($self->_SetDiagInfo (1013, join ", " => | ||||
1002 | map { "$_ ($hdr{$_})" } grep { $hdr{$_} > 1 } keys %hdr)); | ||||
1003 | } | ||||
1004 | $args{'set_column_names'} and $self->column_names (@hdr); | ||||
1005 | wantarray ? @hdr : $self; | ||||
1006 | } # header | ||||
1007 | |||||
1008 | sub bind_columns { | ||||
1009 | my ($self, @refs) = @_; | ||||
1010 | @refs or | ||||
1011 | return defined $self->{'_BOUND_COLUMNS'} ? @{$self->{'_BOUND_COLUMNS'}} : undef; | ||||
1012 | |||||
1013 | if (@refs == 1 && ! defined $refs[0]) { | ||||
1014 | $self->{'_COLUMN_NAMES'} = undef; | ||||
1015 | return $self->{'_BOUND_COLUMNS'} = undef; | ||||
1016 | } | ||||
1017 | |||||
1018 | $self->{'_COLUMN_NAMES'} && @refs != @{$self->{'_COLUMN_NAMES'}} and | ||||
1019 | croak ($self->SetDiag (3003)); | ||||
1020 | |||||
1021 | join "", map { ref $_ eq "SCALAR" ? "" : "*" } @refs and | ||||
1022 | croak ($self->SetDiag (3004)); | ||||
1023 | |||||
1024 | $self->_set_attr_N ("_is_bound", scalar @refs); | ||||
1025 | $self->{'_BOUND_COLUMNS'} = [ @refs ]; | ||||
1026 | @refs; | ||||
1027 | } # bind_columns | ||||
1028 | |||||
1029 | sub getline_hr { | ||||
1030 | my ($self, @args, %hr) = @_; | ||||
1031 | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3002)); | ||||
1032 | my $fr = $self->getline (@args) or return; | ||||
1033 | if (ref $self->{'_FFLAGS'}) { # missing | ||||
1034 | $self->{'_FFLAGS'}[$_] = CSV_FLAGS_IS_MISSING () | ||||
1035 | for (@{$fr} ? $#{$fr} + 1 : 0) .. $#{$self->{'_COLUMN_NAMES'}}; | ||||
1036 | @{$fr} == 1 && (!defined $fr->[0] || $fr->[0] eq "") and | ||||
1037 | $self->{'_FFLAGS'}[0] ||= CSV_FLAGS_IS_MISSING (); | ||||
1038 | } | ||||
1039 | @hr{@{$self->{'_COLUMN_NAMES'}}} = @{$fr}; | ||||
1040 | \%hr; | ||||
1041 | } # getline_hr | ||||
1042 | |||||
1043 | # spent 53714s (31.9ms+53714) within Text::CSV_XS::getline_hr_all which was called 105 times, avg 512s/call:
# 105 times (31.9ms+53714s) by Text::CSV_XS::csv at line 1489, avg 512s/call | ||||
1044 | 105 | 41µs | my ($self, @args) = @_; | ||
1045 | 105 | 51µs | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3002)); | ||
1046 | 105 | 97µs | my @cn = @{$self->{'_COLUMN_NAMES'}}; | ||
1047 | 3876 | 8.26s | 710325 | 107433s | [ map { my %h; @h{@cn} = @{$_}; \%h } @{$self->getline_all (@args)} ]; # spent 53714s making 105 calls to Text::CSV_XS::getline_all, avg 512s/call
# spent 53705s making 133035 calls to Text::CSV_XS::__ANON__[Text/CSV_XS.pm:1460], avg 404ms/call
# spent 5.96s making 153300 calls to IO::Handle::getline, avg 39µs/call
# spent 5.31s making 153300 calls to Text::CSV_XS::CORE:readline, avg 35µs/call
# spent 2.08s making 135240 calls to Encode::utf8::decode, avg 15µs/call
# spent 778ms making 135240 calls to Encode::Encoding::renewed, avg 6µs/call
# spent 2.25ms making 105 calls to Text::CSV_XS::error_diag, avg 21µs/call |
1048 | } # getline_hr_all | ||||
1049 | |||||
1050 | sub say { | ||||
1051 | my ($self, $io, @f) = @_; | ||||
1052 | my $eol = $self->eol (); | ||||
1053 | $eol eq "" and $self->eol ($\ || $/); | ||||
1054 | # say ($fh, undef) does not propage actual undef to print () | ||||
1055 | my $state = $self->print ($io, @f == 1 && !defined $f[0] ? undef : @f); | ||||
1056 | $self->eol ($eol); | ||||
1057 | return $state; | ||||
1058 | } # say | ||||
1059 | |||||
1060 | sub print_hr { | ||||
1061 | my ($self, $io, $hr) = @_; | ||||
1062 | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3009)); | ||||
1063 | ref $hr eq "HASH" or croak ($self->SetDiag (3010)); | ||||
1064 | $self->print ($io, [ map { $hr->{$_} } $self->column_names () ]); | ||||
1065 | } # print_hr | ||||
1066 | |||||
1067 | sub fragment { | ||||
1068 | my ($self, $io, $spec) = @_; | ||||
1069 | |||||
1070 | my $qd = qr{\s* [0-9]+ \s* }x; # digit | ||||
1071 | my $qs = qr{\s* (?: [0-9]+ | \* ) \s*}x; # digit or star | ||||
1072 | my $qr = qr{$qd (?: - $qs )?}x; # range | ||||
1073 | my $qc = qr{$qr (?: ; $qr )*}x; # list | ||||
1074 | defined $spec && $spec =~ m{^ \s* | ||||
1075 | \x23 ? \s* # optional leading # | ||||
1076 | ( row | col | cell ) \s* = | ||||
1077 | ( $qc # for row and col | ||||
1078 | | $qd , $qd (?: - $qs , $qs)? # for cell (ranges) | ||||
1079 | (?: ; $qd , $qd (?: - $qs , $qs)? )* # and cell (range) lists | ||||
1080 | ) \s* $}xi or croak ($self->SetDiag (2013)); | ||||
1081 | my ($type, $range) = (lc $1, $2); | ||||
1082 | |||||
1083 | my @h = $self->column_names (); | ||||
1084 | |||||
1085 | my @c; | ||||
1086 | if ($type eq "cell") { | ||||
1087 | my @spec; | ||||
1088 | my $min_row; | ||||
1089 | my $max_row = 0; | ||||
1090 | for (split m/\s*;\s*/ => $range) { | ||||
1091 | my ($tlr, $tlc, $brr, $brc) = (m{ | ||||
1092 | ^ \s* ([0-9]+ ) \s* , \s* ([0-9]+ ) \s* | ||||
1093 | (?: - \s* ([0-9]+ | \*) \s* , \s* ([0-9]+ | \*) \s* )? | ||||
1094 | $}x) or croak ($self->SetDiag (2013)); | ||||
1095 | defined $brr or ($brr, $brc) = ($tlr, $tlc); | ||||
1096 | $tlr == 0 || $tlc == 0 || | ||||
1097 | ($brr ne "*" && ($brr == 0 || $brr < $tlr)) || | ||||
1098 | ($brc ne "*" && ($brc == 0 || $brc < $tlc)) | ||||
1099 | and croak ($self->SetDiag (2013)); | ||||
1100 | $tlc--; | ||||
1101 | $brc-- unless $brc eq "*"; | ||||
1102 | defined $min_row or $min_row = $tlr; | ||||
1103 | $tlr < $min_row and $min_row = $tlr; | ||||
1104 | $brr eq "*" || $brr > $max_row and | ||||
1105 | $max_row = $brr; | ||||
1106 | push @spec, [ $tlr, $tlc, $brr, $brc ]; | ||||
1107 | } | ||||
1108 | my $r = 0; | ||||
1109 | while (my $row = $self->getline ($io)) { | ||||
1110 | ++$r < $min_row and next; | ||||
1111 | my %row; | ||||
1112 | my $lc; | ||||
1113 | foreach my $s (@spec) { | ||||
1114 | my ($tlr, $tlc, $brr, $brc) = @{$s}; | ||||
1115 | $r < $tlr || ($brr ne "*" && $r > $brr) and next; | ||||
1116 | !defined $lc || $tlc < $lc and $lc = $tlc; | ||||
1117 | my $rr = $brc eq "*" ? $#{$row} : $brc; | ||||
1118 | $row{$_} = $row->[$_] for $tlc .. $rr; | ||||
1119 | } | ||||
1120 | push @c, [ @row{sort { $a <=> $b } keys %row } ]; | ||||
1121 | if (@h) { | ||||
1122 | my %h; @h{@h} = @{$c[-1]}; | ||||
1123 | $c[-1] = \%h; | ||||
1124 | } | ||||
1125 | $max_row ne "*" && $r == $max_row and last; | ||||
1126 | } | ||||
1127 | return \@c; | ||||
1128 | } | ||||
1129 | |||||
1130 | # row or col | ||||
1131 | my @r; | ||||
1132 | my $eod = 0; | ||||
1133 | for (split m/\s*;\s*/ => $range) { | ||||
1134 | my ($from, $to) = m/^\s* ([0-9]+) (?: \s* - \s* ([0-9]+ | \* ))? \s* $/x | ||||
1135 | or croak ($self->SetDiag (2013)); | ||||
1136 | $to ||= $from; | ||||
1137 | $to eq "*" and ($to, $eod) = ($from, 1); | ||||
1138 | # $to cannot be <= 0 due to regex and ||= | ||||
1139 | $from <= 0 || $to < $from and croak ($self->SetDiag (2013)); | ||||
1140 | $r[$_] = 1 for $from .. $to; | ||||
1141 | } | ||||
1142 | |||||
1143 | my $r = 0; | ||||
1144 | $type eq "col" and shift @r; | ||||
1145 | $_ ||= 0 for @r; | ||||
1146 | while (my $row = $self->getline ($io)) { | ||||
1147 | $r++; | ||||
1148 | if ($type eq "row") { | ||||
1149 | if (($r > $#r && $eod) || $r[$r]) { | ||||
1150 | push @c, $row; | ||||
1151 | if (@h) { | ||||
1152 | my %h; @h{@h} = @{$c[-1]}; | ||||
1153 | $c[-1] = \%h; | ||||
1154 | } | ||||
1155 | } | ||||
1156 | next; | ||||
1157 | } | ||||
1158 | push @c, [ map { ($_ > $#r && $eod) || $r[$_] ? $row->[$_] : () } 0..$#{$row} ]; | ||||
1159 | if (@h) { | ||||
1160 | my %h; @h{@h} = @{$c[-1]}; | ||||
1161 | $c[-1] = \%h; | ||||
1162 | } | ||||
1163 | } | ||||
1164 | |||||
1165 | return \@c; | ||||
1166 | } # fragment | ||||
1167 | |||||
1168 | 1 | 100ns | my $csv_usage = q{usage: my $aoa = csv (in => $file);}; | ||
1169 | |||||
1170 | # spent 35.1ms (8.90+26.2) within Text::CSV_XS::_csv_attr which was called 105 times, avg 335µs/call:
# 105 times (8.90ms+26.2ms) by Text::CSV_XS::csv at line 1329, avg 335µs/call | ||||
1171 | 105 | 508µs | my %attr = (@_ == 1 && ref $_[0] eq "HASH" ? %{$_[0]} : @_) or croak (); | ||
1172 | |||||
1173 | 105 | 64µs | $attr{'binary'} = 1; | ||
1174 | |||||
1175 | 105 | 100µs | my $enc = delete $attr{'enc'} || delete $attr{'encoding'} || ""; | ||
1176 | 105 | 32µs | $enc eq "auto" and ($attr{'detect_bom'}, $enc) = (1, ""); | ||
1177 | 105 | 963µs | 105 | 589µs | my $stack = $enc =~ s/(:\w.*)// ? $1 : ""; # spent 589µs making 105 calls to Text::CSV_XS::CORE:subst, avg 6µs/call |
1178 | 105 | 706µs | 105 | 438µs | $enc =~ m/^[-\w.]+$/ and $enc = ":encoding($enc)"; # spent 438µs making 105 calls to Text::CSV_XS::CORE:match, avg 4µs/call |
1179 | 105 | 33µs | $enc .= $stack; | ||
1180 | |||||
1181 | 105 | 14µs | my $fh; | ||
1182 | 105 | 17µs | my $sink = 0; | ||
1183 | 105 | 18µs | my $cls = 0; # If I open a file, I have to close it | ||
1184 | 105 | 57µs | my $in = delete $attr{'in'} || delete $attr{'file'} or croak ($csv_usage); | ||
1185 | my $out = exists $attr{'out'} && !$attr{'out'} ? \"skip" | ||||
1186 | 105 | 85µs | : delete $attr{'out'} || delete $attr{'file'}; | ||
1187 | |||||
1188 | 105 | 59µs | ref $in eq "CODE" || ref $in eq "ARRAY" and $out ||= \*STDOUT; | ||
1189 | |||||
1190 | 105 | 31µs | $in && $out && !ref $in && !ref $out and croak (join "\n" => | ||
1191 | qq{Cannot use a string for both in and out. Instead use:}, | ||||
1192 | qq{ csv (in => csv (in => "$in"), out => "$out");\n}); | ||||
1193 | |||||
1194 | 105 | 16µs | if ($out) { | ||
1195 | if (ref $out and ("ARRAY" eq ref $out or "HASH" eq ref $out)) { | ||||
1196 | delete $attr{'out'}; | ||||
1197 | $sink = 1; | ||||
1198 | } | ||||
1199 | elsif ((ref $out and "SCALAR" ne ref $out) or "GLOB" eq ref \$out) { | ||||
1200 | $fh = $out; | ||||
1201 | } | ||||
1202 | elsif (ref $out and "SCALAR" eq ref $out and defined ${$out} and ${$out} eq "skip") { | ||||
1203 | delete $attr{'out'}; | ||||
1204 | $sink = 1; | ||||
1205 | } | ||||
1206 | else { | ||||
1207 | open $fh, ">", $out or croak ("$out: $!"); | ||||
1208 | $cls = 1; | ||||
1209 | } | ||||
1210 | if ($fh) { | ||||
1211 | if ($enc) { | ||||
1212 | binmode $fh, $enc; | ||||
1213 | my $fn = fileno $fh; # This is a workaround for a bug in PerlIO::via::gzip | ||||
1214 | } | ||||
1215 | unless (defined $attr{'eol'}) { | ||||
1216 | my @layers = eval { PerlIO::get_layers ($fh) }; | ||||
1217 | $attr{'eol'} = (grep m/crlf/ => @layers) ? "\n" : "\r\n"; | ||||
1218 | } | ||||
1219 | } | ||||
1220 | } | ||||
1221 | |||||
1222 | 105 | 337µs | if ( ref $in eq "CODE" or ref $in eq "ARRAY") { | ||
1223 | # All done | ||||
1224 | } | ||||
1225 | elsif (ref $in eq "SCALAR") { | ||||
1226 | # Strings with code points over 0xFF may not be mapped into in-memory file handles | ||||
1227 | # "<$enc" does not change that :( | ||||
1228 | open $fh, "<", $in or croak ("Cannot open from SCALAR using PerlIO"); | ||||
1229 | $cls = 1; | ||||
1230 | } | ||||
1231 | elsif (ref $in or "GLOB" eq ref \$in) { | ||||
1232 | if (!ref $in && $] < 5.008005) { | ||||
1233 | $fh = \*{$in}; # uncoverable statement ancient perl version required | ||||
1234 | } | ||||
1235 | else { | ||||
1236 | $fh = $in; | ||||
1237 | } | ||||
1238 | } | ||||
1239 | else { | ||||
1240 | 107 | 5.64ms | 421 | 21.0ms | open $fh, "<$enc", $in or croak ("$in: $!"); # spent 12.7ms making 105 calls to Text::CSV_XS::CORE:open, avg 121µs/call
# spent 6.16ms making 105 calls to Encode::find_encoding, avg 59µs/call
# spent 1.57ms making 105 calls to Encode::Encoding::renew, avg 15µs/call
# spent 309µs making 1 call to PerlIO::import
# spent 227µs making 105 calls to Encode::Encoding::needs_lines, avg 2µs/call |
1241 | 105 | 43µs | $cls = 1; | ||
1242 | } | ||||
1243 | 105 | 30µs | $fh || $sink or croak (qq{No valid source passed. "in" is required}); | ||
1244 | |||||
1245 | 105 | 63µs | my $hdrs = delete $attr{'headers'}; | ||
1246 | 105 | 26µs | my $frag = delete $attr{'fragment'}; | ||
1247 | 105 | 34µs | my $key = delete $attr{'key'}; | ||
1248 | 105 | 25µs | my $val = delete $attr{'value'}; | ||
1249 | my $kh = delete $attr{'keep_headers'} || | ||||
1250 | delete $attr{'keep_column_names'} || | ||||
1251 | 105 | 74µs | delete $attr{'kh'}; | ||
1252 | |||||
1253 | my $cbai = delete $attr{'callbacks'}{'after_in'} || | ||||
1254 | delete $attr{'after_in'} || | ||||
1255 | delete $attr{'callbacks'}{'after_parse'} || | ||||
1256 | 105 | 92µs | delete $attr{'after_parse'}; | ||
1257 | my $cbbo = delete $attr{'callbacks'}{'before_out'} || | ||||
1258 | 105 | 35µs | delete $attr{'before_out'}; | ||
1259 | my $cboi = delete $attr{'callbacks'}{'on_in'} || | ||||
1260 | 105 | 40µs | delete $attr{'on_in'}; | ||
1261 | |||||
1262 | my $hd_s = delete $attr{'sep_set'} || | ||||
1263 | 105 | 209µs | delete $attr{'seps'}; | ||
1264 | my $hd_b = delete $attr{'detect_bom'} || | ||||
1265 | 105 | 37µs | delete $attr{'bom'}; | ||
1266 | my $hd_m = delete $attr{'munge'} || | ||||
1267 | 105 | 31µs | delete $attr{'munge_column_names'}; | ||
1268 | 105 | 28µs | my $hd_c = delete $attr{'set_column_names'}; | ||
1269 | |||||
1270 | 105 | 401µs | for ([ 'quo' => "quote" ], | ||
1271 | [ 'esc' => "escape" ], | ||||
1272 | [ 'escape' => "escape_char" ], | ||||
1273 | ) { | ||||
1274 | 315 | 100µs | my ($f, $t) = @{$_}; | ||
1275 | 315 | 130µs | exists $attr{$f} and !exists $attr{$t} and $attr{$t} = delete $attr{$f}; | ||
1276 | } | ||||
1277 | |||||
1278 | 105 | 30µs | my $fltr = delete $attr{'filter'}; | ||
1279 | my %fltr = ( | ||||
1280 | 'not_blank' => sub { @{$_[1]} > 1 or defined $_[1][0] && $_[1][0] ne "" }, | ||||
1281 | 'not_empty' => sub { grep { defined && $_ ne "" } @{$_[1]} }, | ||||
1282 | 'filled' => sub { grep { defined && m/\S/ } @{$_[1]} }, | ||||
1283 | 105 | 2.23ms | ); | ||
1284 | defined $fltr && !ref $fltr && exists $fltr{$fltr} and | ||||
1285 | 105 | 47µs | $fltr = { '0' => $fltr{$fltr} }; | ||
1286 | 105 | 109µs | ref $fltr eq "CODE" and $fltr = { 0 => $fltr }; | ||
1287 | 105 | 32µs | ref $fltr eq "HASH" or $fltr = undef; | ||
1288 | |||||
1289 | 105 | 35µs | my $form = delete $attr{'formula'}; | ||
1290 | |||||
1291 | 105 | 76µs | defined $attr{'auto_diag'} or $attr{'auto_diag'} = 1; | ||
1292 | 105 | 54µs | defined $attr{'escape_null'} or $attr{'escape_null'} = 0; | ||
1293 | 105 | 813µs | 105 | 12.5ms | my $csv = delete $attr{'csv'} || Text::CSV_XS->new (\%attr) # spent 12.5ms making 105 calls to Text::CSV_XS::new, avg 119µs/call |
1294 | or croak ($last_new_err); | ||||
1295 | 105 | 20µs | defined $form and $csv->formula ($form); | ||
1296 | |||||
1297 | 105 | 18µs | $kh && !ref $kh && $kh =~ m/^(?:1|yes|true|internal|auto)$/i and | ||
1298 | $kh = \@internal_kh; | ||||
1299 | |||||
1300 | return { | ||||
1301 | 105 | 1.76ms | 'csv' => $csv, | ||
1302 | 'attr' => { %attr }, | ||||
1303 | 'fh' => $fh, | ||||
1304 | 'cls' => $cls, | ||||
1305 | 'in' => $in, | ||||
1306 | 'sink' => $sink, | ||||
1307 | 'out' => $out, | ||||
1308 | 'enc' => $enc, | ||||
1309 | 'hdrs' => $hdrs, | ||||
1310 | 'key' => $key, | ||||
1311 | 'val' => $val, | ||||
1312 | 'kh' => $kh, | ||||
1313 | 'frag' => $frag, | ||||
1314 | 'fltr' => $fltr, | ||||
1315 | 'cbai' => $cbai, | ||||
1316 | 'cbbo' => $cbbo, | ||||
1317 | 'cboi' => $cboi, | ||||
1318 | 'hd_s' => $hd_s, | ||||
1319 | 'hd_b' => $hd_b, | ||||
1320 | 'hd_m' => $hd_m, | ||||
1321 | 'hd_c' => $hd_c, | ||||
1322 | }; | ||||
1323 | } # _csv_attr | ||||
1324 | |||||
1325 | # spent 53715s (24.1ms+53714) within Text::CSV_XS::csv which was called 105 times, avg 512s/call:
# 105 times (24.1ms+53714s) by Gradescope::Translate::read_csv at line 48 of /home/hejohns/documentsNoSync/22f/490/gradescope-utils/lib/Gradescope/Translate.pm, avg 512s/call | ||||
1326 | 105 | 95µs | @_ && ref $_[0] eq __PACKAGE__ and splice @_, 0, 0, "csv"; | ||
1327 | 105 | 25µs | @_ or croak ($csv_usage); | ||
1328 | |||||
1329 | 105 | 498µs | 105 | 35.1ms | my $c = _csv_attr (@_); # spent 35.1ms making 105 calls to Text::CSV_XS::_csv_attr, avg 335µs/call |
1330 | |||||
1331 | 105 | 153µs | my ($csv, $in, $fh, $hdrs) = @{$c}{qw( csv in fh hdrs )}; | ||
1332 | 105 | 21µs | my %hdr; | ||
1333 | 105 | 33µs | if (ref $hdrs eq "HASH") { | ||
1334 | %hdr = %{$hdrs}; | ||||
1335 | $hdrs = "auto"; | ||||
1336 | } | ||||
1337 | |||||
1338 | 105 | 52µs | if ($c->{'out'} && !$c->{'sink'}) { | ||
1339 | !$hdrs && ref $c->{'kh'} && $c->{'kh'} == \@internal_kh and | ||||
1340 | $hdrs = $c->{'kh'}; | ||||
1341 | |||||
1342 | if (ref $in eq "CODE") { | ||||
1343 | my $hdr = 1; | ||||
1344 | while (my $row = $in->($csv)) { | ||||
1345 | if (ref $row eq "ARRAY") { | ||||
1346 | $csv->print ($fh, $row); | ||||
1347 | next; | ||||
1348 | } | ||||
1349 | if (ref $row eq "HASH") { | ||||
1350 | if ($hdr) { | ||||
1351 | $hdrs ||= [ map { $hdr{$_} || $_ } keys %{$row} ]; | ||||
1352 | $csv->print ($fh, $hdrs); | ||||
1353 | $hdr = 0; | ||||
1354 | } | ||||
1355 | $csv->print ($fh, [ @{$row}{@{$hdrs}} ]); | ||||
1356 | } | ||||
1357 | } | ||||
1358 | } | ||||
1359 | elsif (@{$in} == 0 or ref $in->[0] eq "ARRAY") { # aoa | ||||
1360 | ref $hdrs and $csv->print ($fh, $hdrs); | ||||
1361 | for (@{$in}) { | ||||
1362 | $c->{'cboi'} and $c->{'cboi'}->($csv, $_); | ||||
1363 | $c->{'cbbo'} and $c->{'cbbo'}->($csv, $_); | ||||
1364 | $csv->print ($fh, $_); | ||||
1365 | } | ||||
1366 | } | ||||
1367 | else { # aoh | ||||
1368 | my @hdrs = ref $hdrs ? @{$hdrs} : keys %{$in->[0]}; | ||||
1369 | defined $hdrs or $hdrs = "auto"; | ||||
1370 | ref $hdrs || $hdrs eq "auto" and @hdrs and | ||||
1371 | $csv->print ($fh, [ map { $hdr{$_} || $_ } @hdrs ]); | ||||
1372 | for (@{$in}) { | ||||
1373 | local %_; | ||||
1374 | *_ = $_; | ||||
1375 | $c->{'cboi'} and $c->{'cboi'}->($csv, $_); | ||||
1376 | $c->{'cbbo'} and $c->{'cbbo'}->($csv, $_); | ||||
1377 | $csv->print ($fh, [ @{$_}{@hdrs} ]); | ||||
1378 | } | ||||
1379 | } | ||||
1380 | |||||
1381 | $c->{'cls'} and close $fh; | ||||
1382 | return 1; | ||||
1383 | } | ||||
1384 | |||||
1385 | 105 | 23µs | my @row1; | ||
1386 | 105 | 104µs | if (defined $c->{'hd_s'} || defined $c->{'hd_b'} || defined $c->{'hd_m'} || defined $c->{'hd_c'}) { | ||
1387 | my %harg; | ||||
1388 | defined $c->{'hd_s'} and $harg{'sep_set'} = $c->{'hd_s'}; | ||||
1389 | defined $c->{'hd_d'} and $harg{'detect_bom'} = $c->{'hd_b'}; | ||||
1390 | defined $c->{'hd_m'} and $harg{'munge_column_names'} = $hdrs ? "none" : $c->{'hd_m'}; | ||||
1391 | defined $c->{'hd_c'} and $harg{'set_column_names'} = $hdrs ? 0 : $c->{'hd_c'}; | ||||
1392 | @row1 = $csv->header ($fh, \%harg); | ||||
1393 | my @hdr = $csv->column_names (); | ||||
1394 | @hdr and $hdrs ||= \@hdr; | ||||
1395 | } | ||||
1396 | |||||
1397 | 105 | 16µs | if ($c->{'kh'}) { | ||
1398 | @internal_kh = (); | ||||
1399 | ref $c->{'kh'} eq "ARRAY" or croak ($csv->SetDiag (1501)); | ||||
1400 | $hdrs ||= "auto"; | ||||
1401 | } | ||||
1402 | |||||
1403 | 105 | 33µs | my $key = $c->{'key'}; | ||
1404 | 105 | 35µs | if ($key) { | ||
1405 | 105 | 91µs | !ref $key or ref $key eq "ARRAY" && @{$key} > 1 or croak ($csv->SetDiag (1501)); | ||
1406 | 105 | 36µs | $hdrs ||= "auto"; | ||
1407 | } | ||||
1408 | 105 | 25µs | my $val = $c->{'val'}; | ||
1409 | 105 | 39µs | if ($val) { | ||
1410 | 105 | 15µs | $key or croak ($csv->SetDiag (1502)); | ||
1411 | 105 | 278µs | !ref $val or ref $val eq "ARRAY" && @{$val} > 0 or croak ($csv->SetDiag (1503)); | ||
1412 | } | ||||
1413 | |||||
1414 | 105 | 531µs | 105 | 68µs | $c->{'fltr'} && grep m/\D/ => keys %{$c->{'fltr'}} and $hdrs ||= "auto"; # spent 68µs making 105 calls to Text::CSV_XS::CORE:match, avg 650ns/call |
1415 | 105 | 43µs | if (defined $hdrs) { | ||
1416 | 105 | 67µs | if (!ref $hdrs) { | ||
1417 | 105 | 59µs | if ($hdrs eq "skip") { | ||
1418 | $csv->getline ($fh); # discard; | ||||
1419 | } | ||||
1420 | elsif ($hdrs eq "auto") { | ||||
1421 | 105 | 3.71ms | 525 | 9.44ms | my $h = $csv->getline ($fh) or return; # spent 3.76ms making 105 calls to Text::CSV_XS::getline, avg 36µs/call
# spent 2.40ms making 105 calls to IO::Handle::getline, avg 23µs/call
# spent 2.22ms making 105 calls to Text::CSV_XS::CORE:readline, avg 21µs/call
# spent 928µs making 105 calls to Encode::utf8::decode, avg 9µs/call
# spent 129µs making 105 calls to Encode::Encoding::renewed, avg 1µs/call |
1422 | 105 | 368µs | $hdrs = [ map { $hdr{$_} || $_ } @{$h} ]; | ||
1423 | } | ||||
1424 | elsif ($hdrs eq "lc") { | ||||
1425 | my $h = $csv->getline ($fh) or return; | ||||
1426 | $hdrs = [ map { lc ($hdr{$_} || $_) } @{$h} ]; | ||||
1427 | } | ||||
1428 | elsif ($hdrs eq "uc") { | ||||
1429 | my $h = $csv->getline ($fh) or return; | ||||
1430 | $hdrs = [ map { uc ($hdr{$_} || $_) } @{$h} ]; | ||||
1431 | } | ||||
1432 | } | ||||
1433 | elsif (ref $hdrs eq "CODE") { | ||||
1434 | my $h = $csv->getline ($fh) or return; | ||||
1435 | my $cr = $hdrs; | ||||
1436 | $hdrs = [ map { $cr->($hdr{$_} || $_) } @{$h} ]; | ||||
1437 | } | ||||
1438 | 105 | 33µs | $c->{'kh'} and $hdrs and @{$c->{'kh'}} = @{$hdrs}; | ||
1439 | } | ||||
1440 | |||||
1441 | 105 | 64µs | if ($c->{'fltr'}) { | ||
1442 | 105 | 145µs | my %f = %{$c->{'fltr'}}; | ||
1443 | # convert headers to index | ||||
1444 | 105 | 12µs | my @hdr; | ||
1445 | 105 | 45µs | if (ref $hdrs) { | ||
1446 | 105 | 108µs | @hdr = @{$hdrs}; | ||
1447 | 105 | 136µs | for (0 .. $#hdr) { | ||
1448 | 630 | 206µs | exists $f{$hdr[$_]} and $f{$_ + 1} = delete $f{$hdr[$_]}; | ||
1449 | } | ||||
1450 | } | ||||
1451 | # spent 53705s (3.76+53701) within Text::CSV_XS::__ANON__[/home/hejohns/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Text/CSV_XS.pm:1460] which was called 133035 times, avg 404ms/call:
# 133035 times (3.76s+53701s) by Text::CSV_XS::getline_all at line 1047, avg 404ms/call | ||||
1452 | 133035 | 77.3ms | my ($CSV, $ROW) = @_; # lexical sub-variables in caps | ||
1453 | 133035 | 697ms | 133035 | 109ms | foreach my $FLD (sort keys %f) { # spent 109ms making 133035 calls to Text::CSV_XS::CORE:sort, avg 820ns/call |
1454 | 133035 | 218ms | local $_ = $ROW->[$FLD - 1]; | ||
1455 | 133035 | 59.8ms | local %_; | ||
1456 | 133035 | 520ms | @hdr and @_{@hdr} = @{$ROW}; | ||
1457 | 133035 | 2.74s | 133035 | 53701s | $f{$FLD}->($CSV, $ROW) or return \"skip"; # spent 53701s making 133035 calls to main::__ANON__[split.pl:90], avg 404ms/call |
1458 | 1257 | 12.4ms | $ROW->[$FLD - 1] = $_; | ||
1459 | } | ||||
1460 | 105 | 850µs | 105 | 3.33ms | }); # spent 3.33ms making 105 calls to Text::CSV_XS::callbacks, avg 32µs/call |
1461 | } | ||||
1462 | |||||
1463 | 105 | 39µs | my $frag = $c->{'frag'}; | ||
1464 | my $ref = ref $hdrs | ||||
1465 | ? # aoh | ||||
1466 | 105 | 63µs | do { | ||
1467 | 105 | 340µs | 105 | 1.07ms | my @h = $csv->column_names ($hdrs); # spent 1.07ms making 105 calls to Text::CSV_XS::column_names, avg 10µs/call |
1468 | 210 | 358µs | my %h; $h{$_}++ for @h; | ||
1469 | 105 | 26µs | exists $h{""} and croak ($csv->SetDiag (1012)); | ||
1470 | 105 | 45µs | unless (keys %h == @h) { | ||
1471 | croak ($csv->_SetDiagInfo (1013, join ", " => | ||||
1472 | map { "$_ ($h{$_})" } grep { $h{$_} > 1 } keys %h)); | ||||
1473 | } | ||||
1474 | $frag ? $csv->fragment ($fh, $frag) : | ||||
1475 | 105 | 857µs | $key ? do { | ||
1476 | 105 | 110µs | my ($k, $j, @f) = ref $key ? (undef, @{$key}) : ($key); | ||
1477 | 105 | 152µs | if (my @mk = grep { !exists $h{$_} } grep { defined } $k, @f) { | ||
1478 | croak ($csv->_SetDiagInfo (4001, join ", " => @mk)); | ||||
1479 | } | ||||
1480 | +{ map { | ||||
1481 | 1257 | 131µs | my $r = $_; | ||
1482 | 1257 | 610µs | my $K = defined $k ? $r->{$k} : join $j => @{$r}{@f}; | ||
1483 | ( $K => ( | ||||
1484 | $val | ||||
1485 | ? ref $val | ||||
1486 | ? { map { $_ => $r->{$_} } @{$val} } | ||||
1487 | 1257 | 3.33ms | : $r->{$val} | ||
1488 | : $r )); | ||||
1489 | 105 | 2.91ms | 105 | 53714s | } @{$csv->getline_hr_all ($fh)} } # spent 53714s making 105 calls to Text::CSV_XS::getline_hr_all, avg 512s/call |
1490 | } | ||||
1491 | : $csv->getline_hr_all ($fh); | ||||
1492 | } | ||||
1493 | : # aoa | ||||
1494 | $frag ? $csv->fragment ($fh, $frag) | ||||
1495 | : $csv->getline_all ($fh); | ||||
1496 | 105 | 53µs | if ($ref) { | ||
1497 | @row1 && !$c->{'hd_c'} && !ref $hdrs and unshift @{$ref}, \@row1; | ||||
1498 | } | ||||
1499 | else { | ||||
1500 | Text::CSV_XS->auto_diag (); | ||||
1501 | } | ||||
1502 | 105 | 2.17ms | 105 | 1.88ms | $c->{'cls'} and close $fh; # spent 1.88ms making 105 calls to Text::CSV_XS::CORE:close, avg 18µs/call |
1503 | 105 | 113µs | if ($ref and $c->{'cbai'} || $c->{'cboi'}) { | ||
1504 | # Default is ARRAYref, but with key =>, you'll get a hashref | ||||
1505 | foreach my $r (ref $ref eq "ARRAY" ? @{$ref} : values %{$ref}) { | ||||
1506 | local %_; | ||||
1507 | ref $r eq "HASH" and *_ = $r; | ||||
1508 | $c->{'cbai'} and $c->{'cbai'}->($csv, $r); | ||||
1509 | $c->{'cboi'} and $c->{'cboi'}->($csv, $r); | ||||
1510 | } | ||||
1511 | } | ||||
1512 | |||||
1513 | 105 | 42µs | if ($c->{'sink'}) { | ||
1514 | my $ro = ref $c->{'out'} or return; | ||||
1515 | |||||
1516 | $ro eq "SCALAR" && ${$c->{'out'}} eq "skip" and | ||||
1517 | return; | ||||
1518 | |||||
1519 | $ro eq ref $ref or | ||||
1520 | croak ($csv->_SetDiagInfo (5001, "Output type mismatch")); | ||||
1521 | |||||
1522 | if ($ro eq "ARRAY") { | ||||
1523 | if (@{$c->{'out'}} and @$ref and ref $c->{'out'}[0] eq ref $ref->[0]) { | ||||
1524 | push @{$c->{'out'}} => @$ref; | ||||
1525 | return $c->{'out'}; | ||||
1526 | } | ||||
1527 | croak ($csv->_SetDiagInfo (5001, "Output type mismatch")); | ||||
1528 | } | ||||
1529 | |||||
1530 | if ($ro eq "HASH") { | ||||
1531 | @{$c->{'out'}}{keys %{$ref}} = values %{$ref}; | ||||
1532 | return $c->{'out'}; | ||||
1533 | } | ||||
1534 | |||||
1535 | croak ($csv->_SetDiagInfo (5002, "Unsupported output type")); | ||||
1536 | } | ||||
1537 | |||||
1538 | defined wantarray or | ||||
1539 | return csv ( | ||||
1540 | 'in' => $ref, | ||||
1541 | 'headers' => $hdrs, | ||||
1542 | 105 | 31µs | %{$c->{'attr'}}, | ||
1543 | ); | ||||
1544 | |||||
1545 | 105 | 11.1ms | return $ref; | ||
1546 | } # csv | ||||
1547 | |||||
1548 | 1 | 14µs | 1; | ||
1549 | |||||
1550 | __END__ | ||||
# spent 1.88ms within Text::CSV_XS::CORE:close which was called 105 times, avg 18µs/call:
# 105 times (1.88ms+0s) by Text::CSV_XS::csv at line 1502, avg 18µs/call | |||||
# spent 2.12ms within Text::CSV_XS::CORE:match which was called 2520 times, avg 842ns/call:
# 630 times (476µs+0s) by Text::CSV_XS::new at line 193, avg 756ns/call
# 630 times (136µs+0s) by Text::CSV_XS::new at line 213, avg 216ns/call
# 630 times (85µs+0s) by Text::CSV_XS::new at line 215, avg 135ns/call
# 105 times (641µs+0s) by Text::CSV_XS::callbacks at line 659, avg 6µs/call
# 105 times (438µs+0s) by Text::CSV_XS::_csv_attr at line 1178, avg 4µs/call
# 105 times (233µs+0s) by Text::CSV_XS::_check_sanity at line 162, avg 2µs/call
# 105 times (68µs+0s) by Text::CSV_XS::csv at line 1414, avg 650ns/call
# 105 times (23µs+0s) by Text::CSV_XS::_check_sanity at line 167, avg 221ns/call
# 105 times (20µs+0s) by Text::CSV_XS::_check_sanity at line 171, avg 190ns/call | |||||
# spent 12.7ms (4.44+8.26) within Text::CSV_XS::CORE:open which was called 105 times, avg 121µs/call:
# 105 times (4.44ms+8.26ms) by Text::CSV_XS::_csv_attr at line 1240, avg 121µs/call | |||||
sub Text::CSV_XS::CORE:readline; # opcode | |||||
# spent 109ms within Text::CSV_XS::CORE:sort which was called 133035 times, avg 820ns/call:
# 133035 times (109ms+0s) by Text::CSV_XS::__ANON__[/home/hejohns/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Text/CSV_XS.pm:1460] at line 1453, avg 820ns/call | |||||
# spent 589µs within Text::CSV_XS::CORE:subst which was called 105 times, avg 6µs/call:
# 105 times (589µs+0s) by Text::CSV_XS::_csv_attr at line 1177, avg 6µs/call | |||||
sub Text::CSV_XS::SetDiag; # xsub | |||||
# spent 300ns within Text::CSV_XS::__ANON__ which was called:
# once (300ns+0s) by Text::CSV_XS::BEGIN@25 at line 25 | |||||
# spent 90µs within Text::CSV_XS::_cache_set which was called 105 times, avg 862ns/call:
# 105 times (90µs+0s) by Text::CSV_XS::_set_attr_X at line 313, avg 862ns/call | |||||
# spent 3.76ms (1.36+2.40) within Text::CSV_XS::getline which was called 105 times, avg 36µs/call:
# 105 times (1.36ms+2.40ms) by Text::CSV_XS::csv at line 1421, avg 36µs/call | |||||
# spent 53714s (3.70+53711) within Text::CSV_XS::getline_all which was called 105 times, avg 512s/call:
# 105 times (3.70s+53711s) by Text::CSV_XS::getline_hr_all at line 1047, avg 512s/call |