More Problems with Object Designs

April 3rd, 2019

xcode.jpg

This morning I was playing around with the CryptoQuip solver, and realizing that the mapping code I had could be made a little simpler if I used the fact that ASCII character representations are all numerically ascending, and that makes character math something to use - as opposed to simple look-ups. For instance, if we look at the existing code for +createPatternText::

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = [text length];
  5. const char *ascii = "abcdefghijklmnopqrstuvwxyz";
  6. char pattern[255];
  7. for (NSUInteger i = 0; i < len; ++i) {
  8. pattern[i] = ascii[strchr(src, src[i]) - src];
  9. }
  10. pattern[len] = '\0';
  11. return [NSString stringWithUTF8String:pattern];
  12. }

the look-up on line 8, using the const char array defined on line 5, is really just an offset calculation, and can be replaced with:

  1. + (NSString*) createPatternText:(NSString*)text
  2. {
  3. const char *src = [[text lowercaseString] UTF8String];
  4. NSUInteger len = MIN([text length], 255);
  5. char pattern[255];
  6. for (NSUInteger i = 0; i < len; ++i) {
  7. pattern[i] = 'a' + (strchr(src, src[i]) - src);
  8. }
  9. pattern[len] = '\0';
  10. return [NSString stringWithUTF8String:pattern];
  11. }

and the result is exactly the same. But now, we're not creating the const char array on the stack, on each call, and the offset into the array is the same addition as we are doing here. Simpler.

But when we go to Swift, we see that the beauty and elegance of the underlying data is being clouded with the all-too-common object oriented design of Character.

  1. var pattern: String {
  2. get {
  3. let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
  4. "l","m","n","o","p","q","r","s","t","u","v",
  5. "w","x","y","z"]
  6. var ans = ""
  7. let src = Array(self.lowercased().utf8)
  8. for c in src {
  9. ans.append(ascii[src.firstIndex(of: c)!])
  10. }
  11. return ans
  12. }
  13. }

Sadly, we can't simplify line 9 because the Character object doesn't allow for construction based on an ASCII value. Sure, it has all kinds of nice interrogation methods to test and see if it's ASCII, or uppercase, or a number... but you can't actually do the simple math of adding 1 to 'a' and getting 'b'. That is a shame, and it's not something you can easily add because the Character class would need a new -init: method.

So while I really like parts of Swift 5, I think they didn't really think about all the uses that ObjC fits into when making the new classes. It's a process, I get that, but it's causing folks to write code around these issues, and that's a mistake. No one will go back and fix their code when, and if, they add a way to do this.

Adding Pattern Generation to CryptoQuip

April 2nd, 2019

xcode.jpg

This morning I took a little time to bring the pattern matching code from the Clojure and Swift codebases of the Quip solver to the ObjC version because I noticed the other day that the ideas of the pattern matching were added after the ObjC version was working, and I hadn't back-filled them to the older code. I know it won't make a difference in the execution time, as this is all done before the attack is started, but the old code really was a mess, and the new code had to be cleaner.

The original code had the following method on the CypherWord:

- (BOOL) matchesPattern:(NSString*)plaintext
{
    BOOL  match = YES;
 
    // make sure that we have something to work with
    if (match && (([self getCypherText] == nil) || (plaintext == nil))) {
        match = NO;
    }
 
    // check the lengths - gotta be the same here for sure
    if (match && ([[self getCypherText] length] != [plaintext length])) {
        match = NO;
    }
 
    /*
     * Assume that each pair of characters is a new map, and then test that
     * mapping against all other cyphertext/plaintext pairs that SHOULD match
     * in the word. If we get a miss on any one of them, then we need to fail.
     */
    if (match) {
        unichar    cypher, plain, c, p;
        NSUInteger len = [plaintext length];
        for (NSUInteger i = 0; (i < len) && match; ++i) {
            // get the next possible pair in the two words
            cypher = [[self getCypherText] characterAtIndex:i];
            plain = [plaintext characterAtIndex:i];
            // check all the remaining character pairs
            for (NSUInteger j = (i+1); (j < len) && match; ++j) {
                c = [[self getCypherText] characterAtIndex:j];
                p = [plaintext characterAtIndex:j];
                if (((cypher == c) && (plain != p)) ||
                    ((cypher != c) && (plain == p))){
                    match = NO;
                    break;
                }
            }
        }
    }
 
    return match;
}

