<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body><div style="font-family: sans-serif;"><div class="plaintext" style="white-space: normal;"><p dir="auto">(For all you teachers out there, Maurizio’s reply is a
<br>
master class in API design.)</p>
<p dir="auto">On 10 Jul 2023, at 17:09, Maurizio Cimadamore wrote:</p>
</div><blockquote class="embedded" style="margin: 0 0 5px; padding-left: 5px; border-left: 2px solid #777777; color: #777777;"><div id="8532B98E-81D7-423A-8E97-BC68BDF9F46A">

<div class="markdown-here-wrapper" data-md-url="" style="" markdown-here-wrapper-content-modified="true">
<p style="margin: 0px 0px 1.2em !important;">On 10/07/2023 16:02, Brian S O’Neill wrote:</p>
<blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221); padding: 0px 1em; color: rgb(119, 119, 119); quotes: none;">
<p style="margin: 0px 0px 1.2em !important;">I want to avoid polluting the other thread, which no longer has a<br>
valid subject line anyhow…</p>
<p style="margin: 0px 0px 1.2em !important;">1) The size limit of a memory segment is defined by a signed value,<br>
thus limiting the address space to be 63-bit. Is it likely that all<br>
64-bit architectures today and in the future only use the lower half<br>
of the address range?</p>
</blockquote>
<p style="margin: 0px 0px 1.2em !important;">While it’s true that a memory segment can never be big enough as the full address space, in reality that limit is big enough - e.g. it is hard to imagine a developer wanting to express a region of memory whose size is bigger than 2^63. I believe the angle you are coming from here is that you want to use a memory segment to model the entire heap (at least judging from the other thread you started), and then wanting to just use a raw long pointer as an “offset” into the segment. Given the unsigned limitations, this will not work 100%: the FFM API specifies that the “long” argument you pass to MemorySegment::get (same for var handle) is a positive offset, relative to the start of the segment. That is, a negative offset is, by definition, <em>outside</em> the memory segment. So, I do not see a way to add support for full unsigned long as memory segment sizes/offsets - in fact, I believe that allowing negative values in places where we expect an offset is almost always a smell, and a place for bugs to hide.</p>
<p style="margin: 0px 0px 1.2em !important;">In terms of interfacing with native code, note that the FFM API can still wrap whatever address a native library throws at it, by wrapping the base address (a long) into a segment using MemorySegment::ofAddress. This method takes a raw address value, which can be negative. Of course the size of the segment will be limited to “only” Long.MAX_VALUE, which, seems reasonable enough.</p>
<p style="margin: 0px 0px 1.2em !important;">While it’s theoretically possible to add a different kind of segment that behaves in a different way (e.g. allowing negative offsets), I believe the cost vs. benefit ratio for doing so would be very unfavourable.</p>
<blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221); padding: 0px 1em; color: rgb(119, 119, 119); quotes: none;">
<p style="margin: 0px 0px 1.2em !important;">2) The MemorySegment copy operation is safe for use against<br>
overlapping ranges, and is thus a “memmove” operation. My application<br>
would benefit from also having a “memcpy” operation, for those cases<br>
where I know that the ranges don’t overlap. Can such an operation be<br>
added?<br>
This is not an issue, or at least not one that should be solved via new API surface: MemorySegment::copy relies on Unsafe::copyMemory which does memmove vs. memcpy depending on wether it can prove that ranges overlap. Rather than duplicating the API, if we find cases where the existing logic doesn’t work well, we should probably invest in rectifying and/or improving that logic.</p>
<p style="margin: 0px 0px 1.2em !important;">3) Dynamic memory allocation is performed against Arenas, but freeing<br>
the memory is only allowed when the Arena is closed. I find this to<br>
be cumbersome at times, and in one case I ended up creating a single<br>
Arena instance paired with each allocation. The other solution is to<br>
directly call malloc/free, which is what I’m using in some places.<br>
Both solutions are messy, so is it possible to add a simple<br>
malloc/free API?</p>
</blockquote>
<p style="margin: 0px 0px 1.2em !important;">The API embraces the idea that one lifetime might be associated with multiple allocations quite deeply (this is explained in more details here [1]). While this is an arbitrary choice (after all, there is no perfect way to deal with deallocation of native resources in a way that is both safe and efficient), malloc/free is not the right primitive to design a safe API to manage off-heap resources for at least two reasons:</p>
<ul style="margin: 1.2em 0px;padding-left: 2em;">
<li style="margin: 0.5em 0px;">it is too fine-grained, there’s no way to group together logically-related resources (think of the relationship between dlopen, dlsym and dlclose)</li>
<li style="margin: 0.5em 0px;">it is not safe-by-default: anyone can free a pointer, even one they did not create</li>
</ul>
<p style="margin: 0px 0px 1.2em !important;">In contrast, the lifetime-centric approach adopted by the FFM API allows developers to group logically related segments in the same lifetime, which is, often, a very useful move, and allows the API to scale for managing lifetime of things that are not just malloc’ed segments (such specifying lifetime of an upcall stub, or that of a native library loaded with dlopen). This allows the API to detect pesky conditions such as memory leaks and/or use-after free. There are of course cases where this way of doing things is not a perfect fit, and a lower-level access to malloc/free is preferrable. In these cases, developers can, as you observed, just call malloc/free downcall method handles, and deal with memory allocation/deallocation completely manually. Or they can create a one-off arena to deal with that allocation.</p>
<p style="margin: 0px 0px 1.2em !important;">To do the latter, you probably would need some kind of abstraction on top:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;">record HeapAllocation(MemorySegment segment, Arena arena) implements AutoCloseable {   
    public close() { arena().close(); }

    static HeapAllocation mallocConfined(long bytes) {
          Arena arena = Arena.ofConfined();
          return new HeapAllocation(arena.allocate(bytes), arena);
    }
}
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">To do the former you need to write some wrappers for malloc/free:</p>
<pre style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em; line-height: 1.2em;margin: 1.2em 0px;"><code style="font-size: 0.85em; font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em; padding: 0px 0.3em; white-space: pre-wrap; border: 1px solid rgb(234, 234, 234); background-color: rgb(248, 248, 248); border-radius: 3px; display: inline;white-space: pre; overflow: auto; border-radius: 3px; border: 1px solid rgb(204, 204, 204); padding: 0.5em 0.7em; display: block !important;">static final MethodHandle MALLOC = Linker.nativeLinker()....
static final MethodHandle FREE = Linker.nativeLinker()....

