<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>mikeash.com pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html comments</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>mikeash.com Recent Comments</description><lastBuildDate>Fri, 07 Aug 2026 12:17:23 GMT</lastBuildDate><generator>PyRSS2Gen-1.0.0</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>robert engels - 2024-09-10 22:02:38</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>awesome post. really great reverse engineering work and write-up.</description><guid isPermaLink="true">5457f0412d63498f24e4bfcc2aaea369</guid><pubDate>Tue, 10 Sep 2024 22:02:38 GMT</pubDate></item><item><title>Sam - 2020-09-06 22:00:34</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>As of macOS 10.15 (WWDC2019 #210), NSColor is now a tagged value, too!</description><guid isPermaLink="true">635b2f97d0cab83f06cca126fa03af16</guid><pubDate>Sun, 06 Sep 2020 22:00:34 GMT</pubDate></item><item><title>mikeash - 2020-06-20 20:22:22</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>There isn't a whole lot to say about that, it's just a random value XORed into every tagged pointer to make it harder for an attacker with a memory write primitive to forge specific tagged values.</description><guid isPermaLink="true">4a8b5f47134d0061db41cb707b6218ba</guid><pubDate>Sat, 20 Jun 2020 20:22:22 GMT</pubDate></item><item><title>Kentzo - 2020-06-09 15:21:13</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Would love to see an update to this article that would explain objc_debug_taggedpointer_obfuscator</description><guid isPermaLink="true">27715aba5748e0a4d3e6cf35dc0dddfe</guid><pubDate>Tue, 09 Jun 2020 15:21:13 GMT</pubDate></item><item><title>Rob Mayoff - 2018-10-11 17:41:33</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>I just discovered that lldb also implements tagged pointer string decoding in its lldb_private::formatters::NSTaggedString_SummaryProvider function.
&lt;br /&gt;
&lt;br /&gt;&lt;a href="https://github.com/llvm-mirror/lldb/blob/master/source/Plugins/Language/ObjC/NSString.cpp#L338"&gt;https://github.com/llvm-mirror/lldb/blob/master/source/Plugins/Language/ObjC/NSString.cpp#L338&lt;/a&gt;
&lt;br /&gt;</description><guid isPermaLink="true">4cf095dd31296ac64c427075a875e00a</guid><pubDate>Thu, 11 Oct 2018 17:41:33 GMT</pubDate></item><item><title>mikeash - 2017-11-06 19:13:06</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>&lt;b&gt;Ben H.&lt;/b&gt;: Interesting problem. I think I would favor what you say at the end, that you were always doing it wrong and it's just finally manifesting. Even in the absence of tagged pointers, nothing says Apple couldn't intern string objects behind the scenes.
&lt;br /&gt;
&lt;br /&gt;Object equality is still perfectly safe, but I think the problem boils down to it meaning something a bit different from what you (and probably many other people) think it does. Pointer equality just checks: "Are these literally the exact same object in memory?" It sounds like you're using it to answer the question: "Were these created in different places?" There questions always had the same answer for a long time, but nothing says they have to.
&lt;br /&gt;
&lt;br /&gt;I should say, I don't blame you for thinking this way and it seems like an easy mistake to make! Sometimes we just get it wrong.
&lt;br /&gt;
&lt;br /&gt;It sounds like maybe you shouldn't be using string objects at all, but rather create your own custom class and pass those around. Your class could then contain a string, and since it's your own class, you'd have full control over how objects get allocated.
&lt;br /&gt;
&lt;br /&gt;If it's not possible to have a completely separate class, you may be able to achieve the same effect by subclassing NSString.</description><guid isPermaLink="true">1e32e604381961f769447304b032574e</guid><pubDate>Mon, 06 Nov 2017 19:13:06 GMT</pubDate></item><item><title>Ben H. - 2017-11-05 06:16:04</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>A followup on my previous comment.  It turns out that -mutableCopy does not work; I'm putting these strings into an NSDictionary as keys, and it seems NSDictionary makes immutable copies of its keys, which turns them right back into NSTaggedPointerStrings.  However, I have discovered that using +[NSString stringWithString:] does seem to work; you get back a copy of the string that is not an NSTaggedPointerString.  This is surprising, really – I see no reason for them to do that – and it could probably break at any time.  For now, though, it works.  I wish I had a better solution.  This tagged-pointer business is an interesting, cool idea, but I really don't like that it can change the behavior of existing code in unexpected ways like this.  Maybe I should never have been assuming that two separately-created strings would necessarily be two different objects; but does Apple warn against making that assumption anywhere in their docs?</description><guid isPermaLink="true">45f3e890e40a799485bc618d338690ce</guid><pubDate>Sun, 05 Nov 2017 06:16:04 GMT</pubDate></item><item><title>Ben H. - 2017-11-05 03:01:31</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Hi Mike.  Amazing post, very informative.  I have just run into an interesting problem in my app that results from NSTaggedPointerString, and I wonder whether you have any advice.  For reasons not worth going into, I'm reading a bunch of strings from a file and keeping them, and later on I need those strings to be distinct objects – i.e. no two strings should be pointer-identical.  That used to be the case; each separately-created string would be a separate object.  Now, however, as a side effect of Apple's use of NSTaggedPointerString, the short strings are effectively getting uniqued, and pointer equality between strings sometimes results, breaking my code.  So one question is: can you see a safe way to guarantee that the system does not do this for my strings?  I am contemplating using -mutableCopy and keeping mutable strings instead, but of course Apple could choose to tag-ify those as well if they wanted to, so that's not really safe.  Another question is: in this world of tagged points, is the whole idea of using pointer equality now unsafe?  Should (a==b) or (a!=b) where a and b are pointers now be verboten, because it has become essentially meaningless and dependent upon internal implementation?</description><guid isPermaLink="true">94886ace914b2f8427925599bb3ac460</guid><pubDate>Sun, 05 Nov 2017 03:01:31 GMT</pubDate></item><item><title>Chris C. - 2015-08-06 16:17:28</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>My first thought was perhaps a benefit of tagged pointer strings was to improve runtime type encoding performance, but the six-bit encoding table doesn't seem to bear that out.</description><guid isPermaLink="true">3eeb95d20691204b2546b3b1086f9cdc</guid><pubDate>Thu, 06 Aug 2015 16:17:28 GMT</pubDate></item><item><title>Jean-Daniel - 2015-08-05 16:04:10</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>@Shane: There is no guarantee that passing a NSNumber to - [NSString compare:] raises an exception. If you pass anything else than a string to -[NSString compare:], the behavior is undefined, so the implementation is free to returns an invalid value, or raise an exception, or do anything else.</description><guid isPermaLink="true">4c9a0ac89faf995ab81db8f736d3abea</guid><pubDate>Wed, 05 Aug 2015 16:04:10 GMT</pubDate></item><item><title>mikeash - 2015-08-04 02:58:03</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>&lt;b&gt;Björne:&lt;/b&gt; On modern systems, computation is fast and memory accesses are slow. If you can avoid accessing memory by burning some extra CPU cycles, the result is likely to be a net gain. Memory allocation and deallocation is also costly, which tagged pointers avoid. Reducing memory usage also increases speed for the program overall, because cache is a limited resource, and spending less cache on strings means more cache available for other uses.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;David:&lt;/b&gt; Nice find. Makes sense that the debugger would want to know about the internal details like this.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;Shane:&lt;/b&gt; &lt;code&gt;NSTaggedPointer&lt;/code&gt;'s &lt;code&gt;compare:&lt;/code&gt; implementation assumes that any tagged pointer passed in to the method is also an &lt;code&gt;NSTaggedPointer&lt;/code&gt; and does a direct comparison between the contents of the two. This is a valid optimization because the parameter to &lt;code&gt;compare:&lt;/code&gt; is &lt;code&gt;NSString *&lt;/code&gt; and by passing an &lt;code&gt;NSNumber&lt;/code&gt; in, you're violating that static type. So instead of blowing up trying to treat the tagged &lt;code&gt;NSNumber&lt;/code&gt; as a string by sending it string messages, it performs a bogus comparison on the tagged &lt;code&gt;NSNumber&lt;/code&gt;'s internal data.</description><guid isPermaLink="true">9699c016311f6947593fd775a87fc91e</guid><pubDate>Tue, 04 Aug 2015 02:58:03 GMT</pubDate></item><item><title>Shane - 2015-08-04 00:08:35</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>@Rob: I understand that I'm mixing classes. The point is that no exception is being raised when the tagged pointer string is being passed to NSNumber's compare: method.</description><guid isPermaLink="true">f0cf51ff20f262a3a8c706872e488ead</guid><pubDate>Tue, 04 Aug 2015 00:08:35 GMT</pubDate></item><item><title>Rob Mayoff - 2015-08-03 18:59:22</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>&lt;b&gt;@Shane&lt;/b&gt;: Your error has nothing to do with tagged pointers. The problem is that &lt;code&gt;-[NSNumber compare:]&lt;/code&gt; requires an &lt;code&gt;NSNumber*&lt;/code&gt; argument, and &lt;code&gt;-[NSString compare:]&lt;/code&gt; requires an &lt;code&gt;NSString*&lt;/code&gt; argument. You are (indirectly) passing an &lt;code&gt;NSNumber*&lt;/code&gt; argument to &lt;code&gt;-[NSString compare:]&lt;/code&gt; by mixing the objects in your array.</description><guid isPermaLink="true">2aefb50f2fb90491291ff3028c067dd9</guid><pubDate>Mon, 03 Aug 2015 18:59:22 GMT</pubDate></item><item><title>David - 2015-08-03 04:22:35</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Great writeup.
&lt;br /&gt;
&lt;br /&gt;I googled that hardcoded "eilotrm..." string lookup, and got this:
&lt;br /&gt;
&lt;br /&gt;&lt;a href="https://llvm.org/svn/llvm-project/lldb/trunk/source/DataFormatters/CXXFormatterFunctions.cpp"&gt;https://llvm.org/svn/llvm-project/lldb/trunk/source/DataFormatters/CXXFormatterFunctions.cpp&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;The method in question is NSTaggedString_SummaryProvider
&lt;br /&gt;</description><guid isPermaLink="true">81649f789b9c43cb83cd96842672163a</guid><pubDate>Mon, 03 Aug 2015 04:22:35 GMT</pubDate></item><item><title>Björne - 2015-08-02 13:49:28</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>But what is the speedup? The benefit with tagged integers is that they work as normal numbers. The assembly instructions ADD, SUB and IDIV all do the right thing automagically. But to append two tagged strings, you first need to unpack them, do the append and then see if they still fit in the packed format.
&lt;br /&gt;
&lt;br /&gt;Operations also get slower, like taking the hashcode of a packed string. You want both the packed and unpacked version of the string "foo" to have the same hashcode, and since you can't store it on the tagged pointer string itself, you have to recompute it each time.</description><guid isPermaLink="true">5cdae2e1ac6707c489e77e8aec88f8e2</guid><pubDate>Sun, 02 Aug 2015 13:49:28 GMT</pubDate></item><item><title>Shane - 2015-08-02 07:47:58</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>"The next three bits indicate the tagged class." But not everything seems to understand them:
&lt;br /&gt;
&lt;br /&gt;&lt;code&gt;    NSString *seven = @"7";
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSString *taggedSeven = [[seven mutableCopy] copy];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSLog(@"taggedSeven: %p, class: %@", taggedSeven, [taggedSeven class]); //taggedSeven: 0x3715, class: NSTaggedPointerString
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSLog(@"seven: %p,  class: %@", seven, [seven class]); //seven: 0x100002078,  class: __NSCFConstantString
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSArray *array = @[@4, @2.0, taggedSeven, @6, @10];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSArray *result = [array sortedArrayUsingSelector:@selector(compare:)];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NSLog(@"result: %@", result); // 2, 4, 6, 10, 7
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;array = @[@4, @2.0, seven, @6, @10];
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result = [array sortedArrayUsingSelector:@selector(compare:)]; //-[__NSCFNumber length]: unrecognized selector sent to instance 0x627
&lt;br /&gt;&lt;/code&gt;
&lt;br /&gt;</description><guid isPermaLink="true">37e9780541f007b6656b7ab0e97ae8ac</guid><pubDate>Sun, 02 Aug 2015 07:47:58 GMT</pubDate></item><item><title>mikeash - 2015-08-01 19:36:51</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>&lt;b&gt;Daniel:&lt;/b&gt; Interesting thought. However, it looks like either the buffer is large enough, or there's nothing important after it, because logging &lt;code&gt;0xf5&lt;/code&gt; just prints &lt;code&gt;eeeeeeeeeeeeeee&lt;/code&gt; and continues normally.
&lt;br /&gt;
&lt;br /&gt;Note that if you experiment with this, you have to make sure that your code does something else with &lt;code&gt;NSString&lt;/code&gt; first, like a dummy call to &lt;code&gt;[NSString class]&lt;/code&gt;, in order to get it properly registered with the runtime. Otherwise you crash in &lt;code&gt;objc_msgSend&lt;/code&gt; trying to mess with a tagged pointer it doesn't understand.</description><guid isPermaLink="true">0ae5afdb4912d98cc2fe80734a7ca167</guid><pubDate>Sat, 01 Aug 2015 19:36:51 GMT</pubDate></item><item><title>Pete - 2015-08-01 15:59:08</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Uwe: It's always possible that you can come up with pathological cases where an optimization makes existing code run slower, but that's probably not one of them.
&lt;br /&gt;
&lt;br /&gt;The vast majority of strings stored by almost any program (even in other languages) are ASCII.  This optimization only works at all for really short strings.  So the only case where this would matter at all to a "Chinese app" would be if it dealt with a huge number of tiny user-entered strings ... without doing any I/O, perhaps, since I/O tends to dwarf even memory dereferencing.
&lt;br /&gt;
&lt;br /&gt;Remember, the short form of the encoding also doesn't work for 10-character ASCII strings that contain the letter "b" or the number "2".  Does that mean apps that use "b" and "2" a lot will be 'slower'?  Probably not enough for anybody to ever notice.</description><guid isPermaLink="true">56aa8f70c2065d7ab2174482c1e3d31f</guid><pubDate>Sat, 01 Aug 2015 15:59:08 GMT</pubDate></item><item><title>Daniel - 2015-08-01 09:18:38</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>There might be a second amendment to be made regarding the &lt;code&gt;__stack_chk_guard&lt;/code&gt;:
&lt;br /&gt;In a malformed tagged pointer, this would still overflow &lt;code&gt;buffer&lt;/code&gt; by up to four bytes, (probably garbling up the stored &lt;code&gt;index&lt;/code&gt; parameter on stack?) overwriting them with the ASCII code for “e”.
&lt;br /&gt;If it's really writing into the memory of &lt;code&gt;index&lt;/code&gt;, that would then enter the exception branch for any original &lt;code&gt;index&lt;/code&gt; parameter reporting a &lt;i&gt;ridiculously&lt;/i&gt; large out-of-range index—even if the caller passed 0.</description><guid isPermaLink="true">81e39e3ea2ee883ebded6259b88f1a32</guid><pubDate>Sat, 01 Aug 2015 09:18:38 GMT</pubDate></item><item><title>Uwe - 2015-08-01 05:40:27</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Does that mean that eg Chinese apps have a chance to run a bit slower?</description><guid isPermaLink="true">1beab29143321aa5e1bb82ee844d59d6</guid><pubDate>Sat, 01 Aug 2015 05:40:27 GMT</pubDate></item><item><title>mikeash - 2015-08-01 02:47:19</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>An even more fun approach to mixing fast numerics and pointers is to embed your pointer value in the payload of an IEEE754 double NaN. A double NaN is represented by setting the 11 bits of the exponent to 1, and by having at least one 1 bit in the 52-bit fraction area. This gives you 52 bits of storage, or 53 bits if you can guarantee you'll never have all zeroes in the fraction area. If you can squeeze your pointers into that extra space (for example, since x86-64 only uses 48 bits of each pointer value then this is trivial) then you can represent each numeric value as a double, and each object value as a NaN.
&lt;br /&gt;
&lt;br /&gt;NaNs propagate through a calculation. Any calculation that involves a NaN will produce a NaN. That means that you can run through your calculation without checking the inputs, and just fall back to the slow path if the result is NaN.
&lt;br /&gt;
&lt;br /&gt;I don't think Cocoa cares since, as you say, you don't use NSNumber for speed. The Objective-C tagged pointer implementation appears to be all about reducing memory allocations, which will also improve performance but as a sort of side effect. NSNumber gets the tagged pointer treatment not because apps are adding them together a lot, but because they're using a lot of NSNumbers to store in arrays and dictionaries and such.</description><guid isPermaLink="true">53640ec33b60fd9ce550d9688e13a147</guid><pubDate>Sat, 01 Aug 2015 02:47:19 GMT</pubDate></item><item><title>Ken - 2015-08-01 02:39:01</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Interesting.  I wonder: were they constrained by platform/historical decisions, to use all 0's as the pointer tag value?
&lt;br /&gt;
&lt;br /&gt;The way it's implemented here, arithmetic (with NSNumber) has to mask off the low bits before doing anything.  I guess that must be a big gain over the old way, since masking a couple bits is still a lot faster than dereferencing a pointer.  Also (IME), NSNumber is really only used for storage, not arithmetic, so people don't really expect it to be fast.  When you want fast integer math in Cocoa, you use "int".
&lt;br /&gt;
&lt;br /&gt;In contrast, SBCL, for example, uses all 0's as the fixint tag value, so when they want to add 3 plus 4, they can just call x86 ADD.  But this tagged pointer is the default way to do arithmetic, in SBCL, so making this case fast is important to them.
&lt;br /&gt;
&lt;br /&gt;&lt;a href="https://github.com/sbcl/sbcl/blob/master/src/compiler/generic/early-objdef.lisp"&gt;https://github.com/sbcl/sbcl/blob/master/src/compiler/generic/early-objdef.lisp&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;In fact, for fast integer math, they only need to test (and potentially branch) once.  Just bitwise-OR the arguments together, and if the low bits are all still 0 (which in practice they almost always are), you know it's safe to ADD them together!</description><guid isPermaLink="true">cd90e4aed4013bda3a4c578a72b2b058</guid><pubDate>Sat, 01 Aug 2015 02:39:01 GMT</pubDate></item><item><title>mikeash - 2015-08-01 01:28:29</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>&lt;b&gt;Rob Mayoff:&lt;/b&gt; Good eye. There's a reason I said "&lt;i&gt;typically&lt;/i&gt; a multiple of 16" in the article. If you allocate objects, they are 16-byte aligned, but not all objects are allocated. String constants just get packed into your data section, so they get looser alignment. Fortunately 8 bytes is the worst you'll see on 64-bit, since everything still wants pointer alignment. 32-bit will be 4-byte aligned.
&lt;br /&gt;
&lt;br /&gt;Using the high bits works well as long as you know a chunk of the address space is unused. That's true on x86-64 for now, where only 48 bits are used. It's not the case on 32-bit, so a hypothetical tagged pointer implementation there wouldn't be able to use the high bits. If x86-64 expands to the full 64-bit address space then that would stop working, too.
&lt;br /&gt;
&lt;br /&gt;And yes, you could use any of the bottom three bits as a signal for taggedness. This would expand the number of tagged classes you can discriminate between. I suspect it's less efficient to check for, though. Or maybe they just don't need that many (last I checked, the tagged class table wasn't fully used yet) and went for simplicity.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;Bob Peterson:&lt;/b&gt; I know six-bit encodings had some history, and five-bit codings substantially pre-date the computer (e.g. Baudot code), but I never heard about the link between 18-bit words, six-bit encodings, and the standard three-character file extension. Interesting tidbit.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;Matt:&lt;/b&gt; Using the high bits wouldn't break ASLR. You'd only use the high bits if there were portions of the address space that were always unmapped. This is the case in the current implementations of x86-64, for example, where the top 16 bits of a pointer must always be set to the same value, and on the Mac the high values are reserved for the kernel. That means that everything above 2^48 is unmapped from the perspective of userspace, and you can count on those top bits always being zero in a real pointer. ASLR will work within the 48-bit address space, but can't go beyond it.
&lt;br /&gt;
&lt;br /&gt;People always bring up stuff like "32-bit clean" when I post articles about these things, but it simply doesn't apply. The problem with the 24-&amp;gt;32-bit transition on the Mac was that &lt;i&gt;third-party apps&lt;/i&gt; were pulling tricks like this. Apple could update their code, but third-party apps would break until they were fixed. When these tricks are only being done in the OS, it's not a problem. The OS gets updated whenever fundamental things like the process memory map is updated (for the x86-64 48-bit address space limitation, it would require new CPU hardware) and Apple can update their tagged pointer implementation accordingly. If it's burdensome, they can turn it off. It's a single &lt;code&gt;#define&lt;/code&gt; in the Objective-C runtime to toggle it, and I believe you can even toggle it on a process-by-process basis using an environment variable.
&lt;br /&gt;
&lt;br /&gt;As for why Apple doesn't use the high bits, I imagine they just don't need to, and using the low bit as the tagged pointer indicator keeps things simple.
&lt;br /&gt;
&lt;br /&gt;&lt;b&gt;Joseph Gentle:&lt;/b&gt; Good question! The exact same loops can be found in the implementation in other methods, like &lt;code&gt;-[NSTaggedPointerString getCharacters:range:]&lt;/code&gt; and &lt;code&gt;-[NSTaggedPointerString UTF8String]&lt;/code&gt;. My guess is that the code that converts the tagged string storage to a sequence of ASCII bytes lives in a single location in the source code, and then gets inlined into these different methods. &lt;code&gt;characterAtIndex:&lt;/code&gt; would use this rather than reading the character directly to keep the implementation in a single location. The overhead is probably not important, as performance-sensitive code won't typically use &lt;code&gt;characterAtIndex:&lt;/code&gt; in the first place.</description><guid isPermaLink="true">5324853f7976689e710c363350149db8</guid><pubDate>Sat, 01 Aug 2015 01:28:29 GMT</pubDate></item><item><title>Joseph Gentle - 2015-08-01 01:09:30</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>I'm confused - Why did they loop through to make the buffer instead of simply reading the character off directly?
&lt;br /&gt;
&lt;br /&gt;For the 6 bit case:
&lt;br /&gt;
&lt;br /&gt;&lt;code&gt;
&lt;br /&gt;if (length &amp;lt; 0xa) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;tableIndex = stringData &amp;gt;&amp;gt; (index * 6 * 2) &amp;amp; 0x3f;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;return table[tableIndex];
&lt;br /&gt;}
&lt;br /&gt;&lt;/code&gt;</description><guid isPermaLink="true">5b79269441422551856d5724e45d869f</guid><pubDate>Sat, 01 Aug 2015 01:09:30 GMT</pubDate></item><item><title>Matt - 2015-08-01 00:04:20</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>@Rob Mayoff - using the high bits breaks more things; for instance,  security approaches like ASLR use the vastness of 64-bit virtual memory to make attacks harder so the high bits aren't guaranteed to be zero.
&lt;br /&gt;
&lt;br /&gt;Also, there are probably still people at Apple that remember the whole "32-bit clean" debacle, where early MacOS programs used the top 8 of 32 bits of addresses for flags. Worked great when the machines it ran on had 512k of RAM - not so hot when they started to get 16M+. :)</description><guid isPermaLink="true">19b012d8ed42f63c84324286150df949</guid><pubDate>Sat, 01 Aug 2015 00:04:20 GMT</pubDate></item><item><title>Bob Peterson - 2015-07-31 23:54:55</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>SIXBIT was used back on the early DEC/Digital machines. There were bit string pointers that made it easy to fetch an arbitrary string of bits, so encoding characters in 6 bits did not need masking and shifting. This compaction was invaluable when memory and long term storage were excruciatingly limited and slow. The three character file name suffix came directly out of this encoding: the 18 bit word could store three characters. #geezerhistory</description><guid isPermaLink="true">56f64dca469f80d1f10cd318793f38b3</guid><pubDate>Fri, 31 Jul 2015 23:54:55 GMT</pubDate></item><item><title>Rob Mayoff - 2015-07-31 21:52:11</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Great analysis. A few comments:
&lt;br /&gt;
&lt;br /&gt;1. Did you notice that your original constant string pointer 0x10ba41038 is 8-aligned, &lt;b&gt;not&lt;/b&gt; 16-aligned?
&lt;br /&gt;
&lt;br /&gt;2. You could use a tagged pointer scheme even if you allowed byte-aligned objects. Just use the high bits instead of the low bits.
&lt;br /&gt;
&lt;br /&gt;3. Given 8-aligned pointers, you don't need to dedicate a whole bit for a tag flag. The pointer must be fake if any of the low three bits are non-zero.</description><guid isPermaLink="true">78a2cd73ba84f307919670099179976f</guid><pubDate>Fri, 31 Jul 2015 21:52:11 GMT</pubDate></item><item><title>mikeash - 2015-07-31 17:28:36</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Heh, nice catch. I was thinking too much about 8-byte words. Fixed it to 11 now.</description><guid isPermaLink="true">b8ecfa10d8347181d27ab3456fa8d399</guid><pubDate>Fri, 31 Jul 2015 17:28:36 GMT</pubDate></item><item><title>Jean-Daniel - 2015-07-31 15:44:19</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html#comments</link><description>Maybe you shouldn't have removed the ___stack_chk_guard. It may help you to catch the buffer overflow that will arise if you try to decode a string longer than 8 characters ;-) (int8_t buffer[8];)
&lt;br /&gt;
&lt;br /&gt;That said, this is yet an other very interesting Friday Q&amp;amp;A. Thanks for the hard work.
&lt;br /&gt;</description><guid isPermaLink="true">b2687f75c9ba9b1080923fc6820865f7</guid><pubDate>Fri, 31 Jul 2015 15:44:19 GMT</pubDate></item></channel></rss>