And it worked because it checked each pair of characters in the plaintext and cypher text, and looked for mismatches... but it wasn't very clean, and the same scanning had to be done on the same word over and over. So it wasn't all that efficient.

Then I added a class method to CypherWord to generate the pattern:

+ (NSString*) createPatternText:(NSString*)text
{
    const char *src = [[text lowercaseString] UTF8String];
    NSUInteger  len = [text length];
    const char *ascii = "abcdefghijklmnopqrstuvwxyz";
    char        pattern[255];
    for (NSUInteger i = 0; i < len; ++i) {
        pattern[i] = ascii[strchr(src, src[i]) - src];
    }
    pattern[len] = '\0';
    return [NSString stringWithUTF8String:pattern];
}

In the setter we then added:

- (void) setCypherText:(NSString*)text
{
    _cyphertext = text;
    _cypherSize = [text length];
    _cypherPattern = [CypherWord createPatternText:text];
}

So that setting the CypherText set the length and the pattern as well. Not bad at all, and the overhead is minimal for the puzzles we're dealing with.

At this point, we can then re-write the -matchesPattern: to be:

- (BOOL) matchesPattern:(NSString*)plaintext
{
    return ([self getCypherText] != nil) && (plaintext != nil) &&
           ([self length] == [plaintext length]) &&
           [[self getCypherPattern] isEqualToString:[CypherWord
             createPatternText:plaintext]];
}

and leverage the fact that we have instance variables for the length and pattern, and still use all the short-circuit techniques to make sure that we don't make a bad call, or sloppy comparison.

Nothing amazing, but it's nice to get this version up to the same design as the others.

Updating my WordPress CodeHighlighterPlus to GeSHi 1.0.9.0

March 28th, 2019

wordpress.gif

This morning I decided to see if I couldn't fix the Swift highlighting on this blog by updating to the latest GeSHi 1.0.9.0 - which is now using PHP 7, and as it turns out, so is HostMonster, so I'm in luck! 🙂 At the same time, I really wanted to document all the components and links so that this post makes it a lot easier to update things later.

As a point of reference, the CodeHighlighterPlus project is based off the CodeHighlighter WordPress plugin, and it's not bad - it's just not been updated in many years, and there are a lot of added languages in that time. Still... I tip my hat to the original authors, as I couldn't have done it without them.

The steps to do this required a little digging, but that wasn't bad - in that I had a few posts about this process already, and so let's just repeat them here to make sure it's all clear for the next time. I started by making sure that my local CodeHighlighterPlus plugin was up-to-date with the GitHub repo. All good, so let's get the latest code from GeSHi, and just overlay it on the local repo. Replace where necessary, and then we're up to date with GeSHi... but it's not really cleaned up the way I like it.

The next thing was to update the geshi.php file for a few changes. The first thing I wanted to tackle with CodeHighlighterPlus was the line numbers. There was far too much space between the lines in a code sample with line numbers. This is corrected simply in the style for the lines:

  1. /**
  2.   * Line number styles
  3.   * @var string
  4.   */
  5. protected $line_style1 = 'font-weight: normal; vertical-align:top;';
  6.  
  7. /**
  8.   * Line number styles for fancy lines
  9.   * @var string
  10.   */
  11. protected $line_style2 = 'font-weight: bold; vertical-align:top;';

