Poignant: Xml meets Huffman

There’s a spec at the w3c about compressing (XML) named “Efficient XML Interchange” Format taking into account the grammar and likelihood of atoms within the document. They indeed use something similar the the Huffman Coding.

The results are quite impressive – nice charts!

Tags: , , ,

T-Mobile / MMS / iPhone

klick ich den Link, den T-Mobile mir per SMS geschickt hat, kriege ich:

T-Mobile Scherz

T-Mobile Scherz

Tags: , ,

NSCachedURLResponse / NSKeyedUnarchiver pain

as the iPhone SDK comes with a rather dysfunctional NSURLCache — Apple suggests to implement it from scratch yourself in the code examples about caching — I went for just this.

Until I came across the [NSKeyedUnarchiver unarchiveObjectWithData:...] not restoring userInfo, storagePolicy and data of NSCachedURLResponse.

Couldn’t believe it and spent almost the whole day verifying that the error is not within my code but really the unarchiver treats those fields transient.

See yourself:

-(void)testiPhoneSDK312NSKeyedArchiver
{
//  prepare a NSCachedURLResponse
    NSCachedURLResponse *src = nil;
    {
        NSURL *i_url = [NSURL URLWithString:@"http://www.url.example/path/file.html"];
        NSString *i_mime = @"text/html";
        NSInteger expectedContentLength = 112;
        NSString *i_encoding = @"iso-8859-2";
 
        NSURLResponse *i_response = [[NSURLResponse alloc] initWithURL:i_url
            MIMEType:i_mime
            expectedContentLength:expectedContentLength
            textEncodingName:i_encoding];
        NSData *i_data = [@"Hello, world!" dataUsingEncoding:NSISOLatin2StringEncoding];
        NSURLCacheStoragePolicy i_storage =  NSURLCacheStorageAllowed;
        NSDictionary *i_userInfo = [NSDictionary dictionaryWithObject:
        [NSDate dateWithTimeIntervalSince1970:13] forKey:@"era"];
 
        NSCachedURLResponse *src = [[NSCachedURLResponse alloc] initWithResponse:i_response
            data:i_data
            userInfo:i_userInfo
            storagePolicy:i_storage];
 
//      ensure it's all in place:
        STAssertEqualObjects(i_url, src.response.URL, @"");
        STAssertEqualObjects(i_mime, src.response.MIMEType, @"");
        STAssertEquals((int)expectedContentLength, (int)src.response.expectedContentLength, @"");
        STAssertEqualObjects(@"file.html", src.response.suggestedFilename, @"");
        STAssertEqualObjects(i_encoding, src.response.textEncodingName, @"");
 
        STAssertEqualObjects(i_data, src.data, @"");
        STAssertEqualObjects(i_userInfo, src.userInfo, @"");
        STAssertEquals( 0u, src.storagePolicy, @"");
    }
 
//  archive + unarchive:
    NSCachedURLResponse *dst = [NSKeyedUnarchiver unarchiveObjectWithData:
      [NSKeyedArchiver archivedDataWithRootObject:src]];
 
//  check whether src == dst
    STAssertEqualObjects(src.response.URL, dst.response.URL, @"");
    STAssertEqualObjects(src.response.MIMEType, dst.response.MIMEType, @"");
    STAssertEquals(src.response.expectedContentLength, dst.response.expectedContentLength, @"");
    STAssertEqualObjects(src.response.suggestedFilename, dst.response.suggestedFilename, @"");
    STAssertEqualObjects(src.response.textEncodingName, dst.response.textEncodingName, @"");
 
//  !!!!!!!!!!
//  sad information loss after unarchiving:
    STAssertNil( dst.data, @"" );
    STAssertNil( dst.userInfo, @"" );
    STAssertEquals( 2u , dst.storagePolicy, @"" );
}

When my NSURLCache replacement is ready I think about publishing it a github — interested anyone?

Tags: , , , , , , ,

Binary Search NSArray

Though CFArray comes with binary search capability, NSArray does not – at least not within the iPhone SDK. The indexOfObject:inSortedRange:options:usingComparator: can’t be found.

