"""
:mod:`disco.bencode` -- Implementation of `Bencode`_
====================================================

.. _Bencode: http://en.wikipedia.org/wiki/Bencode

>>> obj = {'test': {'a': {'deeply': ['nested', {'dict': 45}, 'and', 100000L]}}}
>>> loads(dumps(obj)) == obj
True
"""
def dumps(object):
    if isinstance(object, (int, long)):
        return encode_int(object)
    if isinstance(object, basestring):
        return encode_bytes(object)
    if isinstance(object, dict):
        return encode_dict(object)
    return encode_list(object)

def encode_int(object):
    return 'i%d\n' % object

def encode_bytes(object):
    return 'b%d\n%s' % (len(object), object)

def encode_dict(object):
    return 'd%s\n' % ','.join('%s,%s' % (dumps(k), dumps(v))
                             for k, v in sorted(object.items()))

def encode_list(object):
    return 'l%s\n' % ','.join(dumps(item) for item in object)

def loads(string):
    from cStringIO import StringIO
    return decode(StringIO(string))

def load(handle):
    return decode(handle)

def decode_int(file):
    return int(file.readline()[:-1])

def decode_bytes(file):
    length = decode_int(file)
    return file.read(length)

def decode_dict(file):
    return dict(n_at_a_time(decode_list(file), 2))

def decode_list(file):
    return list(decode_iter(file))

def decode_iter(file):
    separator = ','
    while separator == ',':
        yield decode(file)
        separator = file.read(1)

def decode(file):
    return {'i': decode_int,
            'd': decode_dict,
            'l': decode_list,
            'b': decode_bytes}[file.read(1)](file)

def n_at_a_time(iter, n):
    from itertools import cycle, groupby
    def key(object, pattern=cycle(x // n for x in xrange(2 * n))):
        return pattern.next()
    return (tuple(vs) for k, vs in groupby(iter, key=key))

@@ -273,16 +273,17 @@
 rue%0A%22%22%22%0A
+%0A
 def dump
@@ -289,24 +289,128 @@
 ps(object):%0A
+    import cStringIO%0A    return dumps2(cStringIO.StringIO(), object).getvalue()%0A%0Adef dumps2(s, object):%0A
     if isins
@@ -459,24 +459,27 @@
  encode_int(
+s, 
 object)%0A    
@@ -537,24 +537,27 @@
 ncode_bytes(
+s, 
 object)%0A    
@@ -608,24 +608,27 @@
 encode_dict(
+s, 
 object)%0A    
@@ -642,24 +642,27 @@
 encode_list(
+s, 
 object)%0A%0Adef
@@ -673,16 +673,19 @@
 ode_int(
+s, 
 object):
@@ -689,23 +689,24 @@
 t):%0A    
-return 
+s.write(
 'i%25d%5Cn' 
@@ -713,16 +713,30 @@
 %25 object
+)%0A    return s
 %0A%0Adef en
@@ -746,16 +746,19 @@
 e_bytes(
+s, 
 object):
@@ -766,28 +766,26 @@
     
-return 
+s.write(
 'b%25d%5Cn
-%25s
 ' %25 
-(
 len(
@@ -791,25 +791,50 @@
 (object)
-, object)
+)%0A    s.write(object)%0A    return s
 %0A%0Adef en
@@ -843,16 +843,19 @@
 de_dict(
+s, 
 object):
@@ -863,129 +863,225 @@
     
-return 'd%25s%5Cn' %25 ','.join('%25s,%25s' %25 (dumps(k), dumps(v))%0A                             for k, v in sorted(object.items()))
+s.write('d')%0A    n = len(object)%0A    for i, (k,v) in enumerate(sorted(object.items())):%0A        dumps2(s, k).write(',')%0A        dumps2(s, v)%0A        if i+1 != n:%0A            s.write(',')%0A    s.write('%5Cn')%0A    return s
 %0A%0Ade
@@ -1094,16 +1094,19 @@
 de_list(
+s, 
 object):
@@ -1114,65 +1114,179 @@
     
-return 'l%25s%5Cn' %25 ','.join(dumps(item) for item in object)
+s.write('l')%0A    n = len(object)%0A    for i, item in enumerate(object):%0A        dumps2(s, item)%0A        if i+1 != n:%0A            s.write(',')%0A    s.write('%5Cn')%0A    return s
 %0A%0Ade

fix encoding errors in dencode.