3 Copyright (c) 2008, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/lazy_entry.hpp"
42 lazy_entry_grow_factor
= 3,
43 lazy_entry_dict_init
= 30,
44 lazy_entry_list_init
= 50
50 int fail_bdecode() { return -1; }
52 // fills in 'val' with what the string between start and the
53 // first occurance of the delimiter is interpreted as an int.
54 // return the pointer to the delimiter, or 0 if there is a
55 // parse error. val should be initialized to zero
56 char const* parse_int(char const* start
, char const* end
, char delimiter
, boost::int64_t& val
)
58 while (start
< end
&& *start
!= delimiter
)
61 if (!isdigit(*start
)) { fail_bdecode(); return 0; }
69 char const* find_char(char const* start
, char const* end
, char delimiter
)
71 while (start
< end
&& *start
!= delimiter
) ++start
;
76 int lazy_bdecode(char const* start
, char const* end
, lazy_entry
& ret
, int depth_limit
)
79 if (start
== end
) return 0;
81 std::vector
<lazy_entry
*> stack
;
83 stack
.push_back(&ret
);
86 if (stack
.empty()) break; // done!
88 lazy_entry
* top
= stack
.back();
90 if (int(stack
.size()) > depth_limit
) return fail_bdecode();
91 if (start
== end
) return fail_bdecode();
94 if (start
== end
&& t
!= 'e') return fail_bdecode();
98 case lazy_entry::dict_t
:
106 boost::int64_t len
= t
- '0';
107 start
= parse_int(start
, end
, ':', len
);
108 if (start
== 0 || start
+ len
+ 3 > end
|| *start
!= ':') return fail_bdecode();
110 lazy_entry
* ent
= top
->dict_append(start
);
112 stack
.push_back(ent
);
117 case lazy_entry::list_t
:
125 lazy_entry
* ent
= top
->list_append();
126 stack
.push_back(ent
);
136 top
->construct_dict(start
- 1);
139 top
->construct_list(start
- 1);
143 char const* int_start
= start
;
144 start
= find_char(start
, end
, 'e');
145 top
->construct_int(int_start
, start
- int_start
);
146 if (start
== end
) return fail_bdecode();
147 TORRENT_ASSERT(*start
== 'e');
155 if (!isdigit(t
)) return fail_bdecode();
157 boost::int64_t len
= t
- '0';
158 start
= parse_int(start
, end
, ':', len
);
159 if (start
== 0 || start
+ len
+ 1 > end
|| *start
!= ':') return fail_bdecode();
161 top
->construct_string(start
, int(len
));
172 size_type
lazy_entry::int_value() const
174 TORRENT_ASSERT(m_type
== int_t
);
175 boost::int64_t val
= 0;
176 bool negative
= false;
177 if (*m_data
.start
== '-') negative
= true;
178 parse_int(negative
?m_data
.start
+1:m_data
.start
, m_data
.start
+ m_size
, 'e', val
);
179 if (negative
) val
= -val
;
183 lazy_entry
* lazy_entry::dict_append(char const* name
)
185 TORRENT_ASSERT(m_type
== dict_t
);
186 TORRENT_ASSERT(m_size
<= m_capacity
);
189 int capacity
= lazy_entry_dict_init
;
190 m_data
.dict
= new (std::nothrow
) std::pair
<char const*, lazy_entry
>[capacity
];
191 if (m_data
.dict
== 0) return 0;
192 m_capacity
= capacity
;
194 else if (m_size
== m_capacity
)
196 int capacity
= m_capacity
* lazy_entry_grow_factor
;
197 std::pair
<char const*, lazy_entry
>* tmp
= new (std::nothrow
) std::pair
<char const*, lazy_entry
>[capacity
];
198 if (tmp
== 0) return 0;
199 std::memcpy(tmp
, m_data
.dict
, sizeof(std::pair
<char const*, lazy_entry
>) * m_size
);
200 for (int i
= 0; i
< m_size
; ++i
) m_data
.dict
[i
].second
.release();
201 delete[] m_data
.dict
;
203 m_capacity
= capacity
;
206 TORRENT_ASSERT(m_size
< m_capacity
);
207 std::pair
<char const*, lazy_entry
>& ret
= m_data
.dict
[m_size
++];
214 // the number of decimal digits needed
215 // to represent the given value
216 int num_digits(int val
)
228 void lazy_entry::construct_string(char const* start
, int length
)
230 TORRENT_ASSERT(m_type
== none_t
);
232 m_data
.start
= start
;
234 m_begin
= start
- 1 - num_digits(length
);
235 m_end
= start
+ length
;
240 // str1 is null-terminated
241 // str2 is not, str2 is len2 chars
242 bool string_equal(char const* str1
, char const* str2
, int len2
)
246 if (*str1
!= *str2
) return false;
247 if (*str1
== 0) return false;
256 std::string
lazy_entry::dict_find_string_value(char const* name
) const
258 lazy_entry
const* e
= dict_find(name
);
259 if (e
== 0 || e
->type() != lazy_entry::string_t
) return std::string();
260 return e
->string_value();
263 lazy_entry
const* lazy_entry::dict_find_string(char const* name
) const
265 lazy_entry
const* e
= dict_find(name
);
266 if (e
== 0 || e
->type() != lazy_entry::string_t
) return 0;
270 size_type
lazy_entry::dict_find_int_value(char const* name
, size_type default_val
) const
272 lazy_entry
const* e
= dict_find(name
);
273 if (e
== 0 || e
->type() != lazy_entry::int_t
) return default_val
;
274 return e
->int_value();
277 lazy_entry
const* lazy_entry::dict_find_dict(char const* name
) const
279 lazy_entry
const* e
= dict_find(name
);
280 if (e
== 0 || e
->type() != lazy_entry::dict_t
) return 0;
284 lazy_entry
const* lazy_entry::dict_find_list(char const* name
) const
286 lazy_entry
const* e
= dict_find(name
);
287 if (e
== 0 || e
->type() != lazy_entry::list_t
) return 0;
291 lazy_entry
* lazy_entry::dict_find(char const* name
)
293 TORRENT_ASSERT(m_type
== dict_t
);
294 for (int i
= 0; i
< m_size
; ++i
)
296 std::pair
<char const*, lazy_entry
> const& e
= m_data
.dict
[i
];
297 if (string_equal(name
, e
.first
, e
.second
.m_begin
- e
.first
))
298 return &m_data
.dict
[i
].second
;
303 lazy_entry
* lazy_entry::list_append()
305 TORRENT_ASSERT(m_type
== list_t
);
306 TORRENT_ASSERT(m_size
<= m_capacity
);
309 int capacity
= lazy_entry_list_init
;
310 m_data
.list
= new (std::nothrow
) lazy_entry
[capacity
];
311 if (m_data
.list
== 0) return 0;
312 m_capacity
= capacity
;
314 else if (m_size
== m_capacity
)
316 int capacity
= m_capacity
* lazy_entry_grow_factor
;
317 lazy_entry
* tmp
= new (std::nothrow
) lazy_entry
[capacity
];
318 if (tmp
== 0) return 0;
319 std::memcpy(tmp
, m_data
.list
, sizeof(lazy_entry
) * m_size
);
320 for (int i
= 0; i
< m_size
; ++i
) m_data
.list
[i
].release();
321 delete[] m_data
.list
;
323 m_capacity
= capacity
;
326 TORRENT_ASSERT(m_size
< m_capacity
);
327 return m_data
.list
+ (m_size
++);
330 std::string
lazy_entry::list_string_value_at(int i
) const
332 lazy_entry
const* e
= list_at(i
);
333 if (e
== 0 || e
->type() != lazy_entry::string_t
) return std::string();
334 return e
->string_value();
337 size_type
lazy_entry::list_int_value_at(int i
, size_type default_val
) const
339 lazy_entry
const* e
= list_at(i
);
340 if (e
== 0 || e
->type() != lazy_entry::int_t
) return default_val
;
341 return e
->int_value();
344 void lazy_entry::clear()
348 case list_t
: delete[] m_data
.list
; break;
349 case dict_t
: delete[] m_data
.dict
; break;
358 std::pair
<char const*, int> lazy_entry::data_section() const
360 typedef std::pair
<char const*, int> return_t
;
361 return return_t(m_begin
, m_end
- m_begin
);
364 std::ostream
& operator<<(std::ostream
& os
, lazy_entry
const& e
)
368 case lazy_entry::none_t
: return os
<< "none";
369 case lazy_entry::int_t
: return os
<< std::dec
<< std::setw(0) << e
.int_value();
370 case lazy_entry::string_t
:
372 bool printable
= true;
373 char const* str
= e
.string_ptr();
374 for (int i
= 0; i
< e
.string_length(); ++i
)
377 if (isprint(str
[i
])) continue;
382 if (printable
) return os
<< e
.string_value() << "'";
383 for (int i
= 0; i
< e
.string_length(); ++i
)
384 os
<< std::hex
<< std::setfill('0') << std::setw(2)
385 << int((unsigned char)(str
[i
]));
388 case lazy_entry::list_t
:
391 bool one_liner
= (e
.list_size() == 0
392 || e
.list_at(0)->type() == lazy_entry::int_t
393 || (e
.list_at(0)->type() == lazy_entry::string_t
394 && (e
.list_at(0)->string_length() < 10
395 || e
.list_size() < 2)))
396 && e
.list_size() < 5;
397 if (!one_liner
) os
<< "\n";
398 for (int i
= 0; i
< e
.list_size(); ++i
)
400 if (i
== 0 && one_liner
) os
<< " ";
402 if (i
< e
.list_size() - 1) os
<< (one_liner
?", ":",\n");
403 else os
<< (one_liner
?" ":"\n");
407 case lazy_entry::dict_t
:
410 bool one_liner
= (e
.dict_size() == 0
411 || e
.dict_at(0).second
->type() == lazy_entry::int_t
412 || (e
.dict_at(0).second
->type() == lazy_entry::string_t
413 && e
.dict_at(0).second
->string_length() < 30)
414 || e
.dict_at(0).first
.size() < 10)
415 && e
.dict_size() < 5;
417 if (!one_liner
) os
<< "\n";
418 for (int i
= 0; i
< e
.dict_size(); ++i
)
420 if (i
== 0 && one_liner
) os
<< " ";
421 std::pair
<std::string
, lazy_entry
const*> ent
= e
.dict_at(i
);
422 os
<< "'" << ent
.first
<< "': " << *ent
.second
;
423 if (i
< e
.dict_size() - 1) os
<< (one_liner
?", ":",\n");
424 else os
<< (one_liner
?" ":"\n");