Plus the CFArrayBSearchValues doesn’t tell you whether the key actually is part of the list or not. That’s what the Java JDK does, so let’s implement some category methods

-(NSInteger)binarySearch:(id)key;
-(NSInteger)binarySearch:(id)key usingSelector:(SEL)comparator;
-(NSInteger)binarySearch:(id)key usingSelector:(SEL)comparator inRange:(NSRange)range;
-(NSInteger)binarySearch:(id)key usingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context;
-(NSInteger)binarySearch:(id)key usingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context inRange:(NSRange)range;
-(NSInteger)binarySearch:(id)key usingDescriptors:(NSArray *)sortDescriptors;
-(NSInteger)binarySearch:(id)key usingDescriptors:(NSArray *)sortDescriptors inRange:(NSRange)range;

ourselves, like

-(NSInteger)binarySearch:(id)key usingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context inRange:(NSRange)range
{
    NSLogD(@"[NSArray(MroBinarySearch) binarySearch:%@ usingFunction:]", key);
    if(self.count == 0 || key == nil || comparator == NULL)
        return [self binarySearch:key usingSelector:nil inRange:range];
 
//	check overflow?
    NSInteger min = range.location;
    NSInteger max = range.location + range.length - 1;
 
    while (min < = max)
    {
        // http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html
        const NSInteger mid = min + (max - min) / 2;
        switch (comparator(key, [self objectAtIndex:mid], context))
        {
            case NSOrderedSame:
                return mid;
            case NSOrderedDescending:
                min = mid + 1;
                break;
            case NSOrderedAscending:
                max = mid - 1;
                break;
        }
    }
    return -(min + 1);
}

See the full interface + implementation + testcase without html encoding dirt at github.

Tags: , , , , , , , , ,

hübsche Dropbox Links

Weil große Email Anhänge stinken, bietet sich z.B. die Dropbox als Dateiaustauschplatform an.

Einfach die Datei in den (lokalen) Dropbox/Public Ordner verfrachten, syncen lassen und per “Rechte Maustaste -> Copy Public Link” in die Mail kopieren.

Redirect

Nun sind solche Links nicht besonders hübsch (Branding!, Branding!, CI! ruft die Marketingabteilung) und  spätestens wenn man’s abtippen muß oder per Telephon weitersagt wird’s gruslig.

Hat man aber eine eigene Website, lassen sich die Dropbox Links aufhübschen und eine Weiterleitung einrichten, dann wird aus

http://dl.dropbox.com/u/1234567/BigDocument.pdf

z.B.

http://dropbox.mydomain.com/BigDocument.pdf

und schon sind die Brandingfreunde happy.

index.html

Geht es nicht um eine einzelne Datei sondern um mehrere, will man evtl. eine Übersicht in einer index.html haben. So eine Übersicht legt z.B. mein ruby Script htmlizedb an. Das Script will im Terminal in dem Verzeichnis gestartet werden, für das die Übersicht her soll.

Redirect + index.html

Bei Webservern ist allgemein üblich, daß man den Dateinamen index.html im Link weglassen kann. Der Webserver nimmt dann an man meint selbige. Die Dropbox verhält sich aber in dieser Hinsicht nicht wie ein normaler Webserver, sondern will immer den kompletten Dateinamen.

Hat man aber bereits den Redirect Trick in Benutzung, läßt sich die Redirect Steuerdatei (.htaccess) aufbohren, daß der Redirect die index.html Angabe ergänzt:

RewriteEngine on
# dropbox abbreviation:
# http://www.cimitan.com/blog/2008/09/17/htaccess-to-redirect-on-dropboxs-public-folder/#
# last path component has no dot -> redirect to index.html
RewriteRule ^(.+/)?([^./]+)/?$	http://dl.dropbox.com/u/1234567/$1$2/index.html	[last,qsappend]
# everything else: redirect as is
RewriteRule ^(.+)$	http://dl.dropbox.com/u/1234567/$1	[last,qsappend]

violá, nun wird von

http://dropbox.mydomain.com/FolderWithBigFiles