to:

  1. /**
  2.   * Line number styles
  3.   * @var string
  4.   */
  5. protected $line_style1 = 'margin: 0; font-weight: normal; vertical-align:top;';
  6.  
  7. /**
  8.   * Line number styles for fancy lines
  9.   * @var string
  10.   */
  11. protected $line_style2 = 'margin: 0; font-weight: bold; vertical-align:top;';

The last change is for the blank lines that start, and end, the code section when you use line numbers. It's just plain annoying. Change:

  1. // Get code into lines
  2. /** NOTE: memorypeak #2 */
  3. $code = explode("\n", $parsed_code);
  4. $parsed_code = $this->header();

to:

  1. // Get code into lines
  2. /** NOTE: memorypeak #2 */
  3. $code = explode("\n", $parsed_code);
  4. // remove a blank first and last line
  5. if ('' == trim($code[count($code) - 1])) {
  6. unset($code[count($code) - 1]);
  7. $code = array_values($code);
  8. }
  9. if ('' == trim($code[0])) {
  10. unset($code[0]);
  11. $code = array_values($code);
  12. }
  13. $parsed_code = $this->header();

and we are good to go with the changes to the code. Check everything in, push it up to GitHub and we're ready to deploy it.

At this point, I just need to deploy this to each of the WordPress sites on my server - and that's as simple as:

  $ cd public_html/blog/wp-content/plugins/CodeHighlighterPlus
  $ git pull

where blog is the directory in the WordPress content for the specific blog I'm working with. I simply use GitHub as the mechanism of deployment - with a pull system to make sure it doesn't mess too much stuff up.

In this release of GeSHi, we now have support for the following languages:

4cs            e              magiksf        qml
6502acme       ecmascript     make           racket
6502kickass    eiffel         mapbasic       rails
6502tasm       email          mathematica    rbs
68000devpac    epc            matlab         rebol
abap           erlang         mercury        reg
actionscript   euphoria       metapost       rexx
actionscript3  ezt            mirc           robots
ada            f1             mk-61          rpmspec
aimms          falcon         mmix           rsplus
algol68        fo             modula2        ruby
apache         fortran        modula3        rust
applescript    freebasic      mpasm          sas
apt_sources    freeswitch     mxml           sass
arm            fsharp         mysql          scala
asm            gambas         nagios         scheme
asp            gdb            netrexx        scilab
asymptote      genero         newlisp        scl
autoconf       genie          nginx          sdlbasic
autohotkey     gettext        nimrod         smalltalk
autoit         glsl           nsis           smarty
avisynth       gml            oberon2        spark
awk            gnuplot        objc           sparql
bascomavr      go             objeck         sql
bash           groovy         ocaml-brief    standardml
basic4gl       gwbasic        ocaml          stonescript
batch          haskell        octave         swift
bf             haxe           oobas          systemverilog
biblatex       hicest         oorexx         tcl
bibtex         hq9plus        oracle11       tclegg
blitzbasic     html4strict    oracle8        teraterm
bnf            html5          oxygene        texgraph
boo            icon           oz             text
c              idl            parasail       thinbasic
c_loadrunner   ini            parigp         tsql
c_mac          inno           pascal         twig
c_winapi       intercal       pcre           typoscript
caddcl         io             per            unicon
cadlisp        ispfpanel      perl           upc
ceylon         j              perl6          urbi
cfdg           java           pf             uscript
cfm            java5          phix           vala
chaiscript     javascript     php-brief      vb
chapel         jcl            php            vbnet
cil            jquery         pic16          vbscript
clojure        julia          pike           vedit
cmake          julia.bak      pixelbender    verilog
cobol          kixtart        pli            vhdl
coffeescript   klonec         plsql          vim
cpp-qt         klonecpp       postgresql     visualfoxpro
cpp-winapi     kotlin         postscript     visualprolog
cpp            latex          povray         whitespace
csharp         lb             powerbuilder   whois
css            ldif           powershell     winbatch
cuesheet       lisp           proftpd        xbasic
d              llvm           progress       xml
dart           locobasic      prolog         xojo
dcl            logtalk        properties     xorg_conf
dcpu16         lolcode        providex       xpp
dcs            lotusformulas  purebasic      xyscript
delphi         lotusscript    pycon          yaml
diff           lscript        pys60          z80
div            lsl2           python         zxbasic
dos            lua            q
dot            m68k           qbasic