static MemorySegment malloc(long bytes) {
    return MALLOC.invokeExact(bytes);
}

static MemorySegment free(MemorySegment segment) {
    return FREE.invokeExact(segment);
}
</code></pre>
<p style="margin: 0px 0px 1.2em !important;">Of course the former approach provides more temporal safety (but might also result in more overhead for enforcing said safety, which, I’m not sure you’d be too happy with). In the latter, what you see is what you get, you are playing the “power user” card, ensuring correctness (read: avoid use-after-free) is now up to you.</p>
<p style="margin: 0px 0px 1.2em !important;">I don’t think either approach looks too “messy”. Of course one-lifetime-per-segment is not the ideal sweet spot the FFM API is designed for, and one has to write some extra code, but the FFM API still allows you to do what you need to do (or to completely bypass temporal safety alltogether, if you decide to do so). After having spent considerable time looking at possible approaches to deal with memory safety, we did not find a “simpler malloc/free API” that was good enough as a building block for managing temporal resources in the FFM API.</p>
<blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221); padding: 0px 1em; color: rgb(119, 119, 119); quotes: none;">
<p style="margin: 0px 0px 1.2em !important;">4) GuardUnsafeAccess. I understand that this was added to ensure that<br>
accessing a memory mapped file which was been truncated doesn’t crash<br>
the JVM. What is the overhead of this check? Given that my module<br>
requires restricted access anyhow, it can already crash the JVM.<br>
Would it be possible to support unguarded memory access operations<br>
too?</p>
</blockquote>
<p style="margin: 0px 0px 1.2em !important;">This seems another case (like memcpy vs memmove) where it is assumed there’s some overhead associated to operation XYZ (which the JVM does to ensure some kind of safety in the common case), hence the request for a backdoor. I’d like to turn these kind of arguments around and instead ask for some benchmark showing that the costs associated with these operations are too high (and, if so, we might be able to find ways to improve the status quo w/o necessarily changing the API).</p>
<p style="margin: 0px 0px 1.2em !important;">Cheers<br>
Maurizio</p>
<p style="margin: 0px 0px 1.2em !important;">[1] - <a href="https://cr.openjdk.org/~mcimadamore/panama/why_lifetimes.html" class="moz-txt-link-freetext">https://cr.openjdk.org/~mcimadamore/panama/why_lifetimes.html</a></p>
<div title="MDH:PGJyPk9uIDEwLzA3LzIwMjMgMTY6MDIsIEJyaWFuIFMgTydOZWlsbCB3cm90ZTo8YnI+PHNwYW4gc3R5bGU9IndoaXRlLXNwYWNlOiBwcmUtd3JhcDsgZGlzcGxheTogYmxvY2s7IHdpZHRoOiA5OHZ3 OyI+Jmd0OyBJIHdhbnQgdG8gYXZvaWQgcG9sbHV0aW5nIHRoZSBvdGhlciB0aHJlYWQsIHdoaWNo IG5vIGxvbmdlciBoYXMgYTxicj4mZ3Q7IHZhbGlkIHN1YmplY3QgbGluZSBhbnlob3cuLi48YnI+ Jmd0OyA8YnI+Jmd0OyAxKSBUaGUgc2l6ZSBsaW1pdCBvZiBhIG1lbW9yeSBzZWdtZW50IGlzIGRl ZmluZWQgYnkgYSBzaWduZWQgdmFsdWUsPGJyPiZndDsgdGh1cyBsaW1pdGluZyB0aGUgYWRkcmVz cyBzcGFjZSB0byBiZSA2My1iaXQuIElzIGl0IGxpa2VseSB0aGF0IGFsbDxicj4mZ3Q7IDY0LWJp dCBhcmNoaXRlY3R1cmVzIHRvZGF5IGFuZCBpbiB0aGUgZnV0dXJlIG9ubHkgdXNlIHRoZSBsb3dl ciBoYWxmPGJyPiZndDsgb2YgdGhlIGFkZHJlc3MgcmFuZ2U/PGJyPjwvc3Bhbj48YnI+V2hpbGUg aXQncyB0cnVlIHRoYXQgYSBtZW1vcnkgc2VnbWVudCBjYW4gbmV2ZXIgYmUgYmlnIGVub3VnaCBh cyB0aGUgZnVsbCBhZGRyZXNzIHNwYWNlLCBpbiByZWFsaXR5IHRoYXQgbGltaXQgaXMgYmlnIGVu b3VnaCAtIGUuZy4gaXQgaXMgaGFyZCB0byBpbWFnaW5lIGEgZGV2ZWxvcGVyIHdhbnRpbmcgdG8g ZXhwcmVzcyBhIHJlZ2lvbiBvZiBtZW1vcnkgd2hvc2Ugc2l6ZSBpcyBiaWdnZXIgdGhhbiAyXjYz LiBJIGJlbGlldmUgdGhlIGFuZ2xlIHlvdSBhcmUgY29taW5nIGZyb20gaGVyZSBpcyB0aGF0IHlv dSB3YW50IHRvIHVzZSBhIG1lbW9yeSBzZWdtZW50IHRvIG1vZGVsIHRoZSBlbnRpcmUgaGVhcCAo YXQgbGVhc3QganVkZ2luZyBmcm9tIHRoZSBvdGhlciB0aHJlYWQgeW91IHN0YXJ0ZWQpLCBhbmQg dGhlbiB3YW50aW5nIHRvIGp1c3QgdXNlIGEgcmF3IGxvbmcgcG9pbnRlciBhcyBhbiAib2Zmc2V0 IiBpbnRvIHRoZSBzZWdtZW50LiBHaXZlbiB0aGUgdW5zaWduZWQgbGltaXRhdGlvbnMsIHRoaXMg d2lsbCBub3Qgd29yayAxMDAlOiB0aGUgRkZNIEFQSSBzcGVjaWZpZXMgdGhhdCB0aGUgImxvbmci IGFyZ3VtZW50IHlvdSBwYXNzIHRvIE1lbW9yeVNlZ21lbnQ6OmdldCAoc2FtZSBmb3IgdmFyIGhh bmRsZSkgaXMgYSBwb3NpdGl2ZSBvZmZzZXQsIHJlbGF0aXZlIHRvIHRoZSBzdGFydCBvZiB0aGUg c2VnbWVudC4gVGhhdCBpcywgYSBuZWdhdGl2ZSBvZmZzZXQgaXMsIGJ5IGRlZmluaXRpb24swqAg X291dHNpZGVfIHRoZSBtZW1vcnkgc2VnbWVudC4gU28sIEkgZG8gbm90IHNlZSBhIHdheSB0byBh ZGQgc3VwcG9ydCBmb3IgZnVsbCB1bnNpZ25lZCBsb25nIGFzIG1lbW9yeSBzZWdtZW50IHNpemVz L29mZnNldHMgLSBpbiBmYWN0LCBJIGJlbGlldmUgdGhhdCBhbGxvd2luZyBuZWdhdGl2ZSB2YWx1 ZXMgaW4gcGxhY2VzIHdoZXJlIHdlIGV4cGVjdCBhbiBvZmZzZXQgaXMgYWxtb3N0IGFsd2F5cyBh IHNtZWxsLCBhbmQgYSBwbGFjZSBmb3IgYnVncyB0byBoaWRlLjxicj48YnI+SW4gdGVybXMgb2Yg aW50ZXJmYWNpbmcgd2l0aCBuYXRpdmUgY29kZSwgbm90ZSB0aGF0IHRoZSBGRk0gQVBJIGNhbiBz dGlsbCB3cmFwIHdoYXRldmVyIGFkZHJlc3MgYSBuYXRpdmUgbGlicmFyeSB0aHJvd3MgYXQgaXQs IGJ5IHdyYXBwaW5nIHRoZSBiYXNlIGFkZHJlc3MgKGEgbG9uZykgaW50byBhIHNlZ21lbnQgdXNp bmcgTWVtb3J5U2VnbWVudDo6b2ZBZGRyZXNzLiBUaGlzIG1ldGhvZCB0YWtlcyBhIHJhdyBhZGRy ZXNzIHZhbHVlLCB3aGljaCBjYW4gYmUgbmVnYXRpdmUuIE9mIGNvdXJzZSB0aGUgc2l6ZSBvZiB0 aGUgc2VnbWVudCB3aWxsIGJlIGxpbWl0ZWQgdG8gIm9ubHkiIExvbmcuTUFYX1ZBTFVFLCB3aGlj aCwgc2VlbXMgcmVhc29uYWJsZSBlbm91Z2guPGJyPjxicj5XaGlsZSBpdCdzIHRoZW9yZXRpY2Fs bHkgcG9zc2libGUgdG8gYWRkIGEgZGlmZmVyZW50IGtpbmQgb2Ygc2VnbWVudCB0aGF0IGJlaGF2 ZXMgaW4gYSBkaWZmZXJlbnQgd2F5IChlLmcuIGFsbG93aW5nIG5lZ2F0aXZlIG9mZnNldHMpLCBJ IGJlbGlldmUgdGhlIGNvc3QgdnMuIGJlbmVmaXQgcmF0aW8gZm9yIGRvaW5nIHNvIHdvdWxkIGJl IHZlcnkgdW5mYXZvdXJhYmxlLjxicj48YnI+PGJyPjxzcGFuIHN0eWxlPSJ3aGl0ZS1zcGFjZTog cHJlLXdyYXA7IGRpc3BsYXk6IGJsb2NrOyB3aWR0aDogOTh2dzsiPiZndDsgPGJyPiZndDsgMikg VGhlIE1lbW9yeVNlZ21lbnQgY29weSBvcGVyYXRpb24gaXMgc2FmZSBmb3IgdXNlIGFnYWluc3Q8 YnI+Jmd0OyBvdmVybGFwcGluZyByYW5nZXMsIGFuZCBpcyB0aHVzIGEgIm1lbW1vdmUiIG9wZXJh dGlvbi4gTXkgYXBwbGljYXRpb248YnI+Jmd0OyB3b3VsZCBiZW5lZml0IGZyb20gYWxzbyBoYXZp bmcgYSAibWVtY3B5IiBvcGVyYXRpb24sIGZvciB0aG9zZSBjYXNlczxicj4mZ3Q7IHdoZXJlIEkg a25vdyB0aGF0IHRoZSByYW5nZXMgZG9uJ3Qgb3ZlcmxhcC4gQ2FuIHN1Y2ggYW4gb3BlcmF0aW9u IGJlPGJyPiZndDsgYWRkZWQ/PGJyPjwvc3Bhbj5UaGlzIGlzIG5vdCBhbiBpc3N1ZSwgb3IgYXQg bGVhc3Qgbm90IG9uZSB0aGF0IHNob3VsZCBiZSBzb2x2ZWQgdmlhIG5ldyBBUEkgc3VyZmFjZTog TWVtb3J5U2VnbWVudDo6Y29weSByZWxpZXMgb24gVW5zYWZlOjpjb3B5TWVtb3J5IHdoaWNoIGRv ZXMgbWVtbW92ZSB2cy4gbWVtY3B5IGRlcGVuZGluZyBvbiB3ZXRoZXIgaXQgY2FuIHByb3ZlIHRo YXQgcmFuZ2VzIG92ZXJsYXAuIFJhdGhlciB0aGFuIGR1cGxpY2F0aW5nIHRoZSBBUEksIGlmIHdl IGZpbmQgY2FzZXMgd2hlcmUgdGhlIGV4aXN0aW5nIGxvZ2ljIGRvZXNuJ3Qgd29yayB3ZWxsLCB3 ZSBzaG91bGQgcHJvYmFibHkgaW52ZXN0IGluIHJlY3RpZnlpbmcgYW5kL29yIGltcHJvdmluZyB0 aGF0IGxvZ2ljLjxicj48c3BhbiBzdHlsZT0id2hpdGUtc3BhY2U6IHByZS13cmFwOyBkaXNwbGF5 OiBibG9jazsgd2lkdGg6IDk4dnc7Ij4mZ3Q7IDxicj4mZ3Q7IDMpIER5bmFtaWMgbWVtb3J5IGFs bG9jYXRpb24gaXMgcGVyZm9ybWVkIGFnYWluc3QgQXJlbmFzLCBidXQgZnJlZWluZzxicj4mZ3Q7 IHRoZSBtZW1vcnkgaXMgb25seSBhbGxvd2VkIHdoZW4gdGhlIEFyZW5hIGlzIGNsb3NlZC4gSSBm aW5kIHRoaXMgdG88YnI+Jmd0OyBiZSBjdW1iZXJzb21lIGF0IHRpbWVzLCBhbmQgaW4gb25lIGNh c2UgSSBlbmRlZCB1cCBjcmVhdGluZyBhIHNpbmdsZTxicj4mZ3Q7IEFyZW5hIGluc3RhbmNlIHBh aXJlZCB3aXRoIGVhY2ggYWxsb2NhdGlvbi4gVGhlIG90aGVyIHNvbHV0aW9uIGlzIHRvPGJyPiZn dDsgZGlyZWN0bHkgY2FsbCBtYWxsb2MvZnJlZSwgd2hpY2ggaXMgd2hhdCBJJ20gdXNpbmcgaW4g c29tZSBwbGFjZXMuPGJyPiZndDsgQm90aCBzb2x1dGlvbnMgYXJlIG1lc3N5LCBzbyBpcyBpdCBw b3NzaWJsZSB0byBhZGQgYSBzaW1wbGU8YnI+Jmd0OyBtYWxsb2MvZnJlZSBBUEk/PGJyPjwvc3Bh bj48YnI+VGhlIEFQSSBlbWJyYWNlcyB0aGUgaWRlYSB0aGF0IG9uZSBsaWZldGltZSBtaWdodCBi ZSBhc3NvY2lhdGVkIHdpdGggbXVsdGlwbGUgYWxsb2NhdGlvbiBxdWl0ZSBkZWVwbHkgKHRoaXMg aXMgZXhwbGFpbmVkIGluIG1vcmUgZGV0YWlscyBoZXJlIFsxXSkuIFdoaWxlIHRoaXMgaXMgYW4g YXJiaXRyYXJ5IGNob2ljZSAoYWZ0ZXIgYWxsLCB0aGVyZSBpcyBubyBwZXJmZWN0IHdheSB0byBk ZWFsIHdpdGggZGVhbGxvY2F0aW9uIG9mIG5hdGl2ZSByZXNvdXJjZXMgaW4gYSB3YXkgdGhhdCBp cyBib3RoIHNhZmUgYW5kIGVmZmljaWVudCksIG1hbGxvYy9mcmVlIGlzIG5vdCB0aGUgcmlnaHQg cHJpbWl0aXZlIHRvIGRlc2lnbiBhIHNhZmUgQVBJIHRvIG1hbmFnZSBvZmYtaGVhcCByZXNvdXJj ZXMgZm9yIGF0IGxlYXN0IHR3byByZWFzb25zOjxicj48YnI+KiBpdCBpcyB0b28gZmluZS1ncmFp bmVkLCB0aGVyZSdzIG5vIHdheSB0byBncm91cCB0b2dldGhlciBsb2dpY2FsbHktcmVsYXRlZCBy ZXNvdXJjZXMgKHRoaW5rIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkbG9wZW4sIGRsc3lt IGFuZCBkbGNsb3NlKTxicj4qIGl0IGlzIG5vdCBzYWZlLWJ5LWRlZmF1bHQ6IGFueW9uZSBjYW4g ZnJlZSBhIHBvaW50ZXIsIGV2ZW4gb25lIHRoZXkgZGlkIG5vdCBjcmVhdGU8YnI+PGJyPkluIGNv bnRyYXN0LCB0aGUgbGlmZXRpbWUtY2VudHJpYyBhcHByb2FjaCBhZG9wdGVkIGJ5IHRoZSBGRk0g QVBJIGFsbG93cyBkZXZlbG9wZXJzIHRvIGdyb3VwIGxvZ2ljYWxseSByZWxhdGVkIHNlZ21lbnRz IGluIHRoZSBzYW1lIGxpZmV0aW1lLCB3aGljaCBpcywgb2Z0ZW4sIGEgdmVyeSB1c2VmdWwgbW92 ZSwgYW5kIGFsbG93cyB0aGUgQVBJIHRvIHNjYWxlIGZvciBtYW5hZ2luZyBsaWZldGltZSBvZiB0 aGluZ3MgdGhhdCBhcmUgbm90IGp1c3QgbWFsbG9jJ2VkIHNlZ21lbnRzIChzdWNoIHNwZWNpZnlp bmcgbGlmZXRpbWUgb2YgYW4gdXBjYWxsIHN0dWIsIG9yIHRoYXQgb2YgYSBuYXRpdmUgbGlicmFy eSBsb2FkZWQgd2l0aCBkbG9wZW4pLiBUaGlzIGFsbG93cyB0aGUgQVBJIHRvIGRldGVjdCBwZXNr eSBjb25kaXRpb25zIHN1Y2ggYXMgbWVtb3J5IGxlYWtzIGFuZC9vciB1c2UtYWZ0ZXIgZnJlZS4g VGhlcmUgYXJlIG9mIGNvdXJzZSBjYXNlcyB3aGVyZSB0aGlzIHdheSBvZiBkb2luZyB0aGluZ3Mg aXMgbm90IHBlcmZlY3QsIGFuZCBhIGxvd2VyLWxldmVsIGFjY2VzcyB0byBtYWxsb2MvZnJlZSBp cyBwcmVmZXJyYWJsZS4gSW4gdGhlc2UgY2FzZXMsIGRldmVsb3BlcnMgY2FuLCBhcyB5b3Ugb2Jz ZXJ2ZWQsIGp1c3QgY2FsbCBtYWxsb2MvZnJlZSBkb3duY2FsbCBtZXRob2QgaGFuZGxlcywgYW5k IGRlYWwgd2l0aCBtZW1vcnkgYWxsb2NhdGlvbi9kZWFsbG9jYXRpb24gY29tcGxldGVseSBtYW51 YWxseS4gT3IgdGhleSBjYW4gY3JlYXRlIGEgb25lLW9mZiBhcmVuYSB0byBkZWFsIHdpdGggdGhh dCBhbGxvY2F0aW9uLjxicj48YnI+VG8gZG8gdGhlIGZvcm1lciwgeW91IHByb2JhYmx5IHdvdWxk IG5lZWQgc29tZSBraW5kIG9mIGFic3RyYWN0aW9uIG9uIHRvcDo8YnI+PGJyPmBgYDxicj5yZWNv cmQgSGVhcEFsbG9jYXRpb24oTWVtb3J5U2VnbWVudCBzZWdtZW50LCBBcmVuYSBhcmVuYSkgaW1w bGVtZW50cyBBdXRvQ2xvc2VhYmxlIHvCoCDCoDxicj7CoMKgwqAgcHVibGljIGNsb3NlKCkgeyBh cmVuYSgpLmNsb3NlKCk7IH08YnI+PGJyPsKgwqDCoCBzdGF0aWMgSGVhcEFsbG9jYXRpb24gbWFs bG9jQ29uZmluZWQobG9uZyBieXRlcykgezxicj7CoMKgwqDCoMKgwqDCoMKgwqAgQXJlbmEgYXJl bmEgPSBBcmVuYS5vZkNvbmZpbmVkKCk7PGJyPsKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gbmV3 IEhlYXBBbGxvY2F0aW9uKGFyZW5hLmFsbG9jYXRlKGJ5dGVzKSwgYXJlbmEpOzxicj7CoMKgwqAg fTxicj59PGJyPmBgYDxicj48YnI+VG8gZG8gdGhlIGxhdHRlciB5b3UgbmVlZCB0byB3cml0ZSBz b21lIHdyYXBwZXJzIGZvciBtYWxsb2MvZnJlZTo8YnI+PGJyPmBgYDxicj5zdGF0aWMgZmluYWwg TWV0aG9kSGFuZGxlIE1BTExPQyA9IExpbmtlci5uYXRpdmVMaW5rZXIoKS4uLi48YnI+c3RhdGlj IGZpbmFsIE1ldGhvZEhhbmRsZSBGUkVFID0gTGlua2VyLm5hdGl2ZUxpbmtlcigpLi4uLjxicj48 YnI+c3RhdGljIE1lbW9yeVNlZ21lbnQgbWFsbG9jKGxvbmcgYnl0ZXMpIHs8YnI+wqDCoMKgIHJl dHVybiBNQUxMT0MuaW52b2tlRXhhY3QoYnl0ZXMpOzxicj59PGJyPjxicj5zdGF0aWMgTWVtb3J5 U2VnbWVudCBmcmVlKE1lbW9yeVNlZ21lbnQgc2VnbWVudCkgezxicj7CoMKgwqAgcmV0dXJuIEZS RUUuaW52b2tlRXhhY3Qoc2VnbWVudCk7PGJyPn08YnI+YGBgPGJyPjxicj5PZiBjb3Vyc2UgdGhl IGZvcm1lciBhcHByb2FjaCBwcm92aWRlcyBtb3JlIHRlbXBvcmFsIHNhZmV0eSAoYnV0IG1pZ2h0 IGFsc28gcmVzdWx0IGluIG1vcmUgb3ZlcmhlYWQgZm9yIGVuZm9yY2luZyBzYWlkIHNhZmV0eSwg d2hpY2gsIEknbSBub3Qgc3VyZSB5b3UnZCBiZSB0b28gaGFwcHkgd2l0aCkuIEluIHRoZSBsYXR0 ZXIsIHdoYXQgeW91IHNlZSBpcyB3aGF0IHlvdSBnZXQsIHlvdSBhcmUgcGxheWluZyB0aGUgInBv d2VyIHVzZXIiIGNhcmQsIGVuc3VyaW5nIGNvcnJlY3RuZXNzIChyZWFkOiBhdm9pZCB1c2UtYWZ0 ZXItZnJlZSkgaXMgbm93IHVwIHRvIHlvdS48YnI+PGJyPkkgZG9uJ3QgdGhpbmsgZWl0aGVyIGFw cHJvYWNoIGxvb2tzIHRvbyAibWVzc3kiLsKgIE9mIGNvdXJzZSBvbmUtbGlmZXRpbWUtcGVyLXNl Z21lbnQgaXMgbm90IHRoZSBpZGVhbCBzd2VldCBzcG90IHRoZSBGRk0gQVBJIGlzIGRlc2lnbmVk IGZvciwgYW5kIG9uZSBoYXMgdG8gd3JpdGUgc29tZSBleHRyYSBjb2RlLCBidXQgdGhlIEZGTSBB UEkgc3RpbGwgYWxsb3dzIHlvdSB0byBkbyB3aGF0IHlvdSBuZWVkIHRvIGRvIChvciB0byBjb21w bGV0ZWx5IGJ5cGFzcyB0ZW1wb3JhbCBzYWZldHkgYWxsdG9nZXRoZXIsIGlmIHlvdSBkZWNpZGUg dG8gZG8gc28pLiBBZnRlciBoYXZpbmcgc3BlbnQgY29uc2lkZXJhYmxlIHRpbWUgbG9va2luZyBh dCBwb3NzaWJsZSBhcHByb2FjaGVzIHRvIGRlYWwgd2l0aCBtZW1vcnkgc2FmZXR5LCB3ZSBkaWQg bm90IGZpbmQgYSAic2ltcGxlciBtYWxsb2MvZnJlZSBBUEkiIHRoYXQgd2FzIGdvb2QgZW5vdWdo IGFzIGEgYnVpbGRpbmcgYmxvY2sgZm9yIG1hbmFnaW5nIHRlbXBvcmFsIHJlc291cmNlcyBpbiB0 aGUgRkZNIEFQSS48YnI+PGJyPjxicj48c3BhbiBzdHlsZT0id2hpdGUtc3BhY2U6IHByZS13cmFw OyBkaXNwbGF5OiBibG9jazsgd2lkdGg6IDk4dnc7Ij4mZ3Q7IDxicj4mZ3Q7IDQpIEd1YXJkVW5z YWZlQWNjZXNzLiBJIHVuZGVyc3RhbmQgdGhhdCB0aGlzIHdhcyBhZGRlZCB0byBlbnN1cmUgdGhh dDxicj4mZ3Q7IGFjY2Vzc2luZyBhIG1lbW9yeSBtYXBwZWQgZmlsZSB3aGljaCB3YXMgYmVlbiB0 cnVuY2F0ZWQgZG9lc24ndCBjcmFzaDxicj4mZ3Q7IHRoZSBKVk0uIFdoYXQgaXMgdGhlIG92ZXJo ZWFkIG9mIHRoaXMgY2hlY2s/IEdpdmVuIHRoYXQgbXkgbW9kdWxlPGJyPiZndDsgcmVxdWlyZXMg cmVzdHJpY3RlZCBhY2Nlc3MgYW55aG93LCBpdCBjYW4gYWxyZWFkeSBjcmFzaCB0aGUgSlZNLjxi cj4mZ3Q7IFdvdWxkIGl0IGJlIHBvc3NpYmxlIHRvIHN1cHBvcnQgdW5ndWFyZGVkIG1lbW9yeSBh Y2Nlc3Mgb3BlcmF0aW9uczxicj4mZ3Q7IHRvbz88YnI+PC9zcGFuPjxicj5UaGlzIHNlZW1zIGFu b3RoZXIgY2FzZSAobGlrZSBtZW1jcHkgdnMgbWVtbW92ZSkgd2hlcmUgaXQgaXMgYXNzdW1lZCB0 aGVyZSdzIHNvbWUgb3ZlcmhlYWQgYXNzb2NpYXRlZCB0byBvcGVyYXRpb24gWFlaICh3aGljaCB0 aGUgSlZNIGRvZXMgdG8gZW5zdXJlIHNvbWUga2luZCBvZiBzYWZldHkgaW4gdGhlIGNvbW1vbiBj YXNlKSwgaGVuY2UgdGhlIHJlcXVlc3QgZm9yIGEgYmFja2Rvb3IuIEknZCBsaWtlIHRvIHR1cm4g dGhlc2Uga2luZCBvZiBhcmd1bWVudHMgYXJvdW5kIGFuZCBpbnN0ZWFkIGFzayBmb3Igc29tZSBi ZW5jaG1hcmsgdGhhdCBzaG93IHRoYXQgdGhlIGNvc3QgYXNzb2NpYXRlZCB3aXRoIHRoZXNlIGV4 dHJhIG9wZXJhdGlvbiBpcyB0b28gaGlnaCAoYW5kLCBpZiBzbywgd2UgbWlnaHQgYmUgYWJsZSB0 byBmaW5kIHdheXMgdG8gaW1wcm92ZSB0aGUgc3RhdHVzIHF1byB3L28gbmVjZXNzYXJpbHkgY2hh bmdpbmcgdGhlIEFQSSkuPGJyPjxicj5DaGVlcnM8YnI+TWF1cml6aW88YnI+PGJyPlsxXSAtIGh0 dHBzOi8vY3Iub3Blbmpkay5vcmcvfm1jaW1hZGFtb3JlL3BhbmFtYS93aHlfbGlmZXRpbWVzLmh0 bWw8YnI+PGJyPjxicj4=" style="height:0;width:0;max-height:0;max-width:0;overflow:hidden;font-size:0em;padding:0;margin:0;">​</div>
</div></div></blockquote>
<div class="plaintext" style="white-space: normal;">
</div>
</div></body>

</html>