automatisch auf den Dropbox Link

http://dl.dropbox.com/u/1234567/FolderWithBigFiles/index.html

weitergeleitet.

Toll, nicht?

Tags: , , , , , , ,

Radiomitschnitt per Rezept

Seit längerem schneide ich mir interessante Sendungen aus dem (Internet-)Radio mit, vor allem Hörspiele auf B2.

Bisher mußte dazu immer mein alter (Linux-)Rechner laufen und den Mitschnitt mußte ich händisch starten – wie unbequem. Oder man ist unterwegs und verpaßt den Mitschnitt. Wie ärgerlich.

Doch das ist ab jetzt vorbei, denn ab sofort schneidet mein vServer (läuft ja eh ständig) mit!

Und so geht’s:

  1. Ein Script auf dem vServer, das den Mitschnitt startet und beendet:
    #!/bin/sh
     
    # id3v2 scheint Bilder nicht zu mögen:
    # - http://ubuntuforums.org/showthread.php?t=1353213
    # - readonly: http://id3v2.cvs.sourceforge.net/viewvc/id3v2/id3v2/id3v2.cpp?revision=1.17&view=markup#l_610
    # - http://www.id3.org/id3v2.4.0-frames Kapitel4.14.
    # erfolglos: id3v2 --APIC "0\0image/jpeg\03hello\0`cat moby_dick.jpg`" $file
     
    if [ $# -lt 3 ]; then
    	echo "rip audio streams to a local file."
    	echo " "
    	echo "Usage:"
    	echo " "
    	echo "  $0 stream marker duration id3v2_tags"
    	echo "    stream     typically a URL, see 'man streamripper'"
    	echo "    marker     filename prefix"
    	echo "    seconds    how long will we dump"
    	echo "    id3v2_tags optional, tag the download with id3v2"
    	echo " "
    	echo "Example:"
    	echo "  $0 http://gffstream.ic.llnwd.net/stream/gffstream_w11a bayern2 3660"
    	echo " "
    	exit 1
    fi
     
    src=$1 ; shift
    basename=$1 ; shift
    seconds=$1 ; shift
     
    dst=$basename-`date --iso-8601=seconds`
     
    echo "dumping $seconds (sec) from $src to $dst ..."
     
    streamripper $src -u Mozilla -i -a $dst -l $seconds
    rm $dst.cue
     
    if [ $# -gt 0 ] ; then
    	id3v2 "$@" $dst.mp3
    fi
     
    chmod a+r $dst.*
     
    echo done.
  2. Ein Cronjob für jeden Mitschnitt:
    PATH=/bin:/usr/bin 
    HOME=/home/USERNAME
     
    # Recorder
     
    rec_cmd=/home/USERNAME/bin/stream-rip
    rec_dir=/home/USERNAME/pub/recorder
     
    ON3=http://gffstream.ic.llnwd.net/stream/gffstream_w9a
    BAYERN1=http://gffstream.ic.llnwd.net/stream/gffstream_w10a
    BAYERN2=http://gffstream.ic.llnwd.net/stream/gffstream_w11a
    M945=http://stream.m945.mwn.de:80/m945-hq.mp3
     
    # B2 Krimihörspiel
    29 20 * * 3 $rec_cmd $BAYERN2 $rec_dir/b2-krimi 3660 -A "B2 Krimi"

Tags: , , , , , ,

underretain in CoreAnimation / iPhone Simulator

After upgrading to Snow Leopard and XCode 3.2.1 I’ve seen such console output

CAUnderRetain(32139,0xa0391500) malloc: *** error for object 0x3838000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

when rotating the iPhone Simulator in a project with base SDK 3.0.

This can be reproduced as follows:

  1. Create a fresh iPhone project (I use a “navigation App” with CoreData)
    Fresh Navigation based App + CoreData

    Fresh Navigation based App + CoreData

  2. add
    // Override to allow orientations other than the default portrait orientation.
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    	// Return YES for supported orientations.
    	return YES;
    }
     
    -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    	NSLog(@"willRotateToInterfaceOrientation");
    }

    to the RootViewConroller.m

  3. set the base SDK to 3.0:

    Base SDk 3.0

    Base SDk 3.0

  4. compile + run with Simulator 3.1.2, everthing fine when rotating the simulator:

    Simulator 3.1.2 rotates fine

    Simulator 3.1.2 rotates fine

  5. Simulator 3.0 shows the underretain:

    Simulator 3.0 underretain

    Simulator 3.0 underretain

    [Session started at 2009-12-12 21:49:31 +0100.]
    GNU gdb 6.3.50-20050815 (Apple version gdb-1346) (Fri Sep 18 20:40:51 UTC 2009)
    Copyright 2004 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and you are
    welcome to change it and/or distribute copies of it under certain conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for details.
    This GDB was configured as "x86_64-apple-darwin".sharedlibrary apply-load-rules all
    Attaching to process 32139.
    2009-12-12 21:49:43.845 CAUnderRetain[32139:207] willRotateToInterfaceOrientation
    CAUnderRetain(32139,0xa0391500) malloc: *** error for object 0x1853000: pointer being freed was not allocated
    *** set a breakpoint in malloc_error_break to debug
    CAUnderRetain(32139,0xa0391500) malloc: *** error for object 0x3824000: pointer being freed was not allocated
    *** set a breakpoint in malloc_error_break to debug
    warning: Unable to restore previously selected frame.
    warning: Couldn't find minimal bounds for "_sigtramp" - backtraces may be unreliable
    (gdb) break malloc_error_break
    Breakpoint 1 at 0x92626072
    (gdb) continue
    2009-12-12 21:49:58.490 CAUnderRetain[32139:207] willRotateToInterfaceOrientation
    CAUnderRetain(32139,0xa0391500) malloc: *** error for object 0x3838000: pointer being freed was not allocated
    *** set a breakpoint in malloc_error_break to debug
     
    Unable to disassemble malloc_error_break.
    (gdb) bt
    #0  0x92626072 in malloc_error_break ()
    #1  0x92535303 in free ()
    #2  0x0015ce49 in dataReleaseInfo ()
    #3  0x001481d9 in data_provider_finalize ()
    #4  0x30204421 in _CFRelease ()
    #5  0x00147fa2 in image_finalize ()
    #6  0x30204421 in _CFRelease ()
    #7  0x00c12ded in CALayerStateRelease ()
    #8  0x00c18290 in -[CALayer dealloc] ()
    #9  0x00c0a00e in CALayerRelease ()
    #10 0x00c0b265 in CA::release_root_if_unused ()
    #11 0x00c0b1ef in x_hash_table_remove_if ()
    #12 0x00c0afd4 in CA::Transaction::commit ()
    #13 0x00c132e0 in CA::Transaction::observer_callback ()
    #14 0x30245c32 in __CFRunLoopDoObservers ()
    #15 0x3024503f in CFRunLoopRunSpecific ()
    #16 0x30244628 in CFRunLoopRunInMode ()
    #17 0x32044c31 in GSEventRunModal ()
    #18 0x32044cf6 in GSEventRun ()
    #19 0x309021ee in UIApplicationMain ()
    #20 0x00002018 in main (argc=1, argv=0xbfffea84) at /Users/.../CAUnderRetain/main.m:14
    (gdb)