And with the addition of Swift, I have highlighting on all the code snippets I've been adding. Very nice!

The Dreadful State of IM Clients

March 28th, 2019

Adium.jpg

Yesterday morning I noticed that ICQ wasn't connecting within Adium - it was failing to connect with the error:

Error: Unable to connect to BOS server: Connection refused

and nothing I could find was changing that error. Now, to be fair, I can't ever remember using ICQ for chatting with anyone, but when I got Adium all those years ago, I made sure to get accounts on all the supported platforms so that I could be available to anyone wanting to chat.

Sadly, one by one, they have fallen off... or shut down... or locked down the services to only allow proprietary client access. I guess I get it, I'm just seeing the monitization of all these chat clients - HipChat, then Slack and Google Hangouts... and those are the ones that are still alive. Hotmail went to Microsoft, and then they shut it down. AOL - gone. Yahoo! - gone.

I now have Adium that hasn't really been updated in about a year, and it's got one client: Gtalk. And we know that this is getting shut down soon by Google to force us all to use their Hangouts client. I guess it's just the pendulum swinging back to the "Give me my money!" - but for folks like Microsoft and Google - do they really need the money they'd get off me using Skype or Hangouts?

The truth is that I won't use either - except for work, and there I have to so it's a captive audience. But what they've really done is pushed me to use my phone. I can text all these folks, and make group texts, and I can use any phone I want.

It's just so very sad.

UPDATE: yup... I did a little searching on the net to see what's up, and it turns out the owners of ICQ have decided that 28-Dec-2018 was the cut-off date for the old protocol that was supported in libpurple - which is used by Adium. Just another case of trying to monetize the IM space. In this case, I'm OK with letting this one go. Not worth the tracking risks for ICQ.

On Object Oriented Designs and Complexity

March 26th, 2019

Code Monkeys

Today, with the update of Xcode 10.2, and Swift 5.0, I had to struggle with the formatting of strings in order to find the pattern they represented. The point is really this: Represent the pattern of the characters in a word so that 'bee' and 'too' and 'see' all look like the same pattern.. This is used in my CryptoQuip solver, and the point is to group words by their patterns because we don't know what the actual characters are, but we do know their patterns - because it's a simple substitution cypher.

So how to do that? Well... when we look at Clojure - which just deals with the string as a sequence of characters - just data, we have:

