<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-29153919</id><updated>2011-11-28T02:56:51.739Z</updated><title type='text'>MonoTorrent</title><subtitle type='html'>A cross platform open source .NET Framework based BitTorrent Client written in C#</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default?start-index=101&amp;max-results=100'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>154</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-29153919.post-3107215474708380167</id><published>2011-04-19T16:44:00.003+01:00</published><updated>2011-04-19T16:47:29.345+01:00</updated><title type='text'>Moonlight 4.0 SDK... it's nearly there!</title><content type='html'>Three delicious screenshots.&lt;br /&gt;&lt;br /&gt;1) Writing some code.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-3zE0gVE7puI/Ta2uGIZTCQI/AAAAAAAAAeo/oD1uB9KxAsA/s1600/A.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 250px;" src="http://3.bp.blogspot.com/-3zE0gVE7puI/Ta2uGIZTCQI/AAAAAAAAAeo/oD1uB9KxAsA/s400/A.png" alt="" id="BLOGGER_PHOTO_ID_5597321332247431426" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;2) Launching from MonoDevelop:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-ls8pU-wnTN8/Ta2uQNTb-CI/AAAAAAAAAew/1T5qZ5h46A4/s1600/B.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 215px;" src="http://3.bp.blogspot.com/-ls8pU-wnTN8/Ta2uQNTb-CI/AAAAAAAAAew/1T5qZ5h46A4/s400/B.png" alt="" id="BLOGGER_PHOTO_ID_5597321505363720226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;3) Debugging!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-hJ3LuAzLoyI/Ta2uZNka-1I/AAAAAAAAAe4/lMP9W-P8myk/s1600/C.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 250px;" src="http://2.bp.blogspot.com/-hJ3LuAzLoyI/Ta2uZNka-1I/AAAAAAAAAe4/lMP9W-P8myk/s400/C.png" alt="" id="BLOGGER_PHOTO_ID_5597321660053781330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;4) Profit?&lt;br /&gt;&lt;br /&gt;This should hopefully be available with the next MonoDevelop release :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3107215474708380167?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3107215474708380167/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3107215474708380167' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3107215474708380167'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3107215474708380167'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2011/04/moonlight-40-sdk-its-nearly-there.html' title='Moonlight 4.0 SDK... it&apos;s nearly there!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-3zE0gVE7puI/Ta2uGIZTCQI/AAAAAAAAAeo/oD1uB9KxAsA/s72-c/A.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4707288099092279869</id><published>2011-03-04T11:13:00.003Z</published><updated>2011-03-04T11:37:28.294Z</updated><title type='text'>Mono.Nat 1.1.0</title><content type='html'>Quite a long time ago support for NAT-PMP devices was contributed to Mono.Nat. As I didn't own a NAT-PMP capable device, I had to trust that the code was good at it worked. From all reports, it did under most circumstances. However it had a couple of issues which prevented it from being enabled by default. Patches to fix these issues have finally been contributed and have now been merged into Mono.Nat and released!&lt;br /&gt;&lt;br /&gt;If you want to forward ports from your application via your upnp or nat-pmp capable routers, look no further, &lt;a href="http://projects.qnetp.net/news/show/8"&gt;Mono.Nat is here&lt;/a&gt;! There are also packages available from the &lt;a href="https://build.opensuse.org/package/show?package=mono-nat&amp;amp;project=home%3Aalan_mcgovern"&gt;opensuse build service project&lt;/a&gt; I have.&lt;br /&gt;&lt;br /&gt;If you have any issues, submit a bug report and I'll get on them!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4707288099092279869?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4707288099092279869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4707288099092279869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4707288099092279869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4707288099092279869'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2011/03/mononat-110.html' title='Mono.Nat 1.1.0'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6649963728538060275</id><published>2010-12-17T14:23:00.005Z</published><updated>2010-12-17T14:27:31.073Z</updated><title type='text'>A picture is worth 1000 words</title><content type='html'>Two to three weeks ago if you ran this battery of garbage collection related tests in moonlight, they'd mostly be red. How things have changed :)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_nFGxSFZnUc0/TQtysUiIU3I/AAAAAAAAAdg/eUktoaf05Ak/s1600/Work.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 237px; height: 400px;" src="http://3.bp.blogspot.com/_nFGxSFZnUc0/TQtysUiIU3I/AAAAAAAAAdg/eUktoaf05Ak/s400/Work.png" alt="" id="BLOGGER_PHOTO_ID_5551657071415939954" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6649963728538060275?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6649963728538060275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6649963728538060275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6649963728538060275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6649963728538060275'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/12/picture-is-worth-1000-words.html' title='A picture is worth 1000 words'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_nFGxSFZnUc0/TQtysUiIU3I/AAAAAAAAAdg/eUktoaf05Ak/s72-c/Work.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-324851949613195988</id><published>2010-12-06T11:49:00.006Z</published><updated>2010-12-06T16:18:51.779Z</updated><title type='text'>Writing a profiler for mono</title><content type='html'>As some of you may know, my day job is working on &lt;a href="http://www.mono-project.com/Moonlight"&gt;Moonlight&lt;/a&gt;, the FOSS implementation of Silverlight. This is a fairly complex piece of code as we have C++ interoperating with C# and Javascript, so managing the lifecycle of objects can be difficult. C++ refcounts, C# has an automatic garbage collector and Javascript... well, let's just say that it complicates things a *lot* when combined with the other two languages.&lt;br /&gt;&lt;br /&gt;One of the tricks we use to ensure that objects do not die early is to use a (normal) &lt;a href="http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28VS.71%29.aspx"&gt;GCHandle&lt;/a&gt; on our C# objects to ensure the garbage collector doesn't mark them as junk before they're definitely finished with in C++. However, we were having issues where some of our objects appeared to never ever get garbage collected even though they should be eligible. We suspected that excess GCHandles were causing it but we had no easy way of tracking this. Without detailed information on when and where the GCHandles were allocated, it would be next to impossible to track down where we were going wrong. After several aborted attempts to track this with Console.WriteLine I figured there must be a better way.&lt;br /&gt;&lt;br /&gt;How about a custom profiler for mono?&lt;br /&gt;&lt;br /&gt;Documentation on this is fairly sparse currently, so hopefully this will help. Thanks go to &lt;a href="http://www.advogato.org/person/lupus/"&gt;Paolo Molaro&lt;/a&gt; for helping me when I got stuck! This code is mostly copied/pasted from moonlight so it's C++ ish and also may contain some moonlight specific code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Step 1:&lt;/span&gt;&lt;br /&gt;Open up the "mono/metadata/profiler.h" file and check out all the profiler hooks that are available. They are all documented in profiler.c, so open that if you want a description of how they all work. Take note of the ones you'll need. All I needed to do was track GCHandle allocations, so I just wanted this hook:&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;void mono_profiler_install_gc_roots    (MonoProfileGCHandleFunc handle_callback, MonoProfileGCRootFunc roots_callback);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Step 2:&lt;/span&gt;&lt;br /&gt;I now know what function I need to provide and the data I want to gather, so it's time to create a struct to store my data. This is an opaque struct which will be passed to the mono runtime and passed back in every profiler hook, so you can stick whatever you want in it. I know I want to store stacktraces, allocated gchandles and a typename, so I'll start with just those. I'll also declare my prototype for my profiler hook here.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;struct _MonoProfiler {&lt;br /&gt;const char *type_name;&lt;br /&gt;GPtrArray *gchandles;&lt;br /&gt;GPtrArray *stacktraces;&lt;br /&gt;Moonlight::Mutex locker; /* used to ensure only one thread accesses the arrays */&lt;br /&gt;&lt;br /&gt;static void track_gchandle (_MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;typedef _MonoProfiler MonoProfiler;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Step 3:&lt;/span&gt;&lt;br /&gt;Implementation time! First the constructor for the MonoProfiler. I use an environment variable to limit which types we store stacktraces for as they're expensive to generate and consume a lot of memory to store when you have 10,000s of them. The important functions are the last three. The first registers our profiler with the runtime. The second installs the hook so we can get notifications for gchandle allocations. The final call enables the gc_handle events in the profiler which makes the runtime invoke the hook we just registered.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;_MonoProfiler::_MonoProfiler ()&lt;br /&gt;{&lt;br /&gt;   type_name = g_getenv ("GCHANDLES_FOR_TYPE");&lt;br /&gt;&lt;br /&gt;   gchandles = g_ptr_array_new ();&lt;br /&gt;   stacktraces = g_ptr_array_new_with_free_func (g_free);&lt;br /&gt;   /* Register the profiler with mono */&lt;br /&gt;   mono_profiler_install (this, NULL);&lt;br /&gt;   /* Supply a function for the gc_roots hook */&lt;br /&gt;   mono_profiler_install_gc_roots (track_gchandle, NULL);&lt;br /&gt;   /* Enable gc_roots tracking in the profiler so our hook is invoked */&lt;br /&gt;   mono_profiler_set_events (MONO_PROFILE_GC_ROOTS);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now all I need is the implementation for track_gchandle and I'm done!&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;void&lt;br /&gt;MonoProfiler::track_gchandle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)&lt;br /&gt;{&lt;br /&gt;   // Ignore anything that isn't a strong GC handle (docs say type == 2 is a strong gchandle)&lt;br /&gt;   if (type != 2)&lt;br /&gt;       return;&lt;br /&gt;&lt;br /&gt;   prof-&amp;gt;locker.Lock ();&lt;br /&gt;&lt;br /&gt;   GPtrArray *gchandles = prof-&amp;gt;gchandles;&lt;br /&gt;   GPtrArray *stacktraces = prof-&amp;gt;stacktraces;&lt;br /&gt;&lt;br /&gt;   if (op == MONO_PROFILER_GC_HANDLE_CREATED) {&lt;br /&gt;       // Add the GCHandle to this array&lt;br /&gt;       g_ptr_array_add (gchandles, (gpointer) handle);&lt;br /&gt;       // If the target of the gchandle is of the correct type, store its stack trace&lt;br /&gt;       // Otherwise store NULL so that we can keep the index of the gchandle and corresponding&lt;br /&gt;       // stack trace in sync.&lt;br /&gt;       if (prof-&amp;gt;type_name &amp;amp;&amp;amp; !strcmp (prof-&amp;gt;type_name, mono_class_get_name (mono_object_get_class(obj))))&lt;br /&gt;           g_ptr_array_add (stacktraces, get_stack_trace ());&lt;br /&gt;       else&lt;br /&gt;           g_ptr_array_add (stacktraces, NULL);&lt;br /&gt;   } else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED) {&lt;br /&gt;       // Walk our list of gchandles and when we find the index of the destroyed handle&lt;br /&gt;       // remove the handle and corresponding stacktrace&lt;br /&gt;       for (int i = 0; i &amp;lt; (int)gchandles-&amp;gt;len; i++) {&lt;br /&gt;           if (g_ptr_array_index (gchandles, i) == (gpointer) handle) {&lt;br /&gt;               g_ptr_array_remove_index_fast (gchandles, i);&lt;br /&gt;               g_ptr_array_remove_index_fast (stacktraces, i);&lt;br /&gt;               break;&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   prof-&amp;gt;locker.Unlock ();&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Step 4:&lt;/span&gt;&lt;br /&gt;Profit! When you're ready, you can iterate over the gchandle array and pull out whatever statistics you want. For example here is the code I use to iterate the gchandles array and work out how many instances of each object type there are.&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;void&lt;br /&gt;accumulate_g_ptr_array_by_type (gpointer data, gpointer user_data)&lt;br /&gt;{&lt;br /&gt;   // The hashtable I passed in as the user_data in g_ptr_array_foreach&lt;br /&gt;   // which i am using to link a type name to a count&lt;br /&gt;   GHashTable *by_type = (GHashTable*) user_data;&lt;br /&gt;&lt;br /&gt;   // Get the MonoObject from the gchandle.&lt;br /&gt;   MonoObject *ob = mono_gchandle_get_target (GPOINTER_TO_INT (data));&lt;br /&gt;&lt;br /&gt;   // Get the type name from the mono_object&lt;br /&gt;   const char *name = mono_class_get_name (mono_object_get_class(ob));&lt;br /&gt;&lt;br /&gt;   // Find out how many instances we have already&lt;br /&gt;   int count = GPOINTER_TO_INT (g_hash_table_lookup (by_type, name)) + 1;&lt;br /&gt;&lt;br /&gt;   // Update the hashtable with an incremented count.&lt;br /&gt;   g_hash_table_insert (by_type, name, GINT_TO_POINTER (count));&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I run that hashtable through another function to order to sort the list by the number of each type of object allocated so I can have a nicely formatted list as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; border: 1px dashed rgb(153, 153, 153); line-height: 14px; padding: 5px; overflow: auto; width: 100%;"&gt;&lt;code&gt;1 instances GCHandled of type Surface&lt;br /&gt;1 instances GCHandled of type Deployment&lt;br /&gt;4 instances GCHandled of type NameScope&lt;br /&gt;20 instances GCHandled of type ControlTemplate&lt;br /&gt;218 instances GCHandled of type MonoType&lt;br /&gt;3985 instances GCHandled of type Uri&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Needless to say, it's now immediately obvious to me that Uris are being GChandled and never freed, so i just have to enable tracing of their stacktraces and I can see exactly where we allocated the handle and from there figure out why we haven't freed it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-324851949613195988?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/324851949613195988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=324851949613195988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/324851949613195988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/324851949613195988'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/12/writing-profiler-for-mono.html' title='Writing a profiler for mono'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5841501321455659247</id><published>2010-10-02T19:38:00.004+01:00</published><updated>2010-10-02T19:50:27.766+01:00</updated><title type='text'>MonoTorrent Lives! (on github)</title><content type='html'>The mono project, who've been hosting MonoTorrent since day 1, recently decided to host all their code at &lt;a href="http://www.github.com"&gt;github&lt;/a&gt;. As such, &lt;a href="http://github.com/mono/monotorrent"&gt;MonoTorrent now lives in github&lt;/a&gt;! I took this opportunity to rename the module from 'bitsharp' to 'monotorrent' to avoid confusing people.&lt;br /&gt;&lt;br /&gt;So what does this mean? Well, it means it's trivial for you to commit patches now! All you do is set up a github account, fork monotorrent, commit your changes to your fork and issue a pull request. I'll then automagically be informed of your changes and can click a button to either merge them directly into monotorrent or reject them (with an explanation). No more emailing patches or attaching them to forum posts. Pretty nifty, eh?&lt;br /&gt;&lt;br /&gt;So get forking and lets see how great we can make MonoTorrent!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5841501321455659247?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5841501321455659247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5841501321455659247' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5841501321455659247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5841501321455659247'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/10/monotorrent-lives-on-github.html' title='MonoTorrent Lives! (on github)'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7066474491781756906</id><published>2010-06-12T14:44:00.002+01:00</published><updated>2010-06-12T15:32:56.441+01:00</updated><title type='text'>Hackwee Day IV - The final showdown</title><content type='html'>Yesterday was a bit of an anti-climax codewise. Pretty much everything I had planned on doing I had completed by midday. I then had a nice long chat with &lt;a href="http://www.lamalex.net/"&gt;lamalex&lt;/a&gt; about what I had to do to get ipod support into his udev branch and hashed out a rough idea about how to upstream these changes. I believe he's going to do a bit of work over the next week or so and once that's complete I'll be able to upstream the remainder of my patches.&lt;br /&gt;&lt;br /&gt;So at the end of the 4 days the state of things is:&lt;br /&gt;&lt;br /&gt;Device support:&lt;br /&gt;1) Hotplugging works&lt;br /&gt;2) Syncing music to and from the device works&lt;br /&gt;3) Removing music from the device works&lt;br /&gt;4) Basic properties can be read about the device and displayed in banshee&lt;br /&gt;5) Playlists aren't supported yet but should be fairly simple to do&lt;br /&gt;&lt;br /&gt;Code written:&lt;br /&gt;1) I've upstreamed my patches for libgpod-sharp&lt;br /&gt;2) I spoke with lamalex about upstreaming the rest of my work there.&lt;br /&gt;3) There are now &lt;a href="https://build.opensuse.org/project/show?project=home%3AFunkyM%3Aiphone"&gt;packages available&lt;/a&gt; for openSUSE 11.2 courtesy of FunkyM which can be used to provide most of the required packages.&lt;br /&gt;4) There are still some &lt;a href="https://launchpad.net/gudev-sharp"&gt;bleeding edge libraries&lt;/a&gt; required which aren't packaged anywhere. These will have to be packaged.&lt;br /&gt;5) I think the best way to support the libgpod based iDevice would be to create a new addin so that you can run both the old addin and the new addin in parallel as some of the required libraries for libgpod based iDevice support are very new and not widely available as of yet. This will take a little bit of time.&lt;br /&gt;&lt;br /&gt;I'm off on holidays next week so when I get back I'll start pushing my remaining work upstream. Once that's done it's just the boring process of streamlining the build to remove the dozen manual steps which are currently required :) All in all, it's been a successful hackweek. But as with all good hacks, the 5:1 rule applies. For every day spent adding functionality, I'll need 5 to make it stable and usable for everyone.&lt;br /&gt;&lt;br /&gt;I have to thank &lt;a href="http://cfergeau.blogspot.com/"&gt;teuf&lt;/a&gt; again for all the help over the last few days (and for libgpod itself!), Nathaniel McCallum for the awesome start to the .NET bindings for libgpod, &lt;a href="http://www.lamalex.net/"&gt;Alex Launi&lt;/a&gt; for the banshee gio/udev work which saved me a lot of time and also for sparing the time yesterday to talk about how I can get my work upstreamed.&lt;br /&gt;&lt;br /&gt;If I've forgotten anyone or anything, sorry! But it's been a hectic week and it was hard enough keeping track of all the packages and patches I had floating around, never mind everyone I was talking to :)&lt;br /&gt;&lt;br /&gt;I'll post again when I get everything upstreamed and I'll have a nice set of instructions for anyone wishing to test out their iDevice with banshee.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7066474491781756906?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7066474491781756906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7066474491781756906' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7066474491781756906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7066474491781756906'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/06/hackwee-day-iv-final-showdown.html' title='Hackwee Day IV - The final showdown'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6285165455500388543</id><published>2010-06-10T22:25:00.002+01:00</published><updated>2010-06-10T22:38:12.739+01:00</updated><title type='text'>Hackweek Day III - The coffee shortage</title><content type='html'>Today was a slow but interesting day (again). I took all my banshee work and rebased it on top of &lt;a href="http://www.lamalex.net/"&gt;lamalexs&lt;/a&gt; work to &lt;a href="http://gitorious.org/~lamalex/banshee/lamalex-udev"&gt;bring udev/gio support&lt;/a&gt; to banshee. This saved me having to write my own backend, but it meant that once again I had to start upgrading core parts of my OS. Luckily nothing blew up this time!&lt;br /&gt;&lt;br /&gt;So after a few hours hacking and pulling my hair out because seemingly simple commands were failing (they still are failing, i just worked around it) I now have proper detection implemented. There are no more hardcoded horrible hacks :) Anyone with the right pre-requisites and the udev branch of banshee with my patch can have basic iDevice support (tracks only).&lt;br /&gt;&lt;br /&gt;This is awesome!&lt;br /&gt;&lt;br /&gt;This will not be pushed to the main banshee repository as it depends on udev/gio to be useful. I will instead push my patches upstream to lamalex and his udev branch. All that has to be done then is to push the udev work to mainline banshee and everyone can enjoy full iDevice support.&lt;br /&gt;&lt;br /&gt;Tomorrow I hope to get in touch with lamalex and iron out a few bugs I've experienced and then upstream my work to him. My bugfixes to the &lt;a href="http://gitorious.org/~teuf/libgpod/teuf-sandbox/commits/mono"&gt;libgpod-sharp bindings&lt;/a&gt; have already been upstreamed and should be merged with the main codebase very soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6285165455500388543?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6285165455500388543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6285165455500388543' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6285165455500388543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6285165455500388543'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/06/hackweek-day-iii-coffee-shortage.html' title='Hackweek Day III - The coffee shortage'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6696506630879099312</id><published>2010-06-09T22:06:00.003+01:00</published><updated>2010-06-09T22:57:42.275+01:00</updated><title type='text'>Hackweek Day II - Attack of the code</title><content type='html'>Today has been reasonably productive. I found some bugs in the libgpod bindings which were fairly major and implemented some more features in banshee.&lt;br /&gt;&lt;br /&gt;First the fun bug. The libgpod bindings did the normal thing of defining a managed struct which mirrored the native struct that libgpod uses. This means we can do a trivial byte copy of the unmanaged memory into one of our managed structs and trivially access the information in a safe way. This is all well and good right up until you realise that when you update a property on that struct, you are not actually changing the value of the unmanaged memory. Essentially you have two copies of the same data which are disconnected - one in managed memory and one in native memory. Updating one copy is not reflected in the other and there's no easy way to keep them synced without the risk of losing data.&lt;br /&gt;&lt;br /&gt;After a bit of brainstorming with &lt;a href="http://blog.neteril.org/"&gt;Jeremie&lt;/a&gt;, we came up with a rather neat and nifty solution. Take this struct as an example:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;struct NativeDate {&lt;br /&gt;    public int Day;&lt;br /&gt;    public int Month;&lt;br /&gt;    public int Year;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If native code allocates one of those and passes it to .NET as an IntPtr, there is a trivial way to map this to managed code without requiring a copy of the data.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;unsafe class Date {&lt;br /&gt;    IntPtr Native {&lt;br /&gt;        get; set;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Day {&lt;br /&gt;        get { return ((NativeDate *)Native)-&amp;gt;Day; }&lt;br /&gt;        set { ((NativeDate *) Native)-&amp;gt;Day = value; }&lt;br /&gt;    }&lt;br /&gt;    public int Month {&lt;br /&gt;        get { return ((NativeDate *) Native)-&amp;gt;Month; }&lt;br /&gt;        set { ((NativeDate *) Native)-&amp;gt;Month = value; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int Year {&lt;br /&gt;        get { return ((NativeDate *) Native)-&amp;gt;Year; }&lt;br /&gt;        set { ((NativeDate *) Native)-&amp;gt;Year = value; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Date (IntPtr native)&lt;br /&gt;    {&lt;br /&gt;        Native = native;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All we do is use a bit of unsafe code to take the pointer that we got from native code and cast it to a NativeStruct* and read or write the data as appropriate. Now both managed land and unmanaged land are working off the same hunk of memory so they can never get out of sync.&lt;br /&gt;&lt;br /&gt;Now for the features! Banshee can now sync music to my iPhone and remove it aswell! This means all the basic stuff that's required for basic syncing is supported now. It took a lot longer than it should've to get this working because my iPhone wasn't actually set up right. There was a command (i can't remember the name of it now) which should've been run automatically to populate iTunes_Control/Device with some required information. This was never run for some bizarre reason (yay for bleeding edge) which meant nothing I did could ever actually update the database.&lt;br /&gt;&lt;br /&gt;However once I got that setup, things progressed pretty rapidly. There's one major feature left which is automatic detection when the device is mounted. If i can get this done tomorrow i'll try to upstream everything and get people testing this by the weekend (or friday!).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6696506630879099312?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6696506630879099312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6696506630879099312' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6696506630879099312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6696506630879099312'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/06/hackweek-day-ii-attack-of-code.html' title='Hackweek Day II - Attack of the code'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1966226601172357921</id><published>2010-06-08T23:03:00.002+01:00</published><updated>2010-06-08T23:27:32.536+01:00</updated><title type='text'>Hackweek V - Day 1</title><content type='html'>So at the end of Day 1, things have progressed well enough. I managed to get all my requirements installed (just about!) though to do it I needed to install older versions of some of the ipod stack. Hopefully this won't bite me in the ass as the week progresses ;)&lt;br /&gt;&lt;br /&gt;So a brief synopsis of the state of the world as I found it after one day:&lt;br /&gt;&lt;br /&gt;1) Things are still very bleeding edge. On opensuse you still need just the right packages and svn checkouts to even begin to get things working. Nothing you need is in the standard repositories, I had to use the awesome &lt;a href="http://en.opensuse.org/Build_Service"&gt;opensuse build service&lt;/a&gt; to provide some packages (gvfs &gt;= 1.5.1, libplist, umuxd) and had to compile just the right version of libimobiledevice manually. Not a pleasant experience.&lt;br /&gt;&lt;br /&gt;2) Once all that horribleness is done, actually detecting the ipod in banshee is difficult. HAL is useless. You need either gio# or udev to be able to detect the device when it's plugged in. This puts further requirements on your distro to ship the latest goodies. I worked around this by hardcoding my iphones mount point for now ;)&lt;br /&gt;&lt;br /&gt;3) Banshee is good to work with. Integrating DAPs with banshee is easier than it used to be. Hopefully things will simplify the more I delve into things and learn what's available. Some things in the existing code I hope are only there for historical reasons and can be removed with this rewrite ;)&lt;br /&gt;&lt;br /&gt;4) So far I have two big checkpoints: Banshee can &lt;a href="http://twitpic.com/1v0cpw"&gt;load the tracks on my iphone&lt;/a&gt; and it can also &lt;a href="http://twitpic.com/1v1toh"&gt;import them to its collection&lt;/a&gt;. I'm in the process of allowing you to copy tracks to the device, but I hit a few issues with libgpod which have yet to be investigated.&lt;br /&gt;&lt;br /&gt;So things are looking good for the first day. Hopefully day 2 will be full of interesting developments. Maybe I'll get playlist syncronisation working, maybe I'll get track uploading working. Who knows!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1966226601172357921?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1966226601172357921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1966226601172357921' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1966226601172357921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1966226601172357921'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/06/hackweek-v-day-1.html' title='Hackweek V - Day 1'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2941578031777712445</id><published>2010-06-04T17:32:00.004+01:00</published><updated>2010-06-04T20:40:24.270+01:00</updated><title type='text'>Hackweek V</title><content type='html'>Next week is Hackweek V in Novell, the week in which we can work on whatever we want. This year, my &lt;a href="https://features.opensuse.org/309693"&gt;hackweek proposal&lt;/a&gt; is to give banshee a new ipod addin based on &lt;a href="http://www.gtkpod.org/libgpod/"&gt;libgpod&lt;/a&gt;. This should fix &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=553311"&gt;some&lt;/a&gt; &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=506537"&gt;of&lt;/a&gt; &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=615476"&gt;banshees&lt;/a&gt; &lt;a href="https://bugzilla.gnome.org/show_bug.cgi?id=532581"&gt;bugs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;While working on this I'll try live &lt;a href="http://twitter.com/amcgovern"&gt;tweet&lt;/a&gt; as I hack so if you're interested, do tune in from tuesday morning onwards (GMT). Mondays a bank holiday so I'm going to have to complete my hack in 4 days.&lt;br /&gt;&lt;br /&gt;If you can, do give the proposal a +1. I've no idea what affect it'll have but do it anyway! ;)&lt;br /&gt;&lt;br /&gt;EDIT: Link the correct hackweek proposal. Whoopsie :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2941578031777712445?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2941578031777712445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2941578031777712445' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2941578031777712445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2941578031777712445'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/06/hackweek-v.html' title='Hackweek V'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-623184263311185257</id><published>2010-05-17T23:02:00.002+01:00</published><updated>2010-05-17T23:25:07.774+01:00</updated><title type='text'>Performance benchmarks... or lack thereof</title><content type='html'>A friend of mine was over the other day and I was showing him this cool website I had recently heard of, &lt;a href="http://www.grooveshark.com"&gt;grooveshark&lt;/a&gt;. So after admiring it for a little while he came out with "pity it's written in flash". So of course I asked the obvious question "Why?" and got the obvious answer "Because it's inefficient and sucking up all your CPU". He popped open 'top' and sure enough it was using 35% of my cpu, his point was proven.&lt;br /&gt;&lt;br /&gt;However, that is completely and utterly irrelevant. This does not make flash a bad technology nor does it make grooveshark a badly written application. Maybe 35% cpu usage is just the cost of running in the browser. It's like the old complaint against firefox, "Firefox uses too much memory". By itself that means absolutely nothing. It's nonsense. For that to have any real meaning you'd have to compare firefox to another browser doing the same task. A legitimate complaint might be "Firefox uses 3 times more memory than opera when I view www.example.com, 300 mb versus 100mb". An arbitrary decision as to what amounts to be "too much" is useless without a reference. This is exactly what far too many people are doing to flash.&lt;br /&gt;&lt;br /&gt;Sure, grooveshark uses more CPU and memory than banshee, but banshee isn't running inside firefox. It annoys me that people make such arbitrary decisions and try to pass off as fact that flash is inherently bad and HTML5 is inherently so much better and yet have no reference to compare against. I haven't seen anything even close to the complexity of grooveshark written using html5. So before you get on your high horse decrying all plugins, show me a comparable application which uses $COMPETING_TECHNOLOGY_OF_CHOICE and performs measurably better.&lt;br /&gt;&lt;br /&gt;&lt;/rant&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-623184263311185257?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/623184263311185257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=623184263311185257' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/623184263311185257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/623184263311185257'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/05/performance-benchmarks-or-lack-thereof.html' title='Performance benchmarks... or lack thereof'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6877395275167892413</id><published>2010-05-10T00:56:00.006+01:00</published><updated>2010-05-10T01:37:44.993+01:00</updated><title type='text'>Zencomic - increasing happiness since nineteen digity two</title><content type='html'>Today I had some spare time and decided to &lt;a href="http://git.neteril.org/zencomic/diff/GarfieldMinusGarfieldComicAddin/GarfieldMinusGarfieldComicAddin.cs?id=5a9b662c37b7faa1dc36af212a5b94a3492363b8"&gt;hack together a quick addin&lt;/a&gt; for &lt;a href="http://blog.neteril.org/2010/05/09/zencomic-0-3-holy-crepe/"&gt;Zencomic&lt;/a&gt;. I choose garfield minus garfield as I feel it reflects my life pretty closely in some aspects (not the manic depressive aspects though ;) ).&lt;br /&gt;&lt;br /&gt;One of the downsides of working with a distributed team is that everyone starts work at a different time. On some of the quieter days when people are away on holidays, I'm sometimes the only hacker actively working on &lt;a href="http://www.mono-project.com/Moonlight"&gt;Moonlight&lt;/a&gt; in the wee small hours of 9am UTC. By the time the Americans wake up, I've sometimes left a nice little monologue in the IRC channel detailing my frustrations and successes during the morning.&lt;br /&gt;&lt;br /&gt;Anyone, now for the obligatory screenshot&lt;br /&gt;&lt;br /&gt;&lt;span style="display: block;" id="formatbar_Buttons"&gt;&lt;span class="on" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 399px; height: 206px;" src="http://1.bp.blogspot.com/_nFGxSFZnUc0/S-dMO66E79I/AAAAAAAAAWs/HHmOFG95FTA/s400/hack+of+the+day.png" alt="" id="BLOGGER_PHOTO_ID_5469424091679485906" border="0" /&gt;&lt;br /&gt;The addin was hacked together over the course of 30 minutes. It should be able to open any garfield-garfield comic, but if you find a particular one it fails on do let me know. Also, please don't suggest I switch my beautiful string splitting code into a Regex unless you plan on writing it yourself ;)&lt;br /&gt;&lt;br /&gt;UPDATE: &lt;a href="http://git.neteril.org/zencomic/commit/?id=2f4b69cfa567e7ab19ed3cb71274ddaa50760310"&gt;I just fixed the last known issue&lt;/a&gt;. The addin now detects exactly how many pages of g-g goodness there are rather than being hardcoded to assume 101 pages (the current number).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6877395275167892413?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6877395275167892413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6877395275167892413' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6877395275167892413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6877395275167892413'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/05/zencomic-increasing-happiness-since.html' title='Zencomic - increasing happiness since nineteen digity two'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_nFGxSFZnUc0/S-dMO66E79I/AAAAAAAAAWs/HHmOFG95FTA/s72-c/hack+of+the+day.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2622248792701918015</id><published>2010-05-05T10:08:00.002+01:00</published><updated>2010-05-05T10:09:20.521+01:00</updated><title type='text'>hapy birfday 2 me</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://icanhascheezburger.files.wordpress.com/2008/07/funny-pictures-cat-wonders-why-the-food-is-on-fire.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 500px; height: 375px;" src="http://icanhascheezburger.files.wordpress.com/2008/07/funny-pictures-cat-wonders-why-the-food-is-on-fire.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center; font-style: italic;"&gt;My confusion this morning was evident&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2622248792701918015?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2622248792701918015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2622248792701918015' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2622248792701918015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2622248792701918015'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/05/hapy-birfday-2-me.html' title='hapy birfday 2 me'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5817801408743858622</id><published>2010-01-10T22:36:00.004Z</published><updated>2010-01-10T22:58:20.881Z</updated><title type='text'>FOSDEM 2010</title><content type='html'>So Ruben has been working on getting a &lt;a href="http://weblog.savanne.be/188-mono-fosdem-2010-schedule"&gt;bunch of talks organised&lt;/a&gt; for the Mono room in FOSDEM and I've been lucky enough to get a slot. I have a 30 minute slot, so I figured that would be long enough to talk about 2 things properly and then take questions.&lt;br /&gt;&lt;br /&gt;I've decided that I want to talk about two things.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The piece picker algorithm&lt;/li&gt;&lt;li&gt;How i handle threading&lt;/li&gt;&lt;/ol&gt;The piece picking code handles which segment of the file will be downloaded next. It has gone through several iterations, each time adding new features. Eventaully the whole class became so hard to maintain and test that I just had to rewrite things from the ground up using a new approach. This might give some people ideas on how to tackle problems in their own projects but even if it doesn't, it should still be interesting ;)&lt;br /&gt;&lt;br /&gt;MonoTorrent handles a lot of concurrent connections. Using one thread per connection just wouldn't scale yet using plain asynchronous sockets and locking has huge potential for deadlocks and race conditions. Getting this right took well over a year - probably due to my lack of experience at this - but right now I think I've hit the winning threading solution. This part of the talk should be useful to anyone who's worked on an app which does a lot of socket operations. Maybe I'll wake up in a few months time and see a dozen apps using that technique.&lt;br /&gt;&lt;br /&gt;Anyway, feel free to drop in for my talk (and all the others!). If you have any questions about any other aspects of MonoTorrent, or programming in general, feel free to find me and ask.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5817801408743858622?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5817801408743858622/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5817801408743858622' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5817801408743858622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5817801408743858622'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2010/01/fosdem-2010.html' title='FOSDEM 2010'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5396038847897053506</id><published>2009-12-20T21:21:00.007Z</published><updated>2009-12-21T11:41:02.669Z</updated><title type='text'>Expression Trees - serializing your data</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; Just to clarify - the code snippets below are under the MIT/X11 license.&lt;br /&gt;&lt;br /&gt;I spent a few hours over the weekend writing a binary serializer using  expression trees. I wanted to see how things would look using the new features available in .NET 4.0. My requirements were pretty simple:&lt;br /&gt;&lt;br /&gt;1) Serialize all public properties in a type or a subset of them&lt;br /&gt;2) Control the order in which they're serialized - sometimes you need to interop with an existing  and you must write your data in a specific order&lt;br /&gt;3) Control how a primitive is converted - Do you need to write value types in big endian, little endian, middle endian?&lt;br /&gt;4) Easy to use API.&lt;br /&gt;&lt;br /&gt;So lets start with the API. This is what I was hoping to use:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class Secondary&lt;br /&gt;{&lt;br /&gt;public int First { get; set; }&lt;br /&gt;public int Second { get; set; }&lt;br /&gt;public int Third { get { return First + Second; } }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyClass&lt;br /&gt;{&lt;br /&gt;public byte ByteProp { get; set; }&lt;br /&gt;public short ShortProp { get; set; }&lt;br /&gt;public int IntProp { get; set; }&lt;br /&gt;public long LongProp { get; set; }&lt;br /&gt;public string StringProp { get; set; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;{&lt;br /&gt;// Register a message so that all public fields will be serialized&lt;br /&gt;Message.Register&amp;lt;MyClass&amp;gt;();&lt;br /&gt;&lt;br /&gt;// Register a message so that only some fields are serialized and&lt;br /&gt;// they are serialized in the specified order&lt;br /&gt;Message.Register&amp;lt;Secondary&amp;gt;(&lt;br /&gt;   d =&amp;gt; d.Second,&lt;br /&gt;   d =&amp;gt; d.First&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;// Create a stream to serialize the data to&lt;br /&gt;Stream s = new MemoryStream();&lt;br /&gt;var message = new MyClass {&lt;br /&gt;   IntProp = 1,&lt;br /&gt;   LongProp= 2,&lt;br /&gt;   ByteProp= 3,&lt;br /&gt;   ShortProp = 4,&lt;br /&gt;   StringProp = "Hello World"&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// Encode the message to the stream&lt;br /&gt;MessageEncoder.Encode(message, s);&lt;br /&gt;&lt;br /&gt;// Rewind the stream and then decode the message&lt;br /&gt;s.Position = 0;&lt;br /&gt;var decoded = MessageDecoder.Decode&amp;lt;MyClass&amp;gt;(s);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It's pretty standard stuff. You can work with the standard serializer logic (serialize properties alphabetically) by registering an object without specifying any specific properties or you can customise which properties are serialized. This could also be done using attributes, but using attributes to control the order in which properties are serialized would be more error prone than the above.&lt;br /&gt;&lt;br /&gt;Firstly, sometimes you need to write your data in big endian, others you need little endian. Sometimes you won't care. What you need is to be able to control this:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;MessageEncoder.RegisterPrimitiveEncoder&amp;lt;int&amp;gt;((value, stream) =&amp;gt; {&lt;br /&gt;  stream.Write(BitConverter.GetBytes(value));&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;It's simple. Any type which can be directly converted to an array of bytes is classified as a 'primitive'. Each primitive can have an encoder/decoder pair registered as above.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public static class MessageEncoder&lt;br /&gt;{&lt;br /&gt;static Dictionary&amp;lt;Type, Delegate&amp;gt; encoders;&lt;br /&gt;static Dictionary&amp;lt;Type, Delegate&amp;gt; primitives;&lt;br /&gt;&lt;br /&gt;static MessageEncoder()&lt;br /&gt;{&lt;br /&gt;   encoders = new Dictionary&amp;lt;Type, Delegate&amp;gt;();&lt;br /&gt;   primitives = new Dictionary&amp;lt;Type, Delegate&amp;gt;();&lt;br /&gt;   RegisterPrimitiveEncoders();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static void RegisterPrimitiveEncoders()&lt;br /&gt;{&lt;br /&gt;   RegisterPrimitiveEncoder&amp;lt;byte&amp;gt;((value, stream) =&amp;gt;&lt;br /&gt;       stream.WriteByte(value)&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   RegisterPrimitiveEncoder&amp;lt;short&amp;gt;((value, stream) =&amp;gt;&lt;br /&gt;       stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   RegisterPrimitiveEncoder&amp;lt;int&amp;gt;((value, stream) =&amp;gt;&lt;br /&gt;       stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   RegisterPrimitiveEncoder&amp;lt;long&amp;gt;((value, stream) =&amp;gt;&lt;br /&gt;       stream.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value)))&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   var intWriter = (Action&amp;lt;int, Stream&amp;gt;)primitives[typeof (int)];&lt;br /&gt;   RegisterPrimitiveEncoder&amp;lt;string&amp;gt;((value, stream) =&amp;gt; {&lt;br /&gt;       var buffer = Encoding.UTF8.GetBytes(value);&lt;br /&gt;       intWriter(buffer.Length, stream);&lt;br /&gt;       stream.Write(buffer);&lt;br /&gt;   });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterPrimitiveEncoder&amp;lt;T&amp;gt;(Action&amp;lt;T, Stream&amp;gt; encoder)&lt;br /&gt;{&lt;br /&gt;   primitives [typeof (T)] = encoder;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterMessage&amp;lt;T&amp;gt;(params Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt;[] properties)&lt;br /&gt;{&lt;br /&gt;   RegisterMessage&amp;lt;T&amp;gt;(properties.Select(p =&amp;gt; p.AsPropertyInfo ()));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void RegisterMessage&amp;lt;T&amp;gt;(IEnumerable&amp;lt;PropertyInfo&amp;gt; properties)&lt;br /&gt;{&lt;br /&gt;   var propertyEncoders = new List&amp;lt;Expression&amp;gt;();&lt;br /&gt;&lt;br /&gt;   // The encode function takes an instance of the class we're decoding and the Stream&lt;br /&gt;   // which we should write the data to.&lt;br /&gt;   ParameterExpression source = Expression.Parameter(typeof(T), "source_param");&lt;br /&gt;   ParameterExpression stream = Expression.Parameter(typeof(Stream), "stream");&lt;br /&gt;&lt;br /&gt;   // For each property, get the encoder which will convert the value of the property to a byte[]&lt;br /&gt;   // which can be written to the stream.&lt;br /&gt;   foreach (var property in properties) {&lt;br /&gt;       // Get the encoder for this property type&lt;br /&gt;       var action = primitives[property.PropertyType];&lt;br /&gt;       // Create a var which holds the Action &amp;lt;T, Stream&amp;gt; which encodes the data to the stream&lt;br /&gt;       Expression converter = Expression.Constant(action, action.GetType ());&lt;br /&gt;       // Invoke the encoder passing the value of the property and the 'stream'&lt;br /&gt;       Expression invoker = Expression.Invoke(converter, Expression.Property(source, property), stream);&lt;br /&gt;       // Add the encoder for this property to the list.&lt;br /&gt;       propertyEncoders.Add(invoker);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   // Create an expression block which will execute each of the encoders one by one&lt;br /&gt;   Expression block = Expression.Block(propertyEncoders);&lt;br /&gt;   encoders.Add(typeof(T), Expression.Lambda&amp;lt;Action&amp;lt;T, Stream&amp;gt;&amp;gt;(&lt;br /&gt;       block,&lt;br /&gt;       source,&lt;br /&gt;       stream&lt;br /&gt;   ).Compile());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public static void Encode&amp;lt;T&amp;gt;(T message, Stream s)&lt;br /&gt;{&lt;br /&gt;   var encoder = (Action&amp;lt;T, Stream&amp;gt;)encoders[typeof (T)];&lt;br /&gt;   encoder (message, s);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using System.Text;&lt;br /&gt;using System.Linq.Expressions;&lt;br /&gt;using System.Reflection;&lt;br /&gt;using System.Net;&lt;br /&gt;using System.IO;&lt;br /&gt;&lt;br /&gt;namespace Encoder&lt;br /&gt;{&lt;br /&gt;public static class MessageDecoder&lt;br /&gt;{&lt;br /&gt;   static Dictionary&amp;lt;Type, Delegate&amp;gt; decoders;&lt;br /&gt;   static Dictionary&amp;lt;Type, Delegate&amp;gt; primitives;&lt;br /&gt;&lt;br /&gt;   static MessageDecoder()&lt;br /&gt;   {&lt;br /&gt;       decoders = new Dictionary&amp;lt;Type, Delegate&amp;gt;();&lt;br /&gt;       primitives = new Dictionary&amp;lt;Type, Delegate&amp;gt;();&lt;br /&gt;       RegisterDefaultDecoders();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   static void RegisterDefaultDecoders()&lt;br /&gt;   {&lt;br /&gt;       RegisterPrimitiveDecoder&amp;lt;byte&amp;gt;((s) =&amp;gt; {&lt;br /&gt;           var val = s.ReadByte();&lt;br /&gt;           if (val == -1)&lt;br /&gt;               throw new EndOfStreamException();&lt;br /&gt;           return (byte)val;&lt;br /&gt;       });&lt;br /&gt;&lt;br /&gt;       RegisterPrimitiveDecoder&amp;lt;short&amp;gt;((s) =&amp;gt; IPAddress.NetworkToHostOrder (s.ReadShort()));&lt;br /&gt;       RegisterPrimitiveDecoder&amp;lt;int&amp;gt;(s =&amp;gt; IPAddress.NetworkToHostOrder (s.ReadInt()));&lt;br /&gt;       RegisterPrimitiveDecoder&amp;lt;long&amp;gt;(s =&amp;gt; IPAddress.NetworkToHostOrder (s.ReadLong()));&lt;br /&gt;&lt;br /&gt;       var intDecoder = (Func&amp;lt;Stream, int&amp;gt;)primitives[typeof(int)];&lt;br /&gt;       RegisterPrimitiveDecoder&amp;lt;string&amp;gt;(s =&amp;gt; {&lt;br /&gt;           var length = intDecoder(s);&lt;br /&gt;           var buffer = new byte[length];&lt;br /&gt;           s.Read(buffer, 0, buffer.Length);&lt;br /&gt;           return Encoding.UTF8.GetString(buffer);&lt;br /&gt;       });&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static void RegisterPrimitiveDecoder&amp;lt;T&amp;gt;(Func&amp;lt;Stream, T&amp;gt; decoder)&lt;br /&gt;   {&lt;br /&gt;       primitives.Add(typeof(T), decoder);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static void RegisterMessage&amp;lt;T&amp;gt;(params Expression&amp;lt;Func&amp;lt;T, object&amp;gt;&amp;gt;[] properties)&lt;br /&gt;   {&lt;br /&gt;       RegisterMessage&amp;lt;T&amp;gt;(properties.Select(d =&amp;gt; d.AsPropertyInfo()));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static void RegisterMessage&amp;lt;T&amp;gt;(IEnumerable&amp;lt;PropertyInfo&amp;gt; properties)&lt;br /&gt;   {&lt;br /&gt;       var propertyDecoders = new List&amp;lt;Expression&amp;gt;();&lt;br /&gt;&lt;br /&gt;       // The decode function takes an instance of the class we're decoding and the Stream&lt;br /&gt;       // containing the data to decode.&lt;br /&gt;       ParameterExpression source = Expression.Parameter(typeof(T), "source_param");&lt;br /&gt;       ParameterExpression stream = Expression.Parameter(typeof(Stream), "stream");&lt;br /&gt;&lt;br /&gt;       // For each property, get the primitive decoder which will read data from the stream and&lt;br /&gt;       // return a value of the correct type.&lt;br /&gt;       foreach (var property in properties) {&lt;br /&gt;           var action = primitives[property.PropertyType];&lt;br /&gt;           // Create a var which holds the Func &amp;lt;Stream, T&amp;gt; which decodes the data from the stream&lt;br /&gt;           Expression decoder = Expression.Constant(action, action.GetType());&lt;br /&gt;           // Invoke the decoder passing 'stream' as the parameter&lt;br /&gt;           Expression invoker = Expression.Invoke(decoder, stream);&lt;br /&gt;           // Store the return value of the decoder in the property.&lt;br /&gt;           Expression setter = Expression.Call(source, property.GetSetMethod(), invoker);&lt;br /&gt;           // Add the decoder for this property to the list.&lt;br /&gt;           propertyDecoders.Add(setter);&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       // Create a block which will execute the decoders for all the fields one after another.&lt;br /&gt;       Expression block = Expression.Block(propertyDecoders);&lt;br /&gt;       decoders.Add (typeof (T), Expression.Lambda&amp;lt;Action&amp;lt;T, Stream&amp;gt;&amp;gt;(&lt;br /&gt;           block,&lt;br /&gt;           source,&lt;br /&gt;           stream&lt;br /&gt;       ).Compile ());&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public static T Decode&amp;lt;T&amp;gt;(Stream s) where T : class, new()&lt;br /&gt;   {&lt;br /&gt;       T t = new T();&lt;br /&gt;       var decoder = (Action&amp;lt;T, Stream&amp;gt;)decoders[typeof(T)];&lt;br /&gt;       decoder(t, s);&lt;br /&gt;       return t;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The idea is quite simple. For each class we can generate an ideal serializer using expression trees which doesn't require boxing or casting. This way we can avoid the use of reflection when serializing objects and so avoid the performance penalties incurred that. The code above only handles the simple case where a class consists of primitive types (int, long, string) , though it'd be easy enough to extend it to support more complex scenarios.&lt;br /&gt;&lt;br /&gt;The serializer as you see it could not have been written with .NET 3.0. Some of the key components like BlockExpression were only introduced with .NET 4.0. If your object contains an array which needs to be serialized, you'll need the new IndexExpression too. Sure, it's possible to fake these using some anonymous delegates and Actions, but that's not pretty :)&lt;br /&gt;&lt;br /&gt;The total implementation is less than 170 LOC. I'd be willing to bet that with another 100 LOC you could support most constructs. If you're currently a heavy user of reflection  to provide object serialization, it's time to update ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5396038847897053506?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5396038847897053506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5396038847897053506' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5396038847897053506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5396038847897053506'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/expression-trees-serializing-your-data.html' title='Expression Trees - serializing your data'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3538113407584638582</id><published>2009-12-15T00:13:00.002Z</published><updated>2009-12-15T00:19:35.268Z</updated><title type='text'>New years resolutions</title><content type='html'>It's tradition in quite a lot of countries to make a &lt;a href="http://en.wikipedia.org/wiki/New_Year%27s_resolution"&gt;new years resolution&lt;/a&gt; on the 1st of January. Most people forget about them within a few days or weeks. This year, I'll be making one I'm going to keep!&lt;br /&gt;&lt;br /&gt;I want to take part in a dancing [0] flash mob whether it's in this country or another.&lt;br /&gt;&lt;br /&gt;&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube-nocookie.com/v/7EYAUazLI9k&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube-nocookie.com/v/7EYAUazLI9k&amp;amp;hl=en_GB&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;What ideas do you have? Anything strange, interesting, unusual? Leave a comment and let me know, maybe you have a better idea than being part of a flash mob.&lt;br /&gt;&lt;br /&gt;[0] Me and dancing don't get on particularly well, so it'll be an interesting challenge ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3538113407584638582?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3538113407584638582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3538113407584638582' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3538113407584638582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3538113407584638582'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/new-years-resolutions.html' title='New years resolutions'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-559923915319757691</id><published>2009-12-06T13:33:00.010Z</published><updated>2009-12-06T16:06:58.651Z</updated><title type='text'>Yet another INotifyPropertyChanged with Expression Trees - Part 2</title><content type='html'>&lt;a href="http://monotorrent.blogspot.com/2009/12/yet-another-inotifypropertychanged-with_05.html"&gt;In my last post&lt;/a&gt;, I described a method whereby you can implement INotifyPropertyChanged with zero performance overhead and near-zero boilerplate code. The only boilerplate left was the delegate you had to create to invoke the event:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public Book()&lt;br /&gt;{&lt;br /&gt;    // Boilerplate - eugh!&lt;br /&gt;    Action&amp;lt;string&amp;gt; notify = (propertyName) =&amp;gt; {&lt;br /&gt;        var h = PropertyChanged;&lt;br /&gt;        if (h != null)&lt;br /&gt;            h(this, new PropertyChangedEventArgs(propertyName));&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    author = new ChangeNotifier&amp;lt;string&amp;gt; (() =&amp;gt; Author, notify);&lt;br /&gt;    price = new ChangeNotifier&amp;lt;decimal&amp;gt; (() =&amp;gt; Price, notify);&lt;br /&gt;    quantity = new ChangeNotifier&amp;lt;int&amp;gt; (() =&amp;gt; Quantity, notify);&lt;br /&gt;    title = new ChangeNotifier&amp;lt;string&amp;gt; (() =&amp;gt; Title, notify);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The entire point of my implementation was to avoid writing boilerplate, so this was slightly irritating. Unfortunately, there's no trivial way around the problem as the .NET framework really limits what you can do with events. The first thing you'd think of is "pass the actual object into the ChangeNotifier constructor and just raise the event that way". For example my constructors would change to:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;new ChangeNotifier&amp;lt;string&amp;gt;(() =&amp;gt; Author, this);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That's well and good, right up until you realise that it's impossible for one object to raise an event that's declared on another object.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class A&lt;br /&gt;{&lt;br /&gt;    public event EventHandler MyEvent;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class B&lt;br /&gt;{&lt;br /&gt;    public void AccessEvent (A a)&lt;br /&gt;    {&lt;br /&gt;        // Invalid - you can't raise an event which is declared in another class&lt;br /&gt;        a.MyEvent(this, EventArgs.Empty);&lt;br /&gt;&lt;br /&gt;        // Invalid - you can't copy the event either&lt;br /&gt;        EventHandler h = a.MyEvent;&lt;br /&gt;        h(this, EventArgs.Empty);&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;Another alternative would be to pass the event itself into the ChangeNotifier object:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;new ChangeNotifier&amp;lt;string&amp;gt; (() =&amp;gt; &lt;/code&gt;&lt;code&gt;Author, PropertyChanged);&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;But this won't work because a &lt;font style="font-style: italic;"&gt;copy&lt;/font&gt; of the delegate list is created. That means if anyone adds event handlers later on, they won't be invoked when the property changes. So with that stuck firmly in my mind, I never gave much thought to removing that last remaining bit of boilerplate. That's about to change!&lt;br /&gt;&lt;br /&gt;What I really want is for my final implementation to look more like this:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class Book : INotifyPropertyChanged&lt;br /&gt;{&lt;br /&gt;    public event PropertyChangedEventHandler PropertyChanged;&lt;br /&gt;&lt;br /&gt;    ChangeNotifier&amp;lt;string&amp;gt; author;&lt;br /&gt;&lt;br /&gt;    public string Author&lt;br /&gt;    {&lt;br /&gt;        get { return author.Value; }&lt;br /&gt;        set { author.Value = value; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Book()&lt;br /&gt;    {&lt;br /&gt;        author = ChangeNotifier.Create(() =&amp;gt; Author, ????);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;That's short and sweet . The generic types should be automatically inferred, you shouldn't have to create the delegate to raise the event,  it's beautiful! The only problem  is to figure out what I should replace the question marks with. I need something that will allow me to get at the current list of event handlers from outside of the Book object, i.e.  something along the lines of this:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;Func&amp;lt;PropertyChangedEventHandler&amp;gt; getter = delegate { return PropertyChanged; };&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Prettying it up a little, this is how my Book class looks:&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class Book : INotifyPropertyChanged&lt;br /&gt;{&lt;br /&gt;    public event PropertyChangedEventHandler PropertyChanged;&lt;br /&gt;&lt;br /&gt;    ChangeNotifier&amp;lt;string&amp;gt; author;&lt;br /&gt;&lt;br /&gt;    public string Author {&lt;br /&gt;        get { return author.Value; }&lt;br /&gt;        set { author.Value = value; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Book()&lt;br /&gt;    {&lt;br /&gt;        author = ChangeNotifier.Create (() =&amp;gt; Author, () =&amp;gt; PropertyChanged);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Beautiful! The more astute readers might notice a problem at this stage. Fine, the ChangeNotifier object can get the event list and raise the event, but it can't fill in the 'sender' - it has no reference to the 'book' object! Have no fear, it's already taken care of! The getter delegate has a reference to the book object (Delegate.Target), so we can fill everything in perfectly! The final implementation of the ChangeNotifier class is this:&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public static class ChangeNotifier&lt;br /&gt;{&lt;br /&gt;    public static ChangeNotifier&amp;lt;TValue&amp;gt; Create&amp;lt;TValue&amp;gt;(Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; expression, Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier)&lt;br /&gt;    {&lt;br /&gt;        return new ChangeNotifier&amp;lt;TValue&amp;gt;(expression, notifier);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class ChangeNotifier&amp;lt;TValue&amp;gt;&lt;br /&gt;{&lt;br /&gt;    Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier;&lt;br /&gt;    string propertyName;&lt;br /&gt;    TValue value;&lt;br /&gt;&lt;br /&gt;    public TValue Value {&lt;br /&gt;        get { return value; }&lt;br /&gt;        set {&lt;br /&gt;            if (!EqualityComparer&amp;lt;TValue&amp;gt;.Default.Equals(this.value, value)) {&lt;br /&gt;                this.value = value;&lt;br /&gt;                // Get the current list of registered event handlers&lt;br /&gt;                // then invoke them with the correct 'sender' and event args&lt;br /&gt;                PropertyChangedEventHandler h = notifier();&lt;br /&gt;                if (h != null)&lt;br /&gt;                    h(notifier.Target, new PropertyChangedEventArgs(propertyName));&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public ChangeNotifier(Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; expression, Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier)&lt;br /&gt;    {&lt;br /&gt;        if (expression.NodeType != ExpressionType.Lambda)&lt;br /&gt;            throw new ArgumentException("Value must be a lamda expression", "expression");&lt;br /&gt;        if (!(expression.Body is MemberExpression))&lt;br /&gt;            throw new ArgumentException("The body of the expression must be a memberref", "expression");&lt;br /&gt;&lt;br /&gt;        MemberExpression m = (MemberExpression)expression.Body;&lt;br /&gt;        this.notifier = notifier;&lt;br /&gt;        this.propertyName = m.Member.Name;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;I have one final trick up my sleeve. Suppose you have a field (Progress) whose value is calculated based on other values (CurrentStep, TotalSteps) and you want to get Notifications whenever any of those fields changes, well, that's easy!&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;public class Worker : INotifyPropertyChanged&lt;br /&gt;{&lt;br /&gt;    public event PropertyChangedEventHandler PropertyChanged;&lt;br /&gt;&lt;br /&gt;    ChangeNotifier&amp;lt;int&amp;gt; currentStep;&lt;br /&gt;    ChangeNotifier&amp;lt;int&amp;gt; totalSteps;&lt;br /&gt;&lt;br /&gt;    public int CurrentStep {&lt;br /&gt;        get { return currentStep.Value; }&lt;br /&gt;        set { currentStep.Value = value; }&lt;br /&gt;    }&lt;br /&gt;    public int TotalSteps {&lt;br /&gt;        get { return totalSteps.Value; }&lt;br /&gt;        set { totalSteps.Value = value; }&lt;br /&gt;    }&lt;br /&gt;    public double Progress&lt;br /&gt;    {&lt;br /&gt;        get { return (double)CurrentStep / TotalSteps; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Worker()&lt;br /&gt;    {&lt;br /&gt;        Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier = () =&amp;gt; PropertyChanged;&lt;br /&gt;&lt;br /&gt;        currentStep = ChangeNotifier.Create(() =&amp;gt; CurrentStep, notifier);&lt;br /&gt;        totalSteps = ChangeNotifier.Create(() =&amp;gt; TotalSteps, notifier);&lt;br /&gt;&lt;br /&gt;        // A PropertyChanged notification will be created for Progress every time&lt;br /&gt;        // either the CurrentStep *or* TotalSteps changes.&lt;br /&gt;        ChangeNotifier.CreateDependent(&lt;br /&gt;            () =&amp;gt; Progress,&lt;br /&gt;            notifier,&lt;br /&gt;            () =&amp;gt; CurrentStep,&lt;br /&gt;            () =&amp;gt; TotalSteps&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;And the new helper methods are:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;public static class ChangeNotifier&lt;br /&gt;{&lt;br /&gt;    static string GetPropertyName(Expression expression)&lt;br /&gt;    {&lt;br /&gt;        while (!(expression is MemberExpression)) {&lt;br /&gt;            if (expression is LambdaExpression)&lt;br /&gt;                expression = ((LambdaExpression)expression).Body;&lt;br /&gt;            else if (expression is UnaryExpression)&lt;br /&gt;                expression = ((UnaryExpression)expression).Operand;&lt;br /&gt;        }&lt;br /&gt;       &lt;br /&gt;        return ((MemberExpression)expression).Member.Name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void CreateDependent&amp;lt;TValue&amp;gt;(Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; property, Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier, params Expression&amp;lt;Func&amp;lt;object&amp;gt;&amp;gt;[] dependents)&lt;br /&gt;    {&lt;br /&gt;        // The name of the property which is dependent on the value of other properties&lt;br /&gt;        var name = GetPropertyName(property);&lt;br /&gt;        // The names of the other properties&lt;br /&gt;        var dependentNames = dependents.Select&amp;lt;Expression, string&amp;gt;(GetPropertyName).ToArray();&lt;br /&gt;       &lt;br /&gt;        INotifyPropertyChanged sender = (INotifyPropertyChanged)notifier.Target;&lt;br /&gt;        sender.PropertyChanged += (o, e) =&amp;gt; {&lt;br /&gt;            // If one of our dependents changes, emit a PropertyChanged notification for our property&lt;br /&gt;            if (dependentNames.Contains(e.PropertyName)) {&lt;br /&gt;                var h = notifier();&lt;br /&gt;                if (h != null)&lt;br /&gt;                    h(o, new PropertyChangedEventArgs (name));&lt;br /&gt;            }&lt;br /&gt;        };&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static ChangeNotifier&amp;lt;TValue&amp;gt; Create&amp;lt;TValue&amp;gt;(Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; expression, Func&amp;lt;PropertyChangedEventHandler&amp;gt; notifier)&lt;br /&gt;    {&lt;br /&gt;        return new ChangeNotifier&amp;lt;TValue&amp;gt;(expression, notifier);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The only change is that I need to use a slightly more complicated method of getting the property name as it's possible for certain types to get wrapped in a ConvertExpression.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-559923915319757691?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/559923915319757691/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=559923915319757691' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/559923915319757691'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/559923915319757691'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/yet-another-inotifypropertychanged-with_06.html' title='Yet another INotifyPropertyChanged with Expression Trees - Part 2'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2193770531152710629</id><published>2009-12-05T14:44:00.001Z</published><updated>2009-12-05T14:44:46.786Z</updated><title type='text'>Yet another INotifyPropertyChanged with Expression Trees</title><content type='html'>There are &lt;a href="http://www.google.ie/#hl=en&amp;amp;source=hp&amp;amp;q=inotifypropertychanged+expression+trees&amp;amp;btnG=Google+Search&amp;amp;meta=&amp;amp;aq=0&amp;amp;oq=inotifypropertychanged+expression+trees&amp;amp;fp=460a3d52f02dd2cb"&gt;dozens of examples &lt;/a&gt;out there showing you how to avoid having to refer to method names as strings when implementing INotifyPropertyChanged. The most important reason why you don't want to have to do this is because method names can get refactored but the hardcoded strings might be forgotten. No-one wants to end up getting a Changed notification for a property which doesn't exist.&lt;br /&gt;&lt;br /&gt;My issue with all these examples is that none of them thought far enough ahead. Fine, they all show you how refer to properties without using hardcoded strings but they still require you to write lots of boilerplate code to raise the PropertyChanged event - boilerplate you have to write for every property. What I want is to be able to declare all my properties like:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public string Title {&lt;br /&gt;    get { return title; }&lt;br /&gt;    set { title = value; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;and yet still get my property change notifications. I also want this method to be reasonably high performance. I don't want every property change to have extra memory or CPU overhead as every developer expects that changing the value of a property will not do any complex calculations. So how can I accomplish this?&lt;br /&gt;&lt;br /&gt;To start off with, we can all tell that it's impossible to achieve the required behaviour using just the snippet above. We're going to have to add (at least) one additional level of indirection.  That means I should be able to implement my requirements using code like:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public string Title {&lt;br /&gt;    get { return title.Value; }&lt;br /&gt;    set { title.Value = value; }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;The object 'title' must then contain all the logic required to raise the property changed notification. So what might this magical object look like?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:monospace;"&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class ChangeNotifier&amp;lt;TValue&amp;gt;&lt;br /&gt;{&lt;br /&gt;    Action&amp;lt;string&amp;gt; notifyHandler;&lt;br /&gt;    string propertyName;&lt;br /&gt;    TValue value;&lt;br /&gt;&lt;br /&gt;    public TValue Value {&lt;br /&gt;        get { return value; }&lt;br /&gt;        set {&lt;br /&gt;            if (!EqualityComparer&amp;lt;TValue&amp;gt;.Default.Equals(this.value, value)) {&lt;br /&gt;                this.value = value;&lt;br /&gt;                notifyHandler(propertyName);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    public ChangeNotifier(Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; expression, Action&amp;lt;string&amp;gt; notifyHandler)&lt;br /&gt;    {&lt;br /&gt;        if (expression.NodeType != ExpressionType.Lambda)&lt;br /&gt;            throw new ArgumentException("Value must be a lamda expression", "expression");&lt;br /&gt;        if (!(expression.Body is MemberExpression))&lt;br /&gt;            throw new ArgumentException("The body of the expression must be a memberref", "expression");&lt;br /&gt;&lt;br /&gt;        MemberExpression m = (MemberExpression)expression.Body;&lt;br /&gt;        this.propertyName = m.Member.Name;&lt;br /&gt;        this.notifyHandler = notifyHandler;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;You're probably looking at this thinking "What the hell is this Expression&amp;lt;Func&amp;lt;TValue&amp;gt;&amp;gt; ? How do I even use that monstrosity?". Well... simples!&lt;br /&gt;&lt;br /&gt;&lt;pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"&gt;&lt;code&gt;public class Book : INotifyPropertyChanged&lt;br /&gt;{&lt;br /&gt;    public event PropertyChangedEventHandler PropertyChanged;&lt;br /&gt;&lt;br /&gt;    ChangeNotifier&amp;lt;string&amp;gt; author;&lt;br /&gt;    ChangeNotifier&amp;lt;decimal&amp;gt; price;&lt;br /&gt;    ChangeNotifier&amp;lt;int&amp;gt; quantity;&lt;br /&gt;    ChangeNotifier&amp;lt;string&amp;gt; title;&lt;br /&gt;&lt;br /&gt;    public string Author {&lt;br /&gt;        get { return author.Value; }&lt;br /&gt;        set { author.Value = value; }&lt;br /&gt;    }&lt;br /&gt;    public decimal Price {&lt;br /&gt;        get { return price.Value; }&lt;br /&gt;        set { price.Value = value; }&lt;br /&gt;    }&lt;br /&gt;    public int Quantity {&lt;br /&gt;        get { return quantity.Value; }&lt;br /&gt;        set { quantity.Value = value; }&lt;br /&gt;    }&lt;br /&gt;    public string Title {&lt;br /&gt;        get { return title.Value; }&lt;br /&gt;        set { title.Value = value; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Book()&lt;br /&gt;    {&lt;br /&gt;        Action&amp;lt;string&amp;gt; notify = (propertyName) =&amp;gt; {&lt;br /&gt;            var h = PropertyChanged;&lt;br /&gt;            if (h != null)&lt;br /&gt;                h(this, new PropertyChangedEventArgs(propertyName));&lt;br /&gt;        };&lt;br /&gt;&lt;br /&gt;        author = new ChangeNotifier&amp;lt;string&amp;gt; (() =&amp;gt; Author, notify);&lt;br /&gt;        price = new ChangeNotifier&amp;lt;decimal&amp;gt; (() =&amp;gt; Price, notify);&lt;br /&gt;        quantity = new ChangeNotifier&amp;lt;int&amp;gt; (() =&amp;gt; Quantity, notify);&lt;br /&gt;        title = new ChangeNotifier&amp;lt;string&amp;gt; (() =&amp;gt; Title, notify);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;All that happens here is that when constructing the ChangeNotifier object, an Expression referencing the required Property is passed into the constructor, along with a delegate which will raise the PropertyChanged event. We parse that expression tree to retrieve the method name and store it. After that everything Just Works (tm) with little to no performance penalty. The days of writing boilerplate code for INotifyPropertyChanged are gone! You also have the benefit that you can't make a mistake writing the boilerplate code because you don't write it anymore!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2193770531152710629?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2193770531152710629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2193770531152710629' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2193770531152710629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2193770531152710629'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/yet-another-inotifypropertychanged-with_05.html' title='Yet another INotifyPropertyChanged with Expression Trees'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7354886535484842512</id><published>2009-12-04T16:44:00.005Z</published><updated>2009-12-04T17:19:59.465Z</updated><title type='text'>Can't you feel the Moonlight? Part deux</title><content type='html'>&lt;a href="http://monotorrent.blogspot.com/2009/12/cant-you-feel-moonlight.html"&gt;As I was saying yesterday&lt;/a&gt;, the live version of the silverlight toolkit site didn't work right in moonlight. All the pretty charts rendered as you see them below, very empty.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_nFGxSFZnUc0/SxlALCJN7MI/AAAAAAAAATY/RXPdJeRC4T8/s1600-h/Fail.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 195px;" src="http://1.bp.blogspot.com/_nFGxSFZnUc0/SxlALCJN7MI/AAAAAAAAATY/RXPdJeRC4T8/s320/Fail.png" alt="" id="BLOGGER_PHOTO_ID_5411426985560632514" border="0" /&gt;&lt;/a&gt;I figured that since a slightly older version worked near-flawlessly, surely I could fix the live version with only a few minor tweaks. It's not like the would've completely rewritten the Chart controls within the space of 1 release.&lt;br /&gt;&lt;br /&gt;I checked everything from DataBinding, to TemplateBinding, to Styles, to Measure/Arrange bugs and nothing was showing up as causing the issue. I finally narrowed it down to a bug in VisualStateGroup. For some reason the Name property was empty even though it was declared with a name in xaml.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://anonsvn.mono-project.com/viewvc/trunk/moon/class/WPF.Toolkit/VSM/System/Windows/VisualStateGroup.cs?r1=141801&amp;amp;r2=147669"&gt;One. Tiny. Patch. Later. &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_nFGxSFZnUc0/Sxk-syLWs4I/AAAAAAAAATQ/XjsQkwUu9WQ/s1600-h/Success.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 195px;" src="http://1.bp.blogspot.com/_nFGxSFZnUc0/Sxk-syLWs4I/AAAAAAAAATQ/XjsQkwUu9WQ/s320/Success.png" alt="" id="BLOGGER_PHOTO_ID_5411425366366925698" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Success. I can't believe that the bug was that simple. In the end, those bugs are actually by far the worst. There's no exception thrown or any kind of visible indication that something has failed other than an empty screen. The only reason I found the bug was because the toolkit is opensource and I was running it locally with a few dozen Console.WriteLines, gradually reducing the area of code where I thought the bug was. Unfortunately this fix arrived too late for the 1.99.9 release, but it will definitely be in the release after it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7354886535484842512?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7354886535484842512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7354886535484842512' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7354886535484842512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7354886535484842512'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/wonders-of-charting.html' title='Can&apos;t you feel the Moonlight? Part deux'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_nFGxSFZnUc0/SxlALCJN7MI/AAAAAAAAATY/RXPdJeRC4T8/s72-c/Fail.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6500799877581820225</id><published>2009-12-02T17:44:00.005Z</published><updated>2009-12-02T17:50:44.495Z</updated><title type='text'>Can't you feel the moonlight?</title><content type='html'>It's time for the obligatory screenshots again. This is what the Data Visualisation demos from the Silverlight Toolkit (March edition) looked like yesterday:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_nFGxSFZnUc0/Sxany0O01YI/AAAAAAAAASo/Jv3MiPkoLuk/s1600-h/Before.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 195px;" src="http://4.bp.blogspot.com/_nFGxSFZnUc0/Sxany0O01YI/AAAAAAAAASo/Jv3MiPkoLuk/s320/Before.png" alt="" id="BLOGGER_PHOTO_ID_5410696493788353922" border="0" /&gt;&lt;/a&gt;Note the empty graphs. It doesn't look very pretty now, does it? However, &lt;a href="http://anonsvn.mono-project.com/viewvc/trunk/moon/src/frameworkelement.cpp?r1=147096&amp;amp;r2=147421"&gt;one very minor fix later&lt;/a&gt; we now have the following:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nFGxSFZnUc0/SxaoCMfpTCI/AAAAAAAAAS4/iiMXzjPVS1E/s1600-h/After.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 195px;" src="http://2.bp.blogspot.com/_nFGxSFZnUc0/SxaoCMfpTCI/AAAAAAAAAS4/iiMXzjPVS1E/s320/After.png" alt="" id="BLOGGER_PHOTO_ID_5410696757999389730" border="0" /&gt;&lt;/a&gt;Things are near-perfect in all the Data Visualization demos. One graph is missing a background colour and the elements in one graph aren't clickable when they should be. Neither should be particularly difficult to fix, the only problem is figuring out the cause.&lt;br /&gt;&lt;br /&gt;Unfortunately the version of the Toolkit Demo on the live site still doesn't render perfectly, but as we already have one version near-perfect, getting a newer revision to work shouldn't be hard! Things are shaping up to give us a great 2.0 release.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6500799877581820225?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6500799877581820225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6500799877581820225' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6500799877581820225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6500799877581820225'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/12/cant-you-feel-moonlight.html' title='Can&apos;t you feel the moonlight?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_nFGxSFZnUc0/Sxany0O01YI/AAAAAAAAASo/Jv3MiPkoLuk/s72-c/Before.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-172731970001170540</id><published>2009-11-30T00:29:00.004Z</published><updated>2009-11-30T00:51:29.070Z</updated><title type='text'>What does this say?</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.bbc.co.uk/london/content/images/2007/06/04/2012_logo_white_385x450.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 385px; height: 450px;" src="http://www.bbc.co.uk/london/content/images/2007/06/04/2012_logo_white_385x450.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;This is the logo for the London olympics. &lt;a href="http://www.wolffolins.com/london2012.php"&gt;Wolff Olins&lt;/a&gt; were paid a whopping &lt;a href="http://news.bbc.co.uk/sport2/hi/other_sports/olympics_2012/6718243.stm"&gt;GBP 400,000&lt;/a&gt; to design this. You'd think that if you spent that kind of money, you'd end up with a logo that can be understood by mere mortals, however that's not what we got.&lt;br /&gt;&lt;br /&gt;When I look at that logo I see the numbers '2' and '0' at the top. That's relatively clear. However, the bottom section is just random gibberish. If I concentrate a little harder, I could convince myself that  there's a '1' in the bottom left, but what the hell is at the bottom right? A square with a squiggle beside it? I can't for the life of me figure out what that little square is for. Is it part of the '2' or is it there because they wanted to write "20-12"? I really can't tell.&lt;br /&gt;&lt;br /&gt;If you're going to design a logo for such an important worldwide event, why can't it be legible? Logo design should be about creating an inventive and visually appealing way of getting some information across. It isn't supposed to be about who can create the most obfusticated advertisement - that  defeats the entire purpose!  Out of &lt;a href="http://news.bbc.co.uk/2/hi/in_pictures/6722205.stm"&gt;this list of a dozen alternative designs&lt;/a&gt; my favourite is this one:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://newsimg.bbc.co.uk/media/images/43009000/jpg/_43009947_jason_salt220.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 220px; height: 300px;" src="http://newsimg.bbc.co.uk/media/images/43009000/jpg/_43009947_jason_salt220.jpg" alt="" border="0" /&gt;&lt;/a&gt;It may not be the best logo I've ever seen, but it does one thing right: It's instantly recognisable and understandable. I know exactly what it's about without having to spend 10 mins rotating the thing trying to make sense out of it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-172731970001170540?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/172731970001170540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=172731970001170540' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/172731970001170540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/172731970001170540'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/11/what-does-this-say.html' title='What does this say?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8465674993922277499</id><published>2009-11-19T14:32:00.002Z</published><updated>2009-11-19T14:42:39.332Z</updated><title type='text'>Dear Thierry Henry</title><content type='html'>&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/51t2segCkCk&amp;amp;hl=en_US&amp;amp;fs=1&amp;amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/51t2segCkCk&amp;amp;hl=en_US&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;If you truly were sorry that you &lt;span style="font-weight: bold;"&gt;twice&lt;/span&gt; hit the ball with your hand to prevent it from going wide and then proceeded to score from that opportunity, why didn't you admit it on the spot. You knew what you did, you did it deliberately, it is useless claiming you're sorry now when it's too late for that to mean anything. Your chance to show your true colours was on the field and show them you did.&lt;br /&gt;&lt;br /&gt;Since the match will not be replayed, why don't you step down from this world cup season if you truly do regret that decision which cost Ireland our world cup chance?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8465674993922277499?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8465674993922277499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8465674993922277499' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8465674993922277499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8465674993922277499'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/11/dear-thierry-henry.html' title='Dear Thierry Henry'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7955417613100281709</id><published>2009-11-02T10:14:00.010Z</published><updated>2009-11-02T22:24:05.126Z</updated><title type='text'>Don't BinaryReader.PeekChar () at me!</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Update:&lt;/span&gt; As pointed out in the comments, there are errors in the implementation of SeekableStream below. That's what happens when you write a class at 2am ;) The errors are that Writing to the stream will be off by 1 byte if you've peeked, and Position will be off by 1 byte if you've peeked. I'll re-read and update the class later.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update 2:&lt;/span&gt; I updated the implementation of PeekableStream to make it a read-only stream wrapper. I'm not pushed about supporting writing on it as it's not something I require. I'll leave it as an exercise to the reader to support writing if they need it ;)&lt;br /&gt;&lt;br /&gt;How many times has that urge hit you to peek inside a stream and see the secrets hidden inside? Have there been times when you wished that Stream exposed a 'public byte Peek ()" method so you could do this easily? Were you delighted when you discovered that BinaryReader exposed PeekChar () , which did nearly the same thing [0]?  If that describes you, you're in for a horrible horrible surprise.&lt;br /&gt;&lt;br /&gt;I received a bug report during the week saying that MonoTorrent reads all data twice from disk when it is hashing. I responded with "No it doesn't! That'd be crazy!", to which I was handed a screenshot of a ~310MB torrent which in the windows task manager reported ~750MB of I/O Read Bytes. I was told this happened after loading the torrent and calling HashCheck () on it. This was irrefutable evidence that something was up, but I was still unconvinced .&lt;br /&gt;&lt;br /&gt;So over the weekend I fired up windows and double checked. I could replicate the bizarre statistic. But strangely enough, it wasn't hashing that was causing it! The I/O Read Bytes was up at around 350MB before hashing even started. But that was strange, because the only thing that happened before that was:&lt;br /&gt;&lt;br /&gt;Torrent torrent = Torrent.Load (path);&lt;br /&gt;&lt;br /&gt;There's no way a simple forward-only parser reading a 100kB file could possibly result in 350MB of IO reads, could it? Actually, it could! Changing the declaration to the following completely vanished the extra 350MB:&lt;br /&gt;&lt;br /&gt;Torrent torrent = Torrent.Load (File.ReadAllBytes (path))&lt;br /&gt;&lt;br /&gt;So what was going wrong? Internally the parser used BinaryReader.PeekChar () to figure out the type of the next element so that correct decoder could be called. I thought this would be a simple array access, or something similar. However what actually happens is that one byte is read from the underlying stream, then the stream seeks 1 byte backwards . &lt;span style="font-style: italic;"&gt;In the case of FileStream, this meant that the entire read buffer was refilled from 'disk' [1] every time I peeked. &lt;/span&gt;A 100kB file really was really being turned into a 350MB monstrosity! And yes, the Mono implementation unfortunately has to do the same. So how could I fix this?&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://i198.photobucket.com/albums/aa299/yupko/ihasanidea.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 318px;" src="http://i198.photobucket.com/albums/aa299/yupko/ihasanidea.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Simples! I could write a PeekableStream, one that's smart enough to not need to do horrible buffer killing seeks. What was the end result? Well, that particular .torrent file loaded nearly 5x faster, ~100ms for everything instead of ~500ms. An average file would experience a much smaller speedup. This one is a bit different in that it contains over 2000 files and the speed up is proportional to the number of BEncoded elements in the .torrent file.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;public class PeekableStream : Stream&lt;br /&gt;{&lt;br /&gt;    bool hasPeek;&lt;br /&gt;    Stream input;&lt;br /&gt;    byte[] peeked;&lt;br /&gt;&lt;br /&gt;    public PeekableStream (Stream input)&lt;br /&gt;    {&lt;br /&gt;        this.input = input;&lt;br /&gt;        this.peeked = new byte[1];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override bool CanRead&lt;br /&gt;    {&lt;br /&gt;        get { return input.CanRead; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override bool CanSeek&lt;br /&gt;    {&lt;br /&gt;        get { return input.CanSeek; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override bool CanWrite&lt;br /&gt;    {&lt;br /&gt;        get { return false; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override void Flush()&lt;br /&gt;    {&lt;br /&gt;        throw new NotSupportedException();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override long Length&lt;br /&gt;    {&lt;br /&gt;        get { return input.Length; }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int PeekByte()&lt;br /&gt;    {&lt;br /&gt;        if (!hasPeek)&lt;br /&gt;            hasPeek = Read(peeked, 0, 1) == 1;&lt;br /&gt;        return hasPeek ? peeked[0] : -1;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override int ReadByte()&lt;br /&gt;    {&lt;br /&gt;        if (hasPeek)&lt;br /&gt;        {&lt;br /&gt;            hasPeek = false;&lt;br /&gt;            return peeked[0];&lt;br /&gt;        }&lt;br /&gt;        return base.ReadByte();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override long Position&lt;br /&gt;    {&lt;br /&gt;        get&lt;br /&gt;        {&lt;br /&gt;            if (hasPeek)&lt;br /&gt;                return input.Position - 1;&lt;br /&gt;            return input.Position;&lt;br /&gt;        }&lt;br /&gt;        set&lt;br /&gt;        {&lt;br /&gt;            if (value != Position)&lt;br /&gt;            {&lt;br /&gt;                hasPeek = false;&lt;br /&gt;                input.Position = value;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override int Read(byte[] buffer, int offset, int count)&lt;br /&gt;    {&lt;br /&gt;        int read = 0;&lt;br /&gt;        if (hasPeek &amp;amp;&amp;amp; count &amp;gt; 0)&lt;br /&gt;        {&lt;br /&gt;            hasPeek = false;&lt;br /&gt;            buffer[offset] = peeked[0];&lt;br /&gt;            offset++;&lt;br /&gt;            count--;&lt;br /&gt;            read++;&lt;br /&gt;        }&lt;br /&gt;        read += input.Read(buffer, offset, count);&lt;br /&gt;        return read;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override long Seek(long offset, SeekOrigin origin)&lt;br /&gt;    {&lt;br /&gt;        long val;&lt;br /&gt;        if (hasPeek &amp;amp;&amp;amp; origin == SeekOrigin.Current)&lt;br /&gt;            val = input.Seek(offset - 1, origin);&lt;br /&gt;        else&lt;br /&gt;            val = input.Seek(offset, origin);&lt;br /&gt;        hasPeek = false;&lt;br /&gt;        return val;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override void SetLength(long value)&lt;br /&gt;    {&lt;br /&gt;        throw new NotSupportedException();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public override void Write(byte[] buffer, int offset, int count)&lt;br /&gt;    {&lt;br /&gt;        throw new NotSupportedException();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This code is under the MIT/X11 license, so everywhere you use PeekChar () and actually just want to Peek at a byte, use this class instead. Your harddrives will love you for it. If you actually want to peek at a char,  extend this class to be able to read a (multi-byte) char from the underlying stream and cache it locally just like the current PeekByte method . A side benefit is that you can now peek at unseekable streams. Not too bad, eh?&lt;br /&gt;&lt;br /&gt;[0] PeekChar does exactly what it says on the tin. It reads one (multi-byte) character from the stream. So if you're using PeerChar on a binary stream which does not contain valid data as defined by the current Encoding, you're going to corrupt some data or get exceptions. I mention this here in case anyone is using PeekChar () as a way of reading bytes from the stream.&lt;br /&gt;&lt;br /&gt;[1] I say that the FileStream buffer was being filled from disk, but that's not quite accurate. It was actually being refilled from either the windows cache or the harddrives cache. It's physically impossible for a mere 7200 RPM harddrive to supply data that fast. However I still was  physically copying 350MB of data around in memory so that was a huge penalty right there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7955417613100281709?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7955417613100281709/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7955417613100281709' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7955417613100281709'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7955417613100281709'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/11/dont-binaryreaderpeekchar-at-me.html' title='Don&apos;t BinaryReader.PeekChar () at me!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5981676318694049622</id><published>2009-10-18T19:22:00.006+01:00</published><updated>2009-10-19T11:40:04.384+01:00</updated><title type='text'>MonoTorrent 0.80 - Up up and away</title><content type='html'>MonoTorrent 0.80 has been released. I'd like to say "It's the best release ever", but that always makes me think "If it wasn't the best release ever, why would I release it?"&lt;br /&gt;&lt;br /&gt;The full release notes can be read on &lt;a href="http://projects.qnetp.net/projects/monotorrent/news"&gt;www.monotorrent.com.&lt;/a&gt; For the lazy, I'll put a quick blurb about the two new most exciting new features available:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Metadata Exchange&lt;br /&gt;&lt;/span&gt;&lt;a class="external" href="http://www.bittorrent.org/beps/bep_0009.html"&gt;http://www.bittorrent.org/beps/bep_0009.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Put simply, this means you can click on a link like this: magnet:?xt=urn:btih:12345678901234567890 and then the torrent will magically [0] be able to download. Behind the scenes what happens is that peers are found via DHT and then they are queried for the .torrent metadata. Once the metadata has been obtained, the actual downloading can commence and away you go. Finally, I can start a download via text message!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Local Peer Discovery&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This allows MonoTorrent to find other peers who are downloading the same torrent on the local network. A simple UDP broadcast message is used for discovery. This is an implementation of  style LDP and so is fully compatible with uTorrent and other clients which have implemented this style. The main benefit of this is that in corporate or educational environments, it's possible that many people will be trying to access the same torrent at the same time. This approach allows all these peers to connect to each other and thus transfer the bulk of their data over the internal LAN rather than all of them fighting for bandwidth on the (usually) limited WAN connection.&lt;br /&gt;&lt;br /&gt;As per usual, there are a bunch of bug fixes and enhancements.  This is one more milestone on the way to the final 1.0 release. One which I'm really looking forward to. I might even do some nostalgia posts about the big disasters I created while learning C# and implementing this library ;)&lt;br /&gt;&lt;br /&gt;[0] Actual product does not contain magic.&lt;br /&gt;&lt;br /&gt;EDIT: Just clarified that the LPD implementation is the uTorrent style.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5981676318694049622?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5981676318694049622/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5981676318694049622' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5981676318694049622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5981676318694049622'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/10/monotorrent-080-up-up-and-away.html' title='MonoTorrent 0.80 - Up up and away'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1155520668978050146</id><published>2009-06-29T23:05:00.002+01:00</published><updated>2009-06-29T23:20:25.084+01:00</updated><title type='text'>Mono.Nat 1.0.2</title><content type='html'>I just tagged and released &lt;a href="http://projects.qnetp.net/news/show/4"&gt;Mono.Nat 1.0.2&lt;/a&gt; . It's a fairly minor bugfix release which addresses a number of minor issues:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Added workaround for certain versions of miniupnpd which incorrectly advertise their available services (bug has been reported upstream)&lt;/li&gt;&lt;li&gt;Fixed some other minor issues with routers reporting incorrect services.&lt;/li&gt;&lt;li&gt;Added extra API to make it easy to log the full handshake/request process to help diagnose issues&lt;/li&gt;&lt;li&gt;Stopping and Starting discovery will rediscover all available devices correctly&lt;/li&gt;&lt;li&gt;Full support for computers with multiple network cards on multiple subnets&lt;/li&gt;&lt;li&gt;Rewrote the internals to ensure that the asynchronous API is 100% asychronous - prevents calls to BeginXXX blocking on some slower routers.&lt;/li&gt;&lt;/ul&gt; Precompiled binaries and sourcecode can be downloaded &lt;a href="http://projects.qnetp.net/projects/list_files/mono-nat"&gt;here&lt;/a&gt; and packages will soon be winding their way to a repository near you.&lt;br /&gt;&lt;br /&gt;If you want to forward ports automagically on a upnp empowered router near you, this is the library for you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1155520668978050146?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1155520668978050146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1155520668978050146' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1155520668978050146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1155520668978050146'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/06/mononat-102.html' title='Mono.Nat 1.0.2'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5356592019409427951</id><published>2009-05-28T02:18:00.003+01:00</published><updated>2009-05-28T02:30:16.165+01:00</updated><title type='text'>Monsoon - blowing down barriers</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nFGxSFZnUc0/Sh3m5QCF-lI/AAAAAAAAAPQ/05Z8rRiDdCs/s1600-h/Screenshot-Add-in+Manager.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 262px;" src="http://2.bp.blogspot.com/_nFGxSFZnUc0/Sh3m5QCF-lI/AAAAAAAAAPQ/05Z8rRiDdCs/s320/Screenshot-Add-in+Manager.png" alt="" id="BLOGGER_PHOTO_ID_5340678604361955922" border="0" /&gt;&lt;/a&gt;Yes, Monsoon is now using Mono.Addins for some delicious plugability. Support for this has only just been added, so there is a severe lack of extension points defined in monsoon, but those can be added as time goes on. Right now there is one extension point. I'm sure you've already guessed what it is.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nFGxSFZnUc0/Sh3myD_qo3I/AAAAAAAAAPI/8U11JN6Crfc/s1600-h/Screenshot-Monsoon-1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 202px;" src="http://2.bp.blogspot.com/_nFGxSFZnUc0/Sh3myD_qo3I/AAAAAAAAAPI/8U11JN6Crfc/s320/Screenshot-Monsoon-1.png" alt="" id="BLOGGER_PHOTO_ID_5340678480871465842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Yup, that's right. That little nifty thing at the bottom is DHT bootstrapping itself. As you can see, it's currently displaying a rather disappointing value of '1' for 'Nodes'. This is either because the bootstrap node is currently unavailable, or my router is playing silly buggers again and not forwarding my ports right. Luckily you can also bootstrap into DHT by just downloading a normal torrent. Other peers advertise when they support DHT and provide the required info to allow you to use them as a bootstrap node.&lt;br /&gt;&lt;br /&gt;What this means is that opensuse users will finally have easy access to a DHT enabled torrent client without having to enable additional repositories. Things will work right out of box... well, it'll work as soon as you click to enable the addin which fetches it from the monsoon website ;)&lt;br /&gt;&lt;br /&gt;Once I solidify everything there'll be a preview release of Monsoon with these features and another slightly big one I've been working on. More on that later. I've reached my word quota for this post ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5356592019409427951?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5356592019409427951/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5356592019409427951' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5356592019409427951'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5356592019409427951'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/05/monsoon-blowing-down-barriers.html' title='Monsoon - blowing down barriers'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_nFGxSFZnUc0/Sh3m5QCF-lI/AAAAAAAAAPQ/05Z8rRiDdCs/s72-c/Screenshot-Add-in+Manager.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4732427896377864663</id><published>2009-05-18T19:19:00.003+01:00</published><updated>2009-05-18T21:16:30.763+01:00</updated><title type='text'>Book memes - Reloaded</title><content type='html'>* Grab the nearest book.&lt;br /&gt;* Open it to page 56.&lt;br /&gt;* Find the fifth sentence.&lt;br /&gt;* Post the text of the sentence in your journal along with these instructions.&lt;br /&gt;* Don't dig for your favorite book, the cool book, or the intellectual one: pick the CLOSEST.&lt;br /&gt;&lt;br /&gt;"Ponder and Ridcully waited for a few moments, but the city stayed full of normal noise, like the collapse of masonry and distant screams"&lt;br /&gt;&lt;br /&gt;Not a bad quote, eh? And yes, I am still a kid at heart :p&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4732427896377864663?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4732427896377864663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4732427896377864663' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4732427896377864663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4732427896377864663'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/05/book-memes-reloaded.html' title='Book memes - Reloaded'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8569078883229477854</id><published>2009-05-17T22:09:00.003+01:00</published><updated>2009-05-17T22:19:28.112+01:00</updated><title type='text'>Polymorphism, why do you fail me?</title><content type='html'>Polymorphism, it's the cornerstone of object oriented programming. We couldn't live without it. So then, tell me why this rather trivial case fails to compile.&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;public class B : A { }&lt;br /&gt;&lt;br /&gt;public void Foo (ref A bar) {  }&lt;br /&gt;&lt;br /&gt;public void Baz ()&lt;br /&gt;{&lt;br /&gt;    B b = new B ();&lt;br /&gt;    Foo (ref b);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The issue is hat you can't pass a 'B' parameter type by ref where a 'ref A' is expected. Why is this? What case could possibly fail if this was allowed?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8569078883229477854?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8569078883229477854/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8569078883229477854' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8569078883229477854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8569078883229477854'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/05/polymorphism-why-do-you-fail-me.html' title='Polymorphism, why do you fail me?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3477653365325982698</id><published>2009-05-15T14:09:00.005+01:00</published><updated>2009-05-15T18:00:21.878+01:00</updated><title type='text'>So what've I been working on?</title><content type='html'>I haven't actually blogged much about my job. That was getting kind of weird, so I thought it was high time I wrote something. Then I thought to myself "A picture is worth a thousand words, so why bother wasting my time with words". So here are two pictures which show what I've been doing over the last 5 days. Though technically i've been working on parts of this over the last 2-3 weeks ;)&lt;br /&gt;&lt;br /&gt;Moonlight - Monday 11th May 2009:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_nFGxSFZnUc0/Sg1qIcIQcEI/AAAAAAAAAO4/5rpilXgYMbQ/s1600-h/Monday.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://1.bp.blogspot.com/_nFGxSFZnUc0/Sg1qIcIQcEI/AAAAAAAAAO4/5rpilXgYMbQ/s320/Monday.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5336037826726228034" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Moonlight - Friday 15th May 2009 (with local patches):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_nFGxSFZnUc0/Sg1qcSTD8aI/AAAAAAAAAPA/lc_1grUA_1Y/s1600-h/Controls.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://4.bp.blogspot.com/_nFGxSFZnUc0/Sg1qcSTD8aI/AAAAAAAAAPA/lc_1grUA_1Y/s320/Controls.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5336038167684575650" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Pretty sweet, eh?&lt;br /&gt;&lt;br /&gt;UPDATE: If you want to look at the actual site, it's available here: &lt;a href="http://silverlight.net/samples/sl2/toolkitcontrolsamples/run/default.html"&gt;http://silverlight.net/samples/sl2/toolkitcontrolsamples/run/default.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Hopefully our next alpha preview will contain the necessary fixes to load the site up so you can see it in all its glory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3477653365325982698?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3477653365325982698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3477653365325982698' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3477653365325982698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3477653365325982698'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/05/so-whatve-i-been-working-on.html' title='So what&apos;ve I been working on?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_nFGxSFZnUc0/Sg1qIcIQcEI/AAAAAAAAAO4/5rpilXgYMbQ/s72-c/Monday.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8363847572993100259</id><published>2009-04-20T22:55:00.002+01:00</published><updated>2009-04-20T22:58:41.216+01:00</updated><title type='text'>Monsoon 0.21</title><content type='html'>Monsoon 0.21 has been released. It now uses &lt;a href="http://monotorrent.blogspot.com/2009/04/monotorrent-072-released.html"&gt;MonoTorrent 0.72&lt;/a&gt;. Other than bumping to a newer version of monotorrent, there were only a few minor fixes for Monsoon 0.21. The most notable of which is a fix for a change in firefoxs behaviour when opening files directly from the browser.&lt;br /&gt;&lt;br /&gt;The update should come to a repository near you soon!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8363847572993100259?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8363847572993100259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8363847572993100259' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8363847572993100259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8363847572993100259'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/04/monsoon-021.html' title='Monsoon 0.21'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8967407082033578093</id><published>2009-04-16T22:30:00.003+01:00</published><updated>2009-04-16T22:36:03.577+01:00</updated><title type='text'>nat-pmp support in Mono.Nat</title><content type='html'>Is there anyone out there with a week or so to complete the implementation of nat-pmp support in &lt;a href="http://projects.qnetp.net/projects/show/mono-nat"&gt;Mono.Nat&lt;/a&gt; that'd be awesome. The implementation is 80-90% complete. The only requirement is that you have a router supporting nat-pmp with which to test against. Unfortunately I don't have one and I can't get the daemon working on my router.&lt;br /&gt;&lt;br /&gt;If you want to work on it, send me an email and I'll fill you in on what needs to be done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8967407082033578093?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8967407082033578093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8967407082033578093' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8967407082033578093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8967407082033578093'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/04/nat-pmp-support-in-mononat.html' title='nat-pmp support in Mono.Nat'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-275535139904524124</id><published>2009-04-06T11:56:00.002+01:00</published><updated>2009-04-06T11:58:58.919+01:00</updated><title type='text'>MonoTorrent 0.72 released</title><content type='html'>This is a bugfix release to address a few reported issues and also a few issues that were discovered via my own testing.&lt;br /&gt;&lt;br /&gt;* Add a helper method which ensures all data is flushed to disk&lt;br /&gt;* Added additional error handling to prevent malformed DHT messages crashing the library&lt;br /&gt;* Fixed issue when zeroing unused bits for torrents with an exact multiple of 32 pieces&lt;br /&gt;* Fixed issue where data could be written to the wrong file if a file with the same name existed in multiple torrents&lt;br /&gt;* Fixed the handling of torrents where the last file(s) are of zero length&lt;br /&gt;* Fixed regression with global download rate limiting&lt;br /&gt;* Fixed a performance regression with the new piece picking pipeline which resulted in lots of CPU cycles being used up on peers which have not sent an unchoke message&lt;br /&gt;&lt;br /&gt;In other news, monotorrent.com is changing its hosting provider. It still brings you to the old website, but it'll be moved to http://projects.qnetp.net/projects/show/monotorrent soon enough. This is were future releases will be made.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-275535139904524124?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/275535139904524124/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=275535139904524124' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/275535139904524124'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/275535139904524124'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/04/monotorrent-072-released.html' title='MonoTorrent 0.72 released'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-946034758382852930</id><published>2009-03-31T10:10:00.002+01:00</published><updated>2009-03-31T10:19:42.861+01:00</updated><title type='text'>Google Summer of Code - 2009</title><content type='html'>The final deadline for applications for the &lt;a href="http://socghop.appspot.com/"&gt;gsoc&lt;/a&gt; is fast approaching. You now have 4 days left, right up until &lt;a href="http://www.timeanddate.com/worldclock/fixedtime.html?month=4&amp;day=3&amp;year=2009&amp;hour=19&amp;min=0&amp;sec=0&amp;p1=0"&gt;19:00 UTC on April 3rd&lt;/a&gt;. There are &lt;a href="http://www.mono-project.com/StudentProjects"&gt;so many cool projects&lt;/a&gt; available this year, so make sure you apply soon before they're all taken!&lt;br /&gt;&lt;br /&gt;Some of my personal favourites would be:&lt;br /&gt;&lt;a href="http://www.mono-project.com/StudentProjects#Use_Mono.Simd_in_the_classlibs"&gt;Writing SIMD code in C#&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.mono-project.com/StudentProjects#VDPAU_VC-1.2FH.264_Support"&gt;VDPAU VC-1/H.264 Support&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.mono-project.com/StudentProjects#Silverlight_3_Demuxers"&gt;Codec Demuxers&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.mono-project.com/StudentProjects#Dirac_Support"&gt;A Dirac decoder&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And many more! Funnily enough, video encoding is what got me interested in programming in the beginning, so if you students don't snap up those projects, I will ;) Hack on something cool for the summer and earn yourself some cash while you're at it. It'll be great!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-946034758382852930?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/946034758382852930/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=946034758382852930' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/946034758382852930'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/946034758382852930'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/03/google-summer-of-code-2009.html' title='Google Summer of Code - 2009'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3553631555505890433</id><published>2009-03-22T23:12:00.004Z</published><updated>2009-03-23T00:07:08.538Z</updated><title type='text'>We got the grand slam!</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.irishtimes.com/newspaper/breaking/2009/0322/breaking5.htm"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 600px; height: 406px;" src="http://www.irishtimes.com/newspaper/breaking/images/2009/0322/226769_1.jpg?ts=1237763215" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We've done it! We've won the &lt;a href="http://en.wikipedia.org/wiki/Grand_Slam_(rugby_union)"&gt;Grand Slam&lt;/a&gt; for the first time in 61 years. It was the most nail-biting match I've watched in a while. It was so close that the result was actually down to the very last kick of the match, which was a penalty awarded* against Ireland. Thankfully Wales missed it and sealed Irelands victory.&lt;br /&gt;&lt;br /&gt;Go on the green!&lt;br /&gt;&lt;br /&gt;* The ref made a bad call here and awarded a penalty against Ireland because the ball bounced forward after it was dropped. The rule are pretty clear that this should be a scrum and *not* a penalty. It wasn't the first time he made that mistake either. Still, it didn't matter in the end, woo!&lt;br /&gt;&lt;br /&gt;Update: A friend just told me that the ref originally signalled a scrum but then changed it to a penalty. Sounds like an irish guy mouthed off or did something off the ball. Did anyone notice what actually made him change his mind?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3553631555505890433?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3553631555505890433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3553631555505890433' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3553631555505890433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3553631555505890433'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/03/we-got-grand-slam.html' title='We got the grand slam!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4537160676862472201</id><published>2009-03-20T01:39:00.003Z</published><updated>2009-03-20T01:41:45.343Z</updated><title type='text'>MonoTorrent 0.70</title><content type='html'>With all the excitement going on, I forgot to blog about MonoTorrent 0.70, so here's a belated summary of what went on:&lt;br /&gt;&lt;br /&gt;* Fixed an issue for torrents with no trackers&lt;br /&gt;* Optimised the Bitfield class to allow for higher performance piece picking&lt;br /&gt;* Rewrote the piece picking API which resulted in a very extensible, easily testable, less buggy and faster implementation&lt;br /&gt;* Fixed an issue where the announce wouldn't happen immediately after a torrent completes&lt;br /&gt;* Fixed several issues with webseeding and rate limiting&lt;br /&gt;* Fixed an issue which resulted in an incorrect encryption level been chosen in a small proportion of cases&lt;br /&gt;* Ensure that announces to trackers always time out correctly&lt;br /&gt;* Fixed a race condition when stopping a torrent while an incoming connection is being processed&lt;br /&gt;* Increased the performance of disk IO so that hashing a torrent is about 10% faster&lt;br /&gt;* Vastly improved performance of the BanList parser&lt;br /&gt;* Fixed issue where fast pieces could be requested multiple times&lt;br /&gt;* Fixed issue with selective downloading where the start/end indices of files could be offset by 1&lt;br /&gt;&lt;br /&gt;A precompiled binary can be found &lt;a href="http://monotorrent.com/Files/0.70/monotorrent-0.70-bin.zip"&gt;here&lt;/a&gt; and the source tarball can be found &lt;a href="http://monotorrent.com/Files/0.70/monotorrent-0.70.tar.gz"&gt;here&lt;/a&gt;. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4537160676862472201?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4537160676862472201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4537160676862472201' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4537160676862472201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4537160676862472201'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/03/monotorrent-070.html' title='MonoTorrent 0.70'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1039705114575546368</id><published>2009-03-15T18:25:00.003Z</published><updated>2009-03-15T18:48:13.975Z</updated><title type='text'>What does it take to download a torrent?</title><content type='html'>The simple answer to this question is "All you need is the .torrent file. Stupid n00bs." This is true. The .torrent file contains all the metadata required to A) download the data and B) verify the data. Without this, you can't really do anything. So, do you actually need the .torrent file to begin a download?&lt;br /&gt;&lt;br /&gt;No! All you need is the infohash for the .torrent file! The infohash is the SHA1 hash of the metadata in a .torrent file. As such, it can be used as a unique identifier for a particular .torrent file. With this infohash, you can query the &lt;a href="http://en.wikipedia.org/wiki/Distributed_hash_table"&gt;BitTorrent DHT&lt;/a&gt; for a list of peers downloading that torrent. Then, with the help of the &lt;a href="http://www.bittorrent.org/beps/bep_0009.html"&gt;Metadata Exchange&lt;/a&gt; extension, you can connect to these peers and request that they send you the metadata from the .torrent file and you're away and downloading. Great!&lt;br /&gt;&lt;br /&gt;"But what if some malicious peer sends you corrupt metadata, then you'd never be able to download the torrent properly!", I hear you asking. Well, in a rather beautiful twist, this is next to impossible. As I said earlier, the infohash is generated by putting the metadata in the .torrent file through a SHA1 hash. So all you have to do is hash the metadata once you have received it and then compare the result of that to the SHA1 hash you used to start the download. If they match, then you can be fairly confident that the metadata has not been corrupted/altered in any way.&lt;br /&gt;&lt;br /&gt;As of 17:00GMT, March 15th MonoTorrent has completed its first download using only a 20 byte hash to begin the download. This is possible because of some tireless work by Olivier Dufour, who also implemented &lt;a href="http://en.wikipedia.org/wiki/Peer_exchange"&gt;Peer Exchange&lt;/a&gt;, a good few parts of DHT, &lt;a href="http://www.bittorrent.org/beps/bep_0019.html"&gt;WebSeeding &lt;/a&gt;and &lt;a href="http://www.bittorrent.org/beps/bep_0016.html"&gt;SuperSeeding&lt;/a&gt;. The code for this still hasn't quite hit SVN, a bit of refactoring remains to be done. It should be in SVN within a week. I'm looking forward to his next patch of awesomenesss now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1039705114575546368?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1039705114575546368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1039705114575546368' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1039705114575546368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1039705114575546368'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/03/what-does-it-take-to-download-torrent.html' title='What does it take to download a torrent?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4918732805394148548</id><published>2009-03-02T10:05:00.002Z</published><updated>2009-03-02T10:09:43.181Z</updated><title type='text'>The monsoon-devel list</title><content type='html'>I've started a mailing list for monsoon so that packagers can be kept updated and various interested people can post questions and all that jazz. Basically it's the standard -devel mailing list. That's mostly why it's called "monsoon-devel" ;)&lt;br /&gt;&lt;br /&gt;If you're interested in keeping up to date on Monsoon, please join the group at: http://groups.google.com/group/monsoon-devel&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4918732805394148548?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4918732805394148548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4918732805394148548' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4918732805394148548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4918732805394148548'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/03/monsoon-devel-list.html' title='The monsoon-devel list'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2939258326032198622</id><published>2009-02-27T10:00:00.004Z</published><updated>2009-02-27T10:21:05.458Z</updated><title type='text'>Banshee - For all your  medical record needs?</title><content type='html'>I'm sure everyone's familiar with Banshee, the most awesome media player of all time ever ;) So, how does it feel to know it has expanded into the medical record field? Good eh? Next time you go to your doctor do you know what they'll be running? That's right, Banshee - Medical Record Edition. Check out its website here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gnumed.org/index.html"&gt;http://gnumed.org/index.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The regular banshee website can be found here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://banshee-project.org/"&gt;http://banshee-project.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's fun to open both in tabs and switch between them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2939258326032198622?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2939258326032198622/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2939258326032198622' title='28 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2939258326032198622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2939258326032198622'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/02/banshee-for-all-your-medical-record.html' title='Banshee - For all your  medical record needs?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>28</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8803428509946826899</id><published>2009-02-19T22:51:00.003Z</published><updated>2009-02-19T22:52:57.779Z</updated><title type='text'>Monsoon 0.20</title><content type='html'>Monsoon 0.20 has been released and should be in a repository near you soon. Release notes can be found &lt;a href="http://www.monsoon-project.org/jaws/index.php?page/releasenotes0.20"&gt;here&lt;/a&gt;. Fun stuff!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8803428509946826899?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8803428509946826899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8803428509946826899' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8803428509946826899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8803428509946826899'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/02/monsoon-020.html' title='Monsoon 0.20'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-929284125757990270</id><published>2009-01-24T13:49:00.002Z</published><updated>2009-01-24T13:56:17.535Z</updated><title type='text'>Excitement abounds!</title><content type='html'>Next weekend is going to be an interesting one. Several releases are coinciding:&lt;br /&gt;&lt;br /&gt;1) MonoTorrent 0.70 is prepped and ready for release. Contains numerous bugfixes and performance enhancements.&lt;br /&gt;2) Monsoon 0.20 will in a repository near you. This will be using MonoTorrent 0.70, so a nice upgrade from the previous 0.40 release.&lt;br /&gt;3) The DBus daemon for monotorrent is ready for its first release. Banshee will be using this to add support for torrent based podcasts.&lt;br /&gt;4) Mono.Nat will be getting it's first official package release too. It's a reasonably mature library which supports UPnP port forwarding/mapping. There's also support for nat-pmp in the library, but it's disabled due to lack of testing. If someone has a nat-pmp capable router, feel free to test the code, fix the few remaining issues and submit a patch.&lt;br /&gt;&lt;br /&gt;But for now, it's holiday time for a week ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-929284125757990270?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/929284125757990270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=929284125757990270' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/929284125757990270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/929284125757990270'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2009/01/excitement-abounds.html' title='Excitement abounds!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7274903126396659098</id><published>2008-12-22T01:47:00.003Z</published><updated>2008-12-22T01:53:18.071Z</updated><title type='text'>A workaround!</title><content type='html'>So there exists *one* way and one way only to safely set the Range header when using 64bit indices. This method was found with the help of NotZhila on #mono:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #000000;"&gt;MethodInfo method &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;typeof&lt;/span&gt;&lt;span style="color: #000000;"&gt;(WebHeaderCollection).GetMethod&lt;br /&gt;                        (&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #800000;"&gt;AddWithoutValidate&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #000000;"&gt;, BindingFlags.Instance &lt;/span&gt;&lt;span style="color: #000000;"&gt;|&lt;/span&gt;&lt;span style="color: #000000;"&gt; BindingFlags.NonPublic);&lt;br /&gt;&lt;br /&gt;HttpWebRequest request &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; (HttpWebRequest) WebRequest.Create (&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #800000;"&gt;http://www.example.com/file.exe&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;long&lt;/span&gt;&lt;span style="color: #000000;"&gt; start &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; int32.MaxValue;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;long&lt;/span&gt;&lt;span style="color: #000000;"&gt; end &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; int32.MaxValue &lt;/span&gt;&lt;span style="color: #000000;"&gt;+&lt;/span&gt;&lt;span style="color: #000000;"&gt;  &lt;/span&gt;&lt;span style="color: #800080;"&gt;100000&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt; key &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #800000;"&gt;Range&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #000000;"&gt;;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt; val &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;string&lt;/span&gt;&lt;span style="color: #000000;"&gt;.Format (&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #800000;"&gt;bytes={0}-{1}&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #000000;"&gt;, start, end);&lt;br /&gt;&lt;br /&gt;method.Invoke (request.Headers, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;object&lt;/span&gt;&lt;span style="color: #000000;"&gt;[] { key, val });&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You need to call the protected method of the WebHeaderCollection class so you can add the "Range" header without being forced to go through the broken HttpWebRequestAddRange method. I tried about a half dozen more legitimate methods, but the API is so locked down that it was impossible.&lt;br /&gt;&lt;br /&gt;For example, HttpWebRequest.Headers is a get/set property, but if you set a new collection, it checks to make sure "Range" isn't there, and then just copies the keys from your new collection into it's internal one and ignores the collection you just gave it. What this means is that:&lt;br /&gt;&lt;br /&gt;CustomCollection c = new CustomCollection ();&lt;br /&gt;request.Headers = c;&lt;br /&gt;Console.WriteLine (request.Headers == c);&lt;br /&gt;&lt;br /&gt;prints false.&lt;br /&gt;&lt;br /&gt;Ouch.&lt;br /&gt;&lt;br /&gt;Anyway, the hack works. If you need 64bit indices when downloading files via HttpWebRequest, here's your guaranteed working hack, on both Mono and MS.NET.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7274903126396659098?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7274903126396659098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7274903126396659098' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7274903126396659098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7274903126396659098'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/12/workaround.html' title='A workaround!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1161598216259746519</id><published>2008-12-20T23:22:00.002Z</published><updated>2008-12-20T23:29:09.105Z</updated><title type='text'>System.Net.WebRequest -&gt; System.Net.WebSuck</title><content type='html'>I can see that throughout the lifetime of the .NET framework, no-one realised that files greater than 2GB exist on the web.&lt;br /&gt;&lt;br /&gt;System.Net.WebRequest.AddRange (int startOffset, int endOffset)&lt;br /&gt;&lt;br /&gt;Thankfully I don't have to rewrite my own class from scratch, I can &lt;a href="http://anonsvn.mono-project.com/viewvc/trunk/mcs/class/System/System.Net/HttpWebRequest.cs?content-type=text%2Fplain&amp;view=co"&gt;cannibalise one from Mono&lt;/a&gt; and add in a workaround, though that's likely to be a pain in the ass too :(&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1161598216259746519?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1161598216259746519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1161598216259746519' title='38 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1161598216259746519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1161598216259746519'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/12/systemnetwebrequest-systemnetwebsuck.html' title='System.Net.WebRequest -&gt; System.Net.WebSuck'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>38</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5721450179759124840</id><published>2008-12-17T11:53:00.004Z</published><updated>2008-12-17T15:17:32.485Z</updated><title type='text'>PiecePicking and runtime method overriding</title><content type='html'>Deciding which chunk of data to download next is a pretty complex task in the bittorrent world. You have to take into account a number of factors, such as:&lt;br /&gt;1) You may want to prefer to download rare pieces first, maybe not.&lt;br /&gt;2) You may want to pick a piece randomly, or maybe you want to download from the start of the file and get pieces in order&lt;br /&gt;3) The user may set a higher priority on a certain file, so you should prefer its pieces.&lt;br /&gt;4) The user may want to ignore certain files and never select their pieces.&lt;br /&gt;&lt;br /&gt;Then imagine trying to NUnit test all the combinations and you'll find that it becomes very difficult to ensure the implementation is correct and impossible to extend with new behavior because the interactions become too complex.&lt;br /&gt;&lt;br /&gt;The basic premise I had when redesigning this area of code was this: When I want to change the picking behaviour, I want to override one single method and then just slot the picker into the pipeline. So here's a reduced version of the PiecePicker class (3 methods instead of 10) and an example of how it's implemented.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;abstract&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; PiecePicker&lt;br /&gt;{&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;protected&lt;/span&gt;&lt;span style="color: #000000;"&gt; PiecePicker(PiecePicker picker)&lt;br /&gt;  {&lt;br /&gt;      &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;.picker &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; picker;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; CheckOverriden()&lt;br /&gt;  {&lt;br /&gt;      &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; (picker &lt;/span&gt;&lt;span style="color: #000000;"&gt;==&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;)&lt;br /&gt;          &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;throw&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; InvalidOperationException(&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #800000;"&gt;This method must be overridden&lt;/span&gt;&lt;span style="color: #800000;"&gt;&amp;quot;&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;virtual&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; Initialise(BitField bitfield, TorrentFile[] files, IEnumerable&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;lt;Piece&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;gt; requests)&lt;br /&gt;  {&lt;br /&gt;      CheckOverriden();&lt;br /&gt;      picker.Initialise(bitfield, files, requests);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;virtual&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; IsInteresting(BitField bitfield)&lt;br /&gt;  {&lt;br /&gt;      CheckOverriden();&lt;br /&gt;      &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; picker.IsInteresting(bitfield);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;virtual&lt;/span&gt;&lt;span style="color: #000000;"&gt; MessageBundle PickPiece(PeerId id, BitField peerBitfield, List&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;lt;PeerId&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: #000000;"&gt;gt; otherPeers, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; startIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; endIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; count)&lt;br /&gt;  {&lt;br /&gt;      CheckOverriden();&lt;br /&gt;      &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; picker.PickPiece(id, peerBitfield, otherPeers, startIndex, endIndex, count);&lt;br /&gt;  }&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; RandomisedPicker : PiecePicker&lt;br /&gt;    {&lt;br /&gt;        Random random &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; Random();&lt;br /&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; RandomisedPicker(PiecePicker picker)&lt;br /&gt;            :&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;(picker)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;override&lt;/span&gt;&lt;span style="color: #000000;"&gt; MessageBundle PickPiece(PeerId id, BitField peerBitfield, List&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;PeerId&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; otherPeers, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; startIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; endIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; count)&lt;br /&gt;        {&lt;br /&gt;            MessageBundle message;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; midpoint &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; random.Next(startIndex, endIndex);&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;if&lt;/span&gt;&lt;span style="color: #000000;"&gt; ((message &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.PickPiece(id, peerBitfield, otherPeers, midpoint, endIndex, count)) &lt;/span&gt;&lt;span style="color: #000000;"&gt;!=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;)&lt;br /&gt;                &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; message;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;else&lt;/span&gt;&lt;span style="color: #000000;"&gt;&lt;br /&gt;                &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.PickPiece(id, peerBitfield, otherPeers, startIndex, midPoint, count);&lt;br /&gt;        }&lt;br /&gt;    }&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here's a usage example:&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; StandardPicker provides an implementation for every method in PiecePicker&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;PiecePicker p &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; RandomisedPicker(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; StandardPicker);&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So what happens is this:&lt;br /&gt;1) p.PickPiece is called which results in RandomisedPicker.PickPiece being called&lt;br /&gt;2) This splits the range between startIndex/endIndex into two and then makes two calls to base.PickPiece.&lt;br /&gt;3) base.PickPiece will call standardPicker.PickPiece (because the 'picker' is non-null)&lt;br /&gt;4) standardPicker.PickPiece will see if there are any pieces to request, and choose the first available one&lt;br /&gt;&lt;br /&gt;Suppose I wanted to be able to ignore all the pieces which I already own, well, that's easy, I just use an IgnoringPicker (not a great name I'll admit):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #000000;"&gt;    &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;class&lt;/span&gt;&lt;span style="color: #000000;"&gt; IgnoringPicker : PiecePicker&lt;br /&gt;    {&lt;br /&gt;        BitField bitfield;&lt;br /&gt;        BitField temp;&lt;br /&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; IgnoringPicker(BitField bitfield, PiecePicker picker)&lt;br /&gt;            : &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;(picker)&lt;br /&gt;        {&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;.bitfield &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; bitfield;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;.temp &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; BitField(bitfield.Length);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;override&lt;/span&gt;&lt;span style="color: #000000;"&gt; MessageBundle PickPiece(PeerId id, BitField peerBitfield, List&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;PeerId&lt;/span&gt;&lt;span style="color: #000000;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt; otherPeers, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; startIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; endIndex, &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;int&lt;/span&gt;&lt;span style="color: #000000;"&gt; count)&lt;br /&gt;        {&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #008000;"&gt;//&lt;/span&gt;&lt;span style="color: #008000;"&gt; In binary operations: temp = peerBitfield &amp;amp; (~bitfield);&lt;/span&gt;&lt;span style="color: #008000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #000000;"&gt;            temp.SetAll(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;false&lt;/span&gt;&lt;span style="color: #000000;"&gt;).Or(peerBitfield).NAnd(bitfield);&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.PickPiece(id, temp, otherPeers, startIndex, endIndex, count);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;public&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;override&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;bool&lt;/span&gt;&lt;span style="color: #000000;"&gt; IsInteresting(BitField bitfield)&lt;br /&gt;        {&lt;br /&gt;            temp.SetAll(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;false&lt;/span&gt;&lt;span style="color: #000000;"&gt;).Or(bitfield).NAnd(&lt;/span&gt;&lt;span style="color: #0000FF;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;.bitfield);&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;return&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;base&lt;/span&gt;&lt;span style="color: #000000;"&gt;.IsInteresting(temp);&lt;br /&gt;        }&lt;br /&gt;    }&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now i just change my usage to:&lt;br /&gt;&lt;pre&gt;&lt;div&gt;&lt;!--&lt;br /&gt;&lt;br /&gt;Code highlighting produced by Actipro CodeHighlighter (freeware)&lt;br /&gt;http://www.CodeHighlighter.com/&lt;br /&gt;&lt;br /&gt;--&gt;&lt;span style="color: #000000;"&gt;Picker picker &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; StandardPicker ();&lt;br /&gt;picker &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; RandomisedPicker (picker);&lt;br /&gt;picker &lt;/span&gt;&lt;span style="color: #000000;"&gt;=&lt;/span&gt;&lt;span style="color: #000000;"&gt; &lt;/span&gt;&lt;span style="color: #0000FF;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; IgnoringPicker (myBitfield, picker);&lt;/span&gt;&lt;/div&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The biggest benefit is that each individual picking logic unit can very easily tested. They can then be combined in any order you want to change the picking behaviour. Random-&gt;Rarest-&gt;Standard would be different to Rarest-&gt;Random-&gt;Standard. The former will give you the rarest piece in a random set, the latter will give you a random piece from the rarest set.&lt;br /&gt;&lt;br /&gt;The implementation of a logic unit is also trivial - you only have to override the behaviour you want to change rather than implementing a massive interface where for 90% of the methods you end up calling basePicker.Method anyway. So I'm quite happy with this change. This code already passes all the NUnit tests from the old picker as well as all the new tests I've written, so it should be going live in the near future. There's still some more work to be done before it's complete.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5721450179759124840?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5721450179759124840/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5721450179759124840' title='26 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5721450179759124840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5721450179759124840'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/12/piecepicking-and-runtime-method.html' title='PiecePicking and runtime method overriding'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>26</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4642064751953313352</id><published>2008-11-08T18:54:00.000Z</published><updated>2008-11-08T18:55:06.693Z</updated><title type='text'>MonoTorrent 0.62</title><content type='html'>MonoTorrent 0.62 has now been released which addresses a few major and minor issues with the 0.60 release. Here's the details:&lt;br /&gt;&lt;br /&gt;* Fixed regression in message handling which resulted in 0.60 not transferring properly. Caused by not running the right NUnit tests before tagging.&lt;br /&gt;* Performance optimisation for sending/receiving messages&lt;br /&gt;* Fixed bug creating torrents with 2gb+ files with TorrentCreator&lt;br /&gt;* Fixed issue with udp sockets in the Dht code which could cause dht to stop sending/receiving messages&lt;br /&gt;&lt;br /&gt;I'd highly recommend upgrading from 0.60 to 0.62 as soon as possible.&lt;br /&gt;&lt;br /&gt;Binary - http://monotorrent.com/Files/0.62/monotorrent-0.62-bin.zip&lt;br /&gt;Source - http://monotorrent.com/Files/0.62/monotorrent-0.62.tar.gz&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4642064751953313352?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4642064751953313352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4642064751953313352' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4642064751953313352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4642064751953313352'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/11/monotorrent-062.html' title='MonoTorrent 0.62'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7980421232482621705</id><published>2008-11-06T13:50:00.002Z</published><updated>2008-11-06T13:54:00.211Z</updated><title type='text'>Are you mapping those dlls?</title><content type='html'>One of the issues with writing cross platform applications is that if you do P/Invoke into a native library, the name of that library changes depending on the OS. Mono has built in support for selecting the right file at runtime. The problem is that it's hard to ensure that you've correctly mapped all the methods you P/Invoke.&lt;br /&gt;&lt;br /&gt;So I wrote a little tool to do that for you. It was inspired by an attempt by &lt;a href="http://abock.org/"&gt;Aaron Bockover &lt;/a&gt;which parsed the raw cs files. I figured it'd be much better to just parse the compiled assembly ;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Xml;&lt;br /&gt;using Mono.Cecil;&lt;br /&gt;using System.IO;&lt;br /&gt;&lt;br /&gt;namespace DllImportVerifier&lt;br /&gt;{&lt;br /&gt;    class MainClass&lt;br /&gt;    {&lt;br /&gt;        static void Main (string [] args)&lt;br /&gt;        {&lt;br /&gt;            args = new string [] { Environment.CurrentDirectory };&lt;br /&gt;            if (args.Length != 1) {&lt;br /&gt;                Console.WriteLine (&amp;quot;You must pass the path to the assemblies to be processed as the argument&amp;quot;);&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            List&amp;lt;string&amp;gt; assemblies = new List&amp;lt;string&amp;gt; ();&lt;br /&gt;&lt;br /&gt;            if (Directory.Exists (args[0])) {&lt;br /&gt;                assemblies.AddRange (Directory.GetFiles (args [0], &amp;quot;*.dll&amp;quot;));&lt;br /&gt;                assemblies.AddRange (Directory.GetFiles (args [0], &amp;quot;*.exe&amp;quot;));&lt;br /&gt;            }&lt;br /&gt;            else if (File.Exists (args [0])) {&lt;br /&gt;                assemblies.Add (args [0]);&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                Console.WriteLine (&amp;quot;{0} is not a valid file or directory&amp;quot;, args [0]);&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            foreach (string assembly in assemblies)&lt;br /&gt;                ProcessConfig (assembly);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        static void ProcessConfig (string assemblyName)&lt;br /&gt;        {&lt;br /&gt;            AssemblyDefinition assembly = null;&lt;br /&gt;            try {&lt;br /&gt;                assembly = AssemblyFactory.GetAssembly (assemblyName);&lt;br /&gt;            } catch {&lt;br /&gt;                Console.WriteLine (&amp;quot;{0} is not a valid .NET assembly&amp;quot;, Path.GetFileName (assemblyName));&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            List&amp;lt;string&amp;gt; dlls = new List&amp;lt;string&amp;gt; ();&lt;br /&gt;            try {&lt;br /&gt;                XmlTextReader doc = new XmlTextReader (assemblyName + &amp;quot;.config&amp;quot;);&lt;br /&gt;                while (doc.Read ()) {&lt;br /&gt;                    if (doc.Name != &amp;quot;dllmap&amp;quot;)&lt;br /&gt;                        continue;&lt;br /&gt;&lt;br /&gt;                    string dll = doc.GetAttribute (&amp;quot;dll&amp;quot;);&lt;br /&gt;                    if (!dlls.Contains (dll))&lt;br /&gt;                        dlls.Add (dll);&lt;br /&gt;                }&lt;br /&gt;            } catch {&lt;br /&gt;                // Ignore malformed or invalid config files. If the config is malformed,&lt;br /&gt;                // drop any successfully parsed dll maps&lt;br /&gt;                dlls.Clear();&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            List&amp;lt;string&amp;gt; unreferenced = VerifyReferences (assembly, dlls);&lt;br /&gt;            foreach (string dll in unreferenced)&lt;br /&gt;                Console.WriteLine(&amp;quot;Assembly: '{0}' references '{1}' without mapping it&amp;quot;, Path.GetFileName (assemblyName), dll);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        static List&amp;lt;string&amp;gt; VerifyReferences (AssemblyDefinition assembly, List&amp;lt;string&amp;gt; dlls)&lt;br /&gt;        {&lt;br /&gt;            List&amp;lt;string&amp;gt; unreferenced = new List&amp;lt;string&amp;gt; ();&lt;br /&gt;            foreach (TypeDefinition type in assembly.MainModule.Types) {&lt;br /&gt;                foreach (MethodDefinition method in type.Methods)  {&lt;br /&gt;                    if (!method.IsPInvokeImpl)&lt;br /&gt;                        continue;&lt;br /&gt;                    &lt;br /&gt;                    string dll = method.PInvokeInfo.Module.Name;&lt;br /&gt;                    if (!dlls.Contains (dll) &amp;amp;&amp;amp; !unreferenced.Contains (dll))&lt;br /&gt;                        unreferenced.Add (dll);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return unreferenced;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7980421232482621705?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7980421232482621705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7980421232482621705' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7980421232482621705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7980421232482621705'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/11/are-you-mapping-those-dlls.html' title='Are you mapping those dlls?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-696661573832749043</id><published>2008-11-05T14:17:00.003Z</published><updated>2008-11-05T14:36:25.579Z</updated><title type='text'>BarrierHandle</title><content type='html'>Did you ever have a case where you have a big dataset which can be processed in parallel, so long as all threads finish Step 1 before any thread starts Step 2? Thing is, there's no built in class to handle this case.&lt;br /&gt;&lt;br /&gt;AutoResetEvent won't do it because it because it only signals *one* of the waiting threads, not *all* of them. What you need is a manual reset event and some complex handling.&lt;br /&gt;&lt;br /&gt;I give you BarrierHandle:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Threading;&lt;br /&gt; &lt;br /&gt;class SpectralNorm&lt;br /&gt;{&lt;br /&gt;    public class BarrierHandle : System.Threading.WaitHandle&lt;br /&gt;    {&lt;br /&gt;        int current;&lt;br /&gt;        int threads;&lt;br /&gt;        ManualResetEvent handle = new ManualResetEvent (false);&lt;br /&gt;         &lt;br /&gt;        public BarrierHandle (int threads)&lt;br /&gt;        {&lt;br /&gt;            this.current = threads;&lt;br /&gt;            this.threads = threads;&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        public override bool WaitOne()&lt;br /&gt;        {&lt;br /&gt;            ManualResetEvent h = handle;&lt;br /&gt;            if (Interlocked.Decrement (ref current) &amp;gt; 0) {&lt;br /&gt;                h.WaitOne ();&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                handle = new ManualResetEvent (false);&lt;br /&gt;                Interlocked.Exchange (ref current, threads);&lt;br /&gt;                h.Set ();&lt;br /&gt;                h.Close ();&lt;br /&gt;            }&lt;br /&gt; &lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;     &lt;br /&gt;    public static void Main(String[] args)&lt;br /&gt;    {&lt;br /&gt;        int threadCount = 5;//Environment.ProcessorCount;&lt;br /&gt; &lt;br /&gt;        // Lets assume that there's 20 units of data for each thread&lt;br /&gt;        int[] dataset = new int [threadCount * 20];&lt;br /&gt; &lt;br /&gt;        // This is the handle we use to ensure all threads complete the current step&lt;br /&gt;        // before continuing to the next step&lt;br /&gt;        BarrierHandle barrier = new BarrierHandle(threadCount);&lt;br /&gt; &lt;br /&gt;        // Fire up the threads&lt;br /&gt;        for (int i = 0; i &amp;lt; threadCount; i++) {&lt;br /&gt;            ThreadPool.QueueUserWorkItem (delegate {&lt;br /&gt;                int jjj = i;&lt;br /&gt;                try {&lt;br /&gt;                    Step1 (dataset, jjj * 20, 20);&lt;br /&gt;                    barrier.WaitOne ();&lt;br /&gt;                    Step2 (dataset, jjj * 20, 20);&lt;br /&gt;                    barrier.WaitOne ();&lt;br /&gt;                    Step3 (dataset, jjj * 20, 20);&lt;br /&gt;                } catch (Exception ex) {&lt;br /&gt;                    Console.WriteLine (ex);&lt;br /&gt;                }&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        System.Threading.Thread.Sleep (3000);&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    private static void Step1 (int[] array, int offset, int count)&lt;br /&gt;    {&lt;br /&gt;        Console.WriteLine (&amp;quot;Step1: {0} - {1}&amp;quot;, offset, offset + count);&lt;br /&gt;        Thread.Sleep (500);&lt;br /&gt;    }&lt;br /&gt;    private static void Step2 (int[] array, int offset, int count)&lt;br /&gt;    {&lt;br /&gt;        Console.WriteLine (&amp;quot;Step2: {0} - {1}&amp;quot;, offset, offset + count);&lt;br /&gt;        Thread.Sleep (500);&lt;br /&gt;    }&lt;br /&gt;    private static void Step3 (int[] array, int offset, int count)&lt;br /&gt;    {&lt;br /&gt;        Console.WriteLine (&amp;quot;Step3: {0} - {1}&amp;quot;, offset, offset + count);&lt;br /&gt;        Thread.Sleep (500);&lt;br /&gt;    }&lt;br /&gt;} &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-696661573832749043?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/696661573832749043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=696661573832749043' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/696661573832749043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/696661573832749043'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/11/barrierhandle.html' title='BarrierHandle'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8281689530610310528</id><published>2008-11-03T11:34:00.001Z</published><updated>2008-11-03T11:35:33.768Z</updated><title type='text'>MonoTorrent 0.60</title><content type='html'>So here are the bugfixes and features for 0.60 as compared to 0.50:&lt;br /&gt;&lt;br /&gt;Bug fixes:&lt;br /&gt;* Fixed critical regression in 0.50 whereby transfers would be incredibly slow.&lt;br /&gt;* Fixed issue where announce/scrape requests to offline trackers may never time out&lt;br /&gt;* Added a few memory optimisations when reading piece data from disk.&lt;br /&gt;* Add the ability to report an alternate IP/Port combo to the tracker&lt;br /&gt;* Optimised the encrypted handshake - its now a bit faster&lt;br /&gt;* Fixed bug where wrong message ID was sent for extension messages&lt;br /&gt;* Fixed bug where pieces which do not exist would be requested from webseeds.&lt;br /&gt;* Fixed several bugs in TorrentCreator class where relative paths were used instead of absolute paths.&lt;br /&gt;* Disabled UdpTracker support as the current implementation locked up until a response was received.&lt;br /&gt;* Fixed a possible issue with multi-tier torrents.&lt;br /&gt;* Added the ability to tell whether peers come from PeerExchange, DHT or the tracker.&lt;br /&gt;* Some big performance boosts for webseeds by using a better method of picking pieces.&lt;br /&gt;* Fixed a corner case whereby certain torrents may not get their last piece requested.&lt;br /&gt;* When hosting a tracker,"/announce" is appended when you use the construtor overload which takes an IPEndPoint.&lt;br /&gt;&lt;br /&gt;New Features:&lt;br /&gt;* Implemented DHT support.&lt;br /&gt;* Implemented sparse file support on the NTFS file system.&lt;br /&gt;&lt;br /&gt;All in all, an awesome release. I have to give a shout out to Matthew Raymer who has been tirelessly testing MonoTorrent for the last few weeks. Without his constant bug reports, some of those bugs would never have been found, such as the one with UdpTrackers. Great stuff.&lt;br /&gt;&lt;br /&gt;Packages:&lt;br /&gt;Binary: &lt;a href="http://www.monotorrent.com/Files/0.60/monotorrent-0.60-bin.zip" target="_blank"&gt;http://www.monotorrent.com/Files/0.60/monotorrent-0.60-bin.zip&lt;/a&gt;&lt;br /&gt;Source: &lt;a href="http://www.monotorrent.com/Files/0.60/monotorrent-0.60.tar.gz" target="_blank"&gt;http://www.monotorrent.com/Files/0.60/monotorrent-0.60.tar.gz&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There are also packages for openSUSE in the openSUSE Build Service for those of you interested in those.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8281689530610310528?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8281689530610310528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8281689530610310528' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8281689530610310528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8281689530610310528'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/11/monotorrent-060.html' title='MonoTorrent 0.60'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1181279185269442306</id><published>2008-10-31T12:03:00.003Z</published><updated>2008-10-31T12:20:51.184Z</updated><title type='text'>A hack too far</title><content type='html'>I was just looking at the &lt;a href="http://msdn.microsoft.com/en-us/library/system.io.packaging.packurihelper.aspx"&gt;PackUriHelper&lt;/a&gt; class, with a view to implementing it in Mono. One of it's many methods converts a regular Uri into a 'pack' Uri. For example:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;http://www.test.com/main/page.html?query=val#middle&lt;br /&gt;&lt;div style="text-align: left;"&gt;converts to&lt;br /&gt;&lt;div style="text-align: center;"&gt;pack://http:,www.test.com,main,page.html?query=val#middle/&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;So I says to myself, this is easy, it's just combining two Uris. Simple!&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:180%;"&gt;Wrong&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;Uri original = new Uri ("http://www.test.com/main/page.html?query=val#middle");&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Uri complete = new Uri ("pack://" + original.OriginalString);  // FAILS&lt;br /&gt;Uri complete = new Uri (new Uri ("pack://", original));  //FAILS&lt;br /&gt;Uri complete = new Uri (new Uri ("pack://", original.OriginalString)); // FAILS&lt;br /&gt;&lt;br /&gt;How about escaping the second string...&lt;br /&gt;string escaped = Uri.HexEscape (original.OriginalString);&lt;br /&gt;Uri complete = new Uri ("pack://" + escaped); // FAILS&lt;br /&gt;&lt;br /&gt;So at this stage I've lost all faith in humanity, so i try a basic test just to make sure i'm not insane. I'll try create a pack uri object myself, just to prove that it really is possible to parse them.&lt;br /&gt;&lt;br /&gt;Uri complete = new Uri ("pack://http:,www.test.com,main,page.html?query=val#middle/");&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-size:180%;"&gt;EPIC FAIL&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;You can't do that. While they can *generate* that Uri for you, you can't generate one for yourself. Funnily enough, they do register a custom parser for the pack scheme, it's just incapable of parsing pack URI's. Don't ask me why.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;After several hours and a lot of wasted time I finally came up with this:&lt;br /&gt;&lt;br /&gt;Uri complete = new Uri (new Uri ("pack://"), escaped);&lt;br /&gt;&lt;br /&gt;This is the *only* way to create a Pack URI. You have to hex escape the original uri and then call the constructor overload which takes a Uri and a string.&lt;br /&gt;&lt;br /&gt;That is one of the stupidest things I've ever seen.&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1181279185269442306?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1181279185269442306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1181279185269442306' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1181279185269442306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1181279185269442306'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/hack-too-far.html' title='A hack too far'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-751199957708698271</id><published>2008-10-25T16:55:00.003+01:00</published><updated>2008-10-25T18:06:52.973+01:00</updated><title type='text'>MonoTorrent 0.60</title><content type='html'>MonoTorrent 0.60 is being prepared for release. 0.50 was released a mere 3 weeks ago, so why 0.60 already?&lt;br /&gt;&lt;br /&gt;Well:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;There was a big regression in 0.50 with regards to download speeds. Transfers were a *lot* slower than 0.40. To backport this fix would be very complex as there have been a lot of changes since 0.50 was branched.&lt;/li&gt;&lt;li&gt;DHT support is now good enough for me to activate by default. This was my milestone for releasing 0.60.&lt;/li&gt;&lt;li&gt;There have been a number of other important bugs fixed aswell (including a critical one with UdpTrackers) which need to be released.&lt;/li&gt;&lt;li&gt;There have been a number memory/performance optimisations since 0.50 which would be nice to release ;)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So, if you're using MonoTorrent, I urge you to check out the branch (when it is created) from http://anonsvn.mono-project.com/source/branches/bitsharp-0.60.&lt;br /&gt;&lt;br /&gt;Until I create the branch, check out revision 117059 of monotorrent from svn. If you have any bugs/issues, let me know and I'll try to get a fix in for 0.60.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-751199957708698271?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/751199957708698271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=751199957708698271' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/751199957708698271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/751199957708698271'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/monotorrent-060.html' title='MonoTorrent 0.60'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1535895076980580593</id><published>2008-10-20T22:40:00.002+01:00</published><updated>2008-10-21T00:25:38.698+01:00</updated><title type='text'>Walking can be tricky...</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_nFGxSFZnUc0/SPz63qyV9QI/AAAAAAAAANM/HCstGJ5GhcU/s1600-h/LEG.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_nFGxSFZnUc0/SPz63qyV9QI/AAAAAAAAANM/HCstGJ5GhcU/s320/LEG.JPG" alt="" id="BLOGGER_PHOTO_ID_5259354299147089154" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;Awesome!&lt;br /&gt;(No, it's not just a knee-high stocking with sticky tape, it's a cast)&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1535895076980580593?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1535895076980580593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1535895076980580593' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1535895076980580593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1535895076980580593'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/walking-can-be-tricky.html' title='Walking can be tricky...'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_nFGxSFZnUc0/SPz63qyV9QI/AAAAAAAAANM/HCstGJ5GhcU/s72-c/LEG.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2186116204740792137</id><published>2008-10-14T20:01:00.002+01:00</published><updated>2008-10-14T20:02:25.699+01:00</updated><title type='text'>The big wahooni</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_nFGxSFZnUc0/SPTstfKkoaI/AAAAAAAAANA/QNzCJce2sw8/s1600-h/DSC00051.JPG"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_nFGxSFZnUc0/SPTstfKkoaI/AAAAAAAAANA/QNzCJce2sw8/s320/DSC00051.JPG" alt="" id="BLOGGER_PHOTO_ID_5257086931252322722" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2186116204740792137?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2186116204740792137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2186116204740792137' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2186116204740792137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2186116204740792137'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/big-wahooni.html' title='The big wahooni'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_nFGxSFZnUc0/SPTstfKkoaI/AAAAAAAAANA/QNzCJce2sw8/s72-c/DSC00051.JPG' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-877435666874756528</id><published>2008-10-11T17:40:00.002+01:00</published><updated>2008-10-11T18:00:27.711+01:00</updated><title type='text'>Sparsely populated, just the way I like it</title><content type='html'>The NTFS filesystem has support for sparse files, but this has to be specially enabled when creating a file. I was originally linked to a &lt;a href="http://blogs.msdn.com/codedebate/archive/2007/12/18/6797175.aspx"&gt;blog post&lt;/a&gt; on the idea, but unfortunately the license pretty much forbids me from using that code.&lt;br /&gt;&lt;br /&gt;So I spent most of yesterday evening and this morning googling API documentation and eventually came up with a fairly basic implementation of sparse file support.  The only two operations supported are:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Creating a sparse file&lt;/li&gt;&lt;li&gt;Setting the size of the sparse data.&lt;/li&gt;&lt;/ol&gt;The benefits of this are seen only on the NTFS filesystem, but what it gives you is the ability to write pieces at arbitrary places in the file &lt;span style="font-style: italic;"&gt;without&lt;/span&gt; having to preallocate up to that point. There's a bit of overhead, but other than that you only use up the space which you've actually written to, i.e. only the pieces which have been downloaded.&lt;br /&gt;&lt;br /&gt;Finally, if you don't have an NTFS filesystem supporting sparse files, you will not be affected by this. Those of you on HFS+ can never get this support, and those of you on ext3, I'm unsure how to enable sparse files. I think it's enabled by default, but if not, any recommendations on setting this up would be appreciated.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://monotorrent.com/Sparse.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px;" src="http://monotorrent.com/Sparse.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-877435666874756528?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/877435666874756528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=877435666874756528' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/877435666874756528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/877435666874756528'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/sparsely-populated-just-way-i-like-it.html' title='Sparsely populated, just the way I like it'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7062617797779086092</id><published>2008-10-06T21:59:00.004+01:00</published><updated>2008-10-06T23:11:19.201+01:00</updated><title type='text'>MonoTorrent 0.50 - The Good, The Bad, and the seriously awesome</title><content type='html'>It's release time! Yes, MonoTorrent 0.50 has hit. There have been a lot of changes since the last release, and this time, it's more than just under the hood fixes. There are several reasons why the new release is so much better than previous releases, I've listed the more important ones below, but first, the packages!&lt;br /&gt;&lt;br /&gt;You can grab &lt;a href="http://monotorrent.com/Files/0.50/monotorrent-0.50-bin.zip"&gt;a precompiled binary&lt;/a&gt; suitable for Windows, Mac OS and Linux.&lt;br /&gt;You can grab the &lt;a href="http://monotorrent.com/Files/0.50/monotorrent-0.50.tar.gz"&gt;sourcecode here&lt;/a&gt; as a .tar.gz archive.&lt;br /&gt;There are also packages available on the OpenSuse build service, and of course the tag for the release can be &lt;a href="http://anonsvn.mono-project.com/viewvc/tags/bitsharp-0.50/"&gt;gotten from svn&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Now, to the features and updates.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://getright.com/seedtorrent.html"&gt;&lt;span style="font-weight: bold;"&gt;WebSeeding Support&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;There is provisional support for &lt;a href="http://getright.com/seedtorrent.html"&gt;Http Web Seeding&lt;/a&gt;. This means when you're hosting a torrent, you can add standard Http servers as 'seeds'. No extra configuration is needed. This is still an experimental feature, and still has some corner cases where it doesn't work, all bug reports on this are welcome!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;IP Address Banning&lt;/span&gt;&lt;br /&gt;You can now ban individual IP addresses or IP Address ranges. Block lists from Emule, PeerGuardian and SafePeer are supported out of box by the built in parser, and any custom list can easily by loaded so long as you can parse the list into IPAddress objects. Internally the banlist is stored using the extremely efficient &lt;a href="http://anonsvn.mono-project.com/viewvc/trunk/bitsharp/src/MonoTorrent/BanLists/RangeCollection.cs?revision=113105&amp;amp;content-type=text%2Fplain"&gt;RangeCollection&lt;/a&gt; written by &lt;a href="http://abock.org/"&gt;Aaron Bockover&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Efficent Torrent Streaming&lt;/span&gt;&lt;br /&gt;Thanks to the efforts of Karthik &lt;span class="HcCDpe"&gt;Kailash&lt;/span&gt; and &lt;span class="HcCDpe"&gt;&lt;span class="JDpiNd"&gt;&lt;/span&gt;David Sanghera, we now have a special downloading mode in MonoTorrent which allows you to efficiently stream audio/video. Psuedo random piece picking is used to ensure you download pieces from a 'high priority' range before anything else. User code can set this 'High priority' range to be the next X bytes of data. When everything in the high priority range is downloaded, standard rarest-first picking is used.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="HcCDpe"&gt;&lt;span style="font-weight: bold;"&gt;Peer Exchange&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;uTorrent style Peer Exchange support is supported thanks to the tireless efforts of Olivier Dufour. This extension allows peer information to be passed across a bittorrent connection. In practice this means that if the tracker only gives you 1 peer, you can discover (potentially) hundreds more via peer exchange.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;br /&gt;Enhanced compatibilty with broken clients&lt;/span&gt;&lt;br /&gt;There are still clients out there which transmit corrupted BEncodedDictionary objects. These guys need to &lt;a href="http://wiki.theory.org/BitTorrentSpecification#dictionaries"&gt;read the spec&lt;/a&gt; and ensure that their dictionaries keys are sorted &lt;/span&gt;using &lt;span style="font-weight: bold; font-style: italic;"&gt;a binary  comparison&lt;/span&gt;. In the cases where the order appears to not matter, I've implemented support for ignoring the error. This should reduce the number of clients which are disconnected due to sending corrupt messages - this means higher performance.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Simplified Threading API&lt;/span&gt;&lt;br /&gt;The core of MonoTorrent has undergone a complete rewrite. Previously, all the worker threads interacted with the core by taking out locks, then doing their work. This meant that implementing something as trivial as &lt;a href="http://anonsvn.mono-project.com/viewvc/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/Managers/ConnectionManager.cs?r1=100056&amp;amp;r2=100055&amp;amp;pathrev=100056"&gt;cancelling a pending asynchronous request&lt;/a&gt; was actually pretty hard. That method was actually horrendously prone to deadlocking the engine.&lt;br /&gt;&lt;br /&gt;Nowadays all the worker threads add a task to the main thread, and the main thread does all the work. "What about the performance" i hear you ask, well, it performs the exact same, but it's so much easier to maintain and add new features to.&lt;br /&gt;&lt;br /&gt;It also means the engine should be deadlock free, because there are no locks anymore. Nice.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;NUnit Tests&lt;/span&gt;&lt;br /&gt;As with all big software projects, regressions are bad. A year ago I had virtually no NUnit tests. Nowadays there are over 130 NUnit tests for the engine. While this doesn't even test 1/2 the code in MonoTorrent, each test adds that little bit more certainty that I don't regress.&lt;br /&gt;&lt;br /&gt;There are also a bunch bugfixes here and there, and more big features in the pipeline. As a taster, DHT support is already active and enabled in SVN should you wish to test it out.&lt;br /&gt;&lt;span class="HcCDpe"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7062617797779086092?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7062617797779086092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7062617797779086092' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7062617797779086092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7062617797779086092'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/monotorrent-050-good-bad-and-seriously.html' title='MonoTorrent 0.50 - The Good, The Bad, and the seriously awesome'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1129782073554725066</id><published>2008-10-03T02:21:00.007+01:00</published><updated>2008-10-03T02:27:11.958+01:00</updated><title type='text'>Who loves lamdas?</title><content type='html'>I finally started a project where I can use all the new fanciness. How would you process a list of names so that each name is printed out 'i' times?&lt;br /&gt;&lt;br /&gt;int i = 5;&lt;br /&gt;IEnumerable&amp;lt;string&amp;gt;() names = new List&amp;lt;string&amp;gt;() { "Alan" };&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;names.ForEach (n =&amp;gt; i.Times().Apply(j =&amp;gt; Console.WriteLine(n)));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;All your lambdas are belong to us!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1129782073554725066?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1129782073554725066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1129782073554725066' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1129782073554725066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1129782073554725066'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/who-loves-lamdas.html' title='Who loves lamdas?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4900187064585478784</id><published>2008-10-02T15:53:00.002+01:00</published><updated>2008-10-02T16:01:28.074+01:00</updated><title type='text'>Webserver for NUnit tests</title><content type='html'>What's the best way of setting up a simple HTTP file server to be used purely for NUnit tests. What I'm looking for is a way to set up a server so that I can host some files in. My NUnit tests will then initiate HTTP requests to 'download' those files.&lt;br /&gt;&lt;br /&gt;Ideally whatever method I choose won't require me to write temporary files to disk. Currently what I do is host a HttpListener and manually fulfill the requested data by creating an in-memory byte[] and writing that into the response. I'd like to do something like:&lt;br /&gt;&lt;br /&gt;webServer.Add("path/where/file/is", CreateFakeByteArray());&lt;br /&gt;&lt;br /&gt;Then the server will fulfill requests from this faked byte[].&lt;br /&gt;&lt;br /&gt;The reason for this setup is that I need a better way to test my implementation of &lt;a href="http://getright.com/seedtorrent.html"&gt;WebSeeding&lt;/a&gt; in MonoTorrent without having to code up my own buggy file server ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4900187064585478784?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4900187064585478784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4900187064585478784' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4900187064585478784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4900187064585478784'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/10/webserver-for-nunit-tests.html' title='Webserver for NUnit tests'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-758946673663952984</id><published>2008-09-29T02:16:00.004+01:00</published><updated>2008-09-29T03:00:07.250+01:00</updated><title type='text'>tee-sharp a.k.a. CopyStream</title><content type='html'>How many times do you have one stream, but you a&lt;a href="http://tirania.org/blog/archive/2008/Sep-24.html"&gt;ctually want to write the data to multiple places simultaneously&lt;/a&gt;[0]? Well, &lt;a href="http://www.monsoon-project.org/jaws/data/files/TeeStream.cs"&gt;now you can&lt;/a&gt;[1]!&lt;br /&gt;&lt;br /&gt;I took an hour and spun up this awesome asynchronous beast of a stream splitter. There is an optimisation that could be applied to it: Reads can be performed at the same time as writes. I figured that for a 1.0 implementation, this was good enough. If anyone wants to try their hand at making the read perform in parallel with the writes, feel free. Patches are welcome ;)&lt;br /&gt;&lt;br /&gt;[0] http://tirania.org/blog/archive/2008/Sep-24.html&lt;br /&gt;[1] http://www.monsoon-project.org/jaws/data/files/TeeStream.cs&lt;br /&gt;&lt;br /&gt;EDIT: There's also a 'deliberate' bug there. 10 points to the first person to spot it and bonus 10 points if you can fix it with less than 5 lines of extra code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-758946673663952984?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/758946673663952984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=758946673663952984' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/758946673663952984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/758946673663952984'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/teestream-sharp-aka-copystream.html' title='tee-sharp a.k.a. CopyStream'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8698912895057704260</id><published>2008-09-24T02:03:00.003+01:00</published><updated>2008-09-24T02:10:04.121+01:00</updated><title type='text'>Captchas, a bridge too far?</title><content type='html'>I was just trying to open a new gmail account when I encountered the unsolvable captcha. I couldn't for the life of me figure out what was written in that horrible mangled imagine of what i can only assume were standard roman-alphabet letters. I can only assume that I am in fact illiterate and that would explain why I couldn't read it.&lt;br /&gt;&lt;br /&gt;Thankfully, google have a second option. They have a little link which reads out the captcha for you. It turns out that I can't understand english either. I couldn't make out a word of what was said. So, if anyone out there can speak english, could you please decypher this for me:&lt;br /&gt;&lt;a href="http://monotorrent.com/GoogleSucks.wav"&gt;&lt;br /&gt;http://monotorrent.com/GoogleSucks.wav&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8698912895057704260?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8698912895057704260/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8698912895057704260' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8698912895057704260'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8698912895057704260'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/captchas-bridge-too-far.html' title='Captchas, a bridge too far?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7484263756283535750</id><published>2008-09-21T13:08:00.002+01:00</published><updated>2008-09-21T13:15:17.506+01:00</updated><title type='text'>It lives!</title><content type='html'>&lt;span&gt;Revision 113625 &lt;wbr&gt;- Enable DHT by &lt;wbr&gt;default in MonoTorrent.&lt;br /&gt;&lt;br /&gt;Awesome!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7484263756283535750?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7484263756283535750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7484263756283535750' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7484263756283535750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7484263756283535750'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/it-lives.html' title='It lives!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8539235860729496030</id><published>2008-09-20T00:18:00.006+01:00</published><updated>2008-09-20T02:10:33.378+01:00</updated><title type='text'>How verbose is too verbose?</title><content type='html'>A &lt;span&gt;common &lt;/span&gt;thing to do in code is to perform an action on each element in an array of objects. In C# there are two main ways to write this:&lt;br /&gt;&lt;blockquote&gt;// Lets just assume this list of strings has been populated with lots of strings&lt;br /&gt;List&amp;lt;string&amp;gt;&lt;string&gt; allStrings = GetLotsOfStrings ();&lt;br /&gt;&lt;br /&gt;// Method 1: The for loop&lt;br /&gt;for (int i = 0; i &lt; allStrings.Count; i++)&lt;br /&gt;&lt;/string&gt;       &lt;string&gt; DoStuff (allStrings[i]);&lt;br /&gt;&lt;br /&gt;// Method 2: The foreach loop&lt;br /&gt;foreach (string s in allStrings)&lt;br /&gt;&lt;/string&gt;        &lt;string&gt;DoStuff (s);&lt;/string&gt;&lt;/blockquote&gt;&lt;string&gt;  However, both of those methods are far too verbose. There is another way, which is much much nicer!&lt;br /&gt;&lt;/string&gt;&lt;string&gt;&lt;/string&gt;&lt;blockquote&gt;&lt;string&gt;allStrings.ForEach(DoStuff); &lt;/string&gt;&lt;/blockquote&gt;&lt;string&gt;  How awesome is that, eh?&lt;/string&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8539235860729496030?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8539235860729496030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8539235860729496030' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8539235860729496030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8539235860729496030'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/how-verbose-is-too-verbose_20.html' title='How verbose is too verbose?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8434619383182927716</id><published>2008-09-03T18:21:00.002+01:00</published><updated>2008-09-03T18:24:03.452+01:00</updated><title type='text'>MonoTorrent is teh awesome</title><content type='html'>&lt;a href="http://www.ayende.com/Blog/archive/2008/04/08/Hibernating-Torrent-A-simple-torrent-server-for-Windows.aspx"&gt;How many lines to host a torrent server&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;Any ideas?&lt;br /&gt;&lt;br /&gt;Guess!&lt;br /&gt;&lt;br /&gt;&lt;a href="https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/SampleApplications/HibernatingTorrent/HibernatingTorrent/HibernatingTorrent/TorrentServer.cs"&gt;Check it out yourself&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8434619383182927716?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8434619383182927716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8434619383182927716' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8434619383182927716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8434619383182927716'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/monotorrent-is-teh-awesome.html' title='MonoTorrent is teh awesome'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-614970818563978739</id><published>2008-09-03T06:02:00.003+01:00</published><updated>2008-09-03T16:19:01.012+01:00</updated><title type='text'>So what happened in SoC 2008?</title><content type='html'>So, after ~3 months of hacking while travelling up the east coast of Austrlia, what exactly have I managed to accomplish in this years &lt;a href="http://code.google.com/soc"&gt;SoC&lt;/a&gt;? Well, quite a lot :) Here's a list of new stuff and upcoming stuff in no real order:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div align="center"&gt;&lt;strong&gt;DHT&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;DHT support is available in SVN. It's pretty much complete, but lacks some real world testing. There are currently about 35 NUnit tests covering all important modules in the code. I need to give this a week or two of solid testing and then I'll be enabling it by default. A few updates will need to be applied to MonoTorrent so that the 'private' flag will be obeyed now that we have DHT support.&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="center"&gt;&lt;strong&gt;IP Address Banning&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Awesome support for this has been added to SVN using a combination of my own code and code written by &lt;a href="http://abock.org/"&gt;The Great Bocky&lt;/a&gt;. It uses an extremely efficient way of storing IP Address ranges so that they can be represented by two integers plus a little overhead. There is a parser which supports all the main ban lists and users can parse other formats manually and add the addresses in that way.&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="center"&gt;&lt;strong&gt;Extended Messaging Protocol&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;Support for the LibTorrent extension protocol is complete. This allows custom messages to be sendable to remote peers over the standard bittorrent protocol. So if you require the ability to send arbitrary data to a remote peer and have them react in a special way, you can!&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="center"&gt;&lt;strong&gt;Http Seeding (Web Seeding)&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Support for the getright style Http Seeding is complete. This (better) specification allows a standard HTTP server act as a seed with no special software required. If MonoTorrent decides that there aren't enough peers available in the swarm to allow the torrent to complete, it will automatically start downloading the necessary chunks from the server.&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="center"&gt;&lt;strong&gt;Peer Exchange&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Support for this has been completed by Olivier Dufour. This pretty cool idea allows peers you are connected to to send you details about other peers which are active in the swarm. This way you gain information about more peers even if the tracker goes offline.&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="center"&gt;&lt;strong&gt;Misc&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Faster SHA1&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Due to both an algorithm change and architecture changes, hashing performance has nearly doubled. This means that hashing a file takes less than 1/2 the time it used to *and* that CPU usage while downloading is reduced.&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Better Encryption&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;There was a bug in header-only encryption that prevented it from working correctly before. As MonoTorrent always defaulted to Full Encryption, this wasn't such a huge issue before. Along with this bugfix, encryption is now more performant than before - using less CPU and less memory. The code also shrunk considerably in size and is much more maintainable than before. &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Deadlock Free&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;It's now impossible to deadlock the library. This isn't so important for the end-user, but for anyone programming with MonoTorrent it's great news. If you are extending the library to add extra functionality internally, it's now easy to ensure that you do everything in a thread-safe and non-deadlocking manner.&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Abort long connection attempts&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Sometimes an operating system might wait an incredibly long time before aborting a connection attempt. This meant that if MonoTorrent tried to connect to a peer that was no longer available, sometimes the OS would take up to 150 seconds to abort the attempt. Worst case scenario is that the first 5 peers you connect to all take 150 seconds to abort and it looks like MonoTorrent is doing nothing. Now MonoTorrent hard-aborts a connection attempt if it takes more than 10 seconds.&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Streaming torrents ahoy!&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Two guys Karthik Kailish and David Sanghera have created a new way to download a torrent with MonoTorrent. Generally speaking, the rarest piece of a torrent is downloaded first, then the second rarest and so on. This new code allows you to specify a range of bytes which is High, Medium or Low priority. Then, from within these ranges the rarest first algorithm is active. For example if you are a video player and you want to start playback from byte 1000, you can tell MonoTorrent that the range from 1000 -&gt; 5000 is important so those bytes are downloaded first, which allows you to start playback as soon as enough of that high-priority data has arrived.&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Unit Tests&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;The number of tests covering MonoTorrent has doubled over the summer, from 55 up to 111. Every test makes the liklehood of accidently introducing a bug less and less. I like tests ;)&lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;&lt;/strong&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;strong&gt;Banshee Plugin&lt;/strong&gt;&lt;/div&gt;&lt;div align="left"&gt;Finally, while not quite related to MonoTorrent itself - A plugin for banshee has been created which allows the downloading of torrent based podcasts. It's still a work in progress, but hopefully that can be cleaned up and completed pretty soon. &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;So with all these changes and features, i'm hoping to push out the next release of MonoTorrent by the end of september. This release is unlikely to include DHT, but I hope to have a second release shortly afterwards which will include DHT.&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div align="left"&gt;&lt;/div&gt;&lt;div align="left"&gt;Anyway, I'm off to pack my bags now so I'm ready to head to Ha Long City at 7am in the morning. I'm enjoying my last week in Viet Nam at the moment. It's been a blast, though sometimes i wonder if they deliberately decide to not understand what I say just because I'm mispronouncing it slightly. 'Ho Chi Minh' isn't *that* hard to understand, is it?&lt;/div&gt;&lt;div align="left"&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-614970818563978739?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/614970818563978739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=614970818563978739' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/614970818563978739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/614970818563978739'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/09/so-what-happened-in-soc-2008.html' title='So what happened in SoC 2008?'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3985242885725935744</id><published>2008-07-17T08:09:00.004+01:00</published><updated>2008-07-17T08:13:45.099+01:00</updated><title type='text'>Socks proxies in .NET</title><content type='html'>I'm just wondering if anyone out there knows a library which has a nice MIT/X11 (or similar) license which will allow me to use a socks based proxy. Alternatively, 10 points to the first person who writes the wrapper to do it ;)&lt;br /&gt;&lt;br /&gt;I had found one before, but I seem to have misplaced the link and google is failing me now.&lt;br /&gt;&lt;br /&gt;EDIT: Ideally it'd be available in source form so i don't have to bundle a binary. I *really* don't want to bundle a library if I can avoid it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3985242885725935744?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3985242885725935744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3985242885725935744' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3985242885725935744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3985242885725935744'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/07/socks-proxies-in-net.html' title='Socks proxies in .NET'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7370563528475077450</id><published>2008-06-20T22:42:00.004+01:00</published><updated>2008-06-20T22:58:02.136+01:00</updated><title type='text'>MonoTorrent 0.4 and Monsoon 0.15</title><content type='html'>MonoTorrent 0.40 has been released. There weren't many changes feature-wise, but there's been quite a lot of under the hood changes. Details can be found on &lt;a href="http://www.monotorrent.com/"&gt;www.monotorrent.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Also, Monsoon 0.15 has been released. The &lt;a href="http://www.monsoon-project.org/jaws/index.php?page/releasenotes0.15"&gt;release notes&lt;/a&gt; are available and your packages can be gotten &lt;a href="http://www.monsoon-project.org/jaws/index.php?page/Download"&gt;from here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Fun times, eh?&lt;br /&gt;&lt;br /&gt;MonoTorrent 0.50 is slated for a few weeks time (don't hold me to this). There are a bucket load of features in the works which will definitely kick some ass. I've been getting some great patches recently from &lt;a href="http://dufoli.wordpress.com/"&gt;Olivier Dufour&lt;/a&gt;, which he has detailed in his post. These should all make 0.50. I've also been getting some awesome work from Karthik Kailash, and his friend David (whose second name i can't find now), implementing a fancy debugging GUI which exposes all the internals in a nice GUI to make it easy for me to detect bugs/issues. He's also implementing &lt;a href="http://www.aqualab.cs.northwestern.edu/projects/Ono.html"&gt;Ono support&lt;/a&gt;, which helps get faster transfers; &lt;a href="http://bittyrant.cs.washington.edu/"&gt;Bit-tyrant like unchoking&lt;/a&gt; which prioritises peers who reciprocate data resulting in faster transfers along with a new Piece Picking algorithm which allows you to stream a media file via torrent efficiently.&lt;br /&gt;&lt;br /&gt;I'm unsure how many of those features will hit 0.50, it depends on when they hit SVN and how much testing i can get in. But hopefully a few of them will get there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7370563528475077450?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7370563528475077450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7370563528475077450' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7370563528475077450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7370563528475077450'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/06/monotorrent-04-and-monsoon-015.html' title='MonoTorrent 0.4 and Monsoon 0.15'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-6801792312069346306</id><published>2008-06-11T16:14:00.002+01:00</published><updated>2008-06-11T16:58:43.859+01:00</updated><title type='text'>MonoTorrent - Expanding your universe</title><content type='html'>As i'm sure everyone has heard at this stage, &lt;a href="http://digg.com/linux_unix/Banshee_1_0_Final_Released"&gt;Banshee 1.0&lt;/a&gt; has been &lt;a href="http://abock.org/2008/06/10/banshee-10-released/"&gt;released&lt;/a&gt;. It's a huge step up from the old 0.13.x releases, and well worth checking out!&lt;br /&gt;&lt;br /&gt;So, now that banshee has some kickass podcast support, along with video support, &lt;a href="http://bugzilla.gnome.org/show_bug.cgi?id=537811"&gt;wouldn't it be nice&lt;/a&gt; if you could &lt;a href="http://ewheel.democracynow.org/rss.xml"&gt;download video podcasts which have a .torrent payload&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;Wouldn't it be awesome if there as a &lt;a href="http://monotorrent.com/"&gt;.NET based torrent library&lt;/a&gt;, that maybe was exposed via a &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp-dbus/"&gt;DBus service&lt;/a&gt; that could be integrated with banshee with&lt;a href="http://monoport.com/17699"&gt; just a few lines of code&lt;/a&gt;. Of course, once you've integrated the actual torrent downloading, how do you make banshee realise that .torrent files need to be handled specially? Well, write &lt;a href="http://monoport.com/17700"&gt;another few lines of code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So all in all, because of banshees awesome extension framework, i wrote less than 200 lines of code of banshee code to enable banshee to download torrents. I was surprised by how easy everything was. I was up with creating the new extension within about 10 minutes. So, if you're interested in this extension, attach yourself to the &lt;a href="http://bugzilla.gnome.org/show_bug.cgi?id=537811"&gt;bug report&lt;/a&gt; and you'll be able to keep up-to-date with the latest happenings.&lt;br /&gt;&lt;br /&gt;After all this, what exactly does it look like when you download a torrent podcast? Well, it looks exactly like it does for a regular podcast download. You don't have to do anything special, it's all just MAGIC! Check out the &lt;a href="http://www.monsoon-project.org/jaws/data/files/BansheeTorrent.ogv"&gt;screencast&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-6801792312069346306?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/6801792312069346306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=6801792312069346306' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6801792312069346306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/6801792312069346306'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/06/monotorrent-expanding-your-universe.html' title='MonoTorrent - Expanding your universe'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8698773289406691846</id><published>2008-05-25T21:42:00.001+01:00</published><updated>2008-05-25T21:43:31.626+01:00</updated><title type='text'>Titles!</title><content type='html'>Why the hell did my blogspot account have titles disabled by default? Did i stupidly manage to turn them off the day i opened my blogspot account?&lt;br /&gt;&lt;br /&gt;Anyway, i have titles... finally!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8698773289406691846?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8698773289406691846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8698773289406691846' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8698773289406691846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8698773289406691846'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/05/titles.html' title='Titles!'/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4241084314987369025</id><published>2008-05-15T00:43:00.003+01:00</published><updated>2008-05-15T01:02:58.802+01:00</updated><title type='text'></title><content type='html'>The &lt;a href="http://code.google.com/soc/2008/"&gt;Summer of Code&lt;/a&gt; is starting soon, and I'm a student once again. So what are the big plans this year? Well, hopefully a lot! Here's a brief outlook on what to expect during this summer. Note, these are in no particular order&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;MonoTorrent&lt;/span&gt;&lt;br /&gt;1) DHT support in MonoTorrent. This is probably the most time-consuming feature that is planned.&lt;br /&gt;&lt;br /&gt;2) DBus based daemon for monotorrent. The idea behind this is to allow applications to consume torrent files without worrying about interop-ing with .NET. This would provide a system-wide torrent service which any application, or many simultaenous applications, could take advantage of.&lt;br /&gt;&lt;br /&gt;This daemon will expose a simplified API as compared to the monotorrent library itself.&lt;br /&gt;&lt;br /&gt;3) HTTP/Socks proxy support.&lt;br /&gt;&lt;br /&gt;4) A proper NUnit testing framework. All the essentials are now implemented in MonoTorrent to allow me to test stuff deep inside monotorrent relatively easily. Now i just need to implement my test harness and then some tests (work on that has started as of yesterday actually :) ).&lt;br /&gt;&lt;br /&gt;5) Implement support for both the Azureus and Libtorrent messaging protocols. Support is mostly there for the Libtorrent protocol. This will also allow user-defined messages to be sent via the torrent connection.&lt;br /&gt;&lt;br /&gt;6) As per usual, i'll be spending time with a profiler seeing where performance can be improved. Don't expect too much from this though, things are already pretty good ;)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;Monsoon&lt;/span&gt;&lt;br /&gt;Now that we have a sweet GUI for monotorrent, which is going to be available in Suse 11.0 (other distros also have packages these days), we need to keep improving it. Once again there are in no particular order:&lt;br /&gt;&lt;br /&gt;1) Get Monsoon portable to both MacOS and Windows. 95% of this work has been completed already. So it's nearly done. Once this has been completed, i need to look into packaging installers for these platforms (or someone can offer to do that for me ;) ).&lt;br /&gt;&lt;br /&gt;2) Next we get cracking on the &lt;a href="https://bugzilla.novell.com/buglist.cgi?cmdtype=runnamed&amp;amp;namedcmd=Monsoon"&gt;buglist&lt;/a&gt; and try and resolve those. Quite a number of cosmetic things need to be looked at and there are also a few bigger issues, such as that memory issue (which is still proving to be quite elusive!).&lt;br /&gt;&lt;br /&gt;3) General nicing up of everything. There isn't really a firm plan for monsoon yet. A more detailed timeline and suchlike will be created as soon i can get together with &lt;a href="http://buchan.esoteriq.org/weblog/2007/08/20/monotorrent-gtk-interface-summer-wrap-up/"&gt;buchan&lt;/a&gt; after my exams and see where we want to take this.&lt;br /&gt;&lt;br /&gt;All in all, a busy summer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4241084314987369025?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4241084314987369025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4241084314987369025' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4241084314987369025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4241084314987369025'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/05/summer-of-code-is-starting-soon-and-im.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7635672926238868863</id><published>2008-05-09T14:16:00.003+01:00</published><updated>2008-05-09T14:18:00.704+01:00</updated><title type='text'></title><content type='html'>It's funny. I always thought that DRM should be used as a way to make life difficult for people pirating software/music. I never though it would be used as a way to convince people that pirating software is the better way:&lt;br /&gt;&lt;br /&gt;http://www.theinquirer.net/gb/inquirer/news/2008/05/07/mass-effect-wins-award-worst-pc&lt;br /&gt;&lt;br /&gt;Seriously, what are these people thinking?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7635672926238868863?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7635672926238868863/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7635672926238868863' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7635672926238868863'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7635672926238868863'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/05/its-funny.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3320545675954976512</id><published>2008-04-30T11:39:00.009+01:00</published><updated>2008-04-30T13:45:37.192+01:00</updated><title type='text'></title><content type='html'>I haven't done a mono-specific post in a while, so i'm glad i can change that with some good news! A few days ago, &lt;a href="http://themonkeysgrinder.blogspot.com/"&gt;Scott Peterson&lt;/a&gt;, (who took part in the Summer of Code last year to port banshee to windows) was complaining that monsoon took an hour to hash a 60GB torrent. I was a bit surprised at this, because it shouldn't be that slow! I did know that SHA1 hashing in mono was a tad slow, but no way should it be *that* slow.&lt;br /&gt;&lt;br /&gt;Anyway, he decided to fire up a profiler and get cracking on optimising the hell out of the SHA1 class in mono, and i decided that i'd skip some study and do the same. We had two main aims in this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;To make the fastest possible implementation using no unsafe code&lt;/li&gt;&lt;li&gt;To make the fastest possible managed SHA1 implementation&lt;/li&gt;&lt;/ol&gt;Aim 1:&lt;br /&gt;In order to do this, we couldn't use unsafe code, everything had to be done without pointers. We also had a constraint that we couldn't make the size of the class significantly longer, in fact, *reducing* the lines of code was also an aim because the more lines of code, the slower it is for the JIT to process.&lt;br /&gt;&lt;br /&gt;We did a number of iterations on the code, testing different things out. We fully unrolled the three main loops, we partially unrolled some of em and benchmarked everything in between. The things we learned in the end were:&lt;br /&gt;&lt;br /&gt;1) In this particular algorithm, there was a great benefit to using local variables rather than fields. That gave a biggish boost. &lt;span style="font-style: italic;"&gt;EDIT: This is because mono doesn't perform array out of bounds check removal (abcrem) on fields. It only performs it on local vars. As array access is *very* frequent, removing these checks is a huge benefit.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2)&lt;br /&gt;&lt;code&gt;buff[i] = ((buff[i-3] ^ buff[i-8] ^ buff[i-14] ^ buff[i-16]) &lt;&lt; 1)&lt;br /&gt;              | ((buff[i-3] ^ buff[i-8] ^ buff[i-14] ^ buff[i-16]) &lt;&lt; 31));&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The above code actually performed significantly slower than:&lt;br /&gt;&lt;code&gt;uint temp = buff[i-3] ^ buff[i-8] ^ buff[i-14] ^ buff[i-16];&lt;br /&gt;buff[i] = (temp &lt;&lt; 1) | (temp &gt;&gt; 31);&lt;br /&gt;&lt;/code&gt;)&lt;br /&gt;&lt;br /&gt;3)Re-rolling some of the loops did not affect performance significantly. The performance delta between when all three of the loops were fully unrolled and when two of them were only partially unrolled was less than 6% on my system, but the IL was reduced by a fairly massive proportion.&lt;br /&gt;&lt;br /&gt;4) Massive methods perform slower. We found that by splitting the  'ProcessBlock' method up, performance increase noticeably. Bear in mind that when we originally tested this, we had all three big loops fully unrolled and they were all in the same method. Still, it's worth bearing in mind.&lt;br /&gt;&lt;br /&gt;So, what was the final performance delta with all these changes?&lt;br /&gt;&lt;br /&gt;Core 2 Duo T7400 @ 2.16GHz&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2.54x&lt;/span&gt; faster&lt;br /&gt;&lt;br /&gt;Athlon 64 3200+&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3.10x&lt;/span&gt; faster&lt;br /&gt;&lt;br /&gt;Pentium 4 @ 2.80GHz&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1.36x&lt;/span&gt; faster&lt;br /&gt;&lt;br /&gt;Xeon X5355  @ 2.66GHz&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1.38x&lt;/span&gt; faster&lt;br /&gt;&lt;br /&gt;64bit PPC:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1.60x&lt;/span&gt; faster&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;EDIT: It seems I've mixed up my numbers when i was recording them yesterday. Some of the numbers compare against SVN head and some compare against the version in Mono 1.9. I think the first 2 results are against the version in 1.9 and the last three are against SVN head.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Not too shabby.&lt;br /&gt;&lt;br /&gt;Aim 2:&lt;br /&gt;More to come on this later, we're still in the optimisation process, but i think it's fair to say that the unsafe version is quite  a fair bit faster thant the safe version so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3320545675954976512?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3320545675954976512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3320545675954976512' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3320545675954976512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3320545675954976512'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/04/i-havent-done-mono-specific-post-in.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-953215484954763488</id><published>2008-04-21T22:40:00.003+01:00</published><updated>2008-04-21T22:43:06.757+01:00</updated><title type='text'></title><content type='html'>So opensuse 11.0 beta 1 has been released. I decided i'd be adventurous and give it a whirl, so i fired up my mac and downloaded the ISO while i was in college. When i got home i realised that i had never actually burned an ISO on my mac before, and there didn't seem to be any built in software to do it.&lt;br /&gt;&lt;br /&gt;So, i fired up google and expected to have to download trial software to burn the image and suffer all sorts of hassle and annoyance. I was pleasantly surprised to find that it was frikin simple to burn an iso:&lt;br /&gt;&lt;br /&gt;$ hdutil burn image.iso&lt;br /&gt;&lt;br /&gt;It's now 5 miutes later, and i'm just about to reboot into the Live CD environment, very nice!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-953215484954763488?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/953215484954763488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=953215484954763488' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/953215484954763488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/953215484954763488'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/04/so-opensuse-11.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1569478267418369302</id><published>2008-04-19T00:16:00.002+01:00</published><updated>2008-04-19T00:59:35.308+01:00</updated><title type='text'></title><content type='html'>MonoTorrent 0.30 has been tagged and released. All i have to do now is update the website. Here's the short changelog.&lt;br /&gt;&lt;br /&gt;Highlights include:&lt;br /&gt;[Client]&lt;br /&gt;* Amazing extensibility - Active connections can be injected from any source you want. So if your application already has an active connection to a peer, that connection can be passed to MonoTorrent and it will be used.&lt;br /&gt;* Udp Tracker support implemented - Can now use the low bandwidth udp protocol if the tracker supports it.&lt;br /&gt;* Initial support for the libtorrent messaging protocol&lt;br /&gt;* Implemented semi-intelligent memory buffer to reduce disk reads/writes.&lt;br /&gt;* Fixed several race conditions when Stopping/Unregistering torrent managers.&lt;br /&gt;* Fixed issue whereby monotorrent would stop connecting to new peers&lt;br /&gt;* Can now handle torrents with 1,000's of files gracefully.&lt;br /&gt;* Implemented IPV6 support.&lt;br /&gt;* 15% faster hash checking&lt;br /&gt;* Enhanced the accuracy of the ratelimiting code when a global limit is applied&lt;br /&gt;* Per-file progress correctly updated when FastResume data is loaded&lt;br /&gt;* If a file is set to 'Do Not Download', it now will definitely not be downloaded.&lt;br /&gt;* Abort a connection attempt if it takes more than 10 seconds to complete. Some operating systems default to 3 minute timeouts which kills performance.&lt;br /&gt;* Some speed and memory enhancements, as always.&lt;br /&gt;&lt;br /&gt;[Tracker]&lt;br /&gt;* Correctly removes zombie peers (peers who crash before telling the tracker they're stopping)&lt;br /&gt;* Added ability to compare peers based on an arbitrary key rather than only based on IP.&lt;br /&gt;* Minor speed and memory enhancements&lt;br /&gt;&lt;br /&gt;A precompiled binary can be found &lt;a href="http://www.blogger.com/www.monotorrent.com/Files/0.30/monotorrent-0.30.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;A tarball can be found &lt;a href="http://www.monotorrent.com/Files/0.30/monotorrent-0.30.tar.gz"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Coinciding nicely with this is the release of Monsoon 0.11.3. The changelog looks something as follows:&lt;br /&gt;* Adding/Removing/Renaming of labels is completely context-menu driven now&lt;br /&gt;* Can drag and drop torrents to add and remove them from a label.&lt;br /&gt;* Fixed several issues persisting state across application restarts&lt;br /&gt;* Global rate limits can be set by clicking on the labels which display the global download/upload rates&lt;br /&gt;* Now takes advantage of the new FastResume API.&lt;br /&gt;* Correctly invoking libgtk and libX11.&lt;br /&gt;* Now supports nat-pmp through the use of a newer mono.nat&lt;br /&gt;* Multi-select enabled in the file view&lt;br /&gt;* When creating a torrent, hashchecking is skipped if you choose to seed it immediately&lt;br /&gt;* You will always be prompted if you choose to remove/delete a torrent&lt;br /&gt;* Made Monsoon fully translatable&lt;br /&gt;* Added tooltips to the main items&lt;br /&gt;&lt;br /&gt;Monsoon can be gotten via 1-Click install or the &lt;a href="http://download.opensuse.org/repositories/GNOME:/Community/"&gt;GNOME:Community repository&lt;/a&gt;:&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://software.opensuse.org/ymp/GNOME%3ACommunity/openSUSE_10.3/monsoon.ymp"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 158px; height: 45px;" src="http://www.wafaa.eu/uploads/SUSE/1ClickInstall/monsoon1click.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1569478267418369302?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1569478267418369302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1569478267418369302' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1569478267418369302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1569478267418369302'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/04/monotorrent-0.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8745001963085038772</id><published>2008-04-08T18:30:00.004+01:00</published><updated>2008-04-08T18:42:02.238+01:00</updated><title type='text'></title><content type='html'>So, some quick news on the MonoTorrent front. As i said a week or two ago, Monsoon is going to be part of the Suse 11 distribution (woo!). Since then a few things have happened.&lt;br /&gt;&lt;br /&gt;1) Monsoon has hit feature freeze and has been branched. This is the version that will be included with suse. If you want to check out the code and put it through it's paces use this url:&lt;br /&gt;&lt;blockquote&gt;http://anonsvn.mono-project.com/source/branches/monsoon-0.11&lt;/blockquote&gt;If you find any bugs, please use the &lt;a href="https://bugzilla.novell.com/enter_bug.cgi?product=Mono%3A+Tools&amp;amp;component=Monsoon"&gt;novell bugzilla&lt;/a&gt; to file a report. Some time early next week, this will be officially tagged and released. So make any bug reports earlier to increase the chance that they'll be fixed.&lt;br /&gt;&lt;br /&gt;2) MonoTorrent itself has also been branched for it's 0.3 release. If you're a developer using MonoTorrent, check the code out from:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;http://anonsvn.mono-project.com/source/branches/bitsharp-0.30&lt;/blockquote&gt;Bug reports are welcome (as always), same place as above (Except use the MonoTorrent module, not the Monsoon module). I'll have full release notes available when the release is made. Some pretty cool stuff has been done along with the usual bug quashing ;)&lt;br /&gt;&lt;br /&gt;3) Finally, &lt;a href="http://www.mono-project.com/MonoCurses"&gt;mono-curses&lt;/a&gt; has been updated  again to run against MonoTorrent 0.30. So if you want a slick cool ncurses GUI for MonoTorrent, check the code out from:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;http://anonsvn.mono-project.com/source/trunk/mono-curses&lt;br /&gt;&lt;/blockquote&gt;Finally, I just want to add: All Your Torrent Are Belong To Us - Use Monsoon! ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8745001963085038772?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8745001963085038772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8745001963085038772' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8745001963085038772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8745001963085038772'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/04/so-some-quick-news-on-monotorrent-front.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-473443793100186369</id><published>2008-04-04T10:24:00.002+01:00</published><updated>2008-04-04T10:42:01.572+01:00</updated><title type='text'></title><content type='html'>There was a meeting yesterday for openSUSE Gnome, one of the important decisions for the day (in my eyes) was the decision about which BitTorrent client was going to be bundled with suse.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;Torrent default app decision:&lt;br /&gt;Monsoon seems the more dynamic app, good response from its maintainer&lt;br /&gt;Transmission seems to have a better UI, actively developed, although a bit mac centric&lt;br /&gt;BitTorrent-gtk very basic but should work for basic needs&lt;br /&gt;AI: add both monsoon and transmission, monsoon as default&lt;br /&gt;AI: suseROCKS to run tests with both apps&lt;br /&gt;AI: FunkyPenguin to package Transmission today&lt;br /&gt;AI: vuntz to move Transmission and monsoon to autobuild and drop &lt;span class="nfakPe"&gt;gnome&lt;/span&gt;-btdownload&lt;/blockquote&gt;We're in :)&lt;br /&gt;&lt;br /&gt;Monsoon had an open bug report on making it translatable, after the above decision was made, the priority on translations became pretty critical ;) &lt;a href="http://www.meebey.net/jaws/"&gt;Meebey&lt;/a&gt; volunteered to go and get translations all set up and spent a few hours yesterday getting that all sorted and creating the first translation (German). &lt;a href="http://dufoli.wordpress.com/"&gt;Olivier Dufour&lt;/a&gt;, who created a &lt;a href="http://dufoli.wordpress.com/2007/08/20/monotorrent-gui/"&gt;Winforms&lt;/a&gt; based GUI for MonoTorrent has also volunteered to do a French translation.&lt;br /&gt;&lt;br /&gt;So, if anyone out there wants to translate Monsoon into their native language (or at least one they're good at ;) ), please join us on our new irc channel, #monsoon on irc.gimp.net/irc.gnome.org. We'll get you sorted out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-473443793100186369?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/473443793100186369/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=473443793100186369' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/473443793100186369'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/473443793100186369'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/04/there-was-meeting-yesterday-for.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3355205894211289131</id><published>2008-03-29T15:30:00.005Z</published><updated>2008-03-29T16:22:18.902Z</updated><title type='text'></title><content type='html'>I got some &lt;span style="font-weight: bold;"&gt;amazing &lt;/span&gt;news just a few minutes ago. I was just chatting away in #mono about how i was pushing a new release out soon, when anf6 (who is also called Alan) piped up with this:&lt;br /&gt;&lt;blockquote&gt;|anf6|    alan: I've been working on a WebUI for MonoTorrent, most of the functionality works ^_^&lt;br /&gt;|alan|    anf6: are you serious?&lt;br /&gt;|anf6|    yes&lt;br /&gt;|alan|    :o&lt;/blockquote&gt;It turns out that anf6 had been chatting to the developer of the uTorrent WebUI (Directrix) about the possibility of creating a MonoTorrent backend for the UI. Directrix was all for that! So, without further ado, here's the obligatory screenshot:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://users.aber.ac.uk/anf6/monotorrent-webui.PNG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 320px;" src="http://users.aber.ac.uk/anf6/monotorrent-webui.PNG" alt="" border="0" /&gt;&lt;/a&gt;All i can say is: WOW! I can't wait for this to hit release!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;UPDATE: I'd just found out that less than 300 lines of code were needed for the monotorrent -&gt; uTorrent WebUI integration. How slick is that? That includes hosting MonoTorrent and a mini asp.net host to serve the pages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3355205894211289131?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3355205894211289131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3355205894211289131' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3355205894211289131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3355205894211289131'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/i-got-some-amazing-news-just-few.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-9201804337150830845</id><published>2008-03-28T00:27:00.004Z</published><updated>2008-03-28T23:17:27.426Z</updated><title type='text'></title><content type='html'>A few days ago, i heard the suse team are looking for &lt;a href="http://lists.opensuse.org/opensuse-gnome/2008-03/msg00104.html"&gt;a new default torrent client&lt;/a&gt; for Suse 11.0. So of course I jumped in saying "Well, why not Monsoon?". So i fired off &lt;a href="http://lists.opensuse.org/opensuse-gnome/2008-03/msg00113.html"&gt;a response&lt;/a&gt; with a ton of links suggesting that Monsoon was the most amazing torrent client ever and there was no need to review any other clients. Of course that didn't work, but Monsoon has been taken into consideration as a viable candidate.&lt;br /&gt;&lt;br /&gt;Andrew Wafaa took it upon himself to do &lt;a href="http://en.opensuse.org/User:FunkyPenguin/TorrentReview"&gt;a little review of his own&lt;/a&gt;, and Monsoon fared pretty well. I was happy enough with that. Some of the points mentioned on the thread include:&lt;br /&gt;&lt;br /&gt;1) Applications which need wizards are bad. For a default client, you want something rough and ready.&lt;br /&gt;2) Something minimalistic but packed with features is good. No massive complicated apps like Azureus.&lt;br /&gt;&lt;br /&gt;Of course, 2 is a bit of a contradiction. It's quite hard to get both. In GUIs to expose a feature or some statistic, you need a new GUI element. So there's a fine balancing act between keeping the GUI clean and small while still exposing all the nice features in an easy to use way.&lt;br /&gt;&lt;br /&gt;Here's a few screenshots of Monsoon anyway, which i hope expose the full diversity of what it can do.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s1_cleanstart.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s1_cleanstart.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The first time you start Monsoon, you aren't greeted by a wizard, everything is set up nicely for you. Everything is shown to you by default.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s2_twoadded.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s2_twoadded.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When you load torrents, they automatically start. If the files already exist (like the kubuntu one did), monsoon check that file to see how much of it is valid data and then resume the download using it. Standard stuff.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s3_fastrampup.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s3_fastrampup.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The ramp-up time is fairly fast. After 30 seconds Monsoon can connect to 50 people (the default limit for a torrent). Note, it is smart in how it connects to people. It doesn't have more than 5 pending connection attempts at any one time. This prevents overloading of cheaper routers.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s4_minimode.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s4_minimode.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;For the people out there who want a really minimalistic client, you can hide everything. The GUI can be reduced down to something even slimmer than &lt;a href="http://www.transmissionbt.com/screenshots.php"&gt;Transmission&lt;/a&gt; should you so wish. There is also plans to implement a mini-mode, which will consist of just a single progress bar per torrent.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s6_speedlimits.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s6_speedlimits.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So what if you want to set the global upload/download rate? Why, it's simple! Just click on the global upload/download speed indicators and away you go.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.monotorrent.com/s7_labels.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: center; cursor: pointer; width: 320px;" src="http://www.monotorrent.com/s7_labels.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to organise your torrents, it's just a simple context menu. You can add/remove/rename the labels with a right click, or double click. Slick, eh? There is also a patch in the works which will allow you to drag'n'drop torrents from the main view into a label and drag'n'drop them between labels. This will replace the existing way of doing it which involves ticking checkboxes in the 'Options' menu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-9201804337150830845?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/9201804337150830845/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=9201804337150830845' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/9201804337150830845'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/9201804337150830845'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/few-days-ago-i-heard-suse-team-are.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2702526899208362607</id><published>2008-03-26T21:37:00.002Z</published><updated>2008-03-26T21:40:12.967Z</updated><title type='text'></title><content type='html'>There's now a proper issue tracker for Monsoon and MonoTorrent. It's being hosted on the novell bugzilla (thanks guys!) so file your bugs there if at all possible. It makes it easier for me to monitor and record what exactly goes on.&lt;br /&gt;&lt;br /&gt;The issue tracker can be found at in the 'Monsoon' section of Mono:Tools. If you have a bugzilla account already, this link should bring you right to the submission form:&lt;br /&gt;&lt;br /&gt;https://bugzilla.novell.com/enter_bug.cgi?classification=&amp;amp;product=Mono%3A+Tools&amp;amp;submit=Use+This+Product&amp;amp;component=Monsoon&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2702526899208362607?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2702526899208362607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2702526899208362607' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2702526899208362607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2702526899208362607'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/theres-now-proper-issue-tracker-for.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2118121130677596988</id><published>2008-03-09T00:57:00.002Z</published><updated>2008-03-09T01:12:39.327Z</updated><title type='text'></title><content type='html'>This is just a shout out to any budding &lt;a href="http://www.dictionary.net/artiste"&gt;Artistes&lt;/a&gt;, (especially that guy who did all those cool looking MonoDevelop icons): I'm looking for a nice logo for Monsoon. I have no ideas in mind, so any suggestions are welcome. Drop a comment here or send me an email if you have anything to show me :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2118121130677596988?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2118121130677596988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2118121130677596988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2118121130677596988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2118121130677596988'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/this-is-just-shout-out-to-any-budding.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-170332103426560611</id><published>2008-03-09T00:06:00.003Z</published><updated>2008-03-09T11:06:38.783Z</updated><title type='text'></title><content type='html'>This is just a quick follow up from my last post. I decided to do a few benchmarks to see what transmission was like as compared the Monsoon (the GTK# gui for the MonoTorrent library). I've heard about tranmission before, but i've never used it. I was pretty shocked by the results. This is a quick summary of what I found:&lt;br /&gt;&lt;br /&gt;1) Memory usage.&lt;br /&gt;Transmission used less memory than MonoTorrent. That came as no surprise to me. No matter how efficently I code, i cannot ever get as efficent as an app written in C/C++. Mostly because in a .NET based app, the .NET runtime/jit must be loaded which consumes a few MB of memory. Percentage wise, yes, the difference is huge, but if you take a look at overall memory, it's not massive.&lt;br /&gt;&lt;br /&gt;MonoTorrent: 35 Res / 20 Shared&lt;br /&gt;Transmission: 20 Res / 13 Shared&lt;br /&gt;&lt;br /&gt;This figure was gotten after extensively using the GUI by opening menu's and flicking around pages.&lt;br /&gt;&lt;br /&gt;2) Hashing performance&lt;br /&gt;I suppose another important metric - how long does it take to hash a complete file.&lt;br /&gt;MonoTorrent: 95 seconds&lt;br /&gt;Transmission: 85 seconds&lt;br /&gt;&lt;br /&gt;This difference is fairly negligible. A full hash will rarely be performed. However, i suppose it was worth measuring. One optimisation i could make in monotorrent which would reduce that gap slightly would be if i read the next chunk of data off disk asynchronously while hashing the current chunk. At the moment it's all sequential, but i suppose it could easily enough be made parallel. It shouldn't make a huge difference though.&lt;br /&gt;&lt;br /&gt;3) Download speeds&lt;br /&gt;This would be the most important metric. This is where the biggest surprise came.&lt;br /&gt;MonoTorrent: 15 seconds - 400kB/sec, 30 seconds - stabilised at 550-600kB/sec (maxed my connection) and connected to 50 people, the maximum allowed by my settings.&lt;br /&gt;Transmission: 15 minutes: still at less than 50kB/sec and still only connected to 6 peers.&lt;br /&gt;&lt;br /&gt;What am i doing wrong with transmission to make it so slow? It's not NAT (even though transmissions uPnP support cannot detect my uPnP enabled router) because i manually forwarded it in the end. I was using both svn head (r97353) of MonoTorrent and svn head (r5227) of Transmission when i ran this quick test.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;EDIT: Just as i finished this, Transmission managed to connect to 3 additional people and one of them had a massive upload capacity which let Transmission reach ~480kB/sec. Still, why did that take so long? These results were consistent every time i started/stopped both transmission and Monsoon. Monsoon consistently maxed out my connection quickly whereas transmission consistently took forever to even break 40 kB/sec.&lt;br /&gt;&lt;br /&gt;UPDATE: I just want to add that i tested using the &lt;a href="http://torrent.ubuntu.com:6969/file?info_hash=%E0.%5E%11zZ%90%80%D5R%A1%1F%A6u%DE%86%8A%05%FEq"&gt;ubuntu-7.10-desktop-i386.iso&lt;/a&gt; torrent on Suse 10.2.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-170332103426560611?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/170332103426560611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=170332103426560611' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/170332103426560611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/170332103426560611'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/this-is-just-quick-follow-up-from-my.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2940164214544325166</id><published>2008-03-04T01:28:00.004Z</published><updated>2008-03-04T08:53:31.171Z</updated><title type='text'></title><content type='html'>So, a thought struck me. Ubuntu currently has a &lt;a href="https://help.ubuntu.com/community/BitTorrent#head-6029dfa310d7411f1bda5a00c2d807f5bff346b5"&gt;horrible default bittorrent client&lt;/a&gt;. It's about to be &lt;a href="https://wiki.ubuntu.com/HardyHeron/Alpha4#head-f812ee3e3577cf89525ac4bcddbba7df8c5b84fc"&gt;replaced&lt;/a&gt; by &lt;a href="http://www.transmissionbt.com/"&gt;Transmission&lt;/a&gt; which, while definately a step up in the world, is still feature lacking (in my opinion) as compared to other available clients, most especially the &lt;a href="http://buchan.esoteriq.org/weblog/2007/08/20/monotorrent-gtk-interface-summer-wrap-up/"&gt;great work done on the GTK Gui &lt;/a&gt;for MonoTorrent.&lt;br /&gt;&lt;br /&gt;I propose that that the MonoTorrent GUI should be bundled with Ubuntu. MonoTorrent supports everything transmission does (except for Peer Exchange[1] and scheduling[2]) along with these extra cool features:&lt;br /&gt;&lt;br /&gt;* &lt;a href="http://www.bittorrent.org/beps/bep_0006.html"&gt;Fast Peer Extensions&lt;/a&gt; - allow you to start a torrent faster&lt;br /&gt;* &lt;a href="http://xbtt.sourceforge.net/udp_tracker_protocol.html"&gt;Udp Tracker Protocol &lt;/a&gt;- allows you to use the bandwidth saving UDP protocol for tracker communications&lt;br /&gt;* &lt;a href="http://www.rasterbar.com/products/libtorrent/extension_protocol.html"&gt;LibTorrent Extension Protocol&lt;/a&gt; - Ok, this is only partially supported. While it's implemented in SVN, it's not fully tested or enabled by default yet.&lt;br /&gt;* &lt;a href="http://buchan.esoteriq.org/weblog/2007/08/20/monotorrent-gtk-interface-summer-wrap-up/"&gt;RSS Feeds&lt;/a&gt; - Automagic downloading from RSS feeds. (see video)&lt;br /&gt;* &lt;a href="http://buchan.esoteriq.org/weblog/2007/08/20/monotorrent-gtk-interface-summer-wrap-up/"&gt;Organise downloads into tags (see video)&lt;/a&gt;&lt;br /&gt;*  &lt;a href="http://wiki.depthstrike.com/index.php/P2P:Protocol:Specifications:Multitracker"&gt;Multi-Tracker Protocol support &lt;/a&gt;- If the main tracker is down, no worries! You can use the backup ones!&lt;a href="http://wiki.depthstrike.com/index.php/P2P:Protocol:Specifications:Multitracker"&gt;&lt;/a&gt;&lt;br /&gt;* Retarget Files before/while they are downloading i.e. you can rename a file as you download!&lt;br /&gt;* Can monitor a folder and automatically load and download new torrents&lt;br /&gt;* Minimizes to the gnome notification area.&lt;br /&gt;&lt;br /&gt;The GUI including all supporting libs is a 384 kB package, and i think in the region of 600kB when installed[3]. What we're looking at is 600kB for a fully fledged, feature rich, uber snazzy bittorrent client. The best thing is [b]all[/b] the dependencies are already on the live CD.&lt;br /&gt;&lt;br /&gt;So, if there are any Ubuntu devs out there, would you like to consider MonoTorrent as the default bittorrent app? If not, why not? I'll gladly do my best to fix any issues you have. Leave your comments below and lets get some discussion going!&lt;br /&gt;&lt;br /&gt;[1] It'd take a few hours to complete support for this with tests.&lt;br /&gt;&lt;br /&gt;[2] I did actually receive a &lt;a href="http://monotorrent.21.forumer.com/viewtopic.php?t=69&amp;amp;start=15&amp;amp;sid=3f3ffe46c5546ff809daa8e3684be112"&gt;patch to implement this &lt;/a&gt;a long while ago, but in the end, it fell by the wayside. I am a horrible person :(&lt;br /&gt;&lt;br /&gt;[3] The package can be reduced a fair bit by splitting out the Tracker component of MonoTorrent using the &lt;a href="http://www.mono-project.com/Linker"&gt;Linker&lt;/a&gt; and removing bundled libraries which are &lt;a href="http://packages.ubuntu.com/hardy/libndesk-dbus1.0-cil"&gt;available as packages&lt;/a&gt; nowadays. I'd guesstimate at least 100kB can be saved from the installed size from these optimisations. 70kB by removing the bundled DBus stuff and at the very least, 30kB by shrinking MonoTorrent.dll.&lt;br /&gt;&lt;br /&gt;UPDATE: I'd just like to point out that an official name has been chosen for the GUI: [b]Monsoon[/b]. The next release will have everything rebadged to this name.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2940164214544325166?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2940164214544325166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2940164214544325166' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2940164214544325166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2940164214544325166'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/03/so-thought-struck-me.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7854211202959161001</id><published>2008-02-27T02:54:00.004Z</published><updated>2008-02-27T02:58:55.977Z</updated><title type='text'></title><content type='html'>Its that time of the year when all us Europeans gather together for a big competition to decide who has the most friends. It's called the &lt;a href="http://www.eurovision.tv/"&gt;Eurovision&lt;/a&gt;. Last year, we did spectacularly. We were within inches of achieving our goal of finishing with zero points until those albanian b******s decided to give us 5 points. I swear, that was our plan all along!&lt;br /&gt;&lt;br /&gt;Anyway, we learned our lesson last year. This time we're going to put in an entry that simply cannot fail! Ladies and gents, i present to you, &lt;a href="http://en.wikipedia.org/wiki/Dustin_the_Turkey"&gt;Dustin The Turkey&lt;/a&gt;, singing &lt;a href="http://www.dailymotion.com/relevance/search/dustin/video/x4h9m5_2008-ireland-dustin-the-turkey_music"&gt;our Eurovision entry&lt;/a&gt;. Save us all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7854211202959161001?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7854211202959161001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7854211202959161001' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7854211202959161001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7854211202959161001'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/its-that-time-of-year-when-all-us.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8168801629530533142</id><published>2008-02-22T00:27:00.002Z</published><updated>2008-02-22T01:17:54.220Z</updated><title type='text'></title><content type='html'>After my &lt;a href="http://monotorrent.blogspot.com/2008/02/it-was-on-28th-january-that-i-released.html"&gt;rather lengthy post&lt;/a&gt; about all the cool stuff I was doing with MonoTorrent, i was asked this question:&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;I'm very curious about what are the real possibilities of MonoTorrent library. I mean, I always thought Bittorrent was just a protocol for P2P file exchange, but when I see so much modularity on MonoTorrent... The question is, how can MonoTorrent help me as a programmer?&lt;/blockquote&gt;Well, that's a pretty good question.&lt;br /&gt;&lt;br /&gt;I'm going to talk briefly about the three important new features that are (or will soon) be available:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1) Custom Peer Connections&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;Aim: To allow the user to route all peer traffic over whatever medium they want.&lt;br /&gt;&lt;br /&gt;Use case 1: You are writing an application which requires that all connections are encrypted end-to-end. This means you have complicated routines to set up the connections. You want to add the ability to transfer files.&lt;br /&gt;&lt;br /&gt;Use case 2: You want to route bittorrent traffic over a network such as &lt;a href="http://www.torproject.org/"&gt;Tor&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Use case 3: Restrictive firewall. Suppose only certain kinds of traffic are allowed through a particular firewall you have. You can push the bittorrent traffic inside a different protocol to allow it to pass through the firewall.&lt;br /&gt;&lt;br /&gt;In case 1 and 2, we assume that normally it is impossible for monotorrent to create the connections itself. So what you do is create the necessary connections manually, then wrap them in the &lt;a href="http://anonsvn.mono-project.com/viewcvs/*checkout*/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/PeerConnections/IConnection.cs"&gt;IConnection&lt;/a&gt; interface and pass them directly into monotorrent. Everything else is automatic.&lt;br /&gt;&lt;br /&gt;For case 3, it might be possible to implement a HttpPeerConnection, whereby you push the bittorrent messages inside of a HTTPWebRequest and then send that to the remote peer and he decodes it back into it's orginal form for processing. Note: I don't recommend you do this to avoid getting around a firewall in work. Also, if you do implement something like that, only other clients who implemented that feature would be able to communicate with you, but it is an interesting application.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2) Custom Trackers&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Aim: To allow someone to add peer data into the engine manually.&lt;br /&gt;&lt;br /&gt;Use case 1: You want to type in your friends ip:port so you can directly connect to him&lt;br /&gt;&lt;br /&gt;Use case 2: In your application, you keep a list of people you can/should connect to, and you know that MonoTorrent can create connect to them directly.&lt;br /&gt;&lt;br /&gt;Use case 3: You want to implement an alternative peer source like the bittorrent DHT protocol&lt;br /&gt;&lt;br /&gt;In all these cases, you just have to implement two simple methods to allow MonoTorrent to get data from your source. For case 1, whenever you enter the ip:port combination, you just raise an event with that IP:Port stored in the event args.&lt;br /&gt;&lt;br /&gt;Case 1 is a manual event, and so MonoTorrent would never query that 'Tracker' for peer details, but for case 2 and 3, it's more fun. All you do is wait for MonoTorrent to call the 'Announce' method on your tracker, then you go off and find the peers and raise the AnnounceComplete event when you're ready. It's as easy as pie.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3) Custom Messages&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Use case 1: You're lazy and don't want to define your own messaging protocol or message format and want things to 'Just Work (tm)', so just piggy back all your messages over the MonoTorrent system.&lt;br /&gt;&lt;br /&gt;Use case 2: You want/need to send a few pieces of information between torrent clients but don't want to have to set up a separate communication channel to do so in.&lt;br /&gt;&lt;br /&gt;Use case 3: You want to make Bittorrent 2.0.&lt;br /&gt;&lt;br /&gt;You need to implement a &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/Messages/Message.cs?view=markup"&gt;slightly more complex interface&lt;/a&gt; than the previous two examples, but it's nothing amazingly difficult. The helper methods for reading/writing the basic types from a byte[] are all there to make creating a message that much easier. So you're left with only having to implement a mere 4 abstract methods. Whilest this feature isn't finished as yet, it will be as simple as defining your custom message, then making a call like:&lt;br /&gt;&lt;br /&gt;byte messageId = 9;&lt;br /&gt;Message.Register(messageId, delegate { return new MyCustomMessage(); });&lt;br /&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;Message.register(messageId, delegate (TorrentManager m) {&lt;br /&gt;    return new MyCustomMessage(m.SomeData, m.OtherData);&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;Once that's done, you can queue up your message to be sent and react to it's arrival with ease. What you might want to send, i don't know. But the ability is there for you to send it.&lt;br /&gt;&lt;br /&gt;So in brief, MonoTorrent can be used as a drop in replacement for either '1 to 1', '1 to many', 'many to 1' or even 'many to many' transfers. I'm not saying it *should* be used for all those cases, it may be the heavy weight alternative to a simple Socket.Send, but the possibility is there. You can send customised messages to the other clients so that they can perform custom logic and you can insert peer details easily from any source.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8168801629530533142?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8168801629530533142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8168801629530533142' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8168801629530533142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8168801629530533142'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/after-my-rather-lengthy-post-about-all.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5044407845572757816</id><published>2008-02-18T00:59:00.002Z</published><updated>2008-02-18T02:08:40.545Z</updated><title type='text'></title><content type='html'>It was on 28th January that i released  &lt;a href="http://monotorrent.blogspot.com/2008/01/monotorrent-0.html"&gt;MonoTorrent 0.20&lt;/a&gt;. Here we are, less than a month later and already i have some cool new stuff to blog about, this is a lot better than the &lt;a href="http://monotorrent.blogspot.com/2007/07/tis-that-time-of-month-again.html"&gt;time gap&lt;/a&gt; between the last two MonoTorrent releases.&lt;br /&gt;&lt;br /&gt;So, what's new? LOTS! Let me give a brief overview of what's possible nowadays.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1) Custom Peer Connections&lt;/span&gt;&lt;br /&gt;It's now possible to plug monotorrent directly into another application and have monotorrent uses connections created by that application. The advantage of this is that you can tunnel MonoTorrent through anything now. You can create an encrypted connection whatever way you want and then just pass it straight into monotorrent and it'll be used. This also makes it easy to push traffic through networks like Tor (or whatever).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2) Custom Trackers&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;First, i'm not talking about the server aspect of MonoTorrent. I'm talking about the clientside class used to deal with announcing/scraping to a server. It's now possible to (very easily) implement your own tracker class. What use is this you say? Well, quite a bit actually! If you want to be able to add peer data into monotorrent (but not active connections as i described above) you can subclass the MonoTorrent.Client.Tracker class and do it from there.&lt;br /&gt;&lt;br /&gt;Suppose you want to implement a new protocol for client-tracker communications. If you want MonoTorrent to support it, just inherit from MonoTorrent.Client.Tracker, register that implementation with the TrackerFactory and you're done! You don't have to modify one letter of MonoTorrent source code to add in support for your new protocol.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3) Custom Messages&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;Ok, so technically speaking, this still isn't possible yet, however all the architecture to enable it is there. I just need to expose it publicly in a nice way. Anyway, when i &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp/src/?rev=94814&amp;amp;view=log"&gt;implemented support&lt;/a&gt; for the &lt;a href="http://www.rasterbar.com/products/libtorrent/extension_protocol.html"&gt;libtorrent extension protocol&lt;/a&gt;, I realised that the way i handled peer messages just wouldn't work. Basically i used a giant switch statement to select the correct message to decode. This meant that all messages had to be defined within MonoTorrent and had to exist at compile. There was no way for the user to define their own message type.&lt;br /&gt;&lt;br /&gt;So, instead i &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/Messages/PeerMessage.cs?rev=95951&amp;amp;r1=95315&amp;amp;r2=95951"&gt;updated the API&lt;/a&gt; to use some .NET 2.0 loveliness to allow for custom messages to be defined outside of MonoTorrent and still allow them to be decoded and handled correctly.&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4) Custom Writers&lt;/span&gt;&lt;br /&gt;If i was to pick a favourite change out of the list, it would be the custom messages, but this comes a close second ;) You can now define you're own 'PieceWriter' class. What this allows you to do is to redirect disk read/write requests wherever you want them to go. Of course, the logical first implementation is a &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/PieceWriter/MemoryWriter.cs?view=markup"&gt;MemoryBuffer&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Yes, MonoTorrent will now cache reads/writes in memory where prudent. For example, each 'piece' you download consists of multiple 'blocks'. Previously each block was written straight to disk as it arrived. When all the blocks had arrived, they were all read straight back off the disk so that a SHA1 hash of the piece could be generated. This was very inefficient.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5) NUnit test-able!&lt;/span&gt;&lt;br /&gt;Yeah, so this isn't really a new feature. In fact, it's something i should've been doing all along, but due to the implementation of MonoTorrent, certain things were far too awkward to test. Previously, in order to test internal logic i'd have to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create a fake .torrent file and write it to disk.&lt;/li&gt;&lt;li&gt;Create fake data and write it to disk so that it can be 'downloaded' by one client and 'seeded' by the other&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Instantiate a MonoTorrent.Tracker and load the torrent into it&lt;/li&gt;&lt;li&gt;Instantiate two MonoTorrent.Clients and load the torrent into them.&lt;/li&gt;&lt;li&gt;Hit 'start' and measure what little i could&lt;/li&gt;&lt;/ol&gt;This method means that it's quite possible temp files will be left on the disk - not good. Also, if something does go wrong, there is no way for me to find out exactly what went wrong. The Client would close the connection and all i'd get would be a ConnectionClosed event. Finally, it was just plain awkward! Creating a fake torrent and the necessary fake files was a pain in the ass.&lt;br /&gt;&lt;br /&gt;Once the above changes had been complete, it became so much easier. Now I create a fake torrent in memory (no need to write it to disk!). I create one MonoTorrent.Client using the in-memory torrent and redirect its reads/writes using a custom PieceWriter. This writer just peforms in-memory operations to fill in the fake data as required. I then create a custom PeerConnection and pass one end of the connection into the engine and hold the other end myself.&lt;br /&gt;&lt;br /&gt;This means when i send a message into the engine, i can receive the reply and verify that it is as expected using NUnit tests. Pretty sweet.&lt;br /&gt;&lt;br /&gt;In other news, the following other nifty things have been implemented:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A) Udp Tracker support&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;I finally got around to implementing UDP tracker support. It has been requested by a few people, so here it is. Enjoy!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;B) Better FastResume support&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;There were a few issues with the old fast resume support. In certain advanced use cases, fast resume support just plain didn't work. This has now been fixed. It's now up to the user to handle where/how fast resume data is stored. This is nearly finished, but not quite. It's on my TODO list for the coming week.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;C) Message Bundles&lt;/span&gt;&lt;br /&gt;An entirely invisible change, but one i want to talk about. Whenever a piece is downloaded succesfully, a 'have' message should be sent to all peers you're connected to. This is used as a way to keep track of who has what. However, have messages are tiny and typically you'd complete a few pieces a second. This means that several times a second, for each connected peer, a single 12 byte message has to be sent. This is hardly worth the effort of calling socket.BeginSend!&lt;br /&gt;&lt;br /&gt;So, why not encode several messages at a time into the send buffer and send em all at once? Well, that just doesn't work. The architecture just does not allow for that to happen. One message and one message alone can be encoded and sent at a time.&lt;br /&gt;&lt;br /&gt;The solution? The &lt;a href="http://anonsvn.mono-project.com/viewcvs/trunk/bitsharp/src/MonoTorrent/MonoTorrent.Client/Messages/MessageBundle.cs?view=markup"&gt;MessageBundle&lt;/a&gt;. The MessageBundle is just another implementation of the PeerMessage base class, except the message bundle can store multiple messages inside it. When you call Encode() on the bundle, it encodes all it's stored messages into the buffer. This allows me to send multiple messages at a time, but for all intents and purposes, MonoTorrent thinks i'm sending one.&lt;br /&gt;&lt;br /&gt;Now HaveMessages are delayed and bundled together.&lt;br /&gt;&lt;br /&gt;I'll release all this fanciness sometime in the near future.&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5044407845572757816?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5044407845572757816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5044407845572757816' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5044407845572757816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5044407845572757816'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/it-was-on-28th-january-that-i-released.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2194096527736966710</id><published>2008-02-07T00:14:00.000Z</published><updated>2008-02-07T00:26:45.840Z</updated><title type='text'></title><content type='html'>What kind of people do credit card companies like to give credit cards to? People who always pay their bills? People who occasionally forget and pay a few days late? How about people who forget quite often, or can't afford to pay the full bill in one month?&lt;br /&gt;&lt;br /&gt;Well, it looks like the last group is exactly who &lt;a href="http://www.egg.com"&gt;Egg&lt;/a&gt; want! They have issued notice to 7% of their customers informing them that due to their bad credit rating, their credit card is being cancelled with no right to appeal. &lt;a href="http://news.bbc.co.uk/2/hi/business/7222336.stm"&gt;According to a fair number of customers&lt;/a&gt;, they have excellent credit ratings and always paid their bill in full and on time. It looks like they're being dumped because they're just not profitable.&lt;br /&gt;&lt;br /&gt;It's kind of ironic that the 'best' customers are the ones being dumped in favour of customers who may not actually be able to pay their bills.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2194096527736966710?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2194096527736966710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2194096527736966710' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2194096527736966710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2194096527736966710'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/what-kind-of-people-do-credit-card.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4692483122049794718</id><published>2008-02-03T16:41:00.000Z</published><updated>2008-02-03T17:03:38.871Z</updated><title type='text'></title><content type='html'>So, after &lt;a href="http://monotorrent.blogspot.com/2008/02/i-recently-changed-internet-providers.html"&gt;asking around&lt;/a&gt; about my new internet situation, i was informed that there was no way for me to set it up as i wanted to. I had to have my WRT54G as the slave and the new neatgear WGR614 as my master device. With that in mind, i reattached the netgear to my new NTL broadband and put the WRT54G back to it's old position beside the two wired computers.&lt;br /&gt;&lt;br /&gt;Just in case anyone else needs to do something like this, this is how you create a wireless bridge between two routers:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Equipment needed:&lt;/span&gt;&lt;br /&gt;1) A router which supports the 'Client' mode. In my case, a linksys WRT54G with the &lt;a href="http://openwrt.org/"&gt;openwrt&lt;/a&gt; firmware, the 'slave' router.&lt;br /&gt;2) Any other wireless router, the 'master' router.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Setup:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;Master Router&lt;/span&gt;&lt;br /&gt;1) Connect it to your internet box (be it an ADSL modem or whatever).&lt;br /&gt;2) Set up the wireless connection as per normal.&lt;br /&gt;3) Enable uPnP and DHCP as required.&lt;br /&gt;&lt;br /&gt;This router will accept all wireless connections, and can also accept wired connections via it's LAN ports as per usual. All port forwarding should be done via this router.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Slave router&lt;/span&gt;&lt;br /&gt;1) Connect any wired computers to LAN ports on the router. Make sure nothing is connected to the WAN port. You won't be using this.&lt;br /&gt;2) Set the wireless to use the same SSID (network name) and frequency as the master router.&lt;br /&gt;3) Set the wireless connection to use 'Client' mode. In the case of openwrt, the correct mode is called 'Client (bridged)'. In this mode, the router will look for the wireless network with the SSID you specified and connect to it like a regular laptop.&lt;br /&gt;4) Disable uPnP and DHCP on this router.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Note: The slave router cannot accept wireless connections anymore. It can only forward &lt;/span&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;wired&lt;/span&gt; computers to your master router.&lt;br /&gt;&lt;br /&gt;With that done, my two desktops remain in the same old room as always and connect wirelessly to my master router, thus giving them access to the net and having them accessible to the wireless clients.&lt;br /&gt;&lt;br /&gt;The only problem is that the new router is a bit limited in it's configuration and so will end up being replaced in the near future. For the moment, it does the job fine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4692483122049794718?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4692483122049794718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4692483122049794718' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4692483122049794718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4692483122049794718'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/so-after-asking-around-about-my-new.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-5104889938578795664</id><published>2008-02-01T22:01:00.000Z</published><updated>2008-02-01T22:06:31.370Z</updated><title type='text'></title><content type='html'>I recently changed internet providers, and i have this situation:&lt;br /&gt;&lt;br /&gt;The new internet arrives in via a cable in my front room. There are two desktops in the house, which are in the back room. The old internet came through a hole in the wall into this room, so everything was peachy.&lt;br /&gt;&lt;br /&gt;I'm now in possession of a linksys WRT54G (with openwrt firmware) and a neatgear WGR614 V7. What i want to do is to bridge the two wireless routers so that i have a single network containing my wireless+wired computers.&lt;br /&gt;&lt;br /&gt;Does anyone know if there's a modified/alternate firmware for the netgear router which supports 'client' mode in wireless networks so i can just make it connect to my main router (the linksys). I'd much prefer to use this as my main router as opposed to the netgear. Any advice on getting this set up would be great. Ideally i wouldn't have to buy new hardware, but if it comes to it, thats what i'll do ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-5104889938578795664?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/5104889938578795664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=5104889938578795664' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5104889938578795664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/5104889938578795664'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/02/i-recently-changed-internet-providers.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-3993495181765453697</id><published>2008-01-28T13:28:00.000Z</published><updated>2008-01-28T13:30:16.659Z</updated><title type='text'></title><content type='html'>MonoTorrent 0.20 has just been shoved unwillingly out of it's cosy spot in SVN into full public view. The last time i tagged and released was way back on July 4th. Far too long ago! Full details of the release can be read &lt;a href="http://monotorrent.21.forumer.com/viewtopic.php?t=159"&gt;here&lt;/a&gt;. Download links &lt;a href="http://monotorrent.21.forumer.com/viewtopic.php?t=159"&gt;here&lt;/a&gt; aswell.&lt;br /&gt;&lt;br /&gt;Here's the changelog:&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 18px; line-height: normal;"&gt;Client&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Features&lt;/strong&gt;&lt;br /&gt;Linear piece picking possible&lt;br /&gt;- This mode of operation chooses pieces towards the start of the file where possible. This should not be a publicly settable option in a GUI. It could be automatically set until the first 20 pieces in a torrent are received to allow for previewing. For general usage, this is a very inefficient way to download.&lt;br /&gt;Full choke/unchoke algorithm implemented (Andy Henderson)&lt;br /&gt;    - Full tit-for-tat algorithm implemented. This improves download rates and upload rates and is pretty slick.&lt;br /&gt;Sub-optimal implementation of code used to connect to peers made optimal&lt;br /&gt;    - Fixed corner case where a connection wouldn't be cleaned up correctly&lt;br /&gt;    - Significantly faster startup performance in cases where remote peers don't support encryption and encryption is enabled&lt;br /&gt;Rate limiting for Disk IO implemented&lt;br /&gt;Configurable amount of open filestreams&lt;br /&gt;Reduced disk thrashing when multiple torrents hashing simultaneously&lt;br /&gt;Files are not preallocated anymore&lt;br /&gt;TorrentManager will never block when .Start() is called&lt;br /&gt;The Tracker base class made more extensible (Eric Butler)&lt;br /&gt;- An example usage would be if a person had peer details stored in a database. They could implement a 'DataBaseTracker' class, register it with the engine, then monotorrent could announce to that tracker and retrieve peers from the database. Thanks to Eric, this is hugely simplified and is now trivial to implement.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Bug Fixes&lt;/strong&gt;&lt;br /&gt;&lt;em&gt;Piece Requesting&lt;/em&gt;&lt;br /&gt;Fixed bug where a piece could be requested twice&lt;br /&gt;When a corrupt piece is received, ensure all contributing peers are marked&lt;br /&gt;Fixed a possible null ref when removing pending piece requests when a connection is closed&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Torrent Creator&lt;/em&gt;&lt;br /&gt;When creating an (optional) MD5 hash in the torrent creator, open the files in read-only mode (Roger Zander)&lt;br /&gt;Creating torrents no longer spins up an extra thread when using the synchronous method (Eric Butler)&lt;br /&gt;Fixed possible race conditions when creating a torrent using the asynchronous methods&lt;br /&gt;Private key is retained when creating torrents&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Misc&lt;/em&gt;&lt;br /&gt;Per-file progress is updated correctly.&lt;br /&gt;Fixed detection of when a tracker supports scrape - It should have been case insensitive&lt;br /&gt;Added extra check to make sure a scrape request is only performed if the tracker supports scraping&lt;br /&gt;Added extra logic checks to help prevent the loading of incorrect fastresume data&lt;br /&gt;When a peer sends a HaveAll message - it is correctly marked as being a seeder&lt;br /&gt;PieceHashed event fired under all circumstances&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-size: 18px; line-height: normal;"&gt;Tracker&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;The MonoTorrent.Tracker got a major rewrite. Highlights include:&lt;br /&gt;- more than 1000x memory reduction for trackers hosting a large number of torrents. 1000 torrents can be hosted in ~30kB. Previously would have required ~30MB.&lt;br /&gt;    - Faster more efficient announce handling. For large trackers, lookups are significantly faster&lt;br /&gt;- Vastly simplified announce handling. It is now trivial to write code to handle incoming connections from differing sources. A typical example would be to create an AspNetHandler to handle connections in an ASP.NEt project. It requires less than 20 lines of code to be written.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It should be fairly solid, with a lot of cool feature enhancements under the hood. There are a few cool things which i managed to implement this time round like rate limiting disk IO, not pre-allocating 100% of the file on startup, being able to properly handle a torrent with 100,000 files in it. Good stuff.&lt;br /&gt;&lt;br /&gt;Plans for the future are pretty cool aswell. I'll blog more about that later though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-3993495181765453697?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/3993495181765453697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=3993495181765453697' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3993495181765453697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/3993495181765453697'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/01/monotorrent-0.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1873943597462347891</id><published>2008-01-26T15:42:00.000Z</published><updated>2008-01-27T15:47:24.517Z</updated><title type='text'></title><content type='html'>Today i'm going to talk about optimisation a little, this is mostly in the MonoTorrent Tracker context, but some of the ideas still apply to other situations.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Sometimes you hear people talking about how they want to optimise their code to make it faster, and they ask questions like 'what's the fastest way to multiply by 2, bitshift or regular multiplication?', or 'Should I null out objects the very second I'm finished using them or just wait for them to fall out of scope?'. These kind of questions are probably the epitome of premature optimisation. These kind of optimisations won't make your application faster or better.&lt;br /&gt;&lt;br /&gt;I had done a lot of work on the Tracker code (the 'server' portion of the bittorrent specification) recently. I had precomputed values which are used regularly, i had optimised the hashcodes used in my dictionary lookups, i had reduced the amount of data that needs to be kept in-memory significantly. I wanted to see what effect all this had on the actual running of the tracker. This was the first time i had run a benchmark on the tracker.&lt;br /&gt;&lt;br /&gt;I went on the net, found a big tracker and checked it's stats. Using these, i decided that this was benchmark was representative of a heavy real-world load:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1) Load 2000 torrents into the engine, each of which contains 1000 peers.&lt;br /&gt;2) Hammer the server with 1000 requests a second choosing a random torrent and random peer from the list and make a fake request from them to the server.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, once that was written, i fired up the tracker and ran the benchmark. My system locked up and i was forced to hard-reboot. What had gone wrong! I started the benchmark again, but monitored memory and CPU usage carefully. I was surprised to find that memory usage was rocketing, which is what caused the massive slowdown of my system! I couldn't understand why. I did a few quick calculations to figure out how much memory i'd expect the tracker to use, they put final memory usage at far less than 300MB. I quickly whipped out my allocation profiler and began optimising. Here are the 'issues' i fixed:&lt;br /&gt;&lt;br /&gt;1) The objects i was using as the 'key' in a dictionary were being recreated every time i used them. Typically this means that for every request to the tracker, at least a dozen complex objects were created/garbage collected needlessly. In this kind of scenario, the objects should be declared as 'static readonly' and reused. I implemented this change.&lt;br /&gt;&lt;br /&gt;The benchmark still couldn't run.&lt;br /&gt;&lt;br /&gt;2) I decided that the next problem was that i was pre-generating two byte[] for each peer when they were added to the server. This was so that a request could be fulfilled by simply copying pregenerated byte[]. I changed this to generate the byte[] at request time rather than storing it in memory. I expected this to fix the issue.&lt;br /&gt;&lt;br /&gt;The benchmark still couldn't run after this change.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3) Finally, i noticed there were a huge number of hashtable related objects being retained in memory. This was a bit weird. There shouldn't have been that many around. A few minutes of checking the code made me realise that the probably cause was keeping a NameValueCollection object in-memory for each peer. I rewrote the peer class to extract the necessary information from the collection and then dump it, rather than holding a reference to it.&lt;br /&gt;&lt;br /&gt;The benchmark could run!&lt;br /&gt;&lt;br /&gt;Now, the memory improvements were gigantic. Previously i had stats like this:&lt;br /&gt;Active Torrents: 500&lt;br /&gt;Active Peers per Torrent: 500&lt;br /&gt;Memory: 350MB&lt;br /&gt;&lt;br /&gt;Now i had:&lt;br /&gt;Active Torrents: 500&lt;br /&gt;Active Peers per Torrent: 500&lt;br /&gt;Memory: 40MB&lt;br /&gt;&lt;br /&gt;Active Torrents: 2000&lt;br /&gt; Active Peers per Torrent: 1000&lt;br /&gt; Memory: 140MB&lt;br /&gt;&lt;br /&gt;There is no way in hell I'd ever have found the cause of the issue unless i had run a profiler. So, for anyone who is trying to make their code run fast and efficiently, you need a profiler. You can't get away without it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1873943597462347891?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1873943597462347891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1873943597462347891' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1873943597462347891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1873943597462347891'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/01/today-im-going-to-talk-about.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-250817538086624735</id><published>2008-01-10T16:18:00.000Z</published><updated>2008-01-10T16:51:33.934Z</updated><title type='text'></title><content type='html'>As you've probably heard by now, &lt;a href="http://abock.org"&gt;Aaron&lt;/a&gt; has just released a &lt;a href="http://abock.org/2008/01/09/banshee-0132-at-last/"&gt;new version of Banshee&lt;/a&gt; has just. Good stuff! As part of the blogpost detailing the changes and features in the new version he says:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Alan McGovern has nearly completely rewritten our MTP support and we are close to enabling it by default. The next release should have solid MTP support, and we hope to roll 0.13.3 within the next two weeks. The new MTP support uses libmtp instead of libgphoto2. This decision was made for a number of reasons, though I am really not informed enough to convey them properly.&lt;/blockquote&gt;&lt;br /&gt;Well, i never detailed the reasons for the switch in my previous blogpost, so i may as well do it now. I'll also mention the current issues with libmtp for those of you interested in what those issues are.&lt;br /&gt;&lt;br /&gt;Reasons for the switch:&lt;br /&gt;The primary reason is that to use libgphoto as the mtp backend, you need svn head of libgphoto and svn head of libgphoto-sharp. This issue is actually a pretty major issue. If banshee wanted to ship MTP support, it would have to ship it's own version of libgphoto and libgphoto-sharp. The other alternative was to enable MTP support by default, but the end-user wouldn't be able to use it until they compiled and installed libgphoto themselves or their distro packaged the required version of libgphoto. This version is likely to be released in the next month or two (i believe), but could take several months to propagate through the various distros.&lt;br /&gt;&lt;br /&gt;With libmtp, banshee can run with version 0.2.0 or higher, which means anything newer than Aug 4th 2007. This is a much more workable solution.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Secondly, API: The libmtp API is aimed at mp3 players. Banshees API is also aimed at mp3 players, this makes it significantly easier to map the banshee API to the libmtp API. Several tasks such as getting track listings, uploading tracks, and retrieving tracks are much simpler in libmtp.&lt;br /&gt;&lt;br /&gt;Thirdly, with libgphoto, downloading or uploading a track requires copying the entire file to memory in C#, then pushing that into libgphoto (which duplicates the memory usage) then the data is pushed to the device. There is an alternative method whereby i create a unix file descriptor which is then passed into libgphoto and the file is read/written to that. However, this is not ideal as there's a good chance that half written temp files can be left over in the event of a crash.&lt;br /&gt;&lt;br /&gt;With libmtp i can pass in a file path and libmtp will write the file directly from that path or read it directly from that path. This means the memory copying is completely removed resulting in much better performance.&lt;br /&gt;&lt;br /&gt;Fourthly, Whilest libgphoto can (technically speaking) run on windows, the necessary code hasn't been written to allow this to happen. libmtp can be compiled and run on windows (or so the documentation says). As there is &lt;a href="http://abock.org/2007/02/21/banshee-on-windows/"&gt;work to make banshee run on windows&lt;/a&gt;, this is an advantage.&lt;br /&gt;&lt;br /&gt;Finally, playlists. I'm still unsure how to create playlists using libgphoto. It'd require a fair bit of messing around and experimenting for me to figure out exactly how it should work. With libmtp, this is trivial to do.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;However, libmtp has some pretty big issues at the moment, which only really came to light a few hours before the release.&lt;br /&gt;&lt;br /&gt;If you connect an MTP device and banshee loads it up, connecting a second DAP (of any kind) will make libmtp choke. Once there's an active device, any call into libmtp to list available devices throws a (non-fatal) error. This means if you plug in a second device, you get an annoying error message popping up. If you do own two devices and were trying to copy from one to the other, this is impossible.&lt;br /&gt;&lt;br /&gt;There was also an issue linking a device reported by libmtp to a device reported by HAL. There is no sure-fire way of doing this using the libmtp API. Under libgphoto this was possible.There are a few possible workarounds for this, but none of them are ideal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-250817538086624735?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/250817538086624735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=250817538086624735' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/250817538086624735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/250817538086624735'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/01/as-youve-probably-heard-by-now-aaron.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1106223132090943623</id><published>2008-01-06T20:35:00.000Z</published><updated>2008-01-06T21:24:31.822Z</updated><title type='text'></title><content type='html'>Banshee now has support for MTP devices in trunk. It's a long and torturous story, but here's the short of it:&lt;br /&gt;&lt;br /&gt;Way back when, the work on implementing MTP support was started by a guy called &lt;a href="http://trick.vanstaveren.us"&gt;Patrick&lt;/a&gt;. At the time, there were two options:&lt;br /&gt;1) &lt;a href="http://www.gphoto.org/proj/libgphoto2/"&gt;libgphoto&lt;/a&gt;&lt;br /&gt;2) &lt;a href="http://libmtp.sourceforge.net/"&gt;libmtp&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;libmtp was still in it's early stages of development and wasn't mature enough to be a viable option, so libgphoto was chosen althought it wasn't that much better. At this time, libgphoto didn't have metadata reading/writing implemented, didn't show any stats on the different filesystems in the device. Note: libmtp was no better at the time. So, a C# binding to libgphoto was started and basic support for syncing was eventually pushed into banshee.&lt;br /&gt;&lt;br /&gt;Fast forward about 18 months. libgphoto has matured significantly in its support for mp3 players. Track metadata has full read/write support, you can get stats on the filesystems, patches and optimisations have been pushed in so that all the basic operations are fast. However, there's still issues with long filename support - there are hardcoded limits in libghoto itself which means if it encounters a path or filename longer than the limit, it just bails out. For mp3 players, this was a serious issue as these limits were quite frequently reached. For instance, if you were a fan of &lt;a href="http://www.last.fm/music/Sufjan+Stevens"&gt;Sufjan Stephens&lt;/a&gt;, you were out of look. His incredibly long track titles like 'Out of Egypt, Into the Great Laugh of Mankind, and I Shake the Dirt From My Sandals as I Run' really throw a spanner in the works&lt;br /&gt;&lt;br /&gt;Around this time, i stepped in to complete the binding and complete banshee MTP support. I pestered poor Marcus for quite a while until i got various fixes/updates into libgphoto such as the change to allow unlimited filename length and path length, and also to remove 'long' types from the public API (as a 'long' type can be 32bit or 64bit depending on architecture and leads to nasty complications binding it in C#). I also completed the initial revision of the libgphoto-sharp API restructuring. The entire endevour probably took about 2 months from start to finish.&lt;br /&gt;&lt;br /&gt;However, the problem now was that in order to have MTP support in banshee, you needed SVN head of libgphoto, svn head of libgphoto2-sharp and a pot of luck that you could get it installed without screwing up your packaged version. A new release of libgphoto was several months away and probably still a further few months until it managed to trickle down into the various distros.&lt;br /&gt;&lt;br /&gt;It was then that i was reminded by &lt;a href="http://abock.org/"&gt;Aaron&lt;/a&gt; that libmtp still existed and was quite mature. I took a quick look at the API showed that it was tailored specifically for the kind of tasks that banshee was trying to do. Better yet, it had a faster release cycle, it didn't require svn head of anything in order to get all the features that were required and it was already available in nearly every distro.&lt;br /&gt;&lt;br /&gt;I spent about 4 days writing a brand new C# wrapper for libmtp and then replaced the libgphoto-sharp code in banshee with the new libmtp-sharp code. While there are things in libmtp that are missing as compared to libgphoto, the simplicity of the new API makes it trivial to implement features which previously would have been quite complicated. For example, Playlist support under libmtp is trivial to implement, yet under libgphoto i'm not 100% sure how i'd go about it!&lt;br /&gt;&lt;br /&gt;What i regret most of all out of this is that i put Marcus through so much hassle for minimal benefits as Banshee is no longer using libgphoto. While i do feel bad about putting so much effort into the libgphoto-sharp binding to never use it myself, it was still the best decision from banshee's point of view.&lt;br /&gt;&lt;br /&gt;So, if anyone out there has an MTP device and wants to make sure that their device works with banshee before the release (so i can fix any bugs should there be any), &lt;a href="http://banshee-project.org/Guide/DAPs/MTP"&gt;check out the guide here&lt;/a&gt;. Post any bug reports on http://bugzilla.gnome.org under the DAAP component in Banshee.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1106223132090943623?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1106223132090943623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1106223132090943623' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1106223132090943623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1106223132090943623'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2008/01/banshee-now-has-support-for-mtp-devices.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-236540917276967707</id><published>2007-12-09T22:22:00.000Z</published><updated>2007-12-09T22:49:48.733Z</updated><title type='text'></title><content type='html'>Looky here, who wants a giant network accessible harddrive which &lt;a href="http://wdc.custhelp.com/cgi-bin/wdc.cfg/php/enduser/std_adp.php?p_faqid=1495&amp;amp;p_created=1168641440&amp;amp;p_sid=bLTfVJLi&amp;amp;p_accessibility=0&amp;amp;p_redirect=&amp;amp;p_lva=&amp;amp;p_sp=cF9zcmNoPTEmcF9zb3J0X2J5PSZwX2dyaWRzb3J0PSZwX3Jvd19jbnQ9NSw1JnBfcHJvZHM9MCZwX2NhdHM9MCZwX3B2PSZwX2N2PSZwX3NlYXJjaF90eXBlPWFuc3dlcnMuc2VhcmNoX2ZubCZwX3BhZ2U9MSZwX3NlYXJjaF90ZXh0PS5hdmk*&amp;amp;p_li=&amp;amp;p_topview=1"&gt;won't let you share the 26 most common filetypes&lt;/a&gt;. Honestly, why would anyone buy a piece of hardware which is unbelievably crippled and completely unsuitable for the purpose it's being advertised for. And more importantly, why would someone design such a horrible piece of hardware?&lt;br /&gt;&lt;br /&gt;On a similar, but more sinister note, if america thinks you might be guilty of a crime, then &lt;a href="http://www.timesonline.co.uk/tol/news/world/us_and_americas/article2982640.ece"&gt;they feel they can just kidnap you&lt;/a&gt;. The mentality of guilty until proven innocent really is showing through here. I wonder what would happen if i  kidnapped an american citizen and brought them to ireland because i suspected them of a crime. I'm sure America wouldn't view that too kindly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-236540917276967707?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/236540917276967707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=236540917276967707' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/236540917276967707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/236540917276967707'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/12/looky-here-who-wants-giant-network.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-8260153200865962983</id><published>2007-12-06T01:44:00.000Z</published><updated>2007-12-06T01:48:59.797Z</updated><title type='text'></title><content type='html'>My christmas exams (worth 35% of my degree) are getting closer and closer. Pretty much every day starting from last saturday has turned into a 10:30am-&gt;7:30pm study-a-thon. I'm trying to learn in the space of a 10 days what i should've been learning over the last 12 weeks. It's mostly working.&lt;br /&gt;&lt;br /&gt;As a result, i've become a minor insomniac. Tis only 2pm, i've only been trying to sleep for 3 hours now. This describes it pretty well:&lt;br /&gt;&lt;br /&gt;&lt;object height="355" width="425"&gt;&lt;param name="movie" value="http://www.youtube.com/v/JOy5LOsV6Vs&amp;amp;rel=1"&gt;&lt;param name="wmode" value="transparent"&gt;&lt;embed src="http://www.youtube.com/v/JOy5LOsV6Vs&amp;amp;rel=1" type="application/x-shockwave-flash" wmode="transparent" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-8260153200865962983?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/8260153200865962983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=8260153200865962983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8260153200865962983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/8260153200865962983'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/12/my-christmas-exams-worth-35-of-my.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1660573050008656198</id><published>2007-11-26T09:26:00.000Z</published><updated>2007-11-26T09:32:04.682Z</updated><title type='text'></title><content type='html'>In two short days i'll be flying out to madrid for the mono summit. Woohoo! Turns out i'm missing my last 3 days of term, in which i should be getting two tutorials and our first problem sheet for a particular subject. So, not the best time to be fecking off to a foreign country.... ah well!&lt;br /&gt;&lt;br /&gt;Also, if that F-Spot guy is about the place who was asking me about upgrading F-Spot to use the latest libgphoto2-sharp, give me a shout via email and let's organise something before i head over. Otherwise, just try and find me between wednesday evening and friday evening.&lt;br /&gt;&lt;br /&gt;p.s. if i fail any exams because of this, i'm holding &lt;a href="http://abock.org/"&gt;Aaron&lt;/a&gt; responsible, he tempted me over with a Chicken Pag! So no new MTP code for banshee if i fail ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1660573050008656198?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1660573050008656198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1660573050008656198' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1660573050008656198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1660573050008656198'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/11/in-two-short-days-ill-be-flying-out-to.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7095854339753113501</id><published>2007-11-07T12:31:00.000Z</published><updated>2007-11-07T14:32:52.156Z</updated><title type='text'></title><content type='html'>I heard some great news earlier in the week and just got around to checking it out. There's &lt;a href="http://seedpeer.com/sitenews/article/31.html"&gt;another C# implementation of the BitTorrent protocol being developed&lt;/a&gt;! Good stuff! They've been working on it for about a year, and have "the core of the beast" pretty much complete. Thats great stuff! Considering i thought i was the only person with a C# based bittorrent implementation, i thought it'd be great to have a chat with them, see how they solved different issues etc.&lt;br /&gt;&lt;br /&gt;So, about 15 mins ago, i loaded up that link and decided to download their alpha client, just to see how it works etc. Thats when i became &lt;span style="font-weight: bold;"&gt;seriously&lt;/span&gt; pissed off.&lt;br /&gt;&lt;br /&gt;Without even having run the 'alpha client' i realised that the seedpeer codebase was actually MonoTorrent*. Yes, these guys had the cheek to take &lt;span style="font-weight: bold;"&gt;my code&lt;/span&gt; and pretend it was theirs. They tried to pass off about 18 months of &lt;span style="font-weight: bold;"&gt;my development time&lt;/span&gt; as their development time, without so much as giving me credit or mentioning that they were, in fact, lying when they said "we have almost finished coding the core of the beast". The worst thing is, they weren't even smart enough to do it properly.&lt;br /&gt;&lt;br /&gt;So, for anyone out there who has actually used SeedPeer, or has a blog who've blogged about it, please try to make it known that seedpeer == MonoTorrent, except that they don't even use all the features i have available. For example, here's there 'TODO' list:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;MultiTracker&lt;/li&gt;&lt;li&gt;Torrent Creation &lt;/li&gt;&lt;li&gt;Custom DHT (faster)&lt;/li&gt;&lt;li&gt;Rss Feeds&lt;/li&gt;&lt;li&gt;Integrated Search Function (with commenting)&lt;/li&gt;&lt;li&gt;Custom Peer Exchange&lt;/li&gt;&lt;li&gt;Fast Resume&lt;/li&gt;&lt;li&gt;Global/Per torrent Settings&lt;/li&gt;&lt;li&gt;Download Priority&lt;/li&gt;&lt;li&gt;more... &lt;/li&gt;&lt;/ul&gt;Here's a list of the features that are already in monotorrent that are in their TODO list:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;MultiTracker&lt;/li&gt;&lt;li&gt;Torrent Creatio &lt;/li&gt;&lt;li&gt;Rss Feeds&lt;/li&gt;&lt;li&gt;Fast Resume&lt;/li&gt;&lt;li&gt;Global/Per torrent Settings&lt;/li&gt;&lt;li&gt;Download Priority&lt;/li&gt;&lt;li&gt;more... &lt;/li&gt;&lt;/ul&gt;I suppose this is what you expect by making the code public. People will use it (which is what i want), but then they'll try to pass it off as their own (which is just plain wrong). So, if anyone wants to email the seedpeer people, their &lt;a href="http://seedpeer.com/contact.html"&gt;contact form is here&lt;/a&gt;, so feel free to let them know you know ;) Also, if you want a nice GUI for monotorrent, check out www.monotorrent.com :)&lt;br /&gt;&lt;br /&gt;* Yes, i am 100% sure of this. I  haven't used reflector to verify, but i'm 100% sure that at least 95% of the code is a direct copy/paste from monotorrent. It might even be 100%. If someone is bored, they can use reflector (or whatever) so they can see i'm not lying. No better proof than looking yourself.&lt;br /&gt;&lt;br /&gt;Also, if the seedpeer guys do read this, using an obfusticator won't prevent me from recognising my own code. I can think of a dozen ways offhand to check if a client is my client without having to see the code. It's ridiculously easy.&lt;br /&gt;&lt;br /&gt;EDIT: For those of you who know these things, The middle paragraph of the &lt;a href="http://www.opensource.org/licenses/mit-license.html"&gt;MIT/X11 license&lt;/a&gt; states:&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;blockquote&gt;The above copyright notice and this permission notice shall be included in&lt;br /&gt;all copies or substantial portions of the Software.&lt;/blockquote&gt;Does that mean if they distribute a binary, they have to include that license header in some form or other?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7095854339753113501?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7095854339753113501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7095854339753113501' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7095854339753113501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7095854339753113501'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/11/i-heard-some-great-news-earlier-in-week.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7226292069878701386</id><published>2007-11-02T12:07:00.000Z</published><updated>2007-11-02T12:17:24.813Z</updated><title type='text'></title><content type='html'>I decided to install the English dictionary in firefox today so I could get proper spelling corrections when writing emails and whatnot. So I fired up Firefox and browsed my way over to &lt;a href="https://addons.mozilla.org/en-US/firefox/browse/type:3"&gt;here&lt;/a&gt; to grab the English dictionary.&lt;br /&gt;&lt;br /&gt;So it turns out that 'American English' is actually 'English' whereas 'British English' is what i wanted. Now, considering 'English' originated in Britain, why the hell do americans insist on referring to 'American English' as 'English' and referrring to English proper as 'British English'. British English &lt;span style="font-weight: bold;"&gt;is&lt;/span&gt; 'English'. You're the one's who created your own seperate spelling and dialect.&lt;br /&gt;&lt;br /&gt;It's the equivalent of referring to 'Canadian French' as 'French' and referring to French proper as 'France French'. C'mon guys, you didn't invent it, stop trying to make it sound like ye did!&lt;br /&gt;&lt;br /&gt;On other news, my macbook is in for repair for an unknown amount of time after it just stopped booting up. So i'll be MIA until it gets back to me. I'm still contactable by email should anyone need to.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7226292069878701386?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7226292069878701386/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7226292069878701386' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7226292069878701386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7226292069878701386'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/11/i-decided-to-install-english-dictionary.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7446897792291185851</id><published>2007-10-14T19:46:00.000+01:00</published><updated>2007-10-14T19:51:27.781+01:00</updated><title type='text'></title><content type='html'>So, i'm looking for a bit of help. It's not a particularly hard task, it's just it'll take me a while and i need to go do some study. Basically, i need a method which parses XML-like data and adds the element name and the element contents to a Dictionary&lt;string, string=""&gt;. It is fairly trivial, but i don't like the idea of having to think through all the string mangling required ;) First person to get me a method which works gets 10 brownie points.&lt;br /&gt;&lt;br /&gt;You need to be able to handle something like this:&lt;br /&gt;http://monoport.com/5078 (sorry for pasting there, but blogger borks on the XML tags)&lt;br /&gt;&lt;br /&gt;Thing is, this is, technically speaking, invalid XML. The &lt;unknown&gt; tag should be escaped, and the '&amp;amp;' in ObjectFileName should also be escaped. Unfortunately, i can't rely on the source of this XML being fixed any time soon, so i need to be able to hand parse the XML. I've outlined a procedure at the end which should be able to handle most of the mess that will be thrown at it. The only thing i'll add is that the manual parser doesn't have to cope with every eventuality. If something goes wrong (for example an artist called themselves &lt;/unknown&gt; which breaks the parsing) then don't worry, just abort. This is a last ditch effort to parse. It should succeed 99% of the time if you follow the procedure below.&lt;br /&gt;&lt;br /&gt;Here's some psuedo code:&lt;br /&gt;while(currentIndex &lt; nexttag =" string.IndexOf('&lt;'," elementname =" string.SubString(nextTag,"&gt;', currentPosition); // read the element name&lt;br /&gt;&lt;br /&gt;currentPosition += the number of characters i've just 'parsed'.&lt;br /&gt;string data = string.SubString(currentPosition, string.IndexOf("&lt;!--" + elementName + '--&gt;'); // The contents are between currentPosition and the end tag.&lt;br /&gt;currentPosition += data.Length;&lt;br /&gt;currentPosition += length of closing tag;&lt;br /&gt;&lt;br /&gt;if(nextCharacter != '&lt;') AbortParse(); // If the next character is not '&lt;', then something has gone wrong, so give up. myDictionary.Add(elementName, contents);&lt;/string,&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7446897792291185851?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7446897792291185851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7446897792291185851' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7446897792291185851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7446897792291185851'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/10/so-im-looking-for-bit-of-help_345.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-2483309159445765755</id><published>2007-10-06T15:59:00.000+01:00</published><updated>2007-10-06T16:06:07.350+01:00</updated><title type='text'></title><content type='html'>When i read the &lt;a href="http://gcc.gnu.org/projects/cli.html"&gt;news about gcc supporting CIL&lt;/a&gt; a few days ago, the very first thought to cross my mind wasn't "Wow, that's really cool", it was "Wow, i wonder how many people are going to start shouting to not use GCC anymore because it's 'contaminated' with microsoft code and you'll get sued if you try use it for anything".&lt;br /&gt;&lt;br /&gt;So should i take it as a good sign that there's no uproar? Have people realised that implementing an open specification cannot get you sued? You never know, it might have happened. Still, it's an amazing achievement. I can't say it's something i'm ever likely to use, but it does have some nice possibilities.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-2483309159445765755?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/2483309159445765755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=2483309159445765755' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2483309159445765755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/2483309159445765755'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/10/when-i-read-news-about-gcc-supporting.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7421276141127494006</id><published>2007-10-03T00:32:00.000+01:00</published><updated>2007-10-03T00:40:24.124+01:00</updated><title type='text'></title><content type='html'>With the mono summit coming up soon, I'm hoping to be able to spare the time from college to head over for at least 2-3 days of it. It'll be interesting to get together with a bunch of people for some hacking or some talking or whatever's going on.&lt;br /&gt;&lt;br /&gt;So, if anyone's out there who's interested in talking about/hacking on (or redesigning  ;) ) any of the projects i've been working on, or projects similar to them:&lt;br /&gt;&lt;br /&gt;MonoTorrent: My pet bittorrent library project&lt;br /&gt;Lunar Eclipse: The xaml designer for silverlight&lt;br /&gt;libgphoto-sharp: The csharp binding for libgphoto - for MTP based media devices&lt;br /&gt;Banshee MTP support: using the above binding&lt;br /&gt;&lt;br /&gt;do give me a shout and i'll see what can be arranged. I'm already looking forward to getting some hacking in on F-Spot over the week to port it to the new libgphoto-sharp bindings. Should be fun!&lt;br /&gt;&lt;br /&gt;For anyone else who's kinda undecided about going, ah sure gwon! Tis a bit of craic ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7421276141127494006?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7421276141127494006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7421276141127494006' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7421276141127494006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7421276141127494006'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/10/with-mono-summit-coming-up-soon-im.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-7707495980677457923</id><published>2007-09-28T18:17:00.001+01:00</published><updated>2007-09-29T13:07:32.980+01:00</updated><title type='text'></title><content type='html'>So over the last few weeks i've been working on improving the C# binding for &lt;a href="http://www.gphoto.org/proj/libgphoto2/"&gt;libgphoto2.&lt;/a&gt;. Thankfully, the backend C wrapper was in fairly good shape with thanks to the guy who originally developed it, and &lt;a href="http://trick.vanstaveren.us/wp/"&gt;trickv&lt;/a&gt;, who became the maintainer of it and is the guy responsible for implementing &lt;a href="http://www.banshee-project.org/Guide/DAPs/MTP"&gt;MTP support in banshee&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For me looking at the code as a newcomer to the libgphoto-sharp 'team', the first thing i realised was that the c# api was a direct copy/paste of the C api. There was no proper frontend which simplified the use of the libghoto2 library. For example, to get a list of connected devices and then connect to a specific one required detailed knowledge of the libgphoto2 API and over 100 lines of code and also required you to be very careful about disposing of objects correctly.&lt;br /&gt;&lt;br /&gt;So, my first task in getting full MTP support in banshee was to write up a new API for libgphoto2-sharp which hid all that nastiness from the end-user. The new API is, i suppose, 80% complete. Quite a few of the methods in the API are blocking, and so asynchronous equivalents will have to be added. One of the more immediate benefits is that detecting and connecting to a camera takes 3 lines of code now ;)&lt;br /&gt;&lt;br /&gt;So, if anyone out there wants to use the new simpler API (fspot and banshee devs, i'm talking to you) you can check the code out with this command:&lt;br /&gt;&lt;br /&gt;svn co http://gphoto.svn.sourceforge.net/svnroot/gphoto/trunk/bindings/libgphoto2-sharp gphoto&lt;br /&gt;&lt;br /&gt;The binding should be considered API unstable until (probably) the release of libgphoto3.x.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-7707495980677457923?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/7707495980677457923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=7707495980677457923' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7707495980677457923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/7707495980677457923'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/09/so-over-last-few-weeks-ive-been-working.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-1308612746215166337</id><published>2007-09-21T18:58:00.000+01:00</published><updated>2007-09-21T19:15:20.162+01:00</updated><title type='text'></title><content type='html'>As both a cyclist and a humanitarian, i think i'm fully qualified to say: Women with babies should not be allowed out in public.&lt;br /&gt;&lt;br /&gt;"What are you on about" i hear you ask. Well, the reason is simple. Whenever a woman sees a baby, they instantly become hypnotised by it's hideous features and are reduced to mumbling cryptic phrases such as "Goochie goochie goo" and "Who's a big boy then?" to said baby. Unfortunately, this distracts them from real-world issues such as how to cross the road safely.&lt;br /&gt;&lt;br /&gt;I was cycling to college yesterday, as i've done for the last 3 years (i cycled to secondary school for the 6 years before that and primary school for 3 years before that). All was going well, i was a mere 2 minutes from my house and was just preparing to take a left turn (in ireland we drive on the left-hand side of the road) when all of a sudden, a woman with a baby decided to cross the road in front of me whilest talking to her baby. So there i was, travelling at approximately 25 miles an hour with a baby in a pram being pushed by an idiot a mere 15-20 metres in front of me.&lt;br /&gt;&lt;br /&gt;I jammed on the brakes, the back tire locked, the wheel skidded on the wet ground and i went flying. Whilest i got away with some scrapes and bruises, my MP3 player wasn't quite so lucky. I only got music from the left earpiece. I was pissed! So, as an electronic engineer, i tried to fix it. A quick google got me instructions on cracking open the case, so i did.&lt;br /&gt;&lt;br /&gt;Initial inspection made it look like very minor damage:&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/alanmcgovern/1418971946/" title="Photo Sharing"&gt;&lt;img src="http://farm2.static.flickr.com/1066/1418971946_0d1654d950_o.jpg" width="512" height="384" alt="Start" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;However as i gently poked it, more and more bits started coming off:&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/alanmcgovern/1418089215/" title="Photo Sharing"&gt;&lt;img src="http://farm2.static.flickr.com/1436/1418089215_ef72b7c35a_o.jpg" width="512" height="384" alt="middle" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Finally, by the end of it, the entire lefthand side was toasted and the top bit was also completely broken off. I was none to happy:&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/alanmcgovern/1418089447/" title="Photo Sharing"&gt;&lt;img src="http://farm2.static.flickr.com/1431/1418089447_a272662310_o.jpg" width="512" height="384" alt="end" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So, my task now is to find something to wedge along the side of the earphone jack which will hold the metally bits in place. Everything works fine at the moment, but unless i support those metally bits, they will bend back out of position through use. Worst case scenario, it's a 30 gig portable harddrive which can play films on any tv via TV-Out. Still pretty useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-1308612746215166337?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/1308612746215166337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=1308612746215166337' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1308612746215166337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/1308612746215166337'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/09/as-both-cyclist-and-humanitarian-i.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-4084749414798383665</id><published>2007-09-10T00:48:00.000+01:00</published><updated>2007-09-10T00:51:10.456+01:00</updated><title type='text'></title><content type='html'>I don't care how i get one, but i &lt;span style="font-weight:bold;"&gt;need&lt;/span&gt; one of these!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.apple.com/ipodtouch/guidedtour/medium.html"&gt;Clicky Linky&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It looks so, so, so nice! I just wish public free wireless was as prevalent in ireland as it is in the US.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-4084749414798383665?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/4084749414798383665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=4084749414798383665' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4084749414798383665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/4084749414798383665'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/09/i-dont-care-how-i-get-one-but-i-need.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29153919.post-9196896651189644031</id><published>2007-09-06T09:14:00.000+01:00</published><updated>2007-09-06T09:16:49.375+01:00</updated><title type='text'></title><content type='html'>Have no fears my American friends, your President has your best interests at heart. Check out this interview where he reveals where that 50 billion in defense money is going:&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/IoXgRtDysLY"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/IoXgRtDysLY" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29153919-9196896651189644031?l=monotorrent.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://monotorrent.blogspot.com/feeds/9196896651189644031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29153919&amp;postID=9196896651189644031' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/9196896651189644031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29153919/posts/default/9196896651189644031'/><link rel='alternate' type='text/html' href='http://monotorrent.blogspot.com/2007/09/have-no-fears-my-american-friends-your.html' title=''/><author><name>Alan</name><uri>http://www.blogger.com/profile/17518005985877464643</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