Tags: , , , , , , , ,

CoreData generic findManyByKey

The base for many of my SELECT-ish queries when querying by exact match is one generic method I created in some category methods on NSManagedObjectContext:

-(NSArray*)entityName:(NSString*)entityName findManyByRelation:(NSDictionary*)dict
{
//  TODO handle dict nil and emptyness
    NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:dict.count];
    for(NSString *key in dict)
    {
        NSExpression *left = [NSExpression expressionForKeyPath:key];
        NSExpression *right = [NSExpression expressionForConstantValue:[dict objectForKey:key]];
        NSComparisonPredicate *cp = [[NSComparisonPredicate alloc] initWithLeftExpression:left
            rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:0];
        [arr addObject:cp];
        [cp release];
    }
    NSPredicate *pred = nil;
    if(arr.count == 1)
        pred = [[arr objectAtIndex:0] retain];    // why do I have to retain here?
    else
        pred = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:arr];
 
    NSFetchRequest *fr = [[NSFetchRequest alloc] init];
    fr.entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
    fr.predicate = pred;
    [arr release];
    [pred release];
//	NSLog(@"predicate effective: %@", fr.predicate);
 
    NSError *err = nil;
    NSArray *ps = [self executeFetchRequest:fr error:&err];
    if(err != nil)
        [NSException raise:@"DB Error" format:@"Fetch problem %@", fr.predicate.predicateFormat];
    [fr release];
    return ps;
}