(defn pattern
  "Function to take a word (as a string) and return a vector that is the
  pattern of that word where the values are the index of the character.
 
    => (pattern \"see\")
      (0 1 1)
    => (pattern \"rabbit\")
      (0 1 2 2 4 5)
  "
  [word]
  (map #(.indexOf word (int %)) word))

and if, for the sake of this post, we drop the comments, we get something very simple:

(defn pattern
  [word]
  (map #(.indexOf word (int %)) word))

if we look at this in Swift, we see:

  extension String {
    /**
     Attributes of a string that return a string that is the pattern of that
     word where the values are the index of the character. This is a simple
     baseline pattern generator for the words so they are comparable.
 
    ```
        => "see".pattern
        "abb"
        => "rabbit".pattern
        "abccef"
    ```
     */
    var pattern: String {
      get {
        let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
                                  "l","m","n","o","p","q","r","s","t","u","v",
                                  "w","x","y","z"]
        var ans = ""
        let src = Array(self.utf8)
        for c in src {
          ans.append(ascii[src.firstIndex(of: c)!])
        }
        return ans
      }
    }
  }

And the reason to use a String as opposed to a sequence of numbers in Swift, is that those comparisons are not nearly as nice in Swift as simple String comparisons are. But I look at all this, and while the protocols in Swift are nice - to add methods to existing classes - it's much the same in Ruby, and it can lead to some very tough bugs - and so you have to be very careful using them.

And this got me to thinking about the complexity of most systems and the OOA&D systems I've seen in C++, Java, Ruby, ObjC, Swift - and it really is hard to come up with a really great design if you don't put in a ton of effort on the work. Sure... Boost for C++, and Java classes, are good designs - but they had a lot of backing and time to get right. ObjC - specifically Foundation, is well-done, but again, that was a serious investment by NeXT. But most of the non-OS-level projects... like those in the wild, they are a mess.

I don't think this is an accident. I think good, solid, OOA&D is hard because there are so many times when a method isn't clearly belonging to one object, or another - and the language might not be set up to have stand-alone functions as an alternative - Java, Scala. So they have to go somewhere, and that means that things get tossed into the closest reasonable object - as long as it's "close enough". But then 6 months later, it's a disaster. No one remembers why each method is on these objects... and the circular references require interfaces, and then implementations of those interfaces... and it just gets to be a mess.

What I believe is that the simpler the code, the better. This means more abstract. More critical thinking, and less "let's just hammer this out" work. I'm sure there are folks that can do a good job on an OOA&D project - and it could be massive and complex... but those people are rare - very rare. And in general, you end up with really bad objects that create horrible inclusion requirements, and even worse threading issues - because it all mutable, and you just have systems that can't get larger than a certain size.

That's not good. Math doesn't work that way. Neither should coding.

Xcode 10.2 is Out

March 26th, 2019

xcode.jpg

This morning, after I updated to macOS 10.14.4, I noticed that Xcode 10.2 was out with Swift 5, and that has the new Strings, and the ABI compatibility, and a host of new things. So let's get that going, and see how it changes things. I really haven't read what is new in Swift 5 - other than the new Strings, so that will be something to read up on this morning.

What I've found in converting an ObjC project is that a few of the localizations needed to be cleaned up - Xcode did that just fine, and then it built and ran just fine. But for the Swift project, it complained about the use of index(of:) and suggested that it be changed to firstIndex(of:) - which is fine, but I don't know that anyone used to coding for very long makes that mistake - but OK... I get it.

The next one said that in this code:

  ans.append(ascii[self.index(of: c)!.encodedOffset])

needed to be changed to:

  ans.append(ascii[self.firstIndex(of: c)!.utf16Offset(in: self)])

and figuring that out took some time.

Specifically, they deprecated the encodedOffset - which they say was being misused. And then didn't really give me a replacement. I had to fiddle and search the docs for what to put in it's place. And given that this is coming off a call to self - how is it that utf16Offset() doesn't default to that?

In any case, I got that fixed, and then I refactored the code for pattern from:

  let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
                            "l","m","n","o","p","q","r","s","t","u","v",
                            "w","x","y","z"]
  var ans = ""
  for c in self {
    ans.append(ascii[self.firstIndex(of: c)!.utf16Offset(in: self)])
  }
  return ans

to:

  let ascii: [Character] = ["a","b","c","d","e","f","g","h","i","j","k",
                            "l","m","n","o","p","q","r","s","t","u","v",
                            "w","x","y","z"]
  var ans = ""
  let src = Array(self.utf8)
  for c in src {
    ans.append(ascii[src.firstIndex(of: c)!])
  }
  return ans

I just didn't like the way the encoding was being handled, and that got to be a real annoyance... so by just converting it to UTF8, and then using that array for indexing, I was able to do the same thing - and it looks a lot cleaner.

I'm less and less a fan of the speed with which they released Swift. I understand they needed to get it out in the wild, and they had to answer requests for features, but they added too many too quickly, and without the long-term consideration that they are usually known for. I mean look at ObjC... the classes changed a little, but the language was shipping product for 5 yrs, and Swift has been around for 5 yrs, and it's still not close to stable.

Part of it is also that they stuck with OOA&D, and that's got to be a killer.

Apple News+ is Pretty Nice

March 26th, 2019

Apple Computers

This morning I signed up for Apple News+ - and I have to say, it's pretty nice. Sure, it's having some growing pains this morning - the News app in iOS 12.2 is crashing a bit when it can't talk to the servers at Apple HQ, but that will get worked out. For the little time it was stable this morning, I was really impressed with the offerings - some things I have paid for in the past, but just haven't kept up with, and others I've always wanted to have available to read:

  • Scientific American
  • Time
  • Runner's World
  • The Wall Street Journal
  • Bloomberg Businessweek
  • The New Yorker
  • The Atlantic
  • Flying

it's a list that keeps going, and it's really impressive. Sure, it's more than $30/yr - but that's for one magazine, and I can carry these all with me in my pocket on my phone. It's really just amazing.

Now, I've been reading a lot more on the News app because I just can't stand Facebook any more, and this is a lot better place to get news - it's a known source, and that's something I find I don't like about all the stuff you find on Facebook - you just can't trust it.

I'm not sure about the other services they introduced yesterday - except the Apple Card. That's something I'll get because of the Daily Cash Back that I can use because I put so much of my life on my Quicksilver card with 1.5% cash back. This is just a better deal.

Can't wait for the Summer when they get that going...

iTerm2 3.3.0beta1 new Title Bar

March 25th, 2019

iTerm2

This morning I saw that iTerm2 had a new release - 3.3.0beta1, and in this new release, there were several new options for the Title Bar, and even a fancy Status Bar for things like the git branch, running app, etc. It is quite a nice visual upgrade. So this is how I've enabled the new Title Bar.

First, set the iTerm2 Theme to Minimal. Go to Settings, select the Appearance tab, and choose "Minimal" from the Theme dropdown.

Next, select the Profiles tab, select Window, and in the dropdown for Settings for New Window, select Compact.

Go to Advanced, and scroll down to the Tab settings, and for Tab bar height for compact windows with minimal theme, I have 22. It's about the exact size of the old window title bar, and it looks just great:

ITerm2 Title Bar

Happy Pi Day!

March 14th, 2019

pi.jpg

This was a day that the kids would always look forward to when they were in elementary school. The teachers at their school would always make a fun day of "Pi Day". Today, it makes me feel like going to Baker's Square and having a piece, but that's just a fleeting emotion... I'll be just fine with the memories of the days the kids would come back from school and tell me all about their "Pi Day".

Some memories are just so good, they survive the bad.

Sublime Text 3.2 is Out

March 13th, 2019

Sublime Text 2

This morning I saw a tweet from @SublimeHQ saying that Sublime Text 3.2 was out, and the list of fixes and features was really quite nice. The git integration is very nice - you can now see the branch you're on in the status bar, and when you click that, it will bring up Sublime Merge. It's not bad... not sure how much I'll use that, but it's nice that they have it integrated, and can show the branch on the editor window.

They also added a lot of nice Mac features:

  • Mac: Added Mojave support
  • Mac: Add full support for macOS native tabs
  • Mac: Ensure context menus are shown without scrolling
  • Mac: Error message dialogs can now be closed with the escape key
  • Mac: Improved window placement
  • Mac: Improved resize performance

I wasn't really disappointed with the performance, but to see the support for native tabs and Mojave - it's just really nice.

They also updated the syntax highlighting for Clojure, D, Go, and Lua - and while I'm not a big fan of the others, the changes they made to Clojure really nice. I also noticed that they have done something with the rendering in the Clojure files - could all be part of the syntax highlighting, but I'll have to see what it turns out to be. Looks like they "thinned out" the strokes on the comments, but we'll see. In any case, it's a lot nicer now for most of the coding I do.

It's an exciting upgrade, and it makes my day a little brighter.