For example it serves under the hood of it’s sibling helpers

-(NSManagedObject*)entityName:(NSString*)entityName findByPrimaryKey:(NSDictionary*)dict
{
    NSArray *ps = [self entityName:entityName findManyByRelation:dict];
    if(ps == nil || ps.count == 0)
        return nil;
    if (ps.count == 1)
        return [ps objectAtIndex:0];
    [NSException raise:@"DB Error" format:@"Multiple hits for %@", dict];
    return (NSManagedObject*)1/0;
}

and

-(NSManagedObject*)entityName:(NSString*)entityName selectOrInsertWithKey:(NSDictionary*)dict
{
    NSManagedObject *o = [self entityName:entityName findByPrimaryKey:dict];
    if(o == nil)
    {
        NSEntityDescription *ed = [NSEntityDescription entityForName:entityName inManagedObjectContext:self];
        o = [[NSManagedObject alloc] initWithEntity:ed insertIntoManagedObjectContext:self];
        for(NSString *key in dict)
            [o setValue:[dict objectForKey:key] forKey:key];
        [o autorelease];
    }
    return o;
}

Those methods again are called by custom convenience wrappers in the model classes loosely following the ActiveRecord Pattern.

Tags: , , , , , , ,

Heute nur für Stammgäste?

Von wegen Schock beim Logout – mich lassen sie gar nicht erst rein:
Bildschirmfoto 2009-12-08 um 01.15.45

Tags:

NSDateFormatter case sensitive trap

Though NSDateFormatter behaves slightly different than documented, the following might even be correct, as strange as it might look (mind the last two lines):

-(void)testNSDateFormatterTrap
{
    NSDateFormatter *lower = [[[NSDateFormatter alloc] init] autorelease];
    lower.dateFormat = @"yyyy-MM-dd HH:mm:SS ZZZ";
 
    NSDateFormatter *upper = [[[NSDateFormatter alloc] init] autorelease];
    upper.dateFormat = @"YYYY-MM-dd HH:mm:SS ZZZ";
 
    lower.timeZone = upper.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
 
    NSDate *d = [lower dateFromString:@"1970-01-01 00:00:00 +0000"];
    STAssertEqualObjects(@"1970-01-01 00:00:00 +0000", [lower stringFromDate:d], @"lower iso wrong");
    STAssertEqualObjects(@"1970-01-01 00:00:00 +0000", [upper stringFromDate:d], @"upper iso wrong");
 
    d = [d addTimeInterval:(-60*60)];
 
    STAssertEqualObjects(@"1969-12-31 23:00:00 +0000", [lower stringFromDate:d], @"lower iso wrong");
    STAssertEqualObjects(@"1970-12-31 23:00:00 +0000", [upper stringFromDate:d], @"upper iso wrong");
}

The Unicode Format Pattern Documentation explains the difference of the upper- and lowercase year format – but frankly I don’t get the “Year of week of year” idea.

But that subtracting one hour in fact adds almost a whole year – that’s odd to me.

So I rather stay away from the uppercase form – be it correct or buggy.

Seen with iPhone SDK 3.1.2 and XCode 3.2.1 on Snow Leopard.

Update:

I think I got it! Uppercase YYYY makes sense only in combination with a calendar week – and not months or quarters.

Look at January 1st 2010. It belongs to calendar week 53 of 2009. Week 1/2010 starts on Jan 4th.

Tags: , , , , , ,