From a50fdd5484ee2ac78cc54dd68ea824ab5659d08e Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 5 Nov 2020 16:27:49 +0000 Subject: [PATCH 001/124] 8219014: (bf) Add absolute bulk put methods which accept a source Buffer Reviewed-by: psandoz, alanb --- .../java/nio/Direct-X-Buffer.java.template | 10 ++ .../java/nio/Heap-X-Buffer.java.template | 10 ++ .../classes/java/nio/X-Buffer.java.template | 80 +++++++++++-- test/jdk/java/nio/Buffer/BulkPutBuffer.java | 108 ++++++++++++++++-- 4 files changed, 189 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index 09c48e86bfc9f..07c7d3c7d26f2 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -416,6 +416,16 @@ class Direct$Type$Buffer$RW$$BO$ #end[rw] } + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { +#if[rw] + checkSegment(); + super.put(index, src, offset, length); + return this; +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + public $Type$Buffer put($type$[] src, int offset, int length) { #if[rw] checkSegment(); diff --git a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index 263e699ac1f23..e4a89efc6d8f2 100644 --- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -251,6 +251,16 @@ class Heap$Type$Buffer$RW$ #end[rw] } + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { +#if[rw] + checkSegment(); + super.put(index, src, offset, length); + return this; +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + public $Type$Buffer put(int index, $type$[] src, int offset, int length) { #if[rw] checkSegment(); diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index 4804f4b43190b..8a5be7ceaf6ac 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -956,6 +956,76 @@ public abstract class $Type$Buffer if (n > limit() - pos) throw new BufferOverflowException(); + putBuffer(pos, src, srcPos, n); + + position(pos + n); + src.position(srcPos + n); + + return this; + } + + /** + * Absolute bulk put method  (optional operation). + * + *

This method transfers {@code length} $type$s into this buffer from + * the given source buffer, starting at the given {@code offset} in the + * source buffer and the given {@code index} in this buffer. The positions + * of both buffers are unchanged. + * + *

In other words, an invocation of this method of the form + * dst.put(index, src, offset, length) + * has exactly the same effect as the loop + * + *

{@code
+     * for (int i = offset, j = index; i < offset + length; i++, j++)
+     *     dst.put(j, src.get(i));
+     * }
+ * + * except that it first checks the consistency of the supplied parameters + * and it is potentially much more efficient. If this buffer and + * the source buffer share the same backing array or memory, then the + * result will be as if the source elements were first copied to an + * intermediate location before being written into this buffer. + * + * @param index + * The index in this buffer at which the first $type$ will be + * written; must be non-negative and less than {@code limit()} + * + * @param src + * The buffer from which $type$s are to be read + * + * @param offset + * The index within the source buffer of the first $type$ to be + * read; must be non-negative and less than {@code src.limit()} + * + * @param length + * The number of $type$s to be read from the given buffer; + * must be non-negative and no larger than the smaller of + * {@code limit() - index} and {@code src.limit() - offset} + * + * @return This buffer + * + * @throws IndexOutOfBoundsException + * If the preconditions on the {@code index}, {@code offset}, and + * {@code length} parameters do not hold + * + * @throws ReadOnlyBufferException + * If this buffer is read-only + * + * @since 16 + */ + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { + Objects.checkFromIndexSize(index, length, limit()); + Objects.checkFromIndexSize(offset, length, src.limit()); + if (isReadOnly()) + throw new ReadOnlyBufferException(); + + putBuffer(index, src, offset, length); + + return this; + } + + void putBuffer(int pos, $Type$Buffer src, int srcPos, int n) { Object srcBase = src.base(); #if[char] @@ -999,18 +1069,14 @@ public abstract class $Type$Buffer } } #end[!byte] - - position(pos + n); - src.position(srcPos + n); #if[char] } else { // src.isAddressable() == false assert StringCharBuffer.class.isInstance(src); - for (int i = 0; i < n; i++) - put(src.get()); + int posMax = pos + n; + for (int i = pos, j = srcPos; i < posMax; i++, j++) + put(i, src.get(j)); } #end[char] - - return this; } /** diff --git a/test/jdk/java/nio/Buffer/BulkPutBuffer.java b/test/jdk/java/nio/Buffer/BulkPutBuffer.java index fc3c55b9187cd..c5c4d0bcd6de6 100644 --- a/test/jdk/java/nio/Buffer/BulkPutBuffer.java +++ b/test/jdk/java/nio/Buffer/BulkPutBuffer.java @@ -49,7 +49,7 @@ /* * @test - * @bug 8245121 + * @bug 8219014 8245121 * @summary Ensure that a bulk put of a buffer into another is correct. * @compile --enable-preview -source ${jdk.version} BulkPutBuffer.java * @run testng/othervm --enable-preview BulkPutBuffer @@ -142,9 +142,11 @@ public static class BufferProxy { MethodHandle alloc; MethodHandle allocBB; MethodHandle allocDirect; + MethodHandle asReadOnlyBuffer; MethodHandle asTypeBuffer; MethodHandle putAbs; MethodHandle getAbs; + MethodHandle putBufAbs; MethodHandle putBufRel; MethodHandle equals; @@ -168,6 +170,9 @@ public static class BufferProxy { MethodType.methodType(ByteBuffer.class, int.class)); allocDirect = lookup.findStatic(ByteBuffer.class, "allocateDirect", MethodType.methodType(ByteBuffer.class, int.class)); + + asReadOnlyBuffer = lookup.findVirtual(bufferType, + "asReadOnlyBuffer", MethodType.methodType(bufferType)); if (elementType != byte.class) { asTypeBuffer = lookup.findVirtual(ByteBuffer.class, "as" + name + "Buffer", MethodType.methodType(bufferType)); @@ -177,8 +182,13 @@ public static class BufferProxy { MethodType.methodType(bufferType, int.class, elementType)); getAbs = lookup.findVirtual(bufferType, "get", MethodType.methodType(elementType, int.class)); + + putBufAbs = lookup.findVirtual(bufferType, "put", + MethodType.methodType(bufferType, int.class, bufferType, + int.class, int.class)); putBufRel = lookup.findVirtual(bufferType, "put", MethodType.methodType(bufferType, bufferType)); + equals = lookup.findVirtual(bufferType, "equals", MethodType.methodType(boolean.class, Object.class)); @@ -239,6 +249,25 @@ void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length) } } + Buffer asReadOnlyBuffer(Buffer buf) throws Throwable { + try { + return (Buffer)asReadOnlyBuffer.invoke(buf); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + void put(Buffer src, int srcOff, Buffer dst, int dstOff, int length) + throws Throwable { + try { + putBufAbs.invoke(dst, dstOff, src, srcOff, length); + } catch (ReadOnlyBufferException ro) { + throw ro; + } catch (Exception e) { + throw new AssertionError(e); + } + } + void put(Buffer src, Buffer dst) throws Throwable { try { putBufRel.invoke(dst, src); @@ -294,6 +323,40 @@ static Object[][] proxyPairs() { return args.toArray(Object[][]::new); } + private static void expectThrows(Class exClass, Assert.ThrowingRunnable r) { + try { + r.run(); + } catch(Throwable e) { + if (e.getClass() != exClass && e.getCause().getClass() != exClass) { + throw new RuntimeException("Expected " + exClass + + "; got " + e.getCause().getClass(), e); + } + } + } + + @Test(dataProvider = "proxies") + public static void testExceptions(BufferProxy bp) throws Throwable { + int cap = 27; + Buffer buf = bp.create(cap); + + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, -1, buf, 0, 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, -1, 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 1, buf, 0, cap)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 1, cap)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 0, cap + 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 0, Integer.MAX_VALUE)); + + Buffer rob = buf.isReadOnly() ? buf : bp.asReadOnlyBuffer(buf); + expectThrows(ReadOnlyBufferException.class, + () -> bp.put(buf, 0, rob, 0, cap)); + } + @Test(dataProvider = "proxies") public static void testSelf(BufferProxy bp) throws Throwable { for (int i = 0; i < ITERATIONS; i++) { @@ -311,22 +374,27 @@ public static void testSelf(BufferProxy bp) throws Throwable { Assert.expectThrows(ReadOnlyBufferException.class, () -> bp.copy(lower, 0, lowerCopy, 0, lowerLength)); break; - } else { - bp.copy(lower, 0, lowerCopy, 0, lowerLength); } + bp.copy(lower, 0, lowerCopy, 0, lowerLength); int middleOffset = RND.nextInt(1 + cap/2); Buffer middle = buf.slice(middleOffset, lowerLength); + Buffer middleCopy = bp.create(lowerLength); + bp.copy(middle, 0, middleCopy, 0, lowerLength); - if (middle.isReadOnly()) { - Assert.expectThrows(ReadOnlyBufferException.class, - () -> bp.put(lower, middle)); - break; - } else { - bp.put(lower, middle); - } + bp.put(lower, middle); middle.flip(); + Assert.assertTrue(bp.equals(lowerCopy, middle), + String.format("%d %s %d %d %d %d%n", SEED, + buf.getClass().getName(), cap, + lowerOffset, lowerLength, middleOffset)); + + bp.copy(lowerCopy, 0, buf, lowerOffset, lowerLength); + bp.copy(middleCopy, 0, buf, middleOffset, lowerLength); + + bp.put(buf, lowerOffset, buf, middleOffset, lowerLength); + Assert.assertTrue(bp.equals(lowerCopy, middle), String.format("%d %s %d %d %d %d%n", SEED, buf.getClass().getName(), cap, @@ -361,13 +429,29 @@ public static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable { Assert.expectThrows(ReadOnlyBufferException.class, () -> bp.put(src, buf)); break; - } else { - bp.put(src, buf); } + Buffer backup = bp.create(slim - spos); + bp.copy(buf, pos, backup, 0, backup.capacity()); + bp.put(src, buf); + buf.reset(); src.reset(); + Assert.assertTrue(bp.equals(src, buf), + String.format("%d %s %d %d %d %s %d %d %d%n", SEED, + buf.getClass().getName(), cap, pos, lim, + src.getClass().getName(), scap, spos, slim)); + + src.clear(); + buf.clear(); + bp.copy(backup, 0, buf, pos, backup.capacity()); + bp.put(src, spos, buf, pos, backup.capacity()); + src.position(spos); + src.limit(slim); + buf.position(pos); + buf.limit(lim); + Assert.assertTrue(bp.equals(src, buf), String.format("%d %s %d %d %d %s %d %d %d%n", SEED, buf.getClass().getName(), cap, pos, lim, From 1b59595e1ef1cdc9d5e672a4b563ad08a7b06534 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Thu, 5 Nov 2020 17:18:19 +0000 Subject: [PATCH 002/124] 8255914: [AOT] Using AOT flag should give warning when AOT is not included in build Reviewed-by: dholmes, iveresov --- src/hotspot/share/runtime/arguments.cpp | 19 +++++++++++++++++++ src/hotspot/share/runtime/arguments.hpp | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 38f0c6c9dc807..c812d5a3ea14f 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -3284,6 +3284,25 @@ jint Arguments::finalize_vm_init_args(bool patch_mod_javabase) { } #endif +#if !INCLUDE_AOT + UNSUPPORTED_OPTION(UseAOT); + UNSUPPORTED_OPTION(PrintAOT); + UNSUPPORTED_OPTION(UseAOTStrictLoading); + UNSUPPORTED_OPTION_NULL(AOTLibrary); + + UNSUPPORTED_OPTION_INIT(Tier3AOTInvocationThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier3AOTMinInvocationThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier3AOTCompileThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier3AOTBackEdgeThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier0AOTInvocationThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier0AOTMinInvocationThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier0AOTCompileThreshold, 0); + UNSUPPORTED_OPTION_INIT(Tier0AOTBackEdgeThreshold, 0); +#ifndef PRODUCT + UNSUPPORTED_OPTION(PrintAOTStatistics); +#endif +#endif + #ifndef CAN_SHOW_REGISTERS_ON_ASSERT UNSUPPORTED_OPTION(ShowRegistersOnAssert); #endif // CAN_SHOW_REGISTERS_ON_ASSERT diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp index eb3312dc58859..7bb0c27453247 100644 --- a/src/hotspot/share/runtime/arguments.hpp +++ b/src/hotspot/share/runtime/arguments.hpp @@ -684,5 +684,14 @@ do { \ } \ } while(0) +// Initialize options not supported in this release, with a warning +// if they were explicitly requested on the command-line +#define UNSUPPORTED_OPTION_INIT(opt, value) \ +do { \ + if (FLAG_IS_CMDLINE(opt)) { \ + warning("-XX flag " #opt " not supported in this VM"); \ + } \ + FLAG_SET_DEFAULT(opt, value); \ +} while(0) #endif // SHARE_RUNTIME_ARGUMENTS_HPP From d6f094040085fb16306fd1e0eea7d8d7969cdb4d Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 5 Nov 2020 18:30:34 +0000 Subject: [PATCH 003/124] 8255913: Decrease number of iterations in TestMaxCachedBufferSize Reviewed-by: lancea, dfuchs --- test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java b/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java index 67520f2798981..c63ac004be365 100644 --- a/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java +++ b/test/jdk/sun/nio/ch/TestMaxCachedBufferSize.java @@ -54,7 +54,7 @@ * @key randomness */ public class TestMaxCachedBufferSize { - private static final int DEFAULT_ITERS = 10 * 1000; + private static final int DEFAULT_ITERS = 5 * 1000; private static final int DEFAULT_THREAD_NUM = 4; private static final int SMALL_BUFFER_MIN_SIZE = 4 * 1024; From fc894ab11b72e5df4e1d3564ff5b1efc877b51c0 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 5 Nov 2020 18:59:33 +0000 Subject: [PATCH 004/124] 8255955: Shenandoah: Only STW GC should process concurrent roots at pauses Reviewed-by: rkennke --- src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index c36ed9103150d..7c38828466528 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -2416,17 +2416,17 @@ void ShenandoahHeap::stw_process_weak_roots(bool full_gc) { ShenandoahForwardedIsAliveClosure is_alive; ShenandoahUpdateRefsClosure keep_alive; ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &keep_alive, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &keep_alive, num_workers, is_stw_gc_in_progress()); _workers->run_task(&cleaning_task); } else { ShenandoahIsAliveClosure is_alive; #ifdef ASSERT ShenandoahAssertNotForwardedClosure verify_cl; ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &verify_cl, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &verify_cl, num_workers, is_stw_gc_in_progress()); #else ShenandoahParallelWeakRootsCleaningTask - cleaning_task(timing_phase, &is_alive, &do_nothing_cl, num_workers, !ShenandoahConcurrentRoots::should_do_concurrent_class_unloading()); + cleaning_task(timing_phase, &is_alive, &do_nothing_cl, num_workers, is_stw_gc_in_progress()); #endif _workers->run_task(&cleaning_task); } From 140c162a0ddf29e897deb0fce9d81a4a841ca329 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 5 Nov 2020 21:18:59 +0000 Subject: [PATCH 005/124] 8255894: Remove unused StubRoutines::_zero_aligned_words Reviewed-by: shade --- src/hotspot/share/runtime/stubRoutines.cpp | 2 -- src/hotspot/share/runtime/stubRoutines.hpp | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index e0bc23f227db3..0656d0499c74f 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -111,8 +111,6 @@ address StubRoutines::_arrayof_jlong_disjoint_arraycopy = CAST_FROM_FN_PTR(addr address StubRoutines::_arrayof_oop_disjoint_arraycopy = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_oop_copy); address StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit = CAST_FROM_FN_PTR(address, StubRoutines::arrayof_oop_copy_uninit); -address StubRoutines::_zero_aligned_words = CAST_FROM_FN_PTR(address, Copy::zero_to_words); - address StubRoutines::_data_cache_writeback = NULL; address StubRoutines::_data_cache_writeback_sync = NULL; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 124b4765bc50a..287e7133dd983 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -207,9 +207,6 @@ class StubRoutines: AllStatic { static address _arrayof_jshort_fill; static address _arrayof_jint_fill; - // zero heap space aligned to jlong (8 bytes) - static address _zero_aligned_words; - static address _aescrypt_encryptBlock; static address _aescrypt_decryptBlock; static address _cipherBlockChaining_encryptAESCrypt; @@ -442,8 +439,6 @@ class StubRoutines: AllStatic { static address select_fill_function(BasicType t, bool aligned, const char* &name); - static address zero_aligned_words() { return _zero_aligned_words; } - // // Safefetch stub support // From e66fd6f0aa43356ab4b4361d6d332e5e3bcabeb6 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 5 Nov 2020 21:20:13 +0000 Subject: [PATCH 006/124] 8255756: Disabling logging does unnecessary work Reviewed-by: iklam --- .../share/logging/logConfiguration.cpp | 33 ++++++++++--------- .../share/logging/logConfiguration.hpp | 4 +-- src/hotspot/share/logging/logOutputList.cpp | 19 +++++++++++ src/hotspot/share/logging/logOutputList.hpp | 4 +++ src/hotspot/share/logging/logTagSet.hpp | 4 +++ 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index c6cd21c27f503..323537b012c76 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -110,9 +110,7 @@ void LogConfiguration::initialize(jlong vm_start_time) { } void LogConfiguration::finalize() { - for (size_t i = _n_outputs; i > 0; i--) { - disable_output(i - 1); - } + disable_outputs(); FREE_C_HEAP_ARRAY(LogOutput*, _outputs); } @@ -272,28 +270,31 @@ void LogConfiguration::configure_output(size_t idx, const LogSelectionList& sele assert(strlen(output->config_string()) > 0, "should always have a config description"); } -void LogConfiguration::disable_output(size_t idx) { - assert(idx < _n_outputs, "invalid index: " SIZE_FORMAT " (_n_outputs: " SIZE_FORMAT ")", idx, _n_outputs); - LogOutput* out = _outputs[idx]; +void LogConfiguration::disable_outputs() { + size_t idx = _n_outputs; - // Remove the output from all tagsets. + // Remove all outputs from all tagsets. for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { - ts->set_output_level(out, LogLevel::Off); - ts->update_decorators(); + ts->disable_outputs(); } - // Delete the output unless stdout or stderr (idx 0 or 1) - if (idx > 1) { - delete_output(idx); - } else { - out->set_config_string("all=off"); + while (idx > 0) { + LogOutput* out = _outputs[--idx]; + // Delete the output unless stdout or stderr (idx 0 or 1) + if (idx > 1) { + delete_output(idx); + } else { + out->set_config_string("all=off"); + } } } void LogConfiguration::disable_logging() { ConfigurationLock cl; - for (size_t i = _n_outputs; i > 0; i--) { - disable_output(i - 1); + disable_outputs(); + // Update the decorators on all tagsets to get rid of unused decorators + for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { + ts->update_decorators(); } notify_update_listeners(); } diff --git a/src/hotspot/share/logging/logConfiguration.hpp b/src/hotspot/share/logging/logConfiguration.hpp index 635a801312b0a..34c0c07b76a76 100644 --- a/src/hotspot/share/logging/logConfiguration.hpp +++ b/src/hotspot/share/logging/logConfiguration.hpp @@ -69,8 +69,8 @@ class LogConfiguration : public AllStatic { // Output should be completely disabled before it is deleted. static void delete_output(size_t idx); - // Disable all logging to the specified output and then delete it (unless it is stdout/stderr). - static void disable_output(size_t idx); + // Disable all logging to all outputs. All outputs except stdout/stderr will be deleted. + static void disable_outputs(); // Get output index by name. Returns SIZE_MAX if output not found. static size_t find_output(const char* name); diff --git a/src/hotspot/share/logging/logOutputList.cpp b/src/hotspot/share/logging/logOutputList.cpp index 56525505ef452..91139c900cfff 100644 --- a/src/hotspot/share/logging/logOutputList.cpp +++ b/src/hotspot/share/logging/logOutputList.cpp @@ -68,6 +68,25 @@ LogOutputList::LogOutputNode* LogOutputList::find(const LogOutput* output) const return NULL; } +void LogOutputList::clear() { + + // Grab the linked list + LogOutputNode* cur = _level_start[LogLevel::Last]; + + // Clear _level_start + for (uint level = LogLevel::First; level < LogLevel::Count; level++) { + _level_start[level] = NULL; + } + + // Delete all nodes from the linked list + wait_until_no_readers(); + while (cur != NULL) { + LogOutputNode* next = cur->_next; + delete cur; + cur = next; + } +} + void LogOutputList::remove_output(LogOutputList::LogOutputNode* node) { assert(node != NULL, "Node must be non-null"); diff --git a/src/hotspot/share/logging/logOutputList.hpp b/src/hotspot/share/logging/logOutputList.hpp index 47b37b6bbb045..d02f50cc8b2c2 100644 --- a/src/hotspot/share/logging/logOutputList.hpp +++ b/src/hotspot/share/logging/logOutputList.hpp @@ -88,6 +88,10 @@ class LogOutputList { // Set (add/update/remove) the output to the specified level. void set_output_level(LogOutput* output, LogLevelType level); + // Removes all outputs. Equivalent of set_output_level(out, Off) + // for all outputs. + void clear(); + class Iterator { friend class LogOutputList; private: diff --git a/src/hotspot/share/logging/logTagSet.hpp b/src/hotspot/share/logging/logTagSet.hpp index 45c33c96c582b..af092473eff98 100644 --- a/src/hotspot/share/logging/logTagSet.hpp +++ b/src/hotspot/share/logging/logTagSet.hpp @@ -98,6 +98,10 @@ class LogTagSet { return _output_list.level_for(output); } + void disable_outputs() { + _output_list.clear(); + } + void set_output_level(LogOutput* output, LogLevelType level) { _output_list.set_output_level(output, level); } From e42c13403878f5c2a2100cddd2a1df445939ee7a Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Thu, 5 Nov 2020 23:18:01 +0000 Subject: [PATCH 007/124] 8255706: The JDWP debug agent unecessarily checks for JVMTI_ERROR_INTERRUPT after calling RawMonitorEnter Reviewed-by: alanb, dholmes, sspitsyn --- src/jdk.jdwp.agent/share/native/libjdwp/util.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c index d40bb32b10192..15ca68b41b06e 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c @@ -1008,16 +1008,9 @@ void debugMonitorEnter(jrawMonitorID monitor) { jvmtiError error; - while (JNI_TRUE) { - error = JVMTI_FUNC_PTR(gdata->jvmti,RawMonitorEnter) - (gdata->jvmti, monitor); - error = ignore_vm_death(error); - if (error == JVMTI_ERROR_INTERRUPT) { - handleInterrupt(); - } else { - break; - } - } + error = JVMTI_FUNC_PTR(gdata->jvmti,RawMonitorEnter) + (gdata->jvmti, monitor); + error = ignore_vm_death(error); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "on raw monitor enter"); } From 57b98fa55a21ebde1bbfcbf807ab793c2078de70 Mon Sep 17 00:00:00 2001 From: Eric Caspole Date: Thu, 5 Nov 2020 23:51:27 +0000 Subject: [PATCH 008/124] 8255965: LogCompilation: add sort by nmethod code size Reviewed-by: kvn, redestad --- .gitignore | 1 + .../hotspot/tools/compiler/Compilation.java | 13 ++++- .../tools/compiler/LogCompilation.java | 5 +- .../sun/hotspot/tools/compiler/LogParser.java | 56 +++++++++++++++++++ .../sun/hotspot/tools/compiler/NMethod.java | 15 ++++- .../tools/compiler/TestLogCompilation.java | 11 +++- 6 files changed, 96 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index c34d27c8470c3..668747f8ce90a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ test/nashorn/lib NashornProfile.txt **/JTreport/** **/JTwork/** +/src/utils/LogCompilation/target/ diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java index 4fd8c517c6547..caf2fe4bbf740 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -221,8 +221,17 @@ public void print(PrintStream stream, int indent, boolean printID, boolean print stream.print(" " + nmethod.getLevel()); } } + + String codeSize = ""; + if (nmethod != null) { + long nmethodSize = nmethod.getInstSize(); + if (nmethodSize > 0) { + codeSize = "(code size: " + nmethodSize + ")"; + } + } + int bc = isOsr() ? getBCI() : -1; - stream.print(getMethod().decodeFlags(bc) + " " + getCompiler() + " " + getMethod().format(bc)); + stream.print(getMethod().decodeFlags(bc) + " " + getCompiler() + " " + getMethod().format(bc) + codeSize); stream.println(); if (getFailureReason() != null) { stream.println("COMPILE SKIPPED: " + getFailureReason() + " (not retryable)"); diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java index e6c00fde72faf..d042b860cb65d 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,6 +95,9 @@ public static void main(String[] args) throws Exception { } else if (a.equals("-s")) { sort = LogParser.sortByStart; index++; + } else if (a.equals("-z")) { + sort = LogParser.sortByNMethodSize; + index++; } else if (a.equals("-t")) { printTimeStamps = true; index++; diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java index 585cda0a91a0d..5b9c72fafe281 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java @@ -381,6 +381,55 @@ public int hashCode() { } }; + static Comparator sortByNMethodSize = new Comparator() { + + public int compare(LogEvent a, LogEvent b) { + Compilation c1 = a.getCompilation(); + Compilation c2 = b.getCompilation(); + if ((c1 != null && c2 == null)) { + return -1; + } else if (c1 == null && c2 != null) { + return 1; + } else if (c1 == null && c2 == null) { + return 0; + } + + if (c1.getNMethod() != null && c2.getNMethod() == null) { + return -1; + } else if (c1.getNMethod() == null && c2.getNMethod() != null) { + return 1; + } else if (c1.getNMethod() == null && c2.getNMethod() == null) { + return 0; + } + + assert c1.getNMethod() != null && c2.getNMethod() != null : "Neither should be null here"; + + long c1Size = c1.getNMethod().getInstSize(); + long c2Size = c2.getNMethod().getInstSize(); + + if (c1Size == 0 && c2Size == 0) { + return 0; + } + + if (c1Size > c2Size) { + return -1; + } else if (c1Size < c2Size) { + return 1; + } + + return 0; + } + + public boolean equals(Object other) { + return false; + } + + @Override + public int hashCode() { + return 7; + } + }; + /** * Shrink-wrapped representation of a JVMState (tailored to meet this * tool's needs). It only records a method and bytecode instruction index. @@ -1119,6 +1168,13 @@ public void startElement(String uri, String localName, String qname, Attributes if (level != null) { nm.setLevel(parseLong(level)); } + String iOffset = atts.getValue("insts_offset"); + String sOffset = atts.getValue("stub_offset"); + if (iOffset != null && sOffset != null) { + long insts_offset = parseLong(iOffset); + long stub_offset = parseLong(sOffset); + nm.setInstSize(stub_offset - insts_offset); + } String compiler = search(atts, "compiler", ""); nm.setCompiler(compiler); nmethods.put(id, nm); diff --git a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java index b9b5d281dc4cc..35c56f8776f4c 100644 --- a/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java +++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,11 @@ public class NMethod extends BasicLogEvent { */ private long size; + /** + * The nmethod's insts size in bytes. + */ + private long instSize; + /** * The nmethod's compilation level. */ @@ -79,6 +84,14 @@ public void setSize(long size) { this.size = size; } + public long getInstSize() { + return instSize; + } + + public void setInstSize(long size) { + this.instSize = size; + } + /** * @return the level */ diff --git a/src/utils/LogCompilation/src/test/java/com/sun/hotspot/tools/compiler/TestLogCompilation.java b/src/utils/LogCompilation/src/test/java/com/sun/hotspot/tools/compiler/TestLogCompilation.java index 2cb1d0a80cf3b..83762df16d68a 100644 --- a/src/utils/LogCompilation/src/test/java/com/sun/hotspot/tools/compiler/TestLogCompilation.java +++ b/src/utils/LogCompilation/src/test/java/com/sun/hotspot/tools/compiler/TestLogCompilation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,4 +177,13 @@ public void testDashn() throws Exception { LogCompilation.main(args); } + + @Test + public void testDashz() throws Exception { + String[] args = {"-z", + logFile + }; + + LogCompilation.main(args); + } } From e730e8b6919e4e7879e2c702b1c3101768110973 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Fri, 6 Nov 2020 00:30:09 +0000 Subject: [PATCH 009/124] 8241806: The sun/awt/shell/FileSystemViewMemoryLeak.java is unstable Reviewed-by: jdv, aivanov --- test/jdk/ProblemList.txt | 1 - .../sun/awt/shell/FileSystemViewMemoryLeak.java | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index fc16e0f81d093..28ac3dfa96bbb 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -260,7 +260,6 @@ java/awt/print/Headless/HeadlessPrinterJob.java 8196088 windows-all java/awt/print/PrinterJob/TestPgfmtSetMPA.java 8198343 generic-all sun/awt/datatransfer/SuplementaryCharactersTransferTest.java 8011371 generic-all sun/awt/shell/ShellFolderMemoryLeak.java 8197794 windows-all -sun/awt/shell/FileSystemViewMemoryLeak.java 8241806 windows-all sun/java2d/DirectX/OnScreenRenderingResizeTest/OnScreenRenderingResizeTest.java 8022403 generic-all sun/java2d/DirectX/OverriddenInsetsTest/OverriddenInsetsTest.java 8196102 generic-all sun/java2d/DirectX/RenderingToCachedGraphicsTest/RenderingToCachedGraphicsTest.java 8196180 windows-all,macosx-all diff --git a/test/jdk/sun/awt/shell/FileSystemViewMemoryLeak.java b/test/jdk/sun/awt/shell/FileSystemViewMemoryLeak.java index a0f8407f184a3..96b5cd360b445 100644 --- a/test/jdk/sun/awt/shell/FileSystemViewMemoryLeak.java +++ b/test/jdk/sun/awt/shell/FileSystemViewMemoryLeak.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,10 +27,12 @@ * @summary FileSystemView.isDrive(File) memory leak on "C:\" file reference * @modules java.desktop/sun.awt.shell * @requires (os.family == "windows") - * @run main/othervm -Xmx8m FileSystemViewMemoryLeak + * @run main/othervm/timeout=320 -Xmx8m FileSystemViewMemoryLeak */ import java.io.File; import java.text.NumberFormat; +import java.util.concurrent.TimeUnit; + import javax.swing.filechooser.FileSystemView; public class FileSystemViewMemoryLeak { @@ -39,6 +41,9 @@ public static void main(String[] args) { test(); } + // Will run the test no more than 300 seconds + static long endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(300); + private static void test() { File root = new File("C:\\"); @@ -52,6 +57,10 @@ private static void test() { int iMax = 50000; long lastPercentFinished = 0L; for (int i = 0; i < iMax; i++) { + if (isComplete()) { + System.out.println("Time is over"); + return; + } long percentFinished = Math.round(((i * 1000d) / (double) iMax)); @@ -77,5 +86,9 @@ private static void test() { boolean drive = fileSystemView.isDrive(root); } } + + private static boolean isComplete() { + return endtime - System.nanoTime() < 0; + } } From 5dfb42fc68099278cbc98e25fb8a91fd957c12e2 Mon Sep 17 00:00:00 2001 From: David Holmes Date: Fri, 6 Nov 2020 01:38:10 +0000 Subject: [PATCH 010/124] 8255563: Missing NULL checks after JDK-8233624 Reviewed-by: kvn --- src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 60d8cf40cf278..5e3e6de80dd15 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -2295,6 +2295,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas // 1) Try JNI short style stringStream st; char* pure_name = NativeLookup::pure_jni_name(method); + guarantee(pure_name != NULL, "Illegal native method name encountered"); os::print_jni_name_prefix_on(&st, args_size); st.print_raw(pure_name); os::print_jni_name_suffix_on(&st, args_size); @@ -2305,6 +2306,7 @@ C2V_VMENTRY_NULL(jlongArray, registerNativeMethods, (JNIEnv* env, jobject, jclas // 2) Try JNI long style st.reset(); char* long_name = NativeLookup::long_jni_name(method); + guarantee(long_name != NULL, "Illegal native method name encountered"); os::print_jni_name_prefix_on(&st, args_size); st.print_raw(pure_name); st.print_raw(long_name); From 952abea47bc1d9a463208fc91ebd77c30a019e73 Mon Sep 17 00:00:00 2001 From: Andy Herrick Date: Fri, 6 Nov 2020 16:14:36 +0000 Subject: [PATCH 011/124] 8254920: Application launched with jpackage produced .exe crashes JVM Reviewed-by: asemenyuk, almatvee, kizune --- .../native/applauncher/WinLauncher.cpp | 6 ++ .../helpers/jdk/jpackage/test/Executor.java | 12 +++ .../helpers/jdk/jpackage/test/HelloApp.java | 18 ++-- .../jpackage/share/RuntimeImageTest.java | 84 +++++++++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 test/jdk/tools/jpackage/share/RuntimeImageTest.java diff --git a/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp b/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp index 9baa1e9178c79..eadbf35754d93 100644 --- a/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp +++ b/src/jdk.jpackage/windows/native/applauncher/WinLauncher.cpp @@ -137,6 +137,8 @@ void launchApp() { const tstring launcherPath = SysInfo::getProcessModulePath(); const tstring appImageRoot = FileUtils::dirname(launcherPath); + const tstring runtimeBinPath = FileUtils::mkpath() + << appImageRoot << _T("runtime") << _T("bin"); std::unique_ptr jvm(AppLauncher() .setImageRoot(appImageRoot) @@ -146,6 +148,10 @@ void launchApp() { << _T("runtime")) .createJvmLauncher()); + // zip.dll may be loaded by java without full path + // make sure it will look in runtime/bin + SetDllDirectory(runtimeBinPath.c_str()); + const DllWrapper jliDll(jvm->getPath()); std::unique_ptr splashDll; if (jvm->isWithSplash()) { diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java index bf5322743815c..53d7341215693 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/Executor.java @@ -52,6 +52,7 @@ public static Executor of(String... cmdline) { public Executor() { saveOutputType = new HashSet<>(Set.of(SaveOutputType.NONE)); + removePath = false; } public Executor setExecutable(String v) { @@ -83,6 +84,11 @@ public Executor setExecutable(JavaTool v) { return setExecutable(v.getPath()); } + public Executor setRemovePath(boolean value) { + removePath = value; + return this; + } + /** * Configures this instance to save full output that command will produce. * This function is mutual exclusive with @@ -289,6 +295,11 @@ private Result runExecutable() throws IOException, InterruptedException { builder.directory(directory.toFile()); sb.append(String.format("; in directory [%s]", directory)); } + if (removePath) { + // run this with cleared Path in Environment + TKit.trace("Clearing PATH in environment"); + builder.environment().remove("PATH"); + } trace("Execute " + sb.toString() + "..."); Process process = builder.start(); @@ -414,6 +425,7 @@ private static void trace(String msg) { private Path executable; private Set saveOutputType; private Path directory; + private boolean removePath; private static enum SaveOutputType { NONE, FULL, FIRST_LINE, DUMP diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 745922d4111ca..0b9704b918257 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -273,14 +273,15 @@ public static void executeLauncherAndVerifyOutput(JPackageCommand cmd, String... args) { AppOutputVerifier av = getVerifier(cmd, args); if (av != null) { - av.executeAndVerifyOutput(args); + // when running app launchers, clear users environment + av.executeAndVerifyOutput(true, args); } } public static Executor.Result executeLauncher(JPackageCommand cmd, String... args) { AppOutputVerifier av = getVerifier(cmd, args); - return av.executeOnly(args); + return av.executeOnly(true, args); } private static AppOutputVerifier getVerifier(JPackageCommand cmd, @@ -351,7 +352,11 @@ public AppOutputVerifier addJavaOptions(Collection v) { } public void executeAndVerifyOutput(String... args) { - getExecutor(args).dumpOutput().execute(); + executeAndVerifyOutput(false, args); + } + + public void executeAndVerifyOutput(boolean removePath, String... args) { + getExecutor(args).dumpOutput().setRemovePath(removePath).execute(); final List launcherArgs = List.of(args); final List appArgs; @@ -365,8 +370,11 @@ public void executeAndVerifyOutput(String... args) { verifyOutputFile(outputFile, appArgs, params); } - public Executor.Result executeOnly(String...args) { - return getExecutor(args).saveOutput().executeWithoutExitCodeCheck(); + public Executor.Result executeOnly(boolean removePath, String...args) { + return getExecutor(args) + .saveOutput() + .setRemovePath(removePath) + .executeWithoutExitCodeCheck(); } private Executor getExecutor(String...args) { diff --git a/test/jdk/tools/jpackage/share/RuntimeImageTest.java b/test/jdk/tools/jpackage/share/RuntimeImageTest.java new file mode 100644 index 0000000000000..3d66a163c0771 --- /dev/null +++ b/test/jdk/tools/jpackage/share/RuntimeImageTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import jdk.jpackage.test.TKit; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.PackageType; +import jdk.jpackage.test.Functional; +import jdk.jpackage.test.Annotations.Test; +import jdk.jpackage.test.Annotations.Parameter; +import jdk.jpackage.test.PackageTest; +import jdk.jpackage.test.JPackageCommand; +import jdk.jpackage.test.JavaTool; +import jdk.jpackage.test.Executor; + +/* + * @test + * @summary jpackage with --runtime-image + * @library ../helpers + * @key jpackagePlatformPackage + * @build jdk.jpackage.test.* + * @modules jdk.incubator.jpackage/jdk.incubator.jpackage.internal + * @compile RuntimeImageTest.java + * @run main/othervm/timeout=1400 -Xmx512m jdk.jpackage.test.Main + * --jpt-run=RuntimeImageTest + */ + +public class RuntimeImageTest { + + @Test + @Parameter("0") + @Parameter("1") + @Parameter("2") + public static void test(String compression) throws Exception { + final Path workDir = TKit.createTempDirectory("runtime").resolve("data"); + final Path jlinkOutputDir = workDir.resolve("temp.runtime"); + Files.createDirectories(jlinkOutputDir.getParent()); + + new Executor() + .setToolProvider(JavaTool.JLINK) + .dumpOutput() + .addArguments( + "--output", jlinkOutputDir.toString(), + "--compress=" + compression, + "--add-modules", "ALL-MODULE-PATH", + "--strip-debug", + "--no-header-files", + "--no-man-pages", + "--strip-native-commands") + .execute(); + + JPackageCommand cmd = JPackageCommand.helloAppImage() + .setArgumentValue("--runtime-image", jlinkOutputDir.toString()); + + cmd.executeAndAssertHelloAppImageCreated(); + } + +} From 727a69f537d6e6a8f3bd14b7004159242aef8f76 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 6 Nov 2020 16:15:14 +0000 Subject: [PATCH 012/124] 8255969: Improve java/io/BufferedInputStream/LargeCopyWithMark.java using jtreg tags Reviewed-by: naoto --- .../LargeCopyWithMark.java | 65 ++++++------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/test/jdk/java/io/BufferedInputStream/LargeCopyWithMark.java b/test/jdk/java/io/BufferedInputStream/LargeCopyWithMark.java index 0519e1483f081..e3c171c022063 100644 --- a/test/jdk/java/io/BufferedInputStream/LargeCopyWithMark.java +++ b/test/jdk/java/io/BufferedInputStream/LargeCopyWithMark.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,65 +23,42 @@ /* @test * @bug 7129312 + * @requires (sun.arch.data.model == "64" & os.maxMemory > 4g) * @summary BufferedInputStream calculates negative array size with large * streams and mark - * @library /test/lib - * @run main/othervm LargeCopyWithMark + * @run main/othervm -Xmx4G LargeCopyWithMark */ import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import static jdk.test.lib.process.ProcessTools.*; - public class LargeCopyWithMark { - public static void main(String[] args) throws Exception { - if (! System.getProperty("os.arch").contains("64")) { - System.out.println("Test runs on 64 bit platforms"); - return; - } - ProcessBuilder pb = createJavaProcessBuilder("-Xmx4G", - "-ea:LargeCopyWithMark$Child", - "LargeCopyWithMark$Child"); - int res = pb.inheritIO().start().waitFor(); - if (res != 0) { - throw new AssertionError("Test failed: exit code = " + res); - } - } + static final int BUFF_SIZE = 8192; + static final int BIS_BUFF_SIZE = Integer.MAX_VALUE / 2 + 100; + static final long BYTES_TO_COPY = 2L * Integer.MAX_VALUE; - public static class Child { - static final int BUFF_SIZE = 8192; - static final int BIS_BUFF_SIZE = Integer.MAX_VALUE / 2 + 100; - static final long BYTES_TO_COPY = 2L * Integer.MAX_VALUE; - - static { - assert BIS_BUFF_SIZE * 2 < 0 : "doubling must overflow"; - } + static { + assert BIS_BUFF_SIZE * 2 < 0 : "doubling must overflow"; + } - public static void main(String[] args) throws Exception { - byte[] buff = new byte[BUFF_SIZE]; + public static void main(String[] args) throws Exception { + byte[] buff = new byte[BUFF_SIZE]; - try (InputStream myis = new MyInputStream(BYTES_TO_COPY); - InputStream bis = new BufferedInputStream(myis, BIS_BUFF_SIZE); - OutputStream myos = new MyOutputStream()) { + try (InputStream myis = new MyInputStream(BYTES_TO_COPY); + InputStream bis = new BufferedInputStream(myis, BIS_BUFF_SIZE); + OutputStream myos = new MyOutputStream()) { - // will require a buffer bigger than BIS_BUFF_SIZE - bis.mark(BIS_BUFF_SIZE + 100); + // will require a buffer bigger than BIS_BUFF_SIZE + bis.mark(BIS_BUFF_SIZE + 100); - for (;;) { - int count = bis.read(buff, 0, BUFF_SIZE); - if (count == -1) - break; - myos.write(buff, 0, count); - } - } catch (java.lang.NegativeArraySizeException e) { - e.printStackTrace(); - System.exit(11); - } catch (Exception e) { - e.printStackTrace(); + for (;;) { + int count = bis.read(buff, 0, BUFF_SIZE); + if (count == -1) + break; + myos.write(buff, 0, count); } } } From 688b10b97055ec83361aa7645a707204d35dd3af Mon Sep 17 00:00:00 2001 From: Vicente Romero Date: Fri, 6 Nov 2020 16:39:40 +0000 Subject: [PATCH 013/124] 8255561: add tests to check binary compatibility rules for records Reviewed-by: jjg --- .../RecordsBinaryCompatibilityTests.java | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java diff --git a/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java b/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java new file mode 100644 index 0000000000000..d526dbc4ef26f --- /dev/null +++ b/test/langtools/tools/javac/records/RecordsBinaryCompatibilityTests.java @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary test binary compatibility rules for record classes + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * jdk.compiler/com.sun.tools.javac.code + * jdk.jdeps/com.sun.tools.classfile + * @build toolbox.ToolBox toolbox.JavacTask + * @run main RecordsBinaryCompatibilityTests + */ + +import java.util.*; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.IntStream; + +import com.sun.tools.classfile.*; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.util.Assert; +import toolbox.TestRunner; +import toolbox.ToolBox; +import toolbox.JavaTask; +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.Task.OutputKind; + +public class RecordsBinaryCompatibilityTests extends TestRunner { + ToolBox tb; + + RecordsBinaryCompatibilityTests() { + super(System.err); + tb = new ToolBox(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[]{Paths.get(m.getName())}); + } + + public static void main(String... args) throws Exception { + RecordsBinaryCompatibilityTests t = new RecordsBinaryCompatibilityTests(); + t.runTests(); + } + + Path[] findJavaFiles(Path... paths) throws IOException { + return tb.findJavaFiles(paths); + } + + @Test + public void testCompatibilityAfterAddingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i) {} + """, + """ + package pkg; + public record R(String i, String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterDeletingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, String j) { + public R(String j) { + this("Hello World!", j); + } + } + """, + """ + package pkg; + public record R(String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hi"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, double j) {} + """, + """ + package pkg; + public record R(String i, int j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!", 1); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterReorderingRecordComponents(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i, int j) {} + """, + """ + package pkg; + public record R(int j, String i) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!", 1); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent2(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String j) { + public static String i() { return "Hello World!"; } + } + """, + """ + package pkg; + public record R(String i) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + IncompatibleClassChangeError.class + ); + } + + @Test + public void testCompatibilityAfterChangingRecordComponent3(Path base) throws Exception { + testCompatibilityAfterModifyingRecord( + base, + """ + package pkg; + public record R(String i) { + } + """, + """ + package pkg; + public record R(String j) {} + """, + """ + package pkg; + public class Client { + public static void main(String... args) { + R r = new R("Hello World!"); + System.out.println(r.i()); + } + } + """, + true, + NoSuchMethodError.class + ); + } + + /* 1- compiles the first version of the record class source code along with the client source code + * 2- executes the client class just to make sure that it works + * 3- compiles the second version of the record class + * 4- executes the client class and makes sure that the VM throws the expected error or not + * depending on the shouldFail argument + */ + private void testCompatibilityAfterModifyingRecord( + Path base, + String recordCode1, + String recordCode2, + String clientCode, + boolean shouldFail, + Class expectedError) throws Exception { + Path src = base.resolve("src"); + Path pkg = src.resolve("pkg"); + Path recordSrc = pkg.resolve("R"); + Path client = pkg.resolve("Client"); + + tb.writeJavaFiles(recordSrc, recordCode1); + tb.writeJavaFiles(client, clientCode); + + Path out = base.resolve("out"); + Files.createDirectories(out); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(pkg)) + .run(); + + // let's execute to check that it's working + String output = new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run() + .writeAll() + .getOutput(Task.OutputKind.STDOUT); + + // let's first check that it runs wo issues + if (!output.contains("Hello World!")) { + throw new AssertionError("execution of Client didn't finish"); + } + + // now lets change the record class + tb.writeJavaFiles(recordSrc, recordCode2); + + new JavacTask(tb) + .outdir(out) + .files(findJavaFiles(recordSrc)) + .run(); + + if (shouldFail) { + // let's now check that we get the expected error + output = new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run(Task.Expect.FAIL) + .writeAll() + .getOutput(Task.OutputKind.STDERR); + if (!output.startsWith("Exception in thread \"main\" " + expectedError.getName())) { + throw new AssertionError(expectedError.getName() + " expected"); + } + } else { + new JavaTask(tb) + .classpath(out.toString()) + .classArgs("pkg.Client") + .run(Task.Expect.SUCCESS); + } + } +} From f5d36e6c9470b1ac5326990b1c4fea80457c4ad6 Mon Sep 17 00:00:00 2001 From: Conor Cleary Date: Fri, 6 Nov 2020 17:35:24 +0000 Subject: [PATCH 014/124] 8246741: NetworkInterface/UniqueMacAddressesTest: mac address uniqueness test failed Reviewed-by: chegar, dfuchs --- .../UniqueMacAddressesTest.java | 110 +++++++----------- 1 file changed, 44 insertions(+), 66 deletions(-) diff --git a/test/jdk/java/net/NetworkInterface/UniqueMacAddressesTest.java b/test/jdk/java/net/NetworkInterface/UniqueMacAddressesTest.java index 4017c0702ed23..a01394831e723 100644 --- a/test/jdk/java/net/NetworkInterface/UniqueMacAddressesTest.java +++ b/test/jdk/java/net/NetworkInterface/UniqueMacAddressesTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,87 +21,68 @@ * questions. */ -import java.net.InetAddress; +import java.io.PrintStream; +import java.io.UncheckedIOException; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; import java.util.List; +import java.util.stream.Collectors; +import jdk.test.lib.NetworkConfiguration; /* * @test * @bug 8021372 * @summary Tests that the MAC addresses returned by NetworkInterface.getNetworkInterfaces are unique for each adapter. - * + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * @run main/othervm UniqueMacAddressesTest */ public class UniqueMacAddressesTest { + static PrintStream log = System.err; + + // A record pair (NetworkInterface::name, NetworkInterface::hardwareAddress) + record NetIfPair(String interfaceName, byte[] address) {} + public static void main(String[] args) throws Exception { new UniqueMacAddressesTest().execute(); - System.out.println("UniqueMacAddressesTest: OK"); + log.println("UniqueMacAddressesTest: OK"); } public UniqueMacAddressesTest() { - System.out.println("UniqueMacAddressesTest: start "); + log.println("UniqueMacAddressesTest: start"); } public void execute() throws Exception { - Enumeration networkInterfaces; - boolean areMacAddressesUnique = false; - List networkInterfaceList = new ArrayList(); - networkInterfaces = NetworkInterface.getNetworkInterfaces(); - - // build a list of NetworkInterface objects to test MAC address - // uniqueness - createNetworkInterfaceList(networkInterfaces, networkInterfaceList); - areMacAddressesUnique = checkMacAddressesAreUnique(networkInterfaceList); - if (!areMacAddressesUnique) { + // build a list of NetworkInterface name address pairs + // to test MAC address uniqueness + List netIfList = createNetworkInterfaceList(NetworkConfiguration.probe()); + if (!macAddressesAreUnique(netIfList)) throw new RuntimeException("mac address uniqueness test failed"); - } } - private boolean checkMacAddressesAreUnique ( - List networkInterfaces) throws Exception { - boolean uniqueMacAddresses = true; - for (NetworkInterface networkInterface : networkInterfaces) { - for (NetworkInterface comparisonNetIf : networkInterfaces) { - System.out.println("Comparing netif " - + networkInterface.getName() + " and netif " - + comparisonNetIf.getName()); - if (testMacAddressesEqual(networkInterface, comparisonNetIf)) { - uniqueMacAddresses = false; - break; - } + private boolean macAddressesAreUnique(List netIfPairs) { + for (NetIfPair netIfPair : netIfPairs) { + for (NetIfPair compNetIfPair : netIfPairs) { + if (!netIfPair.interfaceName.equals(compNetIfPair.interfaceName) && + testMacAddressesEqual(netIfPair, compNetIfPair)) + return false; } - if (uniqueMacAddresses != true) - break; } - return uniqueMacAddresses; + return true; } - private boolean testMacAddressesEqual(NetworkInterface netIf1, - NetworkInterface netIf2) throws Exception { - - byte[] rawMacAddress1 = null; - byte[] rawMacAddress2 = null; - boolean macAddressesEqual = false; - if (!netIf1.getName().equals(netIf2.getName())) { - System.out.println("compare hardware addresses " - + createMacAddressString(netIf1) + " and " + createMacAddressString(netIf2)); - rawMacAddress1 = netIf1.getHardwareAddress(); - rawMacAddress2 = netIf2.getHardwareAddress(); - macAddressesEqual = Arrays.equals(rawMacAddress1, rawMacAddress2); - } else { - // same interface - macAddressesEqual = false; - } - return macAddressesEqual; + private boolean testMacAddressesEqual(NetIfPair if1, NetIfPair if2) { + log.println("Compare hardware addresses of " + if1.interfaceName + " (" + + createMacAddressString(if1.address) + ")" + " and " + if2.interfaceName + + " (" + createMacAddressString(if2.address) + ")"); + return (Arrays.equals(if1.address, if2.address)); } - private String createMacAddressString (NetworkInterface netIf) throws Exception { - byte[] macAddr = netIf.getHardwareAddress(); + private String createMacAddressString(byte[] macAddr) { StringBuilder sb = new StringBuilder(); if (macAddr != null) { for (int i = 0; i < macAddr.length; i++) { @@ -112,21 +93,18 @@ private String createMacAddressString (NetworkInterface netIf) throws Exception return sb.toString(); } - private void createNetworkInterfaceList(Enumeration nis, - List networkInterfaceList) throws Exception { - byte[] macAddr = null; - NetworkInterface netIf = null; - while (nis.hasMoreElements()) { - netIf = (NetworkInterface) nis.nextElement(); - if (netIf.isUp()) { - macAddr = netIf.getHardwareAddress(); - if (macAddr != null) { - System.out.println("Adding NetworkInterface " - + netIf.getName() + " with mac address " - + createMacAddressString(netIf)); - networkInterfaceList.add(netIf); - } - } + private byte[] getNetworkInterfaceHardwareAddress(NetworkInterface inf) { + try { + return inf.getHardwareAddress(); + } catch (SocketException se) { + throw new UncheckedIOException(se); } } + + private List createNetworkInterfaceList(NetworkConfiguration netConf) { + return netConf.interfaces() + .map(netIf -> new NetIfPair(netIf.getName(), getNetworkInterfaceHardwareAddress(netIf))) + .collect(Collectors.filtering(netIfPair -> netIfPair.address != null, + Collectors.toCollection(ArrayList::new))); + } } From 0b7fba75c139bc263cc77c1d29f31703dd88b6f4 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Fri, 6 Nov 2020 19:04:09 +0000 Subject: [PATCH 015/124] 8254270: linux 32 bit build doesn't compile libjdwp/log_messages.c Reviewed-by: redestad, cjplummer, dholmes, stuefe --- .../share/native/libjdwp/log_messages.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c index 2e273137af08b..3e186406e18dc 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,9 +78,12 @@ get_time_stamp(char *tbuf, size_t ltbuf) "%d.%m.%Y %T", localtime(&t)); (void)strftime(timestamp_timezone, TZ_SIZE, "%Z", localtime(&t)); - (void)snprintf(tbuf, ltbuf, - "%s.%.3d %s", timestamp_date_time, - (int)(millisecs), timestamp_timezone); + + // Truncate milliseconds in buffer large enough to hold the + // value which is always < 1000 (and so a maximum of 3 digits for "%.3d") + char tmp[20]; + snprintf(tmp, sizeof(tmp), "%.3d", millisecs); + snprintf(tbuf, ltbuf, "%s.%.3s %s", timestamp_date_time, tmp, timestamp_timezone); } /* Get basename of filename */ From a9dff9420ac60a0cc7c0901bd5b2b1c413d6083b Mon Sep 17 00:00:00 2001 From: Alex Menkov Date: Fri, 6 Nov 2020 21:57:43 +0000 Subject: [PATCH 016/124] 8254864: vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001/TestDescription.java timed out Reviewed-by: sspitsyn, cjplummer --- .../nsk/jvmti/ResourceExhausted/Helper.java | 19 +++++++++++--- .../jvmti/ResourceExhausted/resexhausted.cpp | 14 +++++------ .../ResourceExhausted/resexhausted001.java | 25 +++++++++++++------ .../resexhausted001/TestDescription.java | 2 ++ .../ResourceExhausted/resexhausted002.java | 3 ++- .../ResourceExhausted/resexhausted003.java | 4 ++- 6 files changed, 46 insertions(+), 21 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/Helper.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/Helper.java index 33d0b89b0ec02..72b00cf9b40cc 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/Helper.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/Helper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,28 @@ */ package nsk.jvmti.ResourceExhausted; +import jtreg.SkippedException; + public class Helper { - static native boolean gotExhaustedEvent(); + static native int getExhaustedEventFlags(); static native void resetExhaustedEvent(); - static boolean checkResult(String eventName) { - if ( ! gotExhaustedEvent() ) { + static final int JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 1; + static final int JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 2; + static final int JVMTI_RESOURCE_EXHAUSTED_THREADS = 4; + + static boolean checkResult(int expectedFlag, String eventName) { + int got = getExhaustedEventFlags(); + if (got == 0) { System.err.println("Failure: Expected ResourceExhausted event after " + eventName + " did not occur"); return false; } + if ((got & expectedFlag) == 0) { + System.err.println("Warning: did not get expected flag bit (expected: "+ expectedFlag + ", got: " + got + ")"); + throw new SkippedException("Test did not get expected flag value"); + } System.out.println("Got expected ResourceExhausted event"); return true; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted.cpp b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted.cpp index c6e795837e66e..b3c6304ee0c8d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted.cpp +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ extern "C" { static jvmtiEnv* gJvmti = NULL; -static volatile jboolean gGotEvent = JNI_FALSE; +static volatile jint gEventFlags = 0; void JNICALL resourceExhausted(jvmtiEnv *jvmti_env, @@ -46,19 +46,19 @@ resourceExhausted(jvmtiEnv *jvmti_env, if (flags & JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR) NSK_DISPLAY0("Agent: JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR\n"); if (flags & JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP) NSK_DISPLAY0("Agent: JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP\n"); if (flags & JVMTI_RESOURCE_EXHAUSTED_THREADS) NSK_DISPLAY0("Agent: JVMTI_RESOURCE_EXHAUSTED_THREADS\n"); - gGotEvent = JNI_TRUE; + gEventFlags = flags; } -JNIEXPORT jboolean JNICALL -Java_nsk_jvmti_ResourceExhausted_Helper_gotExhaustedEvent(JNIEnv* env, jclass cls) +JNIEXPORT jint JNICALL +Java_nsk_jvmti_ResourceExhausted_Helper_getExhaustedEventFlags(JNIEnv* env, jclass cls) { - return gGotEvent; + return gEventFlags; } JNIEXPORT void JNICALL Java_nsk_jvmti_ResourceExhausted_Helper_resetExhaustedEvent(JNIEnv* env, jclass cls) { - gGotEvent = JNI_FALSE; + gEventFlags = 0; } #ifdef STATIC_BUILD diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001.java index ac497dfcb0ebe..b147757f9e0a1 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001.java @@ -25,6 +25,7 @@ import java.io.PrintStream; import java.util.concurrent.atomic.AtomicInteger; +import jdk.test.lib.Platform; import nsk.share.Consts; import nsk.share.test.Stresser; import jtreg.SkippedException; @@ -42,6 +43,11 @@ public class resexhausted001 { public static int run(String args[], PrintStream out) { + // Check platform here (instead of @requires) as this test is also called from resexhausted004 + if (Platform.isWindows()) { + throw new SkippedException("Cannot get JVMTI_RESOURCE_EXHAUSTED_THREADS on Windows"); + } + Stresser stress = new Stresser(args); int count = 0; @@ -56,14 +62,15 @@ public static int run(String args[], PrintStream out) { makeThread(); } - System.out.println("Can't reproduce OOME due to a limit on iterations/execution time. Test was useless."); + System.out.println("Can't reproduce OOME due to a limit on iterations/execution time. Test was useless." + + " threadCount=" + threadCount.get()); throw new SkippedException("Test did not get an OutOfMemory error"); } catch (OutOfMemoryError e) { count = threadCount.get(); } finally { - threadsDone = true; synchronized (hanger) { + threadsDone = true; hanger.notifyAll(); } stress.finish(); @@ -74,7 +81,8 @@ public static int run(String args[], PrintStream out) { } System.gc(); - if (!Helper.checkResult("creating " + count + " threads")) { + System.out.println("got OOME with threadCount=" + count); + if (!Helper.checkResult(Helper.JVMTI_RESOURCE_EXHAUSTED_THREADS, "creating " + count + " threads")) { return Consts.TEST_FAILED; } @@ -85,16 +93,17 @@ static Thread makeThread() { final Thread thr = new Thread(new Runnable() { public void run() { threadCount.getAndIncrement(); - while (!threadsDone) { - try { - synchronized (hanger) { + synchronized (hanger) { + while (!threadsDone) { + try { hanger.wait(); - } - } catch (InterruptedException ignored) {} + } catch (InterruptedException ignored) {} + } } threadCount.getAndDecrement(); } }, "fleece"); + thr.setDaemon(true); thr.start(); return thr; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001/TestDescription.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001/TestDescription.java index 57f29ae5173b9..c86d34c9b2c2b 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001/TestDescription.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted001/TestDescription.java @@ -40,6 +40,8 @@ * @run main/othervm/native/timeout=240 * -agentlib:resexhausted=-waittime=5 * -XX:-UseGCOverheadLimit + * -Xms16m + * -Xmx16m * nsk.jvmti.ResourceExhausted.resexhausted001 * -stressTime 220 */ diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted002.java index 6871640833d5b..46832df7dc30c 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted002.java @@ -64,8 +64,9 @@ public static int run(String args[], PrintStream out) { } System.gc(); - if ( ! Helper.checkResult("creating " + count + " objects") ) + if (!Helper.checkResult(Helper.JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "creating " + count + " objects")) { return Consts.TEST_FAILED; + } return Consts.TEST_PASSED; } diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003.java index 9b5e28e6c8e77..f336c1bf3d57d 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003.java @@ -125,8 +125,10 @@ public static int run(String args[], PrintStream out) { } System.gc(); - if ( ! Helper.checkResult("loading " + count + " classes of " + bloatBytes.length + " bytes") ) + if (!Helper.checkResult(Helper.JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR, + "loading " + count + " classes of " + bloatBytes.length + " bytes")) { return Consts.TEST_FAILED; + } return Consts.TEST_PASSED; } From 358f5d2b03f3d2329517a21cddb31c1a8d35462a Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Sat, 7 Nov 2020 06:10:56 +0000 Subject: [PATCH 017/124] 6422025: ThemeReader.cpp can be updated for VC7 Reviewed-by: aivanov --- .../sun/java/swing/plaf/windows/XPStyle.java | 5 +- .../classes/sun/awt/windows/ThemeReader.java | 11 +- .../native/libawt/windows/ThemeReader.cpp | 209 +++++++----------- 3 files changed, 82 insertions(+), 143 deletions(-) diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java index 2c60209186fd9..71bb48a9a1632 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/XPStyle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -593,8 +593,7 @@ void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state) { if (XPStyle.getXP() == null) { return; } - if (ThemeReader.isGetThemeTransitionDurationDefined() - && component instanceof JComponent + if (component instanceof JComponent && SwingUtilities.getAncestorOfClass(CellRendererPane.class, component) == null) { AnimationController.paintSkin((JComponent) component, this, diff --git a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java index 167d4d5b91011..482a92fd885d4 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/ThemeReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,13 +35,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; -/* !!!! WARNING !!!! - * This class has to be in sync with - * src/solaris/classes/sun/awt/windows/ThemeReader.java - * while we continue to build WinL&F on solaris - */ - - /** * Implements Theme Support for Windows XP. * @@ -299,8 +292,6 @@ public static long getThemeTransitionDuration(String widget, int part, } } - public static native boolean isGetThemeTransitionDurationDefined(); - private static native Insets getThemeBackgroundContentMargins(long theme, int part, int state, int boundingWidth, int boundingHeight); diff --git a/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp b/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp index 8a4461be40935..002b502fafbb7 100644 --- a/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp +++ b/src/java.desktop/windows/native/libawt/windows/ThemeReader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,51 +24,13 @@ */ #include "sun_awt_windows_ThemeReader.h" -#include #include "awt.h" #include "awt_Toolkit.h" -#include "awt_Object.h" -#include "awt_Component.h" #include "math.h" -// Important note about VC6 and VC7 (or XP Platform SDK) ! -// -// These type definitions have been imported from UxTheme.h -// They have been imported instead of including them, because -// currently we don't require Platform SDK for building J2SE and -// VC6 includes do not have UxTheme.h. When we move to VC7 -// we should remove these imports and just include -// -// Uncomment these when we start using VC 7 (or XP Platform SDK) -// -// #include -// #incldue - - -// Remove everyting inside this ifdef when we start using VC 7 (or XP Platform SDK) -#ifndef _UXTHEME_H_ -typedef HANDLE HTHEME; // handle to a section of theme data for class - -typedef enum { - TS_MIN, - TS_TRUE, - TS_DRAW -} THEME_SIZE; - - -// Remove these when we start using VC 7 (or XP Platform SDK) -typedef struct _MARGINS -{ - int cxLeftWidth; // width of left border that retains its size - int cxRightWidth; // width of right border that retains its size - int cyTopHeight; // height of top border that retains its size - int cyBottomHeight; // height of bottom border that retains its size -} MARGINS, *PMARGINS; - -#define TMT_TRANSPARENT 2201 -#endif // _UXTHEME_H_ +#include #if defined(_MSC_VER) && _MSC_VER >= 1800 # define ROUND_TO_INT(num) ((int) round(num)) @@ -119,7 +81,7 @@ typedef HRESULT (__stdcall *PFNGETTHEMEENUMVALUE)(HTHEME hTheme, int iPartId, typedef HRESULT (__stdcall *PFNGETTHEMEINT)(HTHEME hTheme, int iPartId, int iStateId, int iPropId, int *val); typedef HRESULT (__stdcall *PFNGETTHEMEPARTSIZE)(HTHEME hTheme, HDC hdc, - int iPartId, int iStateId, RECT *prc, THEME_SIZE eSize, SIZE *size); + int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *size); typedef HRESULT (__stdcall *PFNGETTHEMEPOSITION)(HTHEME hTheme, int iPartId, int iStateId, int propID, POINT *point); @@ -134,25 +96,24 @@ typedef HRESULT (__stdcall *PFNGETTHEMETRANSITIONDURATION) (HTHEME hTheme, int iPartId, int iStateIdFrom, int iStateIdTo, int iPropId, DWORD *pdwDuration); -static PFNOPENTHEMEDATA OpenThemeData = NULL; -static PFNDRAWTHEMEBACKGROUND DrawThemeBackground = NULL; -static PFNCLOSETHEMEDATA CloseThemeData = NULL; -static PFNDRAWTHEMETEXT DrawThemeText = NULL; -static PFNGETTHEMEBACKGROUNDCONTENTRECT GetThemeBackgroundContentRect = NULL; -static PFNGETTHEMEMARGINS GetThemeMargins = NULL; -static PFNISTHEMEPARTDEFINED IsThemePartDefined = NULL; -static PFNGETTHEMEBOOL GetThemeBool=NULL; -static PFNGETTHEMESYSBOOL GetThemeSysBool=NULL; -static PFNGETTHEMECOLOR GetThemeColor=NULL; -static PFNGETTHEMEENUMVALUE GetThemeEnumValue = NULL; -static PFNGETTHEMEINT GetThemeInt = NULL; -static PFNGETTHEMEPARTSIZE GetThemePartSize = NULL; -static PFNGETTHEMEPOSITION GetThemePosition = NULL; -static PFNSETWINDOWTHEME SetWindowTheme = NULL; +static PFNOPENTHEMEDATA OpenThemeDataFunc = NULL; +static PFNDRAWTHEMEBACKGROUND DrawThemeBackgroundFunc = NULL; +static PFNCLOSETHEMEDATA CloseThemeDataFunc = NULL; +static PFNDRAWTHEMETEXT DrawThemeTextFunc = NULL; +static PFNGETTHEMEBACKGROUNDCONTENTRECT GetThemeBackgroundContentRectFunc = NULL; +static PFNGETTHEMEMARGINS GetThemeMarginsFunc = NULL; +static PFNISTHEMEPARTDEFINED IsThemePartDefinedFunc = NULL; +static PFNGETTHEMEBOOL GetThemeBoolFunc=NULL; +static PFNGETTHEMESYSBOOL GetThemeSysBoolFunc=NULL; +static PFNGETTHEMECOLOR GetThemeColorFunc=NULL; +static PFNGETTHEMEENUMVALUE GetThemeEnumValueFunc = NULL; +static PFNGETTHEMEINT GetThemeIntFunc = NULL; +static PFNGETTHEMEPARTSIZE GetThemePartSizeFunc = NULL; +static PFNGETTHEMEPOSITION GetThemePositionFunc = NULL; +static PFNSETWINDOWTHEME SetWindowThemeFunc = NULL; static PFNISTHEMEBACKGROUNDPARTIALLYTRANSPARENT - IsThemeBackgroundPartiallyTransparent = NULL; -//this function might not exist on Windows XP -static PFNGETTHEMETRANSITIONDURATION GetThemeTransitionDuration = NULL; + IsThemeBackgroundPartiallyTransparentFunc = NULL; +static PFNGETTHEMETRANSITIONDURATION GetThemeTransitionDurationFunc = NULL; BOOL InitThemes() { @@ -161,67 +122,67 @@ BOOL InitThemes() { DTRACE_PRINTLN1("InitThemes hModThemes = %x\n", hModThemes); if(hModThemes) { DTRACE_PRINTLN("Loaded UxTheme.dll\n"); - OpenThemeData = (PFNOPENTHEMEDATA)GetProcAddress(hModThemes, + OpenThemeDataFunc = (PFNOPENTHEMEDATA)GetProcAddress(hModThemes, "OpenThemeData"); - DrawThemeBackground = (PFNDRAWTHEMEBACKGROUND)GetProcAddress( + DrawThemeBackgroundFunc = (PFNDRAWTHEMEBACKGROUND)GetProcAddress( hModThemes, "DrawThemeBackground"); - CloseThemeData = (PFNCLOSETHEMEDATA)GetProcAddress( + CloseThemeDataFunc = (PFNCLOSETHEMEDATA)GetProcAddress( hModThemes, "CloseThemeData"); - DrawThemeText = (PFNDRAWTHEMETEXT)GetProcAddress( + DrawThemeTextFunc = (PFNDRAWTHEMETEXT)GetProcAddress( hModThemes, "DrawThemeText"); - GetThemeBackgroundContentRect = (PFNGETTHEMEBACKGROUNDCONTENTRECT) + GetThemeBackgroundContentRectFunc = (PFNGETTHEMEBACKGROUNDCONTENTRECT) GetProcAddress(hModThemes, "GetThemeBackgroundContentRect"); - GetThemeMargins = (PFNGETTHEMEMARGINS)GetProcAddress( + GetThemeMarginsFunc = (PFNGETTHEMEMARGINS)GetProcAddress( hModThemes, "GetThemeMargins"); - IsThemePartDefined = (PFNISTHEMEPARTDEFINED)GetProcAddress( + IsThemePartDefinedFunc = (PFNISTHEMEPARTDEFINED)GetProcAddress( hModThemes, "IsThemePartDefined"); - GetThemeBool = (PFNGETTHEMEBOOL)GetProcAddress( + GetThemeBoolFunc = (PFNGETTHEMEBOOL)GetProcAddress( hModThemes, "GetThemeBool"); - GetThemeSysBool = (PFNGETTHEMESYSBOOL)GetProcAddress(hModThemes, + GetThemeSysBoolFunc = (PFNGETTHEMESYSBOOL)GetProcAddress(hModThemes, "GetThemeSysBool"); - GetThemeColor = (PFNGETTHEMECOLOR)GetProcAddress(hModThemes, + GetThemeColorFunc = (PFNGETTHEMECOLOR)GetProcAddress(hModThemes, "GetThemeColor"); - GetThemeEnumValue = (PFNGETTHEMEENUMVALUE)GetProcAddress(hModThemes, + GetThemeEnumValueFunc = (PFNGETTHEMEENUMVALUE)GetProcAddress(hModThemes, "GetThemeEnumValue"); - GetThemeInt = (PFNGETTHEMEINT)GetProcAddress(hModThemes, "GetThemeInt"); - GetThemePosition = (PFNGETTHEMEPOSITION)GetProcAddress(hModThemes, + GetThemeIntFunc = (PFNGETTHEMEINT)GetProcAddress(hModThemes, "GetThemeInt"); + GetThemePositionFunc = (PFNGETTHEMEPOSITION)GetProcAddress(hModThemes, "GetThemePosition"); - GetThemePartSize = (PFNGETTHEMEPARTSIZE)GetProcAddress(hModThemes, + GetThemePartSizeFunc = (PFNGETTHEMEPARTSIZE)GetProcAddress(hModThemes, "GetThemePartSize"); - SetWindowTheme = (PFNSETWINDOWTHEME)GetProcAddress(hModThemes, + SetWindowThemeFunc = (PFNSETWINDOWTHEME)GetProcAddress(hModThemes, "SetWindowTheme"); - IsThemeBackgroundPartiallyTransparent = + IsThemeBackgroundPartiallyTransparentFunc = (PFNISTHEMEBACKGROUNDPARTIALLYTRANSPARENT)GetProcAddress(hModThemes, "IsThemeBackgroundPartiallyTransparent"); - //this function might not exist - GetThemeTransitionDuration = + GetThemeTransitionDurationFunc = (PFNGETTHEMETRANSITIONDURATION)GetProcAddress(hModThemes, "GetThemeTransitionDuration"); - if(OpenThemeData - && DrawThemeBackground - && CloseThemeData - && DrawThemeText - && GetThemeBackgroundContentRect - && GetThemeMargins - && IsThemePartDefined - && GetThemeBool - && GetThemeSysBool - && GetThemeColor - && GetThemeEnumValue - && GetThemeInt - && GetThemePartSize - && GetThemePosition - && SetWindowTheme - && IsThemeBackgroundPartiallyTransparent + if(OpenThemeDataFunc + && DrawThemeBackgroundFunc + && CloseThemeDataFunc + && DrawThemeTextFunc + && GetThemeBackgroundContentRectFunc + && GetThemeMarginsFunc + && IsThemePartDefinedFunc + && GetThemeBoolFunc + && GetThemeSysBoolFunc + && GetThemeColorFunc + && GetThemeEnumValueFunc + && GetThemeIntFunc + && GetThemePartSizeFunc + && GetThemePositionFunc + && SetWindowThemeFunc + && IsThemeBackgroundPartiallyTransparentFunc + && GetThemeTransitionDurationFunc ) { DTRACE_PRINTLN("Loaded function pointers.\n"); // We need to make sure we can load the Theme. This may not be // the case on a WinXP machine with classic mode enabled. - HTHEME hTheme = OpenThemeData(AwtToolkit::GetInstance().GetHWnd(), L"Button"); + HTHEME hTheme = OpenThemeDataFunc(AwtToolkit::GetInstance().GetHWnd(), L"Button"); if(hTheme) { DTRACE_PRINTLN("Loaded Theme data.\n"); - CloseThemeData(hTheme); + CloseThemeDataFunc(hTheme); return TRUE; } } else { @@ -290,7 +251,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_windows_ThemeReader_openTheme } // We need to open the Theme on a Window that will stick around. // The best one for that purpose is the Toolkit window. - HTHEME htheme = OpenThemeData(AwtToolkit::GetInstance().GetHWnd(), str); + HTHEME htheme = OpenThemeDataFunc(AwtToolkit::GetInstance().GetHWnd(), str); JNU_ReleaseStringPlatformChars(env, widget, str); return (jlong) htheme; } @@ -308,7 +269,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_setWindowTheme str = (LPCTSTR) JNU_GetStringPlatformChars(env, subAppName, NULL); } // We need to set the Window theme on the same theme that we opened it with. - HRESULT hres = SetWindowTheme(AwtToolkit::GetInstance().GetHWnd(), str, NULL); + HRESULT hres = SetWindowThemeFunc(AwtToolkit::GetInstance().GetHWnd(), str, NULL); assert_result(hres, env); if (subAppName != NULL) { JNU_ReleaseStringPlatformChars(env, subAppName, str); @@ -323,7 +284,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_setWindowTheme JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_closeTheme (JNIEnv *env, jclass klass, jlong theme) { - HRESULT hres = CloseThemeData((HTHEME)theme); + HRESULT hres = CloseThemeDataFunc((HTHEME)theme); assert_result(hres, env); } @@ -474,7 +435,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_paintBackground ZeroMemory(pSrcBits,(BITS_PER_PIXEL>>3)*w*h); - HRESULT hres = DrawThemeBackground(hTheme, memDC, part, state, &rect, NULL); + HRESULT hres = DrawThemeBackgroundFunc(hTheme, memDC, part, state, &rect, NULL); assert_result(hres, env); if (SUCCEEDED(hres)) { // Make sure GDI is done. @@ -482,7 +443,7 @@ JNIEXPORT void JNICALL Java_sun_awt_windows_ThemeReader_paintBackground // Copy the resulting pixels to our Java BufferedImage. pDstBits = (int *)env->GetPrimitiveArrayCritical(array, 0); BOOL transparent = FALSE; - transparent = IsThemeBackgroundPartiallyTransparent(hTheme,part,state); + transparent = IsThemeBackgroundPartiallyTransparentFunc(hTheme, part, state); copyDIBToBufferedImage(pDstBits, pSrcBits, transparent, w, h, stride); env->ReleasePrimitiveArrayCritical(array, pDstBits, 0); } @@ -530,7 +491,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getThemeMargins HTHEME hTheme = (HTHEME) theme; if (hTheme != NULL) { - HRESULT hres = GetThemeMargins(hTheme, NULL, part, state, property, NULL, &margins); + HRESULT hres = GetThemeMarginsFunc(hTheme, NULL, part, state, property, NULL, &margins); assert_result(hres, env); if (FAILED(hres)) { return NULL; @@ -551,7 +512,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getThemeMargins JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_isThemePartDefined (JNIEnv *env, jclass klass, jlong theme, jint part, jint state) { HTHEME hTheme = (HTHEME) theme; - return JNI_IS_TRUE(IsThemePartDefined(hTheme, part, state)); + return JNI_IS_TRUE(IsThemePartDefinedFunc(hTheme, part, state)); } /* @@ -567,7 +528,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getColor if (hTheme != NULL) { COLORREF color=0; - if (GetThemeColor(hTheme, part, state, type, &color) != S_OK) { + if (GetThemeColorFunc(hTheme, part, state, type, &color) != S_OK) { return NULL; } @@ -613,7 +574,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_ThemeReader_getInt HTHEME hTheme = (HTHEME) theme; int retVal = -1; if (hTheme != NULL) { - HRESULT hres = GetThemeInt(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeIntFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return retVal; @@ -629,7 +590,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_windows_ThemeReader_getEnum HTHEME hTheme = (HTHEME) theme; int retVal = -1; if (hTheme != NULL) { - HRESULT hres = GetThemeEnumValue(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeEnumValueFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return retVal; @@ -645,7 +606,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_getBoolean HTHEME hTheme = (HTHEME) theme; BOOL retVal = FALSE; if (hTheme != NULL) { - HRESULT hres = GetThemeBool(hTheme, part, state, prop, &retVal); + HRESULT hres = GetThemeBoolFunc(hTheme, part, state, prop, &retVal); assert_result(hres, env); } return JNI_IS_TRUE(retVal); @@ -660,7 +621,7 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_windows_ThemeReader_getSysBoolean (JNIEnv *env, jclass klass, jlong theme, jint prop) { HTHEME hTheme = (HTHEME)theme; if (hTheme != NULL) { - return JNI_IS_TRUE(GetThemeSysBool(hTheme, prop)); + return JNI_IS_TRUE(GetThemeSysBoolFunc(hTheme, prop)); } return JNI_FALSE; } @@ -676,7 +637,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPoint POINT point; if (hTheme != NULL) { - if (GetThemePosition(hTheme, part, state, prop, &point) != S_OK) { + if (GetThemePositionFunc(hTheme, part, state, prop, &point) != S_OK) { return NULL; } @@ -723,7 +684,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPosition POINT point; - HRESULT hres = GetThemePosition(hTheme, part, state, prop, &point); + HRESULT hres = GetThemePositionFunc(hTheme, part, state, prop, &point); assert_result(hres, env); if (FAILED(hres)) { return NULL; @@ -789,7 +750,7 @@ JNIEXPORT jobject JNICALL Java_sun_awt_windows_ThemeReader_getPartSize if (theme != NULL) { SIZE size; - if (SUCCEEDED(GetThemePartSize((HTHEME)theme, NULL, part, state, + if (SUCCEEDED(GetThemePartSizeFunc((HTHEME)theme, NULL, part, state, NULL, TS_TRUE, &size)) && (env->EnsureLocalCapacity(2) >= 0)) { static jmethodID dimMID = NULL; @@ -833,9 +794,10 @@ jint boundingWidth, jint boundingHeight) { boundingRect.right = boundingWidth; boundingRect.bottom = boundingHeight; RECT contentRect; - if (SUCCEEDED(GetThemeBackgroundContentRect((HTHEME) hTheme, NULL, part, - state, &boundingRect, - &contentRect))) { + if (SUCCEEDED(GetThemeBackgroundContentRectFunc((HTHEME) hTheme, NULL, + part, state, + &boundingRect, + &contentRect))) { return newInsets(env, contentRect.top, contentRect.left, boundingHeight - contentRect.bottom, @@ -855,23 +817,10 @@ Java_sun_awt_windows_ThemeReader_getThemeTransitionDuration (JNIEnv *env, jclass klass, jlong theme, jint part, jint stateFrom, jint stateTo, jint propId) { jlong rv = -1; - if (GetThemeTransitionDuration != NULL) { - DWORD duration = 0; - if (SUCCEEDED(GetThemeTransitionDuration((HTHEME) theme, part, - stateFrom, stateTo, propId, &duration))) { - rv = duration; - } + DWORD duration = 0; + if (SUCCEEDED(GetThemeTransitionDurationFunc((HTHEME) theme, part, + stateFrom, stateTo, propId, &duration))) { + rv = duration; } return rv; } - -/* - * Class: sun_awt_windows_ThemeReader - * Method: isGetThemeTransitionDurationDefined - * Signature: ()Z - */ -JNIEXPORT jboolean JNICALL -Java_sun_awt_windows_ThemeReader_isGetThemeTransitionDurationDefined -(JNIEnv *env, jclass klass) { - return (GetThemeTransitionDuration != NULL) ? JNI_TRUE : JNI_FALSE; -} From c5462bb95d5d711f2a7ce8ceaa7ffba82b096a0b Mon Sep 17 00:00:00 2001 From: Pavel Rappo Date: Sat, 7 Nov 2020 12:11:43 +0000 Subject: [PATCH 018/124] 8255989: Remove explicitly unascribed authorship from Java source files Reviewed-by: redestad, mr, mchung, iris, serb --- src/java.base/share/classes/java/io/DataOutputStream.java | 3 +-- src/java.base/share/classes/java/io/Externalizable.java | 3 +-- src/java.base/share/classes/java/io/File.java | 1 - .../share/classes/java/io/FileNotFoundException.java | 1 - src/java.base/share/classes/java/io/IOException.java | 3 +-- .../share/classes/java/io/InterruptedIOException.java | 1 - .../share/classes/java/io/InvalidClassException.java | 3 +-- .../share/classes/java/io/InvalidObjectException.java | 3 +-- src/java.base/share/classes/java/io/NotActiveException.java | 3 +-- .../share/classes/java/io/NotSerializableException.java | 3 +-- src/java.base/share/classes/java/io/ObjectInput.java | 3 +-- .../share/classes/java/io/ObjectInputValidation.java | 3 +-- src/java.base/share/classes/java/io/ObjectOutput.java | 3 +-- .../share/classes/java/io/ObjectStreamConstants.java | 3 +-- .../share/classes/java/io/ObjectStreamException.java | 3 +-- .../share/classes/java/io/OptionalDataException.java | 3 +-- src/java.base/share/classes/java/io/RandomAccessFile.java | 1 - src/java.base/share/classes/java/io/Serializable.java | 3 +-- .../share/classes/java/io/StreamCorruptedException.java | 3 +-- .../share/classes/java/io/WriteAbortedException.java | 3 +-- .../share/classes/java/lang/AbstractMethodError.java | 3 +-- .../share/classes/java/lang/ArithmeticException.java | 3 +-- .../share/classes/java/lang/ArrayStoreException.java | 3 +-- src/java.base/share/classes/java/lang/Class.java | 1 - src/java.base/share/classes/java/lang/ClassCastException.java | 3 +-- .../share/classes/java/lang/ClassCircularityError.java | 3 +-- src/java.base/share/classes/java/lang/ClassFormatError.java | 3 +-- .../share/classes/java/lang/ClassNotFoundException.java | 1 - .../share/classes/java/lang/CloneNotSupportedException.java | 3 +-- src/java.base/share/classes/java/lang/Cloneable.java | 3 +-- src/java.base/share/classes/java/lang/IllegalAccessError.java | 3 +-- .../share/classes/java/lang/IllegalAccessException.java | 3 +-- .../share/classes/java/lang/IllegalArgumentException.java | 3 +-- .../share/classes/java/lang/IllegalMonitorStateException.java | 3 +-- .../share/classes/java/lang/IllegalThreadStateException.java | 3 +-- .../share/classes/java/lang/IncompatibleClassChangeError.java | 3 +-- src/java.base/share/classes/java/lang/InstantiationError.java | 3 +-- .../share/classes/java/lang/InstantiationException.java | 3 +-- src/java.base/share/classes/java/lang/InternalError.java | 3 +-- src/java.base/share/classes/java/lang/Math.java | 1 - .../share/classes/java/lang/NegativeArraySizeException.java | 3 +-- .../share/classes/java/lang/NoClassDefFoundError.java | 3 +-- src/java.base/share/classes/java/lang/NoSuchFieldError.java | 3 +-- .../share/classes/java/lang/NoSuchFieldException.java | 3 +-- src/java.base/share/classes/java/lang/NoSuchMethodError.java | 3 +-- .../share/classes/java/lang/NoSuchMethodException.java | 3 +-- .../share/classes/java/lang/NullPointerException.java | 3 +-- .../share/classes/java/lang/NumberFormatException.java | 3 +-- src/java.base/share/classes/java/lang/Object.java | 1 - src/java.base/share/classes/java/lang/OutOfMemoryError.java | 3 +-- src/java.base/share/classes/java/lang/Runtime.java | 1 - src/java.base/share/classes/java/lang/SecurityException.java | 3 +-- src/java.base/share/classes/java/lang/StackOverflowError.java | 3 +-- src/java.base/share/classes/java/lang/StrictMath.java | 1 - src/java.base/share/classes/java/lang/Thread.java | 1 - src/java.base/share/classes/java/lang/ThreadGroup.java | 3 +-- src/java.base/share/classes/java/lang/Throwable.java | 1 - src/java.base/share/classes/java/lang/UnknownError.java | 3 +-- .../share/classes/java/lang/UnsatisfiedLinkError.java | 3 +-- src/java.base/share/classes/java/lang/VerifyError.java | 3 +-- src/java.base/share/classes/java/lang/Void.java | 3 +-- .../share/classes/java/lang/module/ModuleDescriptor.java | 2 -- src/java.base/share/classes/java/net/ServerSocket.java | 1 - src/java.base/share/classes/java/net/Socket.java | 1 - src/java.base/share/classes/java/net/SocketImpl.java | 1 - .../share/classes/java/net/UnknownServiceException.java | 3 +-- src/java.base/share/classes/java/util/Dictionary.java | 3 +-- .../share/classes/java/util/InputMismatchException.java | 3 +-- .../share/classes/java/util/NoSuchElementException.java | 3 +-- src/java.base/share/classes/java/util/StringTokenizer.java | 3 +-- .../share/classes/java/util/regex/PatternSyntaxException.java | 3 +-- src/java.base/share/classes/java/util/zip/ZipException.java | 3 +-- .../com/sun/java/swing/plaf/motif/MotifLookAndFeel.java | 1 - .../share/classes/javax/swing/SwingUtilities.java | 3 +-- .../classes/javax/swing/UnsupportedLookAndFeelException.java | 3 +-- .../classes/javax/swing/plaf/basic/BasicLookAndFeel.java | 4 +--- .../com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java | 4 +--- 77 files changed, 60 insertions(+), 140 deletions(-) diff --git a/src/java.base/share/classes/java/io/DataOutputStream.java b/src/java.base/share/classes/java/io/DataOutputStream.java index 4d7d68cfb069a..94ba13a320f1a 100644 --- a/src/java.base/share/classes/java/io/DataOutputStream.java +++ b/src/java.base/share/classes/java/io/DataOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * types to an output stream in a portable way. An application can * then use a data input stream to read the data back in. * - * @author unascribed * @see java.io.DataInputStream * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/Externalizable.java b/src/java.base/share/classes/java/io/Externalizable.java index 09f21fdf64645..84029fc5f3aef 100644 --- a/src/java.base/share/classes/java/io/Externalizable.java +++ b/src/java.base/share/classes/java/io/Externalizable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,7 +55,6 @@ * the writeReplace and readResolve methods documented in the Serializable * interface.
* - * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput diff --git a/src/java.base/share/classes/java/io/File.java b/src/java.base/share/classes/java/io/File.java index 553f4ef00c445..ca49cc9d22876 100644 --- a/src/java.base/share/classes/java/io/File.java +++ b/src/java.base/share/classes/java/io/File.java @@ -142,7 +142,6 @@ * additional file operations, file attributes, and I/O exceptions to help * diagnose errors when an operation on a file fails. * - * @author unascribed * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/FileNotFoundException.java b/src/java.base/share/classes/java/io/FileNotFoundException.java index 4bfdd34aff15f..8d13aa3fdb93c 100644 --- a/src/java.base/share/classes/java/io/FileNotFoundException.java +++ b/src/java.base/share/classes/java/io/FileNotFoundException.java @@ -36,7 +36,6 @@ * constructors if the file does exist but for some reason is inaccessible, for * example when an attempt is made to open a read-only file for writing. * - * @author unascribed * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/IOException.java b/src/java.base/share/classes/java/io/IOException.java index fd19dce4e19fc..7a81bd1e6216e 100644 --- a/src/java.base/share/classes/java/io/IOException.java +++ b/src/java.base/share/classes/java/io/IOException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * class is the general class of exceptions produced by failed or * interrupted I/O operations. * - * @author unascribed * @see java.io.InputStream * @see java.io.OutputStream * @since 1.0 diff --git a/src/java.base/share/classes/java/io/InterruptedIOException.java b/src/java.base/share/classes/java/io/InterruptedIOException.java index 7d3ac73acbc2c..cdd45a7d981c6 100644 --- a/src/java.base/share/classes/java/io/InterruptedIOException.java +++ b/src/java.base/share/classes/java/io/InterruptedIOException.java @@ -33,7 +33,6 @@ * indicates how many bytes were successfully transferred before * the interruption occurred. * - * @author unascribed * @see java.io.InputStream * @see java.io.OutputStream * @see java.lang.Thread#interrupt() diff --git a/src/java.base/share/classes/java/io/InvalidClassException.java b/src/java.base/share/classes/java/io/InvalidClassException.java index 71fd5d18c779b..6de0e70f760fe 100644 --- a/src/java.base/share/classes/java/io/InvalidClassException.java +++ b/src/java.base/share/classes/java/io/InvalidClassException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,6 @@ * Specification * * - * @author unascribed * @since 1.1 */ public class InvalidClassException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/InvalidObjectException.java b/src/java.base/share/classes/java/io/InvalidObjectException.java index 01b7ebbb19ac8..c6e626b238fac 100644 --- a/src/java.base/share/classes/java/io/InvalidObjectException.java +++ b/src/java.base/share/classes/java/io/InvalidObjectException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * @see ObjectInputValidation * @since 1.1 * - * @author unascribed * @since 1.1 */ public class InvalidObjectException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/NotActiveException.java b/src/java.base/share/classes/java/io/NotActiveException.java index 318f9f920e2eb..3b8b281cbf4ea 100644 --- a/src/java.base/share/classes/java/io/NotActiveException.java +++ b/src/java.base/share/classes/java/io/NotActiveException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Thrown when serialization or deserialization is not active. * - * @author unascribed * @since 1.1 */ public class NotActiveException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/NotSerializableException.java b/src/java.base/share/classes/java/io/NotSerializableException.java index 93c48da69c1cf..d1010188272f4 100644 --- a/src/java.base/share/classes/java/io/NotSerializableException.java +++ b/src/java.base/share/classes/java/io/NotSerializableException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * The serialization runtime or the class of the instance can throw * this exception. The argument should be the name of the class. * - * @author unascribed * @since 1.1 */ public class NotSerializableException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/ObjectInput.java b/src/java.base/share/classes/java/io/ObjectInput.java index ac1a9b5c54249..c4098a2f458e5 100644 --- a/src/java.base/share/classes/java/io/ObjectInput.java +++ b/src/java.base/share/classes/java/io/ObjectInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * objects. DataInput includes methods for the input of primitive types, * ObjectInput extends that interface to include objects, arrays, and Strings. * - * @author unascribed * @see java.io.InputStream * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream diff --git a/src/java.base/share/classes/java/io/ObjectInputValidation.java b/src/java.base/share/classes/java/io/ObjectInputValidation.java index de1d8a5382568..e22bf884a27bf 100644 --- a/src/java.base/share/classes/java/io/ObjectInputValidation.java +++ b/src/java.base/share/classes/java/io/ObjectInputValidation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * Allows an object to be called when a complete graph of objects has * been deserialized. * - * @author unascribed * @see ObjectInputStream * @see ObjectInputStream#registerValidation(java.io.ObjectInputValidation, int) * @since 1.1 diff --git a/src/java.base/share/classes/java/io/ObjectOutput.java b/src/java.base/share/classes/java/io/ObjectOutput.java index b8a8d5c095ad3..8a825ade99d7f 100644 --- a/src/java.base/share/classes/java/io/ObjectOutput.java +++ b/src/java.base/share/classes/java/io/ObjectOutput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * DataOutput includes methods for output of primitive types, ObjectOutput * extends that interface to include objects, arrays, and Strings. * - * @author unascribed * @see java.io.InputStream * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream diff --git a/src/java.base/share/classes/java/io/ObjectStreamConstants.java b/src/java.base/share/classes/java/io/ObjectStreamConstants.java index 4f7bdf4586ed3..93c2587034a6c 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamConstants.java +++ b/src/java.base/share/classes/java/io/ObjectStreamConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Constants written into the Object Serialization Stream. * - * @author unascribed * @since 1.1 */ public interface ObjectStreamConstants { diff --git a/src/java.base/share/classes/java/io/ObjectStreamException.java b/src/java.base/share/classes/java/io/ObjectStreamException.java index 1e4fb7a46aacd..779cc0627d331 100644 --- a/src/java.base/share/classes/java/io/ObjectStreamException.java +++ b/src/java.base/share/classes/java/io/ObjectStreamException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Superclass of all exceptions specific to Object Stream classes. * - * @author unascribed * @since 1.1 */ public abstract class ObjectStreamException extends IOException { diff --git a/src/java.base/share/classes/java/io/OptionalDataException.java b/src/java.base/share/classes/java/io/OptionalDataException.java index cf1d074a4f427..f98bf079e8e86 100644 --- a/src/java.base/share/classes/java/io/OptionalDataException.java +++ b/src/java.base/share/classes/java/io/OptionalDataException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,6 @@ * is set to 0. * * - * @author unascribed * @since 1.1 */ public class OptionalDataException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java index d72011f0159ed..5f6bcfaa6ac32 100644 --- a/src/java.base/share/classes/java/io/RandomAccessFile.java +++ b/src/java.base/share/classes/java/io/RandomAccessFile.java @@ -55,7 +55,6 @@ * than {@code EOFException} is thrown. In particular, an * {@code IOException} may be thrown if the stream has been closed. * - * @author unascribed * @since 1.0 */ diff --git a/src/java.base/share/classes/java/io/Serializable.java b/src/java.base/share/classes/java/io/Serializable.java index 0b92cec448a96..4686d062108bc 100644 --- a/src/java.base/share/classes/java/io/Serializable.java +++ b/src/java.base/share/classes/java/io/Serializable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -174,7 +174,6 @@ * the default computed value, but the requirement for matching * serialVersionUID values is waived for array classes. * - * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput diff --git a/src/java.base/share/classes/java/io/StreamCorruptedException.java b/src/java.base/share/classes/java/io/StreamCorruptedException.java index 1aa31c1650e44..5124c5ce65700 100644 --- a/src/java.base/share/classes/java/io/StreamCorruptedException.java +++ b/src/java.base/share/classes/java/io/StreamCorruptedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown when control information that was read from an object stream * violates internal consistency checks. * - * @author unascribed * @since 1.1 */ public class StreamCorruptedException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/io/WriteAbortedException.java b/src/java.base/share/classes/java/io/WriteAbortedException.java index a2c8645185064..5b82d0999f16f 100644 --- a/src/java.base/share/classes/java/io/WriteAbortedException.java +++ b/src/java.base/share/classes/java/io/WriteAbortedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ * cause, and may be accessed via the {@link Throwable#getCause()} * method, as well as the aforementioned "legacy field." * - * @author unascribed * @since 1.1 */ public class WriteAbortedException extends ObjectStreamException { diff --git a/src/java.base/share/classes/java/lang/AbstractMethodError.java b/src/java.base/share/classes/java/lang/AbstractMethodError.java index 5c128313c1522..a37fa418eae51 100644 --- a/src/java.base/share/classes/java/lang/AbstractMethodError.java +++ b/src/java.base/share/classes/java/lang/AbstractMethodError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * incompatibly changed since the currently executing method was last * compiled. * - * @author unascribed * @since 1.0 */ public class AbstractMethodError extends IncompatibleClassChangeError { diff --git a/src/java.base/share/classes/java/lang/ArithmeticException.java b/src/java.base/share/classes/java/lang/ArithmeticException.java index aa3efbd5053f7..da74380ad598b 100644 --- a/src/java.base/share/classes/java/lang/ArithmeticException.java +++ b/src/java.base/share/classes/java/lang/ArithmeticException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ * Throwable, boolean, boolean) suppression were disabled and/or the * stack trace was not writable}. * - * @author unascribed * @since 1.0 */ public class ArithmeticException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/ArrayStoreException.java b/src/java.base/share/classes/java/lang/ArrayStoreException.java index 2fb68bf63861f..118c59706ab14 100644 --- a/src/java.base/share/classes/java/lang/ArrayStoreException.java +++ b/src/java.base/share/classes/java/lang/ArrayStoreException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ * x[0] = new Integer(0); * * - * @author unascribed * @since 1.0 */ public class ArrayStoreException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index b0a687a56a572..a0ceb2e9137af 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -186,7 +186,6 @@ * Class}. Use {@code Class} if the class being modeled is * unknown. * - * @author unascribed * @see java.lang.ClassLoader#defineClass(byte[], int, int) * @since 1.0 * @jls 15.8.2 Class Literals diff --git a/src/java.base/share/classes/java/lang/ClassCastException.java b/src/java.base/share/classes/java/lang/ClassCastException.java index 73b00f44741f8..a436f54695350 100644 --- a/src/java.base/share/classes/java/lang/ClassCastException.java +++ b/src/java.base/share/classes/java/lang/ClassCastException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ * System.out.println((String)x); * * - * @author unascribed * @since 1.0 */ public class ClassCastException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/ClassCircularityError.java b/src/java.base/share/classes/java/lang/ClassCircularityError.java index afd38cd96fa67..52f03f6d792e8 100644 --- a/src/java.base/share/classes/java/lang/ClassCircularityError.java +++ b/src/java.base/share/classes/java/lang/ClassCircularityError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown when the Java Virtual Machine detects a circularity in the * superclass hierarchy of a class being loaded. * - * @author unascribed * @since 1.0 */ public class ClassCircularityError extends LinkageError { diff --git a/src/java.base/share/classes/java/lang/ClassFormatError.java b/src/java.base/share/classes/java/lang/ClassFormatError.java index 61145956042b7..2fdc8d56e5b99 100644 --- a/src/java.base/share/classes/java/lang/ClassFormatError.java +++ b/src/java.base/share/classes/java/lang/ClassFormatError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * file and determines that the file is malformed or otherwise cannot * be interpreted as a class file. * - * @author unascribed * @since 1.0 */ public class ClassFormatError extends LinkageError { diff --git a/src/java.base/share/classes/java/lang/ClassNotFoundException.java b/src/java.base/share/classes/java/lang/ClassNotFoundException.java index 2647c947d7d55..2a26786a2973d 100644 --- a/src/java.base/share/classes/java/lang/ClassNotFoundException.java +++ b/src/java.base/share/classes/java/lang/ClassNotFoundException.java @@ -49,7 +49,6 @@ * now known as the cause, and may be accessed via the {@link * Throwable#getCause()} method, as well as the aforementioned "legacy method." * - * @author unascribed * @see java.lang.Class#forName(java.lang.String) * @see java.lang.ClassLoader#findSystemClass(java.lang.String) * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean) diff --git a/src/java.base/share/classes/java/lang/CloneNotSupportedException.java b/src/java.base/share/classes/java/lang/CloneNotSupportedException.java index 58d9731fecad9..a3fea187bd652 100644 --- a/src/java.base/share/classes/java/lang/CloneNotSupportedException.java +++ b/src/java.base/share/classes/java/lang/CloneNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ * throw this exception to indicate that an object could not or * should not be cloned. * - * @author unascribed * @see java.lang.Cloneable * @see java.lang.Object#clone() * @since 1.0 diff --git a/src/java.base/share/classes/java/lang/Cloneable.java b/src/java.base/share/classes/java/lang/Cloneable.java index 7f65223ce7a9a..1dfb8176a469e 100644 --- a/src/java.base/share/classes/java/lang/Cloneable.java +++ b/src/java.base/share/classes/java/lang/Cloneable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -45,7 +45,6 @@ * fact that it implements this interface. Even if the clone method is invoked * reflectively, there is no guarantee that it will succeed. * - * @author unascribed * @see java.lang.CloneNotSupportedException * @see java.lang.Object#clone() * @since 1.0 diff --git a/src/java.base/share/classes/java/lang/IllegalAccessError.java b/src/java.base/share/classes/java/lang/IllegalAccessError.java index 64b8cd4091642..8773c1d4e3542 100644 --- a/src/java.base/share/classes/java/lang/IllegalAccessError.java +++ b/src/java.base/share/classes/java/lang/IllegalAccessError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ * only occur at run time if the definition of a class has * incompatibly changed. * - * @author unascribed * @since 1.0 */ public class IllegalAccessError extends IncompatibleClassChangeError { diff --git a/src/java.base/share/classes/java/lang/IllegalAccessException.java b/src/java.base/share/classes/java/lang/IllegalAccessException.java index 64b3822bcb37d..e2a3a1b2b807a 100644 --- a/src/java.base/share/classes/java/lang/IllegalAccessException.java +++ b/src/java.base/share/classes/java/lang/IllegalAccessException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,6 @@ * executing method does not have access to the definition of * the specified class, field, method or constructor. * - * @author unascribed * @see Class#newInstance() * @see java.lang.reflect.Field#set(Object, Object) * @see java.lang.reflect.Field#setBoolean(Object, boolean) diff --git a/src/java.base/share/classes/java/lang/IllegalArgumentException.java b/src/java.base/share/classes/java/lang/IllegalArgumentException.java index a6e1dd4302229..10794ee06de4b 100644 --- a/src/java.base/share/classes/java/lang/IllegalArgumentException.java +++ b/src/java.base/share/classes/java/lang/IllegalArgumentException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown to indicate that a method has been passed an illegal or * inappropriate argument. * - * @author unascribed * @since 1.0 */ public class IllegalArgumentException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/IllegalMonitorStateException.java b/src/java.base/share/classes/java/lang/IllegalMonitorStateException.java index 12da87d51511e..7ff87a8b0aad5 100644 --- a/src/java.base/share/classes/java/lang/IllegalMonitorStateException.java +++ b/src/java.base/share/classes/java/lang/IllegalMonitorStateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * object's monitor or to notify other threads waiting on an object's * monitor without owning the specified monitor. * - * @author unascribed * @see java.lang.Object#notify() * @see java.lang.Object#notifyAll() * @see java.lang.Object#wait() diff --git a/src/java.base/share/classes/java/lang/IllegalThreadStateException.java b/src/java.base/share/classes/java/lang/IllegalThreadStateException.java index 1937a9f8d45b1..0fae6ef4fb5a1 100644 --- a/src/java.base/share/classes/java/lang/IllegalThreadStateException.java +++ b/src/java.base/share/classes/java/lang/IllegalThreadStateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,6 @@ * {@code suspend} and {@code resume} methods in class * {@code Thread}. * - * @author unascribed * @see java.lang.Thread#resume() * @see java.lang.Thread#suspend() * @since 1.0 diff --git a/src/java.base/share/classes/java/lang/IncompatibleClassChangeError.java b/src/java.base/share/classes/java/lang/IncompatibleClassChangeError.java index fc76a89151bf0..6468a1a6f247e 100644 --- a/src/java.base/share/classes/java/lang/IncompatibleClassChangeError.java +++ b/src/java.base/share/classes/java/lang/IncompatibleClassChangeError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * definition. The definition of some class, on which the currently * executing method depends, has since changed. * - * @author unascribed * @since 1.0 */ public class IncompatibleClassChangeError extends LinkageError { diff --git a/src/java.base/share/classes/java/lang/InstantiationError.java b/src/java.base/share/classes/java/lang/InstantiationError.java index 565447001da18..0c06ef409df33 100644 --- a/src/java.base/share/classes/java/lang/InstantiationError.java +++ b/src/java.base/share/classes/java/lang/InstantiationError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ * only occur at run time if the definition of a class has * incompatibly changed. * - * @author unascribed * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/InstantiationException.java b/src/java.base/share/classes/java/lang/InstantiationException.java index 9c22bd58b09d7..ed3af1bf30acb 100644 --- a/src/java.base/share/classes/java/lang/InstantiationException.java +++ b/src/java.base/share/classes/java/lang/InstantiationException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ *
  • the class has no nullary constructor * * - * @author unascribed * @see java.lang.Class#newInstance() * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/InternalError.java b/src/java.base/share/classes/java/lang/InternalError.java index f701ed19b0539..eda8344523eed 100644 --- a/src/java.base/share/classes/java/lang/InternalError.java +++ b/src/java.base/share/classes/java/lang/InternalError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown to indicate some unexpected internal error has occurred in * the Java Virtual Machine. * - * @author unascribed * @since 1.0 */ public class InternalError extends VirtualMachineError { diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java index 03834741d12cc..ff7ceff6cf208 100644 --- a/src/java.base/share/classes/java/lang/Math.java +++ b/src/java.base/share/classes/java/lang/Math.java @@ -99,7 +99,6 @@ * occurs only with a specific minimum or maximum value and * should be checked against the minimum or maximum as appropriate. * - * @author unascribed * @author Joseph D. Darcy * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/NegativeArraySizeException.java b/src/java.base/share/classes/java/lang/NegativeArraySizeException.java index 73d7740dfd8a2..a0f5eba0d8097 100644 --- a/src/java.base/share/classes/java/lang/NegativeArraySizeException.java +++ b/src/java.base/share/classes/java/lang/NegativeArraySizeException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Thrown if an application tries to create an array with negative size. * - * @author unascribed * @since 1.0 */ public class NegativeArraySizeException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/NoClassDefFoundError.java b/src/java.base/share/classes/java/lang/NoClassDefFoundError.java index 334b03804d979..52387242c8292 100644 --- a/src/java.base/share/classes/java/lang/NoClassDefFoundError.java +++ b/src/java.base/share/classes/java/lang/NoClassDefFoundError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ * executing class was compiled, but the definition can no longer be * found. * - * @author unascribed * @since 1.0 */ public class NoClassDefFoundError extends LinkageError { diff --git a/src/java.base/share/classes/java/lang/NoSuchFieldError.java b/src/java.base/share/classes/java/lang/NoSuchFieldError.java index af31a3e2974d0..2b3a0b25a5c58 100644 --- a/src/java.base/share/classes/java/lang/NoSuchFieldError.java +++ b/src/java.base/share/classes/java/lang/NoSuchFieldError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ * only occur at run time if the definition of a class has * incompatibly changed. * - * @author unascribed * @since 1.0 */ public class NoSuchFieldError extends IncompatibleClassChangeError { diff --git a/src/java.base/share/classes/java/lang/NoSuchFieldException.java b/src/java.base/share/classes/java/lang/NoSuchFieldException.java index 759f6c7ab9c35..d25ba977c8276 100644 --- a/src/java.base/share/classes/java/lang/NoSuchFieldException.java +++ b/src/java.base/share/classes/java/lang/NoSuchFieldException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Signals that the class doesn't have a field of a specified name. * - * @author unascribed * @since 1.1 */ public class NoSuchFieldException extends ReflectiveOperationException { diff --git a/src/java.base/share/classes/java/lang/NoSuchMethodError.java b/src/java.base/share/classes/java/lang/NoSuchMethodError.java index 1dc7a9ac7f69e..9ef207e60e47a 100644 --- a/src/java.base/share/classes/java/lang/NoSuchMethodError.java +++ b/src/java.base/share/classes/java/lang/NoSuchMethodError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,6 @@ * only occur at run time if the definition of a class has * incompatibly changed. * - * @author unascribed * @since 1.0 */ public class NoSuchMethodError extends IncompatibleClassChangeError { diff --git a/src/java.base/share/classes/java/lang/NoSuchMethodException.java b/src/java.base/share/classes/java/lang/NoSuchMethodException.java index abcd687dbbd90..3bd0193d186e4 100644 --- a/src/java.base/share/classes/java/lang/NoSuchMethodException.java +++ b/src/java.base/share/classes/java/lang/NoSuchMethodException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,6 @@ /** * Thrown when a particular method cannot be found. * - * @author unascribed * @since 1.0 */ public class NoSuchMethodException extends ReflectiveOperationException { diff --git a/src/java.base/share/classes/java/lang/NullPointerException.java b/src/java.base/share/classes/java/lang/NullPointerException.java index ff1fd66e5ab1c..7258b711b62d9 100644 --- a/src/java.base/share/classes/java/lang/NullPointerException.java +++ b/src/java.base/share/classes/java/lang/NullPointerException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,6 @@ * Throwable, boolean, boolean) suppression were disabled and/or the * stack trace was not writable}. * - * @author unascribed * @since 1.0 */ public class NullPointerException extends RuntimeException { diff --git a/src/java.base/share/classes/java/lang/NumberFormatException.java b/src/java.base/share/classes/java/lang/NumberFormatException.java index a7d023c1a1522..7a317304f6e2c 100644 --- a/src/java.base/share/classes/java/lang/NumberFormatException.java +++ b/src/java.base/share/classes/java/lang/NumberFormatException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * a string to one of the numeric types, but that the string does not * have the appropriate format. * - * @author unascribed * @see java.lang.Integer#parseInt(String) * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/Object.java b/src/java.base/share/classes/java/lang/Object.java index 15d6bb5b11a4b..a155e1e8ba8d4 100644 --- a/src/java.base/share/classes/java/lang/Object.java +++ b/src/java.base/share/classes/java/lang/Object.java @@ -32,7 +32,6 @@ * Every class has {@code Object} as a superclass. All objects, * including arrays, implement the methods of this class. * - * @author unascribed * @see java.lang.Class * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/OutOfMemoryError.java b/src/java.base/share/classes/java/lang/OutOfMemoryError.java index 45cd03facfbd7..94bf8aea19583 100644 --- a/src/java.base/share/classes/java/lang/OutOfMemoryError.java +++ b/src/java.base/share/classes/java/lang/OutOfMemoryError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ * boolean, boolean) suppression were disabled and/or the stack trace was not * writable}. * - * @author unascribed * @since 1.0 */ public class OutOfMemoryError extends VirtualMachineError { diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java index 499624804dd8b..dae8be95ce693 100644 --- a/src/java.base/share/classes/java/lang/Runtime.java +++ b/src/java.base/share/classes/java/lang/Runtime.java @@ -48,7 +48,6 @@ *

    * An application cannot create its own instance of this class. * - * @author unascribed * @see java.lang.Runtime#getRuntime() * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/SecurityException.java b/src/java.base/share/classes/java/lang/SecurityException.java index b704749e815ab..4e4f966f76a36 100644 --- a/src/java.base/share/classes/java/lang/SecurityException.java +++ b/src/java.base/share/classes/java/lang/SecurityException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ /** * Thrown by the security manager to indicate a security violation. * - * @author unascribed * @see java.lang.SecurityManager * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/StackOverflowError.java b/src/java.base/share/classes/java/lang/StackOverflowError.java index 95ec9a30d4887..45a6af302808e 100644 --- a/src/java.base/share/classes/java/lang/StackOverflowError.java +++ b/src/java.base/share/classes/java/lang/StackOverflowError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown when a stack overflow occurs because an application * recurses too deeply. * - * @author unascribed * @since 1.0 */ public class StackOverflowError extends VirtualMachineError { diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java index d511847d9f682..941834381beea 100644 --- a/src/java.base/share/classes/java/lang/StrictMath.java +++ b/src/java.base/share/classes/java/lang/StrictMath.java @@ -74,7 +74,6 @@ * occurs only with a specific minimum or maximum value and * should be checked against the minimum or maximum as appropriate. * - * @author unascribed * @author Joseph D. Darcy * @since 1.3 */ diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java index 7692b4179e058..f50b024f9d201 100644 --- a/src/java.base/share/classes/java/lang/Thread.java +++ b/src/java.base/share/classes/java/lang/Thread.java @@ -133,7 +133,6 @@ * or method in this class will cause a {@link NullPointerException} to be * thrown. * - * @author unascribed * @see Runnable * @see Runtime#exit(int) * @see #run() diff --git a/src/java.base/share/classes/java/lang/ThreadGroup.java b/src/java.base/share/classes/java/lang/ThreadGroup.java index c942a038b8b4e..5252b2c56a5a5 100644 --- a/src/java.base/share/classes/java/lang/ThreadGroup.java +++ b/src/java.base/share/classes/java/lang/ThreadGroup.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,6 @@ * group, but not to access information about its thread group's * parent thread group or any other thread groups. * - * @author unascribed * @since 1.0 */ /* The locking strategy for this code is to try to lock only one level of the diff --git a/src/java.base/share/classes/java/lang/Throwable.java b/src/java.base/share/classes/java/lang/Throwable.java index 7bca70ec96ed9..72948a36485c0 100644 --- a/src/java.base/share/classes/java/lang/Throwable.java +++ b/src/java.base/share/classes/java/lang/Throwable.java @@ -106,7 +106,6 @@ * {@code String} (the detail message) and a {@code Throwable} (the * cause). * - * @author unascribed * @author Josh Bloch (Added exception chaining and programmatic access to * stack trace in 1.4.) * @jls 11.2 Compile-Time Checking of Exceptions diff --git a/src/java.base/share/classes/java/lang/UnknownError.java b/src/java.base/share/classes/java/lang/UnknownError.java index 3d2bbaaac9da9..d6ced29f92178 100644 --- a/src/java.base/share/classes/java/lang/UnknownError.java +++ b/src/java.base/share/classes/java/lang/UnknownError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown when an unknown but serious exception has occurred in the * Java Virtual Machine. * - * @author unascribed * @since 1.0 */ public class UnknownError extends VirtualMachineError { diff --git a/src/java.base/share/classes/java/lang/UnsatisfiedLinkError.java b/src/java.base/share/classes/java/lang/UnsatisfiedLinkError.java index e171bd1ca928b..2a6e85961460f 100644 --- a/src/java.base/share/classes/java/lang/UnsatisfiedLinkError.java +++ b/src/java.base/share/classes/java/lang/UnsatisfiedLinkError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown if the Java Virtual Machine cannot find an appropriate * native-language definition of a method declared {@code native}. * - * @author unascribed * @see java.lang.Runtime * @since 1.0 */ diff --git a/src/java.base/share/classes/java/lang/VerifyError.java b/src/java.base/share/classes/java/lang/VerifyError.java index 1e3be9122fbef..7409fc6665c4b 100644 --- a/src/java.base/share/classes/java/lang/VerifyError.java +++ b/src/java.base/share/classes/java/lang/VerifyError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * though well formed, contains some sort of internal inconsistency * or security problem. * - * @author unascribed * @since 1.0 */ public class VerifyError extends LinkageError { diff --git a/src/java.base/share/classes/java/lang/Void.java b/src/java.base/share/classes/java/lang/Void.java index 581d0d6f4884a..3ea0e79e61c5b 100644 --- a/src/java.base/share/classes/java/lang/Void.java +++ b/src/java.base/share/classes/java/lang/Void.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * reference to the {@code Class} object representing the Java keyword * void. * - * @author unascribed * @since 1.1 */ public final diff --git a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java index 1020248796d87..09da26d705ea3 100644 --- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java +++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java @@ -354,7 +354,6 @@ public String toString() { } } - /** *

    A package exported by a module, may be qualified or unqualified.

    @@ -892,7 +891,6 @@ public String toString() { } - /** * A module's version string. diff --git a/src/java.base/share/classes/java/net/ServerSocket.java b/src/java.base/share/classes/java/net/ServerSocket.java index 0397d860aa9a2..249c09b967415 100644 --- a/src/java.base/share/classes/java/net/ServerSocket.java +++ b/src/java.base/share/classes/java/net/ServerSocket.java @@ -75,7 +75,6 @@ * * Additional (implementation specific) options may also be supported. * - * @author unascribed * @see java.net.SocketImpl * @see java.net.ServerSocket#setSocketFactory(java.net.SocketImplFactory) * @see java.nio.channels.ServerSocketChannel diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 43b2a31df600e..3357dbfe77738 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -96,7 +96,6 @@ * * Additional (implementation specific) options may also be supported. * - * @author unascribed * @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory) * @see java.net.SocketImpl * @see java.nio.channels.SocketChannel diff --git a/src/java.base/share/classes/java/net/SocketImpl.java b/src/java.base/share/classes/java/net/SocketImpl.java index 4c3e9c08970aa..a747cfff014a9 100644 --- a/src/java.base/share/classes/java/net/SocketImpl.java +++ b/src/java.base/share/classes/java/net/SocketImpl.java @@ -60,7 +60,6 @@ * to use the old implementation. The property and old implementation will be * removed in a future version. * - * @author unascribed * @since 1.0 */ public abstract class SocketImpl implements SocketOptions { diff --git a/src/java.base/share/classes/java/net/UnknownServiceException.java b/src/java.base/share/classes/java/net/UnknownServiceException.java index fb48dd8bd6001..3664e19f8e028 100644 --- a/src/java.base/share/classes/java/net/UnknownServiceException.java +++ b/src/java.base/share/classes/java/net/UnknownServiceException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ * not make sense, or the application is attempting to write to a * read-only URL connection. * - * @author unascribed * @since 1.0 */ public class UnknownServiceException extends IOException { diff --git a/src/java.base/share/classes/java/util/Dictionary.java b/src/java.base/share/classes/java/util/Dictionary.java index 162de2b5d6202..653894765ebe7 100644 --- a/src/java.base/share/classes/java/util/Dictionary.java +++ b/src/java.base/share/classes/java/util/Dictionary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,6 @@ * NOTE: This class is obsolete. New implementations should * implement the Map interface, rather than extending this class. * - * @author unascribed * @see java.util.Map * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.Object#hashCode() diff --git a/src/java.base/share/classes/java/util/InputMismatchException.java b/src/java.base/share/classes/java/util/InputMismatchException.java index 3367a8a6fb5ae..522a5532cd407 100644 --- a/src/java.base/share/classes/java/util/InputMismatchException.java +++ b/src/java.base/share/classes/java/util/InputMismatchException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ * retrieved does not match the pattern for the expected type, or * that the token is out of range for the expected type. * - * @author unascribed * @see java.util.Scanner * @since 1.5 */ diff --git a/src/java.base/share/classes/java/util/NoSuchElementException.java b/src/java.base/share/classes/java/util/NoSuchElementException.java index 243ecb4ca02e7..28c20367958ef 100644 --- a/src/java.base/share/classes/java/util/NoSuchElementException.java +++ b/src/java.base/share/classes/java/util/NoSuchElementException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Thrown by various accessor methods to indicate that the element being requested * does not exist. * - * @author unascribed * @see java.util.Enumeration#nextElement() * @see java.util.Iterator#next() * @since 1.0 diff --git a/src/java.base/share/classes/java/util/StringTokenizer.java b/src/java.base/share/classes/java/util/StringTokenizer.java index 8e8a09b9828ea..f30f7b863a46b 100644 --- a/src/java.base/share/classes/java/util/StringTokenizer.java +++ b/src/java.base/share/classes/java/util/StringTokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,6 @@ * test * * - * @author unascribed * @see java.io.StreamTokenizer * @since 1.0 */ diff --git a/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java b/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java index 4b60fbb9ec0f2..f58034aad3c60 100644 --- a/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java +++ b/src/java.base/share/classes/java/util/regex/PatternSyntaxException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,6 @@ * Unchecked exception thrown to indicate a syntax error in a * regular-expression pattern. * - * @author unascribed * @since 1.4 */ diff --git a/src/java.base/share/classes/java/util/zip/ZipException.java b/src/java.base/share/classes/java/util/zip/ZipException.java index f6dcc59cfe6dd..45048a4254ba7 100644 --- a/src/java.base/share/classes/java/util/zip/ZipException.java +++ b/src/java.base/share/classes/java/util/zip/ZipException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,6 @@ /** * Signals that a Zip exception of some sort has occurred. * - * @author unascribed * @see java.io.IOException * @since 1.1 */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index 34f4bd700d2e1..455c521493da9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java @@ -59,7 +59,6 @@ * version of Swing. A future release of Swing will provide support for * long term persistence. * - * @author unattributed * @deprecated The Motif Look and Feel is deprecated with the intent to remove * it in some future release. It is recommended to use * {@link javax.swing.plaf.metal.MetalLookAndFeel} instead. diff --git a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java index 9dcb5d5ece8eb..db8b71e3b2cf3 100644 --- a/src/java.desktop/share/classes/javax/swing/SwingUtilities.java +++ b/src/java.desktop/share/classes/javax/swing/SwingUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,7 +50,6 @@ /** * A collection of utility methods for Swing. * - * @author unknown * @since 1.2 */ public class SwingUtilities implements SwingConstants diff --git a/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java b/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java index a82c97d994077..02910664ea58d 100644 --- a/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java +++ b/src/java.desktop/share/classes/javax/swing/UnsupportedLookAndFeelException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. * - * @author unattributed * @since 1.2 */ @SuppressWarnings("serial") // Same-version serialization only diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java index 8c709c7207005..d893f62bbd121 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -100,8 +100,6 @@ * of all JavaBeans * has been added to the java.beans package. * Please see {@link java.beans.XMLEncoder}. - * - * @author unattributed */ @SuppressWarnings("serial") // Same-version serialization only public abstract class BasicLookAndFeel extends LookAndFeel implements Serializable diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index d3ab14f8d006e..554da44b67087 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,8 +90,6 @@ * for short term storage or RMI between applications running the same * version of Swing. A future release of Swing will provide support for * long term persistence. - * - * @author unattributed */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class WindowsLookAndFeel extends BasicLookAndFeel From 6a183fbbc32ef542a8ccbfbb5b5799e92eee2eef Mon Sep 17 00:00:00 2001 From: Xin Liu Date: Sun, 8 Nov 2020 15:03:57 +0000 Subject: [PATCH 019/124] 8255562: delete UseRDPCForConstantTableBase Reviewed-by: simonis --- src/hotspot/share/opto/c2_globals.hpp | 3 --- src/hotspot/share/opto/machnode.hpp | 1 - src/hotspot/share/runtime/arguments.cpp | 1 + 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index c70fda70c3da4..0bf650c2eb825 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -360,9 +360,6 @@ "Limit of ops to make speculative when using CMOVE") \ range(0, max_jint) \ \ - product(bool, UseRDPCForConstantTableBase, false, \ - "Use Sparc RDPC instruction for the constant table base.") \ - \ notproduct(bool, PrintIdealGraph, false, \ "Print ideal graph to XML file / network interface. " \ "By default attempts to connect to the visualizer on a socket.") \ diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index a84e895c3938b..badb35e5ce5dd 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -434,7 +434,6 @@ class MachConstantBaseNode : public MachIdealNode { virtual void emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const; virtual uint size(PhaseRegAlloc* ra_) const; - virtual bool pinned() const { return UseRDPCForConstantTableBase; } static const RegMask& static_out_RegMask() { return _out_RegMask; } virtual const RegMask& out_RegMask() const { return static_out_RegMask(); } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index c812d5a3ea14f..3ba33cc027ade 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -555,6 +555,7 @@ static SpecialFlag const special_jvm_flags[] = { { "ForceNUMA", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "InsertMemBarAfterArraycopy", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "Debugging", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, + { "UseRDPCForConstantTableBase", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, #ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS // These entries will generate build errors. Their purpose is to test the macros. From ed7526a66c67e15cb09da733d3dde50ddabc24ba Mon Sep 17 00:00:00 2001 From: Phil Race Date: Sun, 8 Nov 2020 17:06:12 +0000 Subject: [PATCH 020/124] 8247872: Upgrade HarfBuzz to the latest 2.7.2 Reviewed-by: serb --- .../java.desktop/lib/Awt2dLibraries.gmk | 3 +- src/java.desktop/share/legal/harfbuzz.md | 10 +- .../native/libharfbuzz/hb-aat-fdsc-table.hh | 126 - .../libharfbuzz/hb-aat-layout-ankr-table.hh | 5 +- .../libharfbuzz/hb-aat-layout-bsln-table.hh | 6 +- .../libharfbuzz/hb-aat-layout-common.hh | 65 +- .../libharfbuzz/hb-aat-layout-feat-table.hh | 53 +- .../libharfbuzz/hb-aat-layout-just-table.hh | 30 +- .../libharfbuzz/hb-aat-layout-kerx-table.hh | 126 +- .../libharfbuzz/hb-aat-layout-lcar-table.hh | 93 - .../libharfbuzz/hb-aat-layout-morx-table.hh | 106 +- .../libharfbuzz/hb-aat-layout-opbd-table.hh | 173 + .../libharfbuzz/hb-aat-layout-trak-table.hh | 25 +- .../share/native/libharfbuzz/hb-aat-layout.cc | 90 +- .../share/native/libharfbuzz/hb-aat-layout.h | 4 +- .../share/native/libharfbuzz/hb-aat-layout.hh | 16 +- .../native/libharfbuzz/hb-aat-ltag-table.hh | 2 +- .../share/native/libharfbuzz/hb-aat-map.cc | 40 +- .../share/native/libharfbuzz/hb-aat-map.hh | 17 +- .../share/native/libharfbuzz/hb-algs.hh | 1127 +++ .../share/native/libharfbuzz/hb-array.hh | 257 +- .../share/native/libharfbuzz/hb-atomic.hh | 29 +- .../share/native/libharfbuzz/hb-bimap.hh | 166 + .../share/native/libharfbuzz/hb-blob.cc | 87 +- .../share/native/libharfbuzz/hb-blob.h | 6 +- .../share/native/libharfbuzz/hb-blob.hh | 10 +- .../native/libharfbuzz/hb-buffer-serialize.cc | 84 +- .../share/native/libharfbuzz/hb-buffer.cc | 80 +- .../share/native/libharfbuzz/hb-buffer.h | 7 +- .../share/native/libharfbuzz/hb-buffer.hh | 28 +- .../libharfbuzz/hb-cff-interp-common.hh | 143 +- .../libharfbuzz/hb-cff-interp-cs-common.hh | 76 +- .../libharfbuzz/hb-cff-interp-dict-common.hh | 151 +- .../native/libharfbuzz/hb-cff1-interp-cs.hh | 4 +- .../native/libharfbuzz/hb-cff2-interp-cs.hh | 19 +- .../share/native/libharfbuzz/hb-common.cc | 323 +- .../share/native/libharfbuzz/hb-common.h | 73 +- .../share/native/libharfbuzz/hb-config.hh | 163 + .../share/native/libharfbuzz/hb-coretext.cc | 188 +- .../share/native/libharfbuzz/hb-coretext.h | 32 + .../share/native/libharfbuzz/hb-debug.hh | 68 +- .../share/native/libharfbuzz/hb-deprecated.h | 33 +- .../share/native/libharfbuzz/hb-dispatch.hh | 61 + .../share/native/libharfbuzz/hb-draw.cc | 261 + .../share/native/libharfbuzz/hb-draw.h | 98 + .../share/native/libharfbuzz/hb-draw.hh | 139 + .../share/native/libharfbuzz/hb-dsalgs.hh | 632 -- .../share/native/libharfbuzz/hb-face.cc | 22 +- .../share/native/libharfbuzz/hb-face.hh | 2 +- .../native/libharfbuzz/hb-fallback-shape.cc | 3 + .../share/native/libharfbuzz/hb-font.cc | 214 +- .../share/native/libharfbuzz/hb-font.h | 56 + .../share/native/libharfbuzz/hb-font.hh | 55 +- .../share/native/libharfbuzz/hb-ft.cc | 256 +- .../share/native/libharfbuzz/hb-ft.h | 6 + .../share/native/libharfbuzz/hb-iter.hh | 910 ++- .../share/native/libharfbuzz/hb-kern.hh | 3 +- .../share/native/libharfbuzz/hb-machinery.hh | 638 +- .../share/native/libharfbuzz/hb-map.cc | 2 +- .../share/native/libharfbuzz/hb-map.hh | 190 +- .../share/native/libharfbuzz/hb-meta.hh | 410 + .../share/native/libharfbuzz/hb-mutex.hh | 52 +- .../share/native/libharfbuzz/hb-null.hh | 98 +- .../native/libharfbuzz/hb-number-parser.hh | 237 + .../share/native/libharfbuzz/hb-number.cc | 80 + .../share/native/libharfbuzz/hb-number.hh | 41 + .../share/native/libharfbuzz/hb-object.hh | 4 +- .../share/native/libharfbuzz/hb-open-file.hh | 54 +- .../share/native/libharfbuzz/hb-open-type.hh | 568 +- .../native/libharfbuzz/hb-ot-cff-common.hh | 523 +- .../native/libharfbuzz/hb-ot-cff1-std-str.hh | 425 ++ .../native/libharfbuzz/hb-ot-cff1-table.cc | 281 +- .../native/libharfbuzz/hb-ot-cff1-table.hh | 698 +- .../native/libharfbuzz/hb-ot-cff2-table.cc | 121 +- .../native/libharfbuzz/hb-ot-cff2-table.hh | 159 +- .../native/libharfbuzz/hb-ot-cmap-table.hh | 1239 ++- .../libharfbuzz/hb-ot-color-cbdt-table.hh | 674 +- .../libharfbuzz/hb-ot-color-colr-table.hh | 162 +- .../libharfbuzz/hb-ot-color-cpal-table.hh | 21 +- .../libharfbuzz/hb-ot-color-sbix-table.hh | 152 +- .../libharfbuzz/hb-ot-color-svg-table.hh | 6 +- .../share/native/libharfbuzz/hb-ot-color.cc | 156 +- .../share/native/libharfbuzz/hb-ot-color.h | 8 +- .../native/libharfbuzz/hb-ot-deprecated.h | 4 + .../libharfbuzz/hb-ot-face-table-list.hh | 138 + .../share/native/libharfbuzz/hb-ot-face.cc | 9 +- .../share/native/libharfbuzz/hb-ot-face.hh | 52 +- .../share/native/libharfbuzz/hb-ot-font.cc | 99 +- .../native/libharfbuzz/hb-ot-glyf-table.hh | 1277 +++- .../native/libharfbuzz/hb-ot-hdmx-table.hh | 163 +- .../native/libharfbuzz/hb-ot-head-table.hh | 12 + .../native/libharfbuzz/hb-ot-hhea-table.hh | 63 +- .../native/libharfbuzz/hb-ot-hmtx-table.hh | 286 +- .../native/libharfbuzz/hb-ot-kern-table.hh | 85 +- .../libharfbuzz/hb-ot-layout-base-table.hh | 146 +- .../native/libharfbuzz/hb-ot-layout-common.hh | 1598 +++- .../libharfbuzz/hb-ot-layout-gdef-table.hh | 283 +- .../libharfbuzz/hb-ot-layout-gpos-table.hh | 1279 +++- .../libharfbuzz/hb-ot-layout-gsub-table.hh | 878 ++- .../libharfbuzz/hb-ot-layout-gsubgpos.hh | 1353 +++- .../libharfbuzz/hb-ot-layout-jstf-table.hh | 2 +- .../share/native/libharfbuzz/hb-ot-layout.cc | 828 +- .../share/native/libharfbuzz/hb-ot-layout.h | 78 +- .../share/native/libharfbuzz/hb-ot-layout.hh | 35 +- .../share/native/libharfbuzz/hb-ot-map.cc | 42 +- .../share/native/libharfbuzz/hb-ot-map.hh | 18 +- .../native/libharfbuzz/hb-ot-math-table.hh | 180 +- .../share/native/libharfbuzz/hb-ot-math.cc | 156 +- .../share/native/libharfbuzz/hb-ot-math.h | 23 +- .../native/libharfbuzz/hb-ot-maxp-table.hh | 59 +- .../native/libharfbuzz/hb-ot-meta-table.hh | 127 + .../share/native/libharfbuzz/hb-ot-meta.cc | 77 + .../share/native/libharfbuzz/hb-ot-meta.h | 71 + .../share/native/libharfbuzz/hb-ot-metrics.cc | 231 + .../share/native/libharfbuzz/hb-ot-metrics.h | 122 + .../{hb-subset-glyf.hh => hb-ot-metrics.hh} | 19 +- ...guage.cc => hb-ot-name-language-static.hh} | 23 +- .../native/libharfbuzz/hb-ot-name-table.hh | 149 +- .../share/native/libharfbuzz/hb-ot-name.cc | 10 +- .../native/libharfbuzz/hb-ot-os2-table.hh | 125 +- .../libharfbuzz/hb-ot-os2-unicode-ranges.hh | 24 +- .../native/libharfbuzz/hb-ot-post-table.hh | 55 +- .../hb-ot-shape-complex-arabic-fallback.hh | 45 +- ...hb-ot-shape-complex-arabic-joining-list.hh | 46 + .../hb-ot-shape-complex-arabic-table.hh | 60 +- .../libharfbuzz/hb-ot-shape-complex-arabic.cc | 26 +- .../hb-ot-shape-complex-default.cc | 27 + .../libharfbuzz/hb-ot-shape-complex-hangul.cc | 10 +- .../libharfbuzz/hb-ot-shape-complex-hebrew.cc | 11 + .../hb-ot-shape-complex-indic-machine.hh | 1187 +-- .../hb-ot-shape-complex-indic-table.cc | 145 +- .../libharfbuzz/hb-ot-shape-complex-indic.cc | 308 +- .../libharfbuzz/hb-ot-shape-complex-indic.hh | 47 +- .../hb-ot-shape-complex-khmer-machine.hh | 254 +- .../libharfbuzz/hb-ot-shape-complex-khmer.cc | 172 +- .../libharfbuzz/hb-ot-shape-complex-khmer.hh | 19 +- .../hb-ot-shape-complex-myanmar-machine.hh | 271 +- .../hb-ot-shape-complex-myanmar.cc | 138 +- .../hb-ot-shape-complex-myanmar.hh | 18 +- .../libharfbuzz/hb-ot-shape-complex-thai.cc | 11 + .../hb-ot-shape-complex-use-machine.hh | 628 +- .../hb-ot-shape-complex-use-table.cc | 168 +- .../libharfbuzz/hb-ot-shape-complex-use.cc | 276 +- .../libharfbuzz/hb-ot-shape-complex-use.hh | 13 +- .../hb-ot-shape-complex-vowel-constraints.cc | 41 +- .../native/libharfbuzz/hb-ot-shape-complex.hh | 12 +- .../libharfbuzz/hb-ot-shape-fallback.cc | 29 +- .../libharfbuzz/hb-ot-shape-normalize.cc | 13 +- .../share/native/libharfbuzz/hb-ot-shape.cc | 234 +- .../share/native/libharfbuzz/hb-ot-shape.hh | 49 +- .../native/libharfbuzz/hb-ot-stat-table.hh | 166 +- .../native/libharfbuzz/hb-ot-tag-table.hh | 2200 +++--- .../share/native/libharfbuzz/hb-ot-tag.cc | 176 +- .../libharfbuzz/hb-ot-var-avar-table.hh | 37 +- .../libharfbuzz/hb-ot-var-fvar-table.hh | 219 +- .../libharfbuzz/hb-ot-var-gvar-table.hh | 701 ++ .../libharfbuzz/hb-ot-var-hvar-table.hh | 338 +- .../libharfbuzz/hb-ot-var-mvar-table.hh | 8 +- .../share/native/libharfbuzz/hb-ot-var.cc | 13 +- .../share/native/libharfbuzz/hb-ot-var.h | 2 +- .../native/libharfbuzz/hb-ot-vorg-table.hh | 127 +- .../share/native/libharfbuzz/hb-ot.h | 2 + .../share/native/libharfbuzz/hb-pool.hh | 100 + .../share/native/libharfbuzz/hb-sanitize.hh | 412 + .../share/native/libharfbuzz/hb-serialize.hh | 553 ++ .../share/native/libharfbuzz/hb-set.cc | 8 +- .../share/native/libharfbuzz/hb-set.hh | 222 +- .../share/native/libharfbuzz/hb-shape-plan.cc | 12 +- .../share/native/libharfbuzz/hb-shape-plan.hh | 22 +- .../share/native/libharfbuzz/hb-shape.cc | 6 +- .../native/libharfbuzz/hb-shaper-list.hh | 14 +- .../share/native/libharfbuzz/hb-shaper.cc | 3 + .../share/native/libharfbuzz/hb-shaper.hh | 2 +- .../share/native/libharfbuzz/hb-static.cc | 41 +- .../native/libharfbuzz/hb-string-array.hh | 6 +- .../share/native/libharfbuzz/hb-style.cc | 135 + .../libharfbuzz/{hb-warning.cc => hb-style.h} | 28 +- .../libharfbuzz/hb-subset-cff-common.cc | 143 +- .../libharfbuzz/hb-subset-cff-common.hh | 299 +- .../native/libharfbuzz/hb-subset-cff1.cc | 721 +- .../native/libharfbuzz/hb-subset-cff1.hh | 3 +- .../native/libharfbuzz/hb-subset-cff2.cc | 416 +- .../native/libharfbuzz/hb-subset-cff2.hh | 3 +- .../native/libharfbuzz/hb-subset-glyf.cc | 310 - .../native/libharfbuzz/hb-subset-input.cc | 105 +- .../native/libharfbuzz/hb-subset-input.hh | 14 +- .../native/libharfbuzz/hb-subset-plan.cc | 324 +- .../native/libharfbuzz/hb-subset-plan.hh | 108 +- .../share/native/libharfbuzz/hb-subset.cc | 310 +- .../share/native/libharfbuzz/hb-subset.h | 27 +- .../share/native/libharfbuzz/hb-subset.hh | 28 +- .../share/native/libharfbuzz/hb-ucd-table.hh | 6780 +++++++++++++++++ .../share/native/libharfbuzz/hb-ucd.cc | 248 + .../share/native/libharfbuzz/hb-ucdn.cc | 268 - .../share/native/libharfbuzz/hb-ucdn/ucdn.c | 360 - .../share/native/libharfbuzz/hb-ucdn/ucdn.h | 461 -- .../native/libharfbuzz/hb-ucdn/ucdn_db.h | 5730 -------------- .../libharfbuzz/hb-unicode-emoji-table.hh | 132 +- .../share/native/libharfbuzz/hb-unicode.cc | 37 +- .../share/native/libharfbuzz/hb-unicode.hh | 24 +- .../share/native/libharfbuzz/hb-vector.hh | 181 +- .../share/native/libharfbuzz/hb-version.h | 6 +- .../share/native/libharfbuzz/hb.h | 2 + .../share/native/libharfbuzz/hb.hh | 402 +- 204 files changed, 31881 insertions(+), 19412 deletions(-) delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-algs.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-bimap.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-config.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-draw.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-draw.h create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-draw.hh delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-meta.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-number.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-number.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h rename src/java.desktop/share/native/libharfbuzz/{hb-subset-glyf.hh => hb-ot-metrics.hh} (70%) rename src/java.desktop/share/native/libharfbuzz/{hb-ot-name-language.cc => hb-ot-name-language-static.hh} (96%) create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-pool.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-serialize.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-style.cc rename src/java.desktop/share/native/libharfbuzz/{hb-warning.cc => hb-style.h} (70%) delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh create mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucd.cc delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h delete mode 100644 src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk index 7fbd1049f8957..3203378d00a49 100644 --- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk +++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk @@ -435,7 +435,6 @@ endif ifeq ($(USE_EXTERNAL_HARFBUZZ), true) LIBHARFBUZZ_LIBS := $(HARFBUZZ_LIBS) else - HARFBUZZ_CFLAGS := -DHAVE_OT -DHAVE_FALLBACK -DHAVE_UCDN -DHAVE_ROUND # This is better than adding EXPORT_ALL_SYMBOLS ifneq ($(filter $(TOOLCHAIN_TYPE), gcc clang), ) @@ -493,7 +492,7 @@ else maybe-uninitialized class-memaccess, \ DISABLED_WARNINGS_clang := unused-value incompatible-pointer-types \ tautological-constant-out-of-range-compare int-to-pointer-cast \ - undef missing-field-initializers, \ + undef missing-field-initializers range-loop-analysis, \ DISABLED_WARNINGS_microsoft := 4267 4244 4090 4146 4334 4819 4101 4068 4805 4138, \ LDFLAGS := $(LDFLAGS_JDKLIB) \ $(call SET_SHARED_LIBRARY_ORIGIN), \ diff --git a/src/java.desktop/share/legal/harfbuzz.md b/src/java.desktop/share/legal/harfbuzz.md index 16698bc86acf3..465bcf5be3c99 100644 --- a/src/java.desktop/share/legal/harfbuzz.md +++ b/src/java.desktop/share/legal/harfbuzz.md @@ -1,16 +1,18 @@ -## Harfbuzz v2.3.1 +## Harfbuzz v2.7.2 ### Harfbuzz License -http://cgit.freedesktop.org/harfbuzz/tree/COPYING +https://github.com/harfbuzz/harfbuzz/blob/master/COPYING
     
    -HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
    +HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
     For parts of HarfBuzz that are licensed under different licenses see individual
     files names COPYING in subdirectories where applicable.
     
    -Copyright © 2010,2011,2012  Google, Inc.
    +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
    +Copyright © 2018,2019,2020  Ebrahim Byagowi
    +Copyright © 2019,2020  Facebook, Inc. 
     Copyright © 2012  Mozilla Foundation
     Copyright © 2011  Codethink Limited
     Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh
    deleted file mode 100644
    index 4ee7353346d8d..0000000000000
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-fdsc-table.hh
    +++ /dev/null
    @@ -1,126 +0,0 @@
    -/*
    - * Copyright © 2018  Ebrahim Byagowi
    - *
    - *  This is part of HarfBuzz, a text shaping library.
    - *
    - * Permission is hereby granted, without written agreement and without
    - * license or royalty fees, to use, copy, modify, and distribute this
    - * software and its documentation for any purpose, provided that the
    - * above copyright notice and the following two paragraphs appear in
    - * all copies of this software.
    - *
    - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    - * DAMAGE.
    - *
    - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    - * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    - */
    -
    -#ifndef HB_AAT_FDSC_TABLE_HH
    -#define HB_AAT_FDSC_TABLE_HH
    -
    -#include "hb-aat-layout-common.hh"
    -#include "hb-open-type.hh"
    -
    -/*
    - * fdsc -- Font descriptors
    - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fdsc.html
    - */
    -#define HB_AAT_TAG_fdsc HB_TAG('f','d','s','c')
    -
    -
    -namespace AAT {
    -
    -
    -struct FontDescriptor
    -{
    -  bool has_data () const { return tag; }
    -
    -  int cmp (hb_tag_t a) const { return tag.cmp (a); }
    -
    -  float get_value () const { return u.value.to_float (); }
    -
    -  enum non_alphabetic_value_t {
    -    Alphabetic          = 0,
    -    Dingbats            = 1,
    -    PiCharacters        = 2,
    -    Fleurons            = 3,
    -    DecorativeBorders   = 4,
    -    InternationalSymbols= 5,
    -    MathSymbols         = 6
    -  };
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this));
    -  }
    -
    -  protected:
    -  Tag           tag;            /* The 4-byte table tag name. */
    -  union {
    -  Fixed         value;          /* The value for the descriptor tag. */
    -  HBUINT32      nalfType;       /* If the tag is `nalf`, see non_alphabetic_value_t */
    -  } u;
    -  public:
    -  DEFINE_SIZE_STATIC (8);
    -};
    -
    -struct fdsc
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_fdsc;
    -
    -  enum {
    -    Weight       = HB_TAG ('w','g','h','t'),
    -                                /* Percent weight relative to regular weight.
    -                                 * (defaul value: 1.0) */
    -    Width        = HB_TAG ('w','d','t','h'),
    -                                /* Percent width relative to regular width.
    -                                 * (default value: 1.0) */
    -    Slant        = HB_TAG ('s','l','n','t'),
    -                                /* Angle of slant in degrees, where positive
    -                                 * is clockwise from straight up.
    -                                 * (default value: 0.0) */
    -    OpticalSize  = HB_TAG ('o','p','s','z'),
    -                                /* Point size the font was designed for.
    -                                 * (default value: 12.0) */
    -    NonAlphabetic= HB_TAG ('n','a','l','f')
    -                                /* These values are treated as integers,
    -                                 * not fixed32s. 0 means alphabetic, and greater
    -                                 * integers mean the font is non-alphabetic (e.g. symbols).
    -                                 * (default value: 0) */
    -  };
    -
    -  const FontDescriptor &get_descriptor (hb_tag_t style) const
    -  { return descriptors.lsearch (style); }
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this) &&
    -                  descriptors.sanitize (c));
    -  }
    -
    -  protected:
    -  Fixed         version;        /* Version number of the font descriptors
    -                                 * table (0x00010000 for the current version). */
    -  LArrayOf
    -                descriptors;    /* List of tagged-coordinate pairs style descriptors
    -                                 * that will be included to characterize this font.
    -                                 * Each descriptor consists of a  pair.
    -                                 * These pairs are located in the gxFontDescriptor
    -                                 * array that follows. */
    -  public:
    -  DEFINE_SIZE_ARRAY (8, descriptors);
    -};
    -
    -} /* namespace AAT */
    -
    -
    -#endif /* HB_AAT_FDSC_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    index f8495f384b981..90dd949a50a65 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-ankr-table.hh
    @@ -66,7 +66,7 @@ struct ankr
       {
         const NNOffsetTo *offset = (this+lookupTable).get_value (glyph_id, num_glyphs);
         if (!offset)
    -      return Null(Anchor);
    +      return Null (Anchor);
         const GlyphAnchors &anchors = &(this+anchorData) + *offset;
         return anchors[i];
       }
    @@ -76,13 +76,14 @@ struct ankr
         TRACE_SANITIZE (this);
         return_trace (likely (c->check_struct (this) &&
                               version == 0 &&
    +                          c->check_range (this, anchorData) &&
                               lookupTable.sanitize (c, this, &(this+anchorData))));
       }
     
       protected:
       HBUINT16      version;        /* Version number (set to zero) */
       HBUINT16      flags;          /* Flags (currently unused; set to zero) */
    -  LOffsetTo > >
    +  LOffsetTo>>
                     lookupTable;    /* Offset to the table's lookup table */
       LNNOffsetTo
                     anchorData;     /* Offset to the glyph data table */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    index 746da3ae5bcea..7dcf1c3bd9d0d 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-bsln-table.hh
    @@ -82,7 +82,7 @@ struct BaselineTableFormat2Part
       }
     
       protected:
    -  GlyphID       stdGlyph;       /* The specific glyph index number in this
    +  HBGlyphID     stdGlyph;       /* The specific glyph index number in this
                                      * font that is used to set the baseline values.
                                      * This is the standard glyph.
                                      * This glyph must contain a set of control points
    @@ -101,11 +101,11 @@ struct BaselineTableFormat3Part
       bool sanitize (hb_sanitize_context_t *c) const
       {
         TRACE_SANITIZE (this);
    -    return_trace (c->check_struct (this) && lookupTable.sanitize (c));
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c)));
       }
     
       protected:
    -  GlyphID       stdGlyph;       /* ditto */
    +  HBGlyphID     stdGlyph;       /* ditto */
       HBUINT16      ctlPoints[32];  /* ditto */
       Lookup
                     lookupTable;    /* Lookup table that maps glyphs to their
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    index 7c8e3cec16fb6..e1dcd6f7102d9 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-common.hh
    @@ -93,8 +93,8 @@ struct LookupSegmentSingle
         return_trace (c->check_struct (this) && value.sanitize (c, base));
       }
     
    -  GlyphID       last;           /* Last GlyphID in this segment */
    -  GlyphID       first;          /* First GlyphID in this segment */
    +  HBGlyphID     last;           /* Last GlyphID in this segment */
    +  HBGlyphID     first;          /* First GlyphID in this segment */
       T             value;          /* The lookup value (only one) */
       public:
       DEFINE_SIZE_STATIC (4 + T::static_size);
    @@ -125,7 +125,7 @@ struct LookupFormat2
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 2 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     segments;       /* The actual segments. These must already be sorted,
                                      * according to the first word in each one (the last
                                      * glyph in each segment). */
    @@ -153,18 +153,18 @@ struct LookupSegmentArray
                       first <= last &&
                       valuesZ.sanitize (c, base, last - first + 1));
       }
    -  template 
    -  bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const
    +  template 
    +  bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
       {
         TRACE_SANITIZE (this);
         return_trace (c->check_struct (this) &&
                       first <= last &&
    -                  valuesZ.sanitize (c, base, last - first + 1, user_data));
    +                  valuesZ.sanitize (c, base, last - first + 1, hb_forward (ds)...));
       }
     
    -  GlyphID       last;           /* Last GlyphID in this segment */
    -  GlyphID       first;          /* First GlyphID in this segment */
    -  NNOffsetTo >
    +  HBGlyphID     last;           /* Last GlyphID in this segment */
    +  HBGlyphID     first;          /* First GlyphID in this segment */
    +  NNOffsetTo>
                     valuesZ;        /* A 16-bit offset from the start of
                                      * the table to the data. */
       public:
    @@ -196,7 +196,7 @@ struct LookupFormat4
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 4 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     segments;       /* The actual segments. These must already be sorted,
                                      * according to the first word in each one (the last
                                      * glyph in each segment). */
    @@ -222,7 +222,7 @@ struct LookupSingle
         return_trace (c->check_struct (this) && value.sanitize (c, base));
       }
     
    -  GlyphID       glyph;          /* Last GlyphID */
    +  HBGlyphID     glyph;          /* Last GlyphID */
       T             value;          /* The lookup value (only one) */
       public:
       DEFINE_SIZE_STATIC (2 + T::static_size);
    @@ -253,7 +253,7 @@ struct LookupFormat6
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 6 */
    -  VarSizedBinSearchArrayOf >
    +  VarSizedBinSearchArrayOf>
                     entries;        /* The actual entries, sorted by glyph index. */
       public:
       DEFINE_SIZE_ARRAY (8, entries);
    @@ -284,7 +284,7 @@ struct LookupFormat8
     
       protected:
       HBUINT16      format;         /* Format identifier--format = 8 */
    -  GlyphID       firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID     firstGlyph;     /* First glyph index included in the trimmed array. */
       HBUINT16      glyphCount;     /* Total number of glyphs (equivalent to the last
                                      * glyph minus the value of firstGlyph plus 1). */
       UnsizedArrayOf
    @@ -303,7 +303,7 @@ struct LookupFormat10
       const typename T::type get_value_or_null (hb_codepoint_t glyph_id) const
       {
         if (!(firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount))
    -      return Null(T);
    +      return Null (T);
     
         const HBUINT8 *p = &valueArrayZ[(glyph_id - firstGlyph) * valueSize];
     
    @@ -326,7 +326,7 @@ struct LookupFormat10
       protected:
       HBUINT16      format;         /* Format identifier--format = 8 */
       HBUINT16      valueSize;      /* Byte size of each value. */
    -  GlyphID       firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID     firstGlyph;     /* First glyph index included in the trimmed array. */
       HBUINT16      glyphCount;     /* Total number of glyphs (equivalent to the last
                                      * glyph minus the value of firstGlyph plus 1). */
       UnsizedArrayOf
    @@ -358,7 +358,7 @@ struct Lookup
           case 10: return u.format10.get_value_or_null (glyph_id);
           default:
           const T *v = get_value (glyph_id, num_glyphs);
    -      return v ? *v : Null(T);
    +      return v ? *v : Null (T);
         }
       }
     
    @@ -418,15 +418,11 @@ struct Lookup
     } /* Close namespace. */
     /* Ugly hand-coded null objects for template Lookup<> :(. */
     extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
    -template <>
    -/*static*/ inline const AAT::Lookup& Null > ()
    -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    -template <>
    -/*static*/ inline const AAT::Lookup& Null > ()
    -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    -template <>
    -/*static*/ inline const AAT::Lookup >& Null > > ()
    -{ return *reinterpret_cast > *> (_hb_Null_AAT_Lookup); }
    +template 
    +struct Null> {
    +  static AAT::Lookup const & get_null ()
    +  { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); }
    +};
     namespace AAT {
     
     enum { DELETED_GLYPH = 0xFFFF };
    @@ -514,7 +510,7 @@ struct StateTable
       const Entry &get_entry (int state, unsigned int klass) const
       {
         if (unlikely (klass >= nClasses))
    -      klass = StateTable >::CLASS_OUT_OF_BOUNDS;
    +      klass = StateTable>::CLASS_OUT_OF_BOUNDS;
     
         const HBUSHORT *states = (this+stateArrayTable).arrayZ;
         const Entry *entries = (this+entryTable).arrayZ;
    @@ -580,7 +576,7 @@ struct StateTable
               if (unlikely (stop > states))
                 return_trace (false);
               for (const HBUSHORT *p = states; stop < p; p--)
    -            num_entries = MAX (num_entries, *(p - 1) + 1);
    +            num_entries = hb_max (num_entries, *(p - 1) + 1);
               state_neg = min_state;
             }
           }
    @@ -601,7 +597,7 @@ struct StateTable
               if (unlikely (stop < states))
                 return_trace (false);
               for (const HBUSHORT *p = &states[state_pos * num_classes]; p < stop; p++)
    -            num_entries = MAX (num_entries, *p + 1);
    +            num_entries = hb_max (num_entries, *p + 1);
               state_pos = max_state + 1;
             }
           }
    @@ -615,8 +611,8 @@ struct StateTable
             for (const Entry *p = &entries[entry]; p < stop; p++)
             {
               int newState = new_state (p->newState);
    -          min_state = MIN (min_state, newState);
    -          max_state = MAX (max_state, newState);
    +          min_state = hb_min (min_state, newState);
    +          max_state = hb_max (max_state, newState);
             }
             entry = num_entries;
           }
    @@ -635,7 +631,7 @@ struct StateTable
                     classTable;     /* Offset to the class table. */
       NNOffsetTo, HBUINT>
                     stateArrayTable;/* Offset to the state array. */
    -  NNOffsetTo >, HBUINT>
    +  NNOffsetTo>, HBUINT>
                     entryTable;     /* Offset to the entry array. */
     
       public:
    @@ -662,7 +658,7 @@ struct ClassTable
         return_trace (c->check_struct (this) && classArray.sanitize (c));
       }
       protected:
    -  GlyphID               firstGlyph;     /* First glyph index included in the trimmed array. */
    +  HBGlyphID             firstGlyph;     /* First glyph index included in the trimmed array. */
       ArrayOf      classArray;     /* The class codes (indexed by glyph index minus
                                              * firstGlyph). */
       public:
    @@ -682,7 +678,7 @@ struct ObsoleteTypes
                                          const void *base,
                                          const T *array)
       {
    -    return (offset - ((const char *) array - (const char *) base)) / sizeof (T);
    +    return (offset - ((const char *) array - (const char *) base)) / T::static_size;
       }
       template 
       static unsigned int byteOffsetToIndex (unsigned int offset,
    @@ -824,12 +820,11 @@ struct hb_aat_apply_context_t :
     
       /* Unused. For debug tracing only. */
       unsigned int lookup_index;
    -  unsigned int debug_depth;
     
       HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
                                           hb_font_t *font_,
                                           hb_buffer_t *buffer_,
    -                                      hb_blob_t *blob = const_cast (&Null(hb_blob_t)));
    +                                      hb_blob_t *blob = const_cast (&Null (hb_blob_t)));
     
       HB_INTERNAL ~hb_aat_apply_context_t ();
     
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    index 910a94f0bc341..06c48d2f64a4b 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-feat-table.hh
    @@ -47,17 +47,16 @@ struct SettingName
       hb_aat_layout_feature_selector_t get_selector () const
       { return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
     
    -  void get_info (hb_aat_layout_feature_selector_info_t *s,
    -                        hb_aat_layout_feature_selector_t default_selector) const
    +  hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
       {
    -    s->name_id = nameIndex;
    -
    -    s->enable = (hb_aat_layout_feature_selector_t) (unsigned int) setting;
    -    s->disable = default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID ?
    -                 (hb_aat_layout_feature_selector_t) (s->enable + 1) :
    -                 default_selector;
    -
    -    s->reserved = 0;
    +    return {
    +      nameIndex,
    +      (hb_aat_layout_feature_selector_t) (unsigned int) setting,
    +      default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
    +        ? (hb_aat_layout_feature_selector_t) (setting + 1)
    +        : default_selector,
    +      0
    +    };
       }
     
       bool sanitize (hb_sanitize_context_t *c) const
    @@ -117,9 +116,10 @@ struct FeatureName
     
         if (selectors_count)
         {
    -      hb_array_t arr = settings_table.sub_array (start_offset, selectors_count);
    -      for (unsigned int i = 0; i < arr.length; i++)
    -        settings_table[start_offset + i].get_info (&selectors[i], default_selector);
    +      + settings_table.sub_array (start_offset, selectors_count)
    +      | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
    +      | hb_sink (hb_array (selectors, *selectors_count))
    +      ;
         }
         return settings_table.length;
       }
    @@ -129,6 +129,11 @@ struct FeatureName
     
       hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
     
    +  bool is_exclusive () const { return featureFlags & Exclusive; }
    +
    +  /* A FeatureName with no settings is meaningless */
    +  bool has_data () const { return nSettings; }
    +
       bool sanitize (hb_sanitize_context_t *c, const void *base) const
       {
         TRACE_SANITIZE (this);
    @@ -139,7 +144,7 @@ struct FeatureName
       protected:
       HBUINT16      feature;        /* Feature type. */
       HBUINT16      nSettings;      /* The number of records in the setting name array. */
    -  LOffsetTo, false>
    +  LNNOffsetTo>
                     settingTableZ;  /* Offset in bytes from the beginning of this table to
                                      * this feature's setting name array. The actual type of
                                      * record this offset refers to will depend on the
    @@ -162,21 +167,21 @@ struct feat
                                       unsigned int                 *count,
                                       hb_aat_layout_feature_type_t *features) const
       {
    -    unsigned int feature_count = featureNameCount;
    -    if (count && *count)
    +    if (count)
         {
    -      unsigned int len = MIN (feature_count - start_offset, *count);
    -      for (unsigned int i = 0; i < len; i++)
    -        features[i] = namesZ[i + start_offset].get_feature_type ();
    -      *count = len;
    +      + namesZ.as_array (featureNameCount).sub_array (start_offset, count)
    +      | hb_map (&FeatureName::get_feature_type)
    +      | hb_sink (hb_array (features, *count))
    +      ;
         }
         return featureNameCount;
       }
     
    +  bool exposes_feature (hb_aat_layout_feature_type_t feature_type) const
    +  { return get_feature (feature_type).has_data (); }
    +
       const FeatureName& get_feature (hb_aat_layout_feature_type_t feature_type) const
    -  {
    -    return namesZ.bsearch (featureNameCount, feature_type);
    -  }
    +  { return namesZ.bsearch (featureNameCount, feature_type); }
     
       hb_ot_name_id_t get_feature_name_id (hb_aat_layout_feature_type_t feature) const
       { return get_feature (feature).get_feature_name_id (); }
    @@ -209,7 +214,7 @@ struct feat
       SortedUnsizedArrayOf
                     namesZ;         /* The feature name array. */
       public:
    -  DEFINE_SIZE_STATIC (24);
    +  DEFINE_SIZE_ARRAY (12, namesZ);
     };
     
     } /* namespace AAT */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    index c3817ea0b9c35..7ebd6a5ec5e8c 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-just-table.hh
    @@ -70,9 +70,9 @@ struct DecompositionAction
     
       ActionSubrecordHeader
                     header;
    -  Fixed         lowerLimit;     /* If the distance factor is less than this value,
    +  HBFixed       lowerLimit;     /* If the distance factor is less than this value,
                                      * then the ligature is decomposed. */
    -  Fixed         upperLimit;     /* If the distance factor is greater than this value,
    +  HBFixed       upperLimit;     /* If the distance factor is greater than this value,
                                      * then the ligature is decomposed. */
       HBUINT16      order;          /* Numerical order in which this ligature will
                                      * be decomposed; you may want infrequent ligatures
    @@ -100,7 +100,7 @@ struct UnconditionalAddGlyphAction
       protected:
       ActionSubrecordHeader
                     header;
    -  GlyphID       addGlyph;       /* Glyph that should be added if the distance factor
    +  HBGlyphID     addGlyph;       /* Glyph that should be added if the distance factor
                                      * is growing. */
     
       public:
    @@ -118,14 +118,14 @@ struct ConditionalAddGlyphAction
       protected:
       ActionSubrecordHeader
                     header;
    -  Fixed         substThreshold; /* Distance growth factor (in ems) at which
    +  HBFixed       substThreshold; /* Distance growth factor (in ems) at which
                                      * this glyph is replaced and the growth factor
                                      * recalculated. */
    -  GlyphID       addGlyph;       /* Glyph to be added as kashida. If this value is
    +  HBGlyphID     addGlyph;       /* Glyph to be added as kashida. If this value is
                                      * 0xFFFF, no extra glyph will be added. Note that
                                      * generally when a glyph is added, justification
                                      * will need to be redone. */
    -  GlyphID       substGlyph;     /* Glyph to be substituted for this glyph if the
    +  HBGlyphID     substGlyph;     /* Glyph to be substituted for this glyph if the
                                      * growth factor equals or exceeds the value of
                                      * substThreshold. */
       public:
    @@ -146,13 +146,13 @@ struct DuctileGlyphAction
       HBUINT32      variationAxis;  /* The 4-byte tag identifying the ductile axis.
                                      * This would normally be 0x64756374 ('duct'),
                                      * but you may use any axis the font contains. */
    -  Fixed         minimumLimit;   /* The lowest value for the ductility axis tha
    +  HBFixed       minimumLimit;   /* The lowest value for the ductility axis tha
                                      * still yields an acceptable appearance. Normally
                                      * this will be 1.0. */
    -  Fixed         noStretchValue; /* This is the default value that corresponds to
    +  HBFixed       noStretchValue; /* This is the default value that corresponds to
                                      * no change in appearance. Normally, this will
                                      * be 1.0. */
    -  Fixed         maximumLimit;   /* The highest value for the ductility axis that
    +  HBFixed       maximumLimit;   /* The highest value for the ductility axis that
                                      * still yields an acceptable appearance. */
       public:
       DEFINE_SIZE_STATIC (22);
    @@ -170,7 +170,7 @@ struct RepeatedAddGlyphAction
       ActionSubrecordHeader
                     header;
       HBUINT16      flags;          /* Currently unused; set to 0. */
    -  GlyphID       glyph;          /* Glyph that should be added if the distance factor
    +  HBGlyphID     glyph;          /* Glyph that should be added if the distance factor
                                      * is growing. */
       public:
       DEFINE_SIZE_STATIC (10);
    @@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
       };
     
       protected:
    -  Fixed         beforeGrowLimit;/* The ratio by which the advance width of the
    +  HBFixed       beforeGrowLimit;/* The ratio by which the advance width of the
                                      * glyph is permitted to grow on the left or top side. */
    -  Fixed         beforeShrinkLimit;
    +  HBFixed       beforeShrinkLimit;
                                     /* The ratio by which the advance width of the
                                      * glyph is permitted to shrink on the left or top side. */
    -  Fixed         afterGrowLimit; /* The ratio by which the advance width of the glyph
    +  HBFixed       afterGrowLimit; /* The ratio by which the advance width of the glyph
                                      * is permitted to shrink on the left or top side. */
    -  Fixed         afterShrinkLimit;
    +  HBFixed       afterShrinkLimit;
                                     /* The ratio by which the advance width of the glyph
                                      * is at most permitted to shrink on the right or
                                      * bottom side. */
    @@ -371,7 +371,7 @@ struct JustificationHeader
                                      * of postcompensation subtable (set to zero if none).
                                      *
                                      * The postcompensation subtable, if present in the font. */
    -  Lookup >
    +  Lookup>
                     lookupTable;    /* Lookup table associating glyphs with width delta
                                      * clusters. See the description of Width Delta Clusters
                                      * table for details on how to interpret the lookup values. */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    index b5519480e239f..76e1da06f3ef7 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-kerx-table.hh
    @@ -82,8 +82,8 @@ struct KernPair
       }
     
       protected:
    -  GlyphID       left;
    -  GlyphID       right;
    +  HBGlyphID     left;
    +  HBGlyphID     right;
       FWORD         value;
       public:
       DEFINE_SIZE_STATIC (6);
    @@ -229,9 +229,7 @@ struct KerxSubTableFormat1
     
         bool is_actionable (StateTableDriver *driver HB_UNUSED,
                             const Entry &entry)
    -    {
    -      return Format1EntryT::performAction (entry);
    -    }
    +    { return Format1EntryT::performAction (entry); }
         void transition (StateTableDriver *driver,
                          const Entry &entry)
         {
    @@ -251,7 +249,7 @@ struct KerxSubTableFormat1
     
           if (Format1EntryT::performAction (entry) && depth)
           {
    -        unsigned int tuple_count = MAX (1u, table->header.tuple_count ());
    +        unsigned int tuple_count = hb_max (1u, table->header.tuple_count ());
     
             unsigned int kern_idx = Format1EntryT::kernActionIndex (entry);
             kern_idx = Types::byteOffsetToIndex (kern_idx, &table->machine, kernAction.arrayZ);
    @@ -281,35 +279,28 @@ struct KerxSubTableFormat1
     
               hb_glyph_position_t &o = buffer->pos[idx];
     
    -          /* Testing shows that CoreText only applies kern (cross-stream or not)
    -           * if none has been applied by previous subtables.  That is, it does
    -           * NOT seem to accumulate as otherwise implied by specs. */
    -
    -          /* The following flag is undocumented in the spec, but described
    -           * in the 'kern' table example. */
    -          if (v == -0x8000)
    -          {
    -            o.attach_type() = ATTACH_TYPE_NONE;
    -            o.attach_chain() = 0;
    -            o.x_offset = o.y_offset = 0;
    -          }
    -          else if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
    +          if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
               {
                 if (crossStream)
                 {
    -              if (buffer->pos[idx].attach_type() && !buffer->pos[idx].y_offset)
    +              /* The following flag is undocumented in the spec, but described
    +               * in the 'kern' table example. */
    +              if (v == -0x8000)
                   {
    -                o.y_offset = c->font->em_scale_y (v);
    +                o.attach_type() = ATTACH_TYPE_NONE;
    +                o.attach_chain() = 0;
    +                o.y_offset = 0;
    +              }
    +              else if (o.attach_type())
    +              {
    +                o.y_offset += c->font->em_scale_y (v);
                     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
                   }
                 }
                 else if (buffer->info[idx].mask & kern_mask)
                 {
    -              if (!buffer->pos[idx].x_offset)
    -              {
    -                buffer->pos[idx].x_advance += c->font->em_scale_x (v);
    -                buffer->pos[idx].x_offset += c->font->em_scale_x (v);
    -              }
    +              o.x_advance += c->font->em_scale_x (v);
    +              o.x_offset += c->font->em_scale_x (v);
                 }
               }
               else
    @@ -317,19 +308,22 @@ struct KerxSubTableFormat1
                 if (crossStream)
                 {
                   /* CoreText doesn't do crossStream kerning in vertical.  We do. */
    -              if (buffer->pos[idx].attach_type() && !buffer->pos[idx].x_offset)
    +              if (v == -0x8000)
                   {
    -                o.x_offset = c->font->em_scale_x (v);
    +                o.attach_type() = ATTACH_TYPE_NONE;
    +                o.attach_chain() = 0;
    +                o.x_offset = 0;
    +              }
    +              else if (o.attach_type())
    +              {
    +                o.x_offset += c->font->em_scale_x (v);
                     buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT;
                   }
                 }
                 else if (buffer->info[idx].mask & kern_mask)
                 {
    -              if (!buffer->pos[idx].y_offset)
    -              {
    -                buffer->pos[idx].y_advance += c->font->em_scale_y (v);
    -                buffer->pos[idx].y_offset += c->font->em_scale_y (v);
    -              }
    +              o.y_advance += c->font->em_scale_y (v);
    +              o.y_offset += c->font->em_scale_y (v);
                 }
               }
             }
    @@ -392,7 +386,7 @@ struct KerxSubTableFormat2
     
         const UnsizedArrayOf &arrayZ = this+array;
         unsigned int kern_idx = l + r;
    -    kern_idx = Types::offsetToIndex (kern_idx, this, &arrayZ);
    +    kern_idx = Types::offsetToIndex (kern_idx, this, arrayZ.arrayZ);
         const FWORD *v = &arrayZ[kern_idx];
         if (unlikely (!v->sanitize (&c->sanitizer))) return 0;
     
    @@ -488,7 +482,7 @@ struct KerxSubTableFormat4
         };
     
         driver_context_t (const KerxSubTableFormat4 *table,
    -                             hb_aat_apply_context_t *c_) :
    +                      hb_aat_apply_context_t *c_) :
             c (c_),
             action_type ((table->flags & ActionType) >> 30),
             ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
    @@ -497,9 +491,7 @@ struct KerxSubTableFormat4
     
         bool is_actionable (StateTableDriver *driver HB_UNUSED,
                             const Entry &entry)
    -    {
    -      return entry.data.ankrActionIndex != 0xFFFF;
    -    }
    +    { return entry.data.ankrActionIndex != 0xFFFF; }
         void transition (StateTableDriver *driver,
                          const Entry &entry)
         {
    @@ -512,11 +504,13 @@ struct KerxSubTableFormat4
             {
               case 0: /* Control Point Actions.*/
               {
    -            /* indexed into glyph outline. */
    -            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
    +            /* Indexed into glyph outline. */
    +            /* Each action (record in ankrData) contains two 16-bit fields, so we must
    +               double the ankrActionIndex to get the correct offset here. */
    +            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
                 if (!c->sanitizer.check_array (data, 2)) return;
    -            HB_UNUSED unsigned int markControlPoint = *data++;
    -            HB_UNUSED unsigned int currControlPoint = *data++;
    +            unsigned int markControlPoint = *data++;
    +            unsigned int currControlPoint = *data++;
                 hb_position_t markX = 0;
                 hb_position_t markY = 0;
                 hb_position_t currX = 0;
    @@ -538,8 +532,10 @@ struct KerxSubTableFormat4
     
               case 1: /* Anchor Point Actions. */
               {
    -           /* Indexed into 'ankr' table. */
    -            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex];
    +            /* Indexed into 'ankr' table. */
    +            /* Each action (record in ankrData) contains two 16-bit fields, so we must
    +               double the ankrActionIndex to get the correct offset here. */
    +            const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
                 if (!c->sanitizer.check_array (data, 2)) return;
                 unsigned int markAnchorPoint = *data++;
                 unsigned int currAnchorPoint = *data++;
    @@ -557,7 +553,9 @@ struct KerxSubTableFormat4
     
               case 2: /* Control Point Coordinate Actions. */
               {
    -            const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex];
    +            /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex
    +               by 4 to get the correct offset for the given action. */
    +            const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
                 if (!c->sanitizer.check_array (data, 4)) return;
                 int markX = *data++;
                 int markY = *data++;
    @@ -628,7 +626,7 @@ struct KerxSubTableFormat6
       bool is_long () const { return flags & ValuesAreLong; }
     
       int get_kerning (hb_codepoint_t left, hb_codepoint_t right,
    -                          hb_aat_apply_context_t *c) const
    +                   hb_aat_apply_context_t *c) const
       {
         unsigned int num_glyphs = c->sanitizer.get_num_glyphs ();
         if (is_long ())
    @@ -712,18 +710,18 @@ struct KerxSubTableFormat6
       {
         struct Long
         {
    -      LNNOffsetTo >            rowIndexTable;
    -      LNNOffsetTo >            columnIndexTable;
    -      LNNOffsetTo >     array;
    +      LNNOffsetTo>             rowIndexTable;
    +      LNNOffsetTo>             columnIndexTable;
    +      LNNOffsetTo>      array;
         } l;
         struct Short
         {
    -      LNNOffsetTo >            rowIndexTable;
    -      LNNOffsetTo >            columnIndexTable;
    -      LNNOffsetTo >       array;
    +      LNNOffsetTo>             rowIndexTable;
    +      LNNOffsetTo>             columnIndexTable;
    +      LNNOffsetTo>        array;
         } s;
       } u;
    -  LNNOffsetTo >   vector;
    +  LNNOffsetTo>    vector;
       public:
       DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 24);
     };
    @@ -733,8 +731,8 @@ struct KerxSubTableHeader
     {
       typedef ExtendedTypes Types;
     
    -  unsigned int tuple_count () const { return tupleCount; }
    -  bool is_horizontal () const       { return !(coverage & Vertical); }
    +  unsigned   tuple_count () const { return tupleCount; }
    +  bool     is_horizontal () const { return !(coverage & Vertical); }
     
       enum Coverage
       {
    @@ -771,17 +769,17 @@ struct KerxSubTable
       unsigned int get_size () const { return u.header.length; }
       unsigned int get_type () const { return u.header.coverage & u.header.SubtableType; }
     
    -  template 
    -  typename context_t::return_t dispatch (context_t *c) const
    +  template 
    +  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
       {
         unsigned int subtable_type = get_type ();
         TRACE_DISPATCH (this, subtable_type);
         switch (subtable_type) {
    -    case 0:     return_trace (c->dispatch (u.format0));
    -    case 1:     return_trace (c->dispatch (u.format1));
    -    case 2:     return_trace (c->dispatch (u.format2));
    -    case 4:     return_trace (c->dispatch (u.format4));
    -    case 6:     return_trace (c->dispatch (u.format6));
    +    case 0:     return_trace (c->dispatch (u.format0, hb_forward (ds)...));
    +    case 1:     return_trace (c->dispatch (u.format1, hb_forward (ds)...));
    +    case 2:     return_trace (c->dispatch (u.format2, hb_forward (ds)...));
    +    case 4:     return_trace (c->dispatch (u.format4, hb_forward (ds)...));
    +    case 6:     return_trace (c->dispatch (u.format6, hb_forward (ds)...));
         default:    return_trace (c->default_return_value ());
         }
       }
    @@ -891,7 +889,7 @@ struct KerxTable
           reverse = bool (st->u.header.coverage & st->u.header.Backwards) !=
                     HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
     
    -      if (!c->buffer->message (c->font, "start %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index))
    +      if (!c->buffer->message (c->font, "start subtable %d", c->lookup_index))
             goto skip;
     
           if (!seenCrossStream &&
    @@ -923,7 +921,7 @@ struct KerxTable
           if (reverse)
             c->buffer->reverse ();
     
    -      (void) c->buffer->message (c->font, "end %c%c%c%c subtable %d", HB_UNTAG (thiz()->tableTag), c->lookup_index);
    +      (void) c->buffer->message (c->font, "end subtable %d", c->lookup_index);
     
         skip:
           st = &StructAfter (*st);
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh
    deleted file mode 100644
    index 58f1ee02fce79..0000000000000
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-lcar-table.hh
    +++ /dev/null
    @@ -1,93 +0,0 @@
    -/*
    - * Copyright © 2018  Ebrahim Byagowi
    - *
    - *  This is part of HarfBuzz, a text shaping library.
    - *
    - * Permission is hereby granted, without written agreement and without
    - * license or royalty fees, to use, copy, modify, and distribute this
    - * software and its documentation for any purpose, provided that the
    - * above copyright notice and the following two paragraphs appear in
    - * all copies of this software.
    - *
    - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    - * DAMAGE.
    - *
    - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    - * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    - */
    -#ifndef HB_AAT_LAYOUT_LCAR_TABLE_HH
    -#define HB_AAT_LAYOUT_LCAR_TABLE_HH
    -
    -#include "hb-open-type.hh"
    -#include "hb-aat-layout-common.hh"
    -
    -/*
    - * lcar -- Ligature caret
    - * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6lcar.html
    - */
    -#define HB_AAT_TAG_lcar HB_TAG('l','c','a','r')
    -
    -
    -namespace AAT {
    -
    -typedef ArrayOf LigCaretClassEntry;
    -
    -struct lcar
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_lcar;
    -
    -  unsigned int get_lig_carets (hb_font_t      *font,
    -                               hb_direction_t  direction,
    -                               hb_codepoint_t  glyph,
    -                               unsigned int    start_offset,
    -                               unsigned int   *caret_count /* IN/OUT */,
    -                               hb_position_t  *caret_array /* OUT */) const
    -  {
    -    const OffsetTo* entry_offset = lookup.get_value (glyph,
    -                                                                         font->face->get_num_glyphs ());
    -    const LigCaretClassEntry& array = entry_offset ? this+*entry_offset : Null (LigCaretClassEntry);
    -    if (caret_count)
    -    {
    -      hb_array_t arr = array.sub_array (start_offset, caret_count);
    -      unsigned int count = arr.length;
    -      for (unsigned int i = 0; i < count; ++i)
    -        switch (format)
    -        {
    -        case 0: caret_array[i] = font->em_scale_dir (arr[i], direction); break;
    -        case 1:
    -          hb_position_t x, y;
    -          font->get_glyph_contour_point_for_origin (glyph, arr[i], direction, &x, &y);
    -          caret_array[i] = HB_DIRECTION_IS_HORIZONTAL (direction) ? x : y;
    -          break;
    -        }
    -    }
    -    return array.len;
    -  }
    -
    -  bool sanitize (hb_sanitize_context_t *c) const
    -  {
    -    TRACE_SANITIZE (this);
    -    return_trace (likely (c->check_struct (this) &&
    -                          version.major == 1 &&
    -                          lookup.sanitize (c, this)));
    -  }
    -
    -  protected:
    -  FixedVersion<>version;        /* Version number of the ligature caret table */
    -  HBUINT16      format;         /* Format of the ligature caret table. */
    -  Lookup >
    -                lookup;         /* data Lookup table associating glyphs */
    -
    -  public:
    -  DEFINE_SIZE_MIN (8);
    -};
    -
    -} /* namespace AAT */
    -
    -#endif /* HB_AAT_LAYOUT_LCAR_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    index f52d2ab301bd5..a0d137836b163 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-morx-table.hh
    @@ -88,7 +88,7 @@ struct RearrangementSubtable
             start = buffer->idx;
     
           if (flags & MarkLast)
    -        end = MIN (buffer->idx + 1, buffer->len);
    +        end = hb_min (buffer->idx + 1, buffer->len);
     
           if ((flags & Verb) && start < end)
           {
    @@ -117,14 +117,14 @@ struct RearrangementSubtable
             };
     
             unsigned int m = map[flags & Verb];
    -        unsigned int l = MIN (2, m >> 4);
    -        unsigned int r = MIN (2, m & 0x0F);
    +        unsigned int l = hb_min (2u, m >> 4);
    +        unsigned int r = hb_min (2u, m & 0x0F);
             bool reverse_l = 3 == (m >> 4);
             bool reverse_r = 3 == (m & 0x0F);
     
             if (end - start >= l + r)
             {
    -          buffer->merge_clusters (start, MIN (buffer->idx + 1, buffer->len));
    +          buffer->merge_clusters (start, hb_min (buffer->idx + 1, buffer->len));
               buffer->merge_clusters (start, end);
     
               hb_glyph_info_t *info = buffer->info;
    @@ -240,46 +240,46 @@ struct ContextualSubtable
           if (buffer->idx == buffer->len && !mark_set)
             return;
     
    -      const GlyphID *replacement;
    +      const HBGlyphID *replacement;
     
           replacement = nullptr;
           if (Types::extended)
           {
             if (entry.data.markIndex != 0xFFFF)
             {
    -          const Lookup &lookup = subs[entry.data.markIndex];
    +          const Lookup &lookup = subs[entry.data.markIndex];
               replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs);
             }
           }
           else
           {
             unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint;
    -        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
    +        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
             replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
             if (!replacement->sanitize (&c->sanitizer) || !*replacement)
               replacement = nullptr;
           }
           if (replacement)
           {
    -        buffer->unsafe_to_break (mark, MIN (buffer->idx + 1, buffer->len));
    +        buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
             buffer->info[mark].codepoint = *replacement;
             ret = true;
           }
     
           replacement = nullptr;
    -      unsigned int idx = MIN (buffer->idx, buffer->len - 1);
    +      unsigned int idx = hb_min (buffer->idx, buffer->len - 1);
           if (Types::extended)
           {
             if (entry.data.currentIndex != 0xFFFF)
             {
    -          const Lookup &lookup = subs[entry.data.currentIndex];
    +          const Lookup &lookup = subs[entry.data.currentIndex];
               replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs);
             }
           }
           else
           {
             unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint;
    -        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
    +        const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs;
             replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
             if (!replacement->sanitize (&c->sanitizer) || !*replacement)
               replacement = nullptr;
    @@ -304,7 +304,7 @@ struct ContextualSubtable
         bool mark_set;
         unsigned int mark;
         const ContextualSubtable *table;
    -    const UnsizedOffsetListOf, HBUINT, false> &subs;
    +    const UnsizedOffsetListOf, HBUINT, false> &subs;
       };
     
       bool apply (hb_aat_apply_context_t *c) const
    @@ -337,9 +337,9 @@ struct ContextualSubtable
           const EntryData &data = entries[i].data;
     
           if (data.markIndex != 0xFFFF)
    -        num_lookups = MAX (num_lookups, 1 + data.markIndex);
    +        num_lookups = hb_max (num_lookups, 1 + data.markIndex);
           if (data.currentIndex != 0xFFFF)
    -        num_lookups = MAX (num_lookups, 1 + data.currentIndex);
    +        num_lookups = hb_max (num_lookups, 1 + data.currentIndex);
         }
     
         return_trace (substitutionTables.sanitize (c, this, num_lookups));
    @@ -348,7 +348,7 @@ struct ContextualSubtable
       protected:
       StateTable
                     machine;
    -  NNOffsetTo, HBUINT, false>, HBUINT>
    +  NNOffsetTo, HBUINT, false>, HBUINT>
                     substitutionTables;
       public:
       DEFINE_SIZE_STATIC (20);
    @@ -520,7 +520,7 @@ struct LigatureSubtable
               if (action & (LigActionStore | LigActionLast))
               {
                 ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
    -            const GlyphID &ligatureData = ligature[ligature_idx];
    +            const HBGlyphID &ligatureData = ligature[ligature_idx];
                 if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
                 hb_codepoint_t lig = ligatureData;
     
    @@ -554,7 +554,7 @@ struct LigatureSubtable
         const LigatureSubtable *table;
         const UnsizedArrayOf &ligAction;
         const UnsizedArrayOf &component;
    -    const UnsizedArrayOf &ligature;
    +    const UnsizedArrayOf &ligature;
         unsigned int match_length;
         unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
       };
    @@ -586,7 +586,7 @@ struct LigatureSubtable
                     ligAction;      /* Offset to the ligature action table. */
       NNOffsetTo, HBUINT>
                     component;      /* Offset to the component table. */
    -  NNOffsetTo, HBUINT>
    +  NNOffsetTo, HBUINT>
                     ligature;       /* Offset to the actual ligature lists. */
       public:
       DEFINE_SIZE_STATIC (28);
    @@ -606,7 +606,7 @@ struct NoncontextualSubtable
         unsigned int count = c->buffer->len;
         for (unsigned int i = 0; i < count; i++)
         {
    -      const GlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
    +      const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
           if (replacement)
           {
             info[i].codepoint = *replacement;
    @@ -624,7 +624,7 @@ struct NoncontextualSubtable
       }
     
       protected:
    -  Lookup       substitute;
    +  Lookup     substitute;
       public:
       DEFINE_SIZE_MIN (2);
     };
    @@ -725,8 +725,9 @@ struct InsertionSubtable
           if (entry.data.markedInsertIndex != 0xFFFF)
           {
             unsigned int count = (flags & MarkedInsertCount);
    +        if (unlikely ((buffer->max_ops -= count) <= 0)) return;
             unsigned int start = entry.data.markedInsertIndex;
    -        const GlyphID *glyphs = &insertionAction[start];
    +        const HBGlyphID *glyphs = &insertionAction[start];
             if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
     
             bool before = flags & MarkedInsertBefore;
    @@ -744,7 +745,7 @@ struct InsertionSubtable
     
             buffer->move_to (end + count);
     
    -        buffer->unsafe_to_break_from_outbuffer (mark, MIN (buffer->idx + 1, buffer->len));
    +        buffer->unsafe_to_break_from_outbuffer (mark, hb_min (buffer->idx + 1, buffer->len));
           }
     
           if (flags & SetMark)
    @@ -753,8 +754,9 @@ struct InsertionSubtable
           if (entry.data.currentInsertIndex != 0xFFFF)
           {
             unsigned int count = (flags & CurrentInsertCount) >> 5;
    +        if (unlikely ((buffer->max_ops -= count) <= 0)) return;
             unsigned int start = entry.data.currentInsertIndex;
    -        const GlyphID *glyphs = &insertionAction[start];
    +        const HBGlyphID *glyphs = &insertionAction[start];
             if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
     
             bool before = flags & CurrentInsertBefore;
    @@ -793,7 +795,7 @@ struct InsertionSubtable
         private:
         hb_aat_apply_context_t *c;
         unsigned int mark;
    -    const UnsizedArrayOf &insertionAction;
    +    const UnsizedArrayOf &insertionAction;
       };
     
       bool apply (hb_aat_apply_context_t *c) const
    @@ -819,7 +821,7 @@ struct InsertionSubtable
       protected:
       StateTable
                     machine;
    -  NNOffsetTo, HBUINT>
    +  NNOffsetTo, HBUINT>
                     insertionAction;        /* Byte offset from stateHeader to the start of
                                              * the insertion glyph table. */
       public:
    @@ -883,17 +885,17 @@ struct ChainSubtable
         Insertion           = 5
       };
     
    -  template 
    -  typename context_t::return_t dispatch (context_t *c) const
    +  template 
    +  typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
       {
         unsigned int subtable_type = get_type ();
         TRACE_DISPATCH (this, subtable_type);
         switch (subtable_type) {
    -    case Rearrangement:         return_trace (c->dispatch (u.rearrangement));
    -    case Contextual:            return_trace (c->dispatch (u.contextual));
    -    case Ligature:              return_trace (c->dispatch (u.ligature));
    -    case Noncontextual:         return_trace (c->dispatch (u.noncontextual));
    -    case Insertion:             return_trace (c->dispatch (u.insertion));
    +    case Rearrangement:         return_trace (c->dispatch (u.rearrangement, hb_forward (ds)...));
    +    case Contextual:            return_trace (c->dispatch (u.contextual, hb_forward (ds)...));
    +    case Ligature:              return_trace (c->dispatch (u.ligature, hb_forward (ds)...));
    +    case Noncontextual:         return_trace (c->dispatch (u.noncontextual, hb_forward (ds)...));
    +    case Insertion:             return_trace (c->dispatch (u.insertion, hb_forward (ds)...));
         default:                    return_trace (c->default_return_value ());
         }
       }
    @@ -948,8 +950,10 @@ struct Chain
             hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
             hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
           retry:
    -        const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type);
    -        if (info && info->setting == setting)
    +        // Check whether this type/setting pair was requested in the map, and if so, apply its flags.
    +        // (The search here only looks at the type and setting fields of feature_info_t.)
    +        hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
    +        if (map->features.bsearch (info))
             {
               flags &= feature.disableFlags;
               flags |= feature.enableFlags;
    @@ -967,9 +971,9 @@ struct Chain
       }
     
       void apply (hb_aat_apply_context_t *c,
    -                     hb_mask_t flags) const
    +              hb_mask_t flags) const
       {
    -    const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount));
    +    const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
         unsigned int count = subtableCount;
         for (unsigned int i = 0; i < count; i++)
         {
    @@ -1015,7 +1019,7 @@ struct Chain
                     bool (subtable->get_coverage () & ChainSubtable::Backwards) !=
                     HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
     
    -      if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index))
    +      if (!c->buffer->message (c->font, "start chainsubtable %d", c->lookup_index))
             goto skip;
     
           if (reverse)
    @@ -1026,12 +1030,12 @@ struct Chain
           if (reverse)
             c->buffer->reverse ();
     
    -      (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index);
    +      (void) c->buffer->message (c->font, "end chainsubtable %d", c->lookup_index);
     
           if (unlikely (!c->buffer->successful)) return;
     
         skip:
    -      subtable = &StructAfter > (*subtable);
    +      subtable = &StructAfter> (*subtable);
           c->set_lookup_index (c->lookup_index + 1);
         }
       }
    @@ -1049,13 +1053,13 @@ struct Chain
         if (!c->check_array (featureZ.arrayZ, featureCount))
           return_trace (false);
     
    -    const ChainSubtable *subtable = &StructAfter > (featureZ.as_array (featureCount));
    +    const ChainSubtable *subtable = &StructAfter> (featureZ.as_array (featureCount));
         unsigned int count = subtableCount;
         for (unsigned int i = 0; i < count; i++)
         {
           if (!subtable->sanitize (c))
             return_trace (false);
    -      subtable = &StructAfter > (*subtable);
    +      subtable = &StructAfter> (*subtable);
         }
     
         return_trace (true);
    @@ -1080,10 +1084,10 @@ struct Chain
      * The 'mort'/'morx' Table
      */
     
    -template 
    +template 
     struct mortmorx
     {
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
    +  static constexpr hb_tag_t tableTag = TAG;
     
       bool has_data () const { return version != 0; }
     
    @@ -1095,7 +1099,7 @@ struct mortmorx
         for (unsigned int i = 0; i < count; i++)
         {
           map->chain_flags.push (chain->compile_flags (mapper));
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
       }
     
    @@ -1109,7 +1113,7 @@ struct mortmorx
         {
           chain->apply (c, c->plan->aat_map.chain_flags[i]);
           if (unlikely (!c->buffer->successful)) return;
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
       }
     
    @@ -1125,7 +1129,7 @@ struct mortmorx
         {
           if (!chain->sanitize (c, version))
             return_trace (false);
    -      chain = &StructAfter > (*chain);
    +      chain = &StructAfter> (*chain);
         }
     
         return_trace (true);
    @@ -1143,14 +1147,8 @@ struct mortmorx
       DEFINE_SIZE_MIN (8);
     };
     
    -struct morx : mortmorx
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_morx;
    -};
    -struct mort : mortmorx
    -{
    -  static constexpr hb_tag_t tableTag = HB_AAT_TAG_mort;
    -};
    +struct morx : mortmorx {};
    +struct mort : mortmorx {};
     
     
     } /* namespace AAT */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
    new file mode 100644
    index 0000000000000..bfd476c77e079
    --- /dev/null
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-opbd-table.hh
    @@ -0,0 +1,173 @@
    +/*
    + * Copyright © 2019  Ebrahim Byagowi
    + *
    + *  This is part of HarfBuzz, a text shaping library.
    + *
    + * Permission is hereby granted, without written agreement and without
    + * license or royalty fees, to use, copy, modify, and distribute this
    + * software and its documentation for any purpose, provided that the
    + * above copyright notice and the following two paragraphs appear in
    + * all copies of this software.
    + *
    + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    + * DAMAGE.
    + *
    + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    + * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    + */
    +
    +#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
    +#define HB_AAT_LAYOUT_OPBD_TABLE_HH
    +
    +#include "hb-aat-layout-common.hh"
    +#include "hb-open-type.hh"
    +
    +/*
    + * opbd -- Optical Bounds
    + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
    + */
    +#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
    +
    +
    +namespace AAT {
    +
    +struct OpticalBounds
    +{
    +  bool sanitize (hb_sanitize_context_t *c) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this)));
    +  }
    +
    +  FWORD         leftSide;
    +  FWORD         topSide;
    +  FWORD         rightSide;
    +  FWORD         bottomSide;
    +  public:
    +  DEFINE_SIZE_STATIC (8);
    +};
    +
    +struct opbdFormat0
    +{
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents, const void *base) const
    +  {
    +    const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
    +    if (!bounds_offset) return false;
    +    const OpticalBounds &bounds = base+*bounds_offset;
    +
    +    if (extents)
    +      *extents = {
    +        font->em_scale_x (bounds.leftSide),
    +        font->em_scale_y (bounds.topSide),
    +        font->em_scale_x (bounds.rightSide),
    +        font->em_scale_y (bounds.bottomSide)
    +      };
    +    return true;
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c, const void *base) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
    +  }
    +
    +  protected:
    +  Lookup>
    +                lookupTable;    /* Lookup table associating glyphs with the four
    +                                 * int16 values for the left-side, top-side,
    +                                 * right-side, and bottom-side optical bounds. */
    +  public:
    +  DEFINE_SIZE_MIN (2);
    +};
    +
    +struct opbdFormat1
    +{
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents, const void *base) const
    +  {
    +    const OffsetTo *bounds_offset = lookupTable.get_value (glyph_id, font->face->get_num_glyphs ());
    +    if (!bounds_offset) return false;
    +    const OpticalBounds &bounds = base+*bounds_offset;
    +
    +    hb_position_t left = 0, top = 0, right = 0, bottom = 0, ignore;
    +    if (font->get_glyph_contour_point (glyph_id, bounds.leftSide, &left, &ignore) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.topSide, &ignore, &top) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.rightSide, &right, &ignore) ||
    +        font->get_glyph_contour_point (glyph_id, bounds.bottomSide, &ignore, &bottom))
    +    {
    +      if (extents)
    +        *extents = {left, top, right, bottom};
    +      return true;
    +    }
    +    return false;
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c, const void *base) const
    +  {
    +    TRACE_SANITIZE (this);
    +    return_trace (likely (c->check_struct (this) && lookupTable.sanitize (c, base)));
    +  }
    +
    +  protected:
    +  Lookup>
    +                lookupTable;    /* Lookup table associating glyphs with the four
    +                                 * int16 values for the left-side, top-side,
    +                                 * right-side, and bottom-side optical bounds. */
    +  public:
    +  DEFINE_SIZE_MIN (2);
    +};
    +
    +struct opbd
    +{
    +  static constexpr hb_tag_t tableTag = HB_AAT_TAG_opbd;
    +
    +  bool get_bounds (hb_font_t *font, hb_codepoint_t glyph_id,
    +                   hb_glyph_extents_t *extents) const
    +  {
    +    switch (format)
    +    {
    +    case 0: return u.format0.get_bounds (font, glyph_id, extents, this);
    +    case 1: return u.format1.get_bounds (font, glyph_id, extents, this);
    +    default:return false;
    +    }
    +  }
    +
    +  bool sanitize (hb_sanitize_context_t *c) const
    +  {
    +    TRACE_SANITIZE (this);
    +    if (unlikely (!c->check_struct (this) || version.major != 1))
    +      return_trace (false);
    +
    +    switch (format)
    +    {
    +    case 0: return_trace (u.format0.sanitize (c, this));
    +    case 1: return_trace (u.format1.sanitize (c, this));
    +    default:return_trace (true);
    +    }
    +  }
    +
    +  protected:
    +  FixedVersion<>version;        /* Version number of the optical bounds
    +                                 * table (0x00010000 for the current version). */
    +  HBUINT16      format;         /* Format of the optical bounds table.
    +                                 * Format 0 indicates distance and Format 1 indicates
    +                                 * control point. */
    +  union {
    +  opbdFormat0   format0;
    +  opbdFormat1   format1;
    +  } u;
    +  public:
    +  DEFINE_SIZE_MIN (8);
    +};
    +
    +} /* namespace AAT */
    +
    +
    +#endif /* HB_AAT_LAYOUT_OPBD_TABLE_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    index 469cae5a677bb..1643e14222957 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout-trak-table.hh
    @@ -62,11 +62,11 @@ struct TrackTableEntry
       }
     
       protected:
    -  Fixed         track;          /* Track value for this record. */
    +  HBFixed       track;          /* Track value for this record. */
       NameID        trackNameID;    /* The 'name' table index for this track.
                                      * (a short word or phrase like "loose"
                                      * or "very tight") */
    -  NNOffsetTo >
    +  NNOffsetTo>
                     valuesZ;        /* Offset from start of tracking table to
                                      * per-size tracking values for this track. */
     
    @@ -82,7 +82,7 @@ struct TrackData
                             const void *base) const
       {
         unsigned int sizes = nSizes;
    -    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
    +    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
     
         float s0 = size_table[idx].to_float ();
         float s1 = size_table[idx + 1].to_float ();
    @@ -93,13 +93,6 @@ struct TrackData
     
       int get_tracking (const void *base, float ptem) const
       {
    -    /* CoreText points are CSS pixels (96 per inch),
    -     * NOT typographic points (72 per inch).
    -     *
    -     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
    -     */
    -    float csspx = ptem * 96.f / 72.f;
    -
         /*
          * Choose track.
          */
    @@ -127,14 +120,14 @@ struct TrackData
         if (!sizes) return 0.;
         if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
     
    -    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
    +    hb_array_t size_table ((base+sizeTable).arrayZ, sizes);
         unsigned int size_index;
         for (size_index = 0; size_index < sizes - 1; size_index++)
    -      if (size_table[size_index].to_float () >= csspx)
    +      if (size_table[size_index].to_float () >= ptem)
             break;
     
    -    return round (interpolate_at (size_index ? size_index - 1 : 0, csspx,
    -                                  *trackTableEntry, base));
    +    return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
    +                                   *trackTableEntry, base));
       }
     
       bool sanitize (hb_sanitize_context_t *c, const void *base) const
    @@ -148,7 +141,7 @@ struct TrackData
       protected:
       HBUINT16      nTracks;        /* Number of separate tracks included in this table. */
       HBUINT16      nSizes;         /* Number of point sizes included in this table. */
    -  LOffsetTo, false>
    +  LNNOffsetTo>
                     sizeTable;      /* Offset from start of the tracking table to
                                      * Array[nSizes] of size values.. */
       UnsizedArrayOf
    @@ -217,7 +210,7 @@ struct trak
     
       protected:
       FixedVersion<>version;        /* Version of the tracking table
    -                                         * (0x00010000u for version 1.0). */
    +                                 * (0x00010000u for version 1.0). */
       HBUINT16      format;         /* Format of the tracking table (set to 0). */
       OffsetTo
                     horizData;      /* Offset from start of tracking table to TrackData
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    index a49729dd76191..38a5c57519dfd 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.cc
    @@ -25,11 +25,9 @@
      * Google Author(s): Behdad Esfahbod
      */
     
    -#include "hb-open-type.hh"
    +#include "hb.hh"
     
    -#include "hb-ot-face.hh"
     #include "hb-aat-layout.hh"
    -#include "hb-aat-fdsc-table.hh" // Just so we compile it; unused otherwise.
     #include "hb-aat-layout-ankr-table.hh"
     #include "hb-aat-layout-bsln-table.hh" // Just so we compile it; unused otherwise.
     #include "hb-aat-layout-feat-table.hh"
    @@ -40,6 +38,41 @@
     #include "hb-aat-ltag-table.hh"
     
     
    +/*
    + * hb_aat_apply_context_t
    + */
    +
    +/* Note: This context is used for kerning, even without AAT, hence the condition. */
    +#if !defined(HB_NO_AAT) || !defined(HB_NO_OT_KERN)
    +
    +AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
    +                                                     hb_font_t *font_,
    +                                                     hb_buffer_t *buffer_,
    +                                                     hb_blob_t *blob) :
    +                                                       plan (plan_),
    +                                                       font (font_),
    +                                                       face (font->face),
    +                                                       buffer (buffer_),
    +                                                       sanitizer (),
    +                                                       ankr_table (&Null (AAT::ankr)),
    +                                                       lookup_index (0)
    +{
    +  sanitizer.init (blob);
    +  sanitizer.set_num_glyphs (face->get_num_glyphs ());
    +  sanitizer.start_processing ();
    +  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
    +}
    +
    +AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
    +{ sanitizer.end_processing (); }
    +
    +void
    +AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
    +{ ankr_table = ankr_table_; }
    +
    +#endif
    +
    +
     /**
      * SECTION:hb-aat-layout
      * @title: hb-aat-layout
    @@ -50,6 +83,8 @@
      **/
     
     
    +#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT)
    +
     /* Table data courtesy of Apple.  Converted from mnemonics to integers
      * when moving to this file. */
     static const hb_aat_feature_mapping_t feature_mappings[] =
    @@ -135,44 +170,12 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
     const hb_aat_feature_mapping_t *
     hb_aat_layout_find_feature_mapping (hb_tag_t tag)
     {
    -  return (const hb_aat_feature_mapping_t *) bsearch (&tag,
    -                                                     feature_mappings,
    -                                                     ARRAY_LENGTH (feature_mappings),
    -                                                     sizeof (feature_mappings[0]),
    -                                                     hb_aat_feature_mapping_t::cmp);
    -}
    -
    -
    -/*
    - * hb_aat_apply_context_t
    - */
    -
    -AAT::hb_aat_apply_context_t::hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
    -                                                     hb_font_t *font_,
    -                                                     hb_buffer_t *buffer_,
    -                                                     hb_blob_t *blob) :
    -                                                       plan (plan_),
    -                                                       font (font_),
    -                                                       face (font->face),
    -                                                       buffer (buffer_),
    -                                                       sanitizer (),
    -                                                       ankr_table (&Null(AAT::ankr)),
    -                                                       lookup_index (0),
    -                                                       debug_depth (0)
    -{
    -  sanitizer.init (blob);
    -  sanitizer.set_num_glyphs (face->get_num_glyphs ());
    -  sanitizer.start_processing ();
    -  sanitizer.set_max_ops (HB_SANITIZE_MAX_OPS_MAX);
    +  return hb_sorted_array (feature_mappings).bsearch (tag);
     }
    +#endif
     
    -AAT::hb_aat_apply_context_t::~hb_aat_apply_context_t ()
    -{ sanitizer.end_processing (); }
    -
    -void
    -AAT::hb_aat_apply_context_t::set_ankr_table (const AAT::ankr *ankr_table_)
    -{ ankr_table = ankr_table_; }
     
    +#ifndef HB_NO_AAT
     
     /*
      * mort/morx/kerx/trak
    @@ -311,14 +314,6 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
       trak.apply (&c);
     }
     
    -
    -hb_language_t
    -_hb_aat_language_get (hb_face_t *face,
    -                      unsigned int i)
    -{
    -  return face->table.ltag->get_language (i);
    -}
    -
     /**
      * hb_aat_layout_get_feature_types:
      * @face: a face object
    @@ -382,3 +377,6 @@ hb_aat_layout_feature_type_get_selector_infos (hb_face_t
     {
       return face->table.feat->get_selector_infos (feature_type, start_offset, selector_count, selectors, default_index);
     }
    +
    +
    +#endif
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    index 42540f264e3a5..977599e6cc498 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.h
    @@ -85,7 +85,7 @@ typedef enum
       HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE                  = 39,
       HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE             = 103,
     
    -  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
    +  _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
     } hb_aat_layout_feature_type_t;
     
     /**
    @@ -424,7 +424,7 @@ typedef enum
       HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_CJK_ROMAN              = 2,
       HB_AAT_LAYOUT_FEATURE_SELECTOR_FULL_WIDTH_CJK_ROMAN           = 3,
     
    -  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE= 0x7FFFFFFFu, /*< skip >*/
    +  _HB_AAT_LAYOUT_FEATURE_SELECTOR_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
     } hb_aat_layout_feature_selector_t;
     
     HB_EXTERN unsigned int
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    index 80b6a1d0973de..1a95507ccee5e 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-layout.hh
    @@ -30,7 +30,7 @@
     #include "hb.hh"
     
     #include "hb-ot-shape.hh"
    -
    +#include "hb-aat-ltag-table.hh"
     
     struct hb_aat_feature_mapping_t
     {
    @@ -39,14 +39,8 @@ struct hb_aat_feature_mapping_t
       hb_aat_layout_feature_selector_t selectorToEnable;
       hb_aat_layout_feature_selector_t selectorToDisable;
     
    -  static int cmp (const void *key_, const void *entry_)
    -  {
    -    hb_tag_t key = * (unsigned int *) key_;
    -    const hb_aat_feature_mapping_t * entry = (const hb_aat_feature_mapping_t *) entry_;
    -    return key < entry->otFeatureTag ? -1 :
    -           key > entry->otFeatureTag ? 1 :
    -           0;
    -  }
    +  int cmp (hb_tag_t key) const
    +  { return key < otFeatureTag ? -1 : key > otFeatureTag ? 1 : 0; }
     };
     
     HB_INTERNAL const hb_aat_feature_mapping_t *
    @@ -77,9 +71,5 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
                          hb_font_t *font,
                          hb_buffer_t *buffer);
     
    -HB_INTERNAL hb_language_t
    -_hb_aat_language_get (hb_face_t *face,
    -                      unsigned int i);
    -
     
     #endif /* HB_AAT_LAYOUT_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    index 23649f827ff04..f42ca23e0d87b 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-ltag-table.hh
    @@ -50,7 +50,7 @@ struct FTStringRange
       }
     
       protected:
    -  NNOffsetTo >
    +  NNOffsetTo>
                     tag;            /* Offset from the start of the table to
                                      * the beginning of the string */
       HBUINT16      length;         /* String length (in bytes) */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    index c3d078dbd6520..ad3eff7935fe2 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.cc
    @@ -26,28 +26,55 @@
      * Google Author(s): Behdad Esfahbod
      */
     
    +#include "hb.hh"
    +
    +#ifndef HB_NO_AAT_SHAPE
    +
     #include "hb-aat-map.hh"
     
     #include "hb-aat-layout.hh"
    +#include "hb-aat-layout-feat-table.hh"
     
     
    -void hb_aat_map_builder_t::add_feature (hb_tag_t tag,
    -                                        unsigned int value)
    +void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
     {
    +  if (!face->table.feat->has_data ()) return;
    +
       if (tag == HB_TAG ('a','a','l','t'))
       {
    +    if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
    +      return;
         feature_info_t *info = features.push();
         info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
         info->setting = (hb_aat_layout_feature_selector_t) value;
    +    info->seq = features.length;
    +    info->is_exclusive = true;
         return;
       }
     
       const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
       if (!mapping) return;
     
    +  const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
    +  if (!feature->has_data ())
    +  {
    +    /* Special case: Chain::compile_flags will fall back to the deprecated version of
    +     * small-caps if necessary, so we need to check for that possibility.
    +     * https://github.com/harfbuzz/harfbuzz/issues/2307 */
    +    if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
    +        mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
    +    {
    +      feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
    +      if (!feature->has_data ()) return;
    +    }
    +    else return;
    +  }
    +
       feature_info_t *info = features.push();
       info->type = mapping->aatFeatureType;
       info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
    +  info->seq = features.length;
    +  info->is_exclusive = feature->is_exclusive ();
     }
     
     void
    @@ -59,10 +86,17 @@ hb_aat_map_builder_t::compile (hb_aat_map_t  &m)
         features.qsort ();
         unsigned int j = 0;
         for (unsigned int i = 1; i < features.length; i++)
    -      if (features[i].type != features[j].type)
    +      if (features[i].type != features[j].type ||
    +          /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
    +           * respectively, so we mask out the low-order bit when checking for "duplicates"
    +           * (selectors referring to the same feature setting) here. */
    +          (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
             features[++j] = features[i];
         features.shrink (j + 1);
       }
     
       hb_aat_layout_compile_map (this, &m);
     }
    +
    +
    +#endif
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    index 594d48e7cd51b..ce30daa608451 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-aat-map.hh
    @@ -64,19 +64,24 @@ struct hb_aat_map_builder_t
       {
         hb_aat_layout_feature_type_t  type;
         hb_aat_layout_feature_selector_t  setting;
    +    bool is_exclusive;
         unsigned  seq; /* For stable sorting only. */
     
    -    static int cmp (const void *pa, const void *pb)
    +    HB_INTERNAL static int cmp (const void *pa, const void *pb)
         {
           const feature_info_t *a = (const feature_info_t *) pa;
           const feature_info_t *b = (const feature_info_t *) pb;
    -      return (a->type != b->type) ? (a->type < b->type ? -1 : 1) :
    -             (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
    +      if (a->type != b->type) return (a->type < b->type ? -1 : 1);
    +      if (!a->is_exclusive &&
    +          (a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1);
    +            return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
         }
     
    -    int cmp (hb_aat_layout_feature_type_t ty) const
    +    /* compares type & setting only, not is_exclusive flag or seq number */
    +    int cmp (const feature_info_t& f) const
         {
    -      return (type != ty) ? (type < ty ? -1 : 1) : 0;
    +      return (f.type != type) ? (f.type < type ? -1 : 1) :
    +             (f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0;
         }
       };
     
    @@ -84,7 +89,7 @@ struct hb_aat_map_builder_t
       hb_face_t *face;
     
       public:
    -  hb_vector_t features;
    +  hb_sorted_vector_t features;
     };
     
     
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-algs.hh b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
    new file mode 100644
    index 0000000000000..e21cb5429c90c
    --- /dev/null
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-algs.hh
    @@ -0,0 +1,1127 @@
    +/*
    + * Copyright © 2017  Google, Inc.
    + * Copyright © 2019  Facebook, Inc.
    + *
    + *  This is part of HarfBuzz, a text shaping library.
    + *
    + * Permission is hereby granted, without written agreement and without
    + * license or royalty fees, to use, copy, modify, and distribute this
    + * software and its documentation for any purpose, provided that the
    + * above copyright notice and the following two paragraphs appear in
    + * all copies of this software.
    + *
    + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
    + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
    + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
    + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    + * DAMAGE.
    + *
    + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
    + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    + * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
    + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
    + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    + *
    + * Google Author(s): Behdad Esfahbod
    + * Facebook Author(s): Behdad Esfahbod
    + */
    +
    +#ifndef HB_ALGS_HH
    +#define HB_ALGS_HH
    +
    +#include "hb.hh"
    +#include "hb-meta.hh"
    +#include "hb-null.hh"
    +#include "hb-number.hh"
    +
    +
    +/* Encodes three unsigned integers in one 64-bit number.  If the inputs have more than 21 bits,
    + * values will be truncated / overlap, and might not decode exactly. */
    +#define HB_CODEPOINT_ENCODE3(x,y,z) (((uint64_t) (x) << 42) | ((uint64_t) (y) << 21) | (uint64_t) (z))
    +#define HB_CODEPOINT_DECODE3_1(v) ((hb_codepoint_t) ((v) >> 42))
    +#define HB_CODEPOINT_DECODE3_2(v) ((hb_codepoint_t) ((v) >> 21) & 0x1FFFFFu)
    +#define HB_CODEPOINT_DECODE3_3(v) ((hb_codepoint_t) (v) & 0x1FFFFFu)
    +
    +/* Custom encoding used by hb-ucd. */
    +#define HB_CODEPOINT_ENCODE3_11_7_14(x,y,z) (((uint32_t) ((x) & 0x07FFu) << 21) | (((uint32_t) (y) & 0x007Fu) << 14) | (uint32_t) ((z) & 0x3FFFu))
    +#define HB_CODEPOINT_DECODE3_11_7_14_1(v) ((hb_codepoint_t) ((v) >> 21))
    +#define HB_CODEPOINT_DECODE3_11_7_14_2(v) ((hb_codepoint_t) (((v) >> 14) & 0x007Fu) | 0x0300)
    +#define HB_CODEPOINT_DECODE3_11_7_14_3(v) ((hb_codepoint_t) (v) & 0x3FFFu)
    +
    +struct
    +{
    +  /* Note.  This is dangerous in that if it's passed an rvalue, it returns rvalue-reference. */
    +  template  constexpr auto
    +  operator () (T&& v) const HB_AUTO_RETURN ( hb_forward (v) )
    +}
    +HB_FUNCOBJ (hb_identity);
    +struct
    +{
    +  /* Like identity(), but only retains lvalue-references.  Rvalues are returned as rvalues. */
    +  template  constexpr T&
    +  operator () (T& v) const { return v; }
    +
    +  template  constexpr hb_remove_reference
    +  operator () (T&& v) const { return v; }
    +}
    +HB_FUNCOBJ (hb_lidentity);
    +struct
    +{
    +  /* Like identity(), but always returns rvalue. */
    +  template  constexpr hb_remove_reference
    +  operator () (T&& v) const { return v; }
    +}
    +HB_FUNCOBJ (hb_ridentity);
    +
    +struct
    +{
    +  template  constexpr bool
    +  operator () (T&& v) const { return bool (hb_forward (v)); }
    +}
    +HB_FUNCOBJ (hb_bool);
    +
    +struct
    +{
    +  private:
    +
    +  template  constexpr auto
    +  impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
    +
    +  template  constexpr auto
    +  impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    /* Knuth's multiplicative method: */
    +    (uint32_t) v * 2654435761u
    +  )
    +
    +  public:
    +
    +  template  constexpr auto
    +  operator () (const T& v) const HB_RETURN (uint32_t, impl (v, hb_prioritize))
    +}
    +HB_FUNCOBJ (hb_hash);
    +
    +
    +struct
    +{
    +  private:
    +
    +  /* Pointer-to-member-function. */
    +  template  auto
    +  impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN
    +  ((hb_deref (hb_forward (v)).*hb_forward (a)) (hb_forward (ds)...))
    +
    +  /* Pointer-to-member. */
    +  template  auto
    +  impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN
    +  ((hb_deref (hb_forward (v))).*hb_forward (a))
    +
    +  /* Operator(). */
    +  template  auto
    +  impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (a)) (hb_forward (ds)...))
    +
    +  public:
    +
    +  template  auto
    +  operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN
    +  (
    +    impl (hb_forward (a),
    +          hb_prioritize,
    +          hb_forward (ds)...)
    +  )
    +}
    +HB_FUNCOBJ (hb_invoke);
    +
    +template 
    +struct hb_partial_t
    +{
    +  hb_partial_t (Appl a, V v) : a (a), v (v) {}
    +
    +  static_assert (Pos > 0, "");
    +
    +  template  auto
    +  operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
    +                                                   hb_declval (V),
    +                                                   hb_declval (Ts)...))
    +  {
    +    return hb_invoke (hb_forward (a),
    +                      hb_forward (v),
    +                      hb_forward (ds)...);
    +  }
    +  template  auto
    +  operator () (T0&& d0, Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl),
    +                                                            hb_declval (T0),
    +                                                            hb_declval (V),
    +                                                            hb_declval (Ts)...))
    +  {
    +    return hb_invoke (hb_forward (a),
    +                      hb_forward (d0),
    +                      hb_forward (v),
    +                      hb_forward (ds)...);
    +  }
    +
    +  private:
    +  hb_reference_wrapper a;
    +  V v;
    +};
    +template 
    +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN
    +(( hb_partial_t (a, v) ))
    +
    +/* The following, HB_PARTIALIZE, macro uses a particular corner-case
    + * of C++11 that is not particularly well-supported by all compilers.
    + * What's happening is that it's using "this" in a trailing return-type
    + * via decltype().  Broken compilers deduce the type of "this" pointer
    + * in that context differently from what it resolves to in the body
    + * of the function.
    + *
    + * One probable cause of this is that at the time of trailing return
    + * type declaration, "this" points to an incomplete type, whereas in
    + * the function body the type is complete.  That doesn't justify the
    + * error in any way, but is probably what's happening.
    + *
    + * In the case of MSVC, we get around this by using C++14 "decltype(auto)"
    + * which deduces the type from the actual return statement.  For gcc 4.8
    + * we use "+this" instead of "this" which produces an rvalue that seems
    + * to be deduced as the same type with this particular compiler, and seem
    + * to be fine as default code path as well.
    + */
    +#ifdef _MSC_VER
    +/* https://github.com/harfbuzz/harfbuzz/issues/1730 */ \
    +#define HB_PARTIALIZE(Pos) \
    +  template  \
    +  decltype(auto) operator () (_T&& _v) const \
    +  { return hb_partial (this, hb_forward<_T> (_v)); } \
    +  static_assert (true, "")
    +#else
    +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */
    +#define HB_PARTIALIZE(Pos) \
    +  template  \
    +  auto operator () (_T&& _v) const HB_AUTO_RETURN \
    +  (hb_partial (+this, hb_forward<_T> (_v))) \
    +  static_assert (true, "")
    +#endif
    +
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (p)).has (hb_forward (v)))
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_invoke (hb_forward (p),
    +               hb_forward (v))
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
    +    impl (hb_forward (p),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_has);
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (
    +    hb_has (hb_forward (p),
    +            hb_forward (v))
    +  )
    +
    +  template  auto
    +  impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_forward (p) == hb_forward (v)
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
    +    impl (hb_forward (p),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_match);
    +
    +struct
    +{
    +  private:
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN
    +  (hb_deref (hb_forward (f)).get (hb_forward (v)))
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
    +  (
    +    hb_invoke (hb_forward (f),
    +               hb_forward (v))
    +  )
    +
    +  template  auto
    +  impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
    +  (
    +    hb_forward (f)[hb_forward (v)]
    +  )
    +
    +  public:
    +
    +  template  auto
    +  operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN
    +  (
    +    impl (hb_forward (f),
    +          hb_forward (v),
    +          hb_prioritize)
    +  )
    +}
    +HB_FUNCOBJ (hb_get);
    +
    +
    +template 
    +struct hb_pair_t
    +{
    +  typedef T1 first_t;
    +  typedef T2 second_t;
    +  typedef hb_pair_t pair_t;
    +
    +  hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
    +
    +  template 
    +  operator hb_pair_t () { return hb_pair_t (first, second); }
    +
    +  hb_pair_t reverse () const
    +  { return hb_pair_t (second, first); }
    +
    +  bool operator == (const pair_t& o) const { return first == o.first && second == o.second; }
    +  bool operator != (const pair_t& o) const { return !(*this == o); }
    +  bool operator < (const pair_t& o) const { return first < o.first || (first == o.first && second < o.second); }
    +  bool operator >= (const pair_t& o) const { return !(*this < o); }
    +  bool operator > (const pair_t& o) const { return first > o.first || (first == o.first && second > o.second); }
    +  bool operator <= (const pair_t& o) const { return !(*this > o); }
    +
    +  T1 first;
    +  T2 second;
    +};
    +#define hb_pair_t(T1,T2) hb_pair_t
    +template  static inline hb_pair_t
    +hb_pair (T1&& a, T2&& b) { return hb_pair_t (a, b); }
    +
    +struct
    +{
    +  template  constexpr typename Pair::first_t
    +  operator () (const Pair& pair) const { return pair.first; }
    +}
    +HB_FUNCOBJ (hb_first);
    +
    +struct
    +{
    +  template  constexpr typename Pair::second_t
    +  operator () (const Pair& pair) const { return pair.second; }
    +}
    +HB_FUNCOBJ (hb_second);
    +
    +/* Note.  In min/max impl, we can use hb_type_identity for second argument.
    + * However, that would silently convert between different-signedness integers.
    + * Instead we accept two different types, such that compiler can err if
    + * comparing integers of different signedness. */
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
    +  (hb_forward (a) <= hb_forward (b) ? hb_forward (a) : hb_forward (b))
    +}
    +HB_FUNCOBJ (hb_min);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& a, T2&& b) const HB_AUTO_RETURN
    +  (hb_forward (a) >= hb_forward (b) ? hb_forward (a) : hb_forward (b))
    +}
    +HB_FUNCOBJ (hb_max);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN
    +  (hb_min (hb_max (hb_forward (x), hb_forward (min)), hb_forward (max)))
    +}
    +HB_FUNCOBJ (hb_clamp);
    +
    +
    +/*
    + * Bithacks.
    + */
    +
    +/* Return the number of 1 bits in v. */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_popcount (T v)
    +{
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return __builtin_popcount (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return __builtin_popcountl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return __builtin_popcountll (v);
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "HACKMEM 169" */
    +    uint32_t y;
    +    y = (v >> 1) &033333333333;
    +    y = v - y - ((y >>1) & 033333333333);
    +    return (((y + (y >> 3)) & 030707070707) % 077);
    +  }
    +
    +  if (sizeof (T) == 8)
    +  {
    +    unsigned int shift = 32;
    +    return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift));
    +  }
    +
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift));
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +/* Returns the number of bits needed to store number */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_bit_storage (T v)
    +{
    +  if (unlikely (!v)) return 0;
    +
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return sizeof (unsigned int) * 8 - __builtin_clz (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return sizeof (unsigned long) * 8 - __builtin_clzl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return sizeof (unsigned long long) * 8 - __builtin_clzll (v);
    +#endif
    +
    +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
    +  if (sizeof (T) <= sizeof (unsigned int))
    +  {
    +    unsigned long where;
    +    _BitScanReverse (&where, v);
    +    return 1 + where;
    +  }
    +# if defined(_WIN64)
    +  if (sizeof (T) <= 8)
    +  {
    +    unsigned long where;
    +    _BitScanReverse64 (&where, v);
    +    return 1 + where;
    +  }
    +# endif
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "bithacks" */
    +    const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
    +    const unsigned int S[] = {1, 2, 4, 8, 16};
    +    unsigned int r = 0;
    +    for (int i = 4; i >= 0; i--)
    +      if (v & b[i])
    +      {
    +        v >>= S[i];
    +        r |= S[i];
    +      }
    +    return r + 1;
    +  }
    +  if (sizeof (T) <= 8)
    +  {
    +    /* "bithacks" */
    +    const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL};
    +    const unsigned int S[] = {1, 2, 4, 8, 16, 32};
    +    unsigned int r = 0;
    +    for (int i = 5; i >= 0; i--)
    +      if (v & b[i])
    +      {
    +        v >>= S[i];
    +        r |= S[i];
    +      }
    +    return r + 1;
    +  }
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift :
    +                          hb_bit_storage ((uint64_t) v);
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +/* Returns the number of zero bits in the least significant side of v */
    +template 
    +static inline HB_CONST_FUNC unsigned int
    +hb_ctz (T v)
    +{
    +  if (unlikely (!v)) return 8 * sizeof (T);
    +
    +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
    +  if (sizeof (T) <= sizeof (unsigned int))
    +    return __builtin_ctz (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long))
    +    return __builtin_ctzl (v);
    +
    +  if (sizeof (T) <= sizeof (unsigned long long))
    +    return __builtin_ctzll (v);
    +#endif
    +
    +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || (defined(__MINGW32__) && (__GNUC__ < 4))
    +  if (sizeof (T) <= sizeof (unsigned int))
    +  {
    +    unsigned long where;
    +    _BitScanForward (&where, v);
    +    return where;
    +  }
    +# if defined(_WIN64)
    +  if (sizeof (T) <= 8)
    +  {
    +    unsigned long where;
    +    _BitScanForward64 (&where, v);
    +    return where;
    +  }
    +# endif
    +#endif
    +
    +  if (sizeof (T) <= 4)
    +  {
    +    /* "bithacks" */
    +    unsigned int c = 32;
    +    v &= - (int32_t) v;
    +    if (v) c--;
    +    if (v & 0x0000FFFF) c -= 16;
    +    if (v & 0x00FF00FF) c -= 8;
    +    if (v & 0x0F0F0F0F) c -= 4;
    +    if (v & 0x33333333) c -= 2;
    +    if (v & 0x55555555) c -= 1;
    +    return c;
    +  }
    +  if (sizeof (T) <= 8)
    +  {
    +    /* "bithacks" */
    +    unsigned int c = 64;
    +    v &= - (int64_t) (v);
    +    if (v) c--;
    +    if (v & 0x00000000FFFFFFFFULL) c -= 32;
    +    if (v & 0x0000FFFF0000FFFFULL) c -= 16;
    +    if (v & 0x00FF00FF00FF00FFULL) c -= 8;
    +    if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4;
    +    if (v & 0x3333333333333333ULL) c -= 2;
    +    if (v & 0x5555555555555555ULL) c -= 1;
    +    return c;
    +  }
    +  if (sizeof (T) == 16)
    +  {
    +    unsigned int shift = 64;
    +    return (uint64_t) v ? hb_bit_storage ((uint64_t) v) :
    +                          hb_bit_storage ((uint64_t) (v >> shift)) + shift;
    +  }
    +
    +  assert (0);
    +  return 0; /* Shut up stupid compiler. */
    +}
    +
    +
    +/*
    + * Tiny stuff.
    + */
    +
    +/* ASCII tag/character handling */
    +static inline bool ISALPHA (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
    +static inline bool ISALNUM (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
    +static inline bool ISSPACE (unsigned char c)
    +{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
    +static inline unsigned char TOUPPER (unsigned char c)
    +{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
    +static inline unsigned char TOLOWER (unsigned char c)
    +{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; }
    +static inline bool ISHEX (unsigned char c)
    +{ return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); }
    +static inline unsigned char TOHEX (uint8_t c)
    +{ return (c & 0xF) <= 9 ? (c & 0xF) + '0' : (c & 0xF) + 'a' - 10; }
    +static inline uint8_t FROMHEX (unsigned char c)
    +{ return (c >= '0' && c <= '9') ? c - '0' : TOLOWER (c) - 'a' + 10; }
    +
    +static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
    +{ return (a + (b - 1)) / b; }
    +
    +
    +#undef  ARRAY_LENGTH
    +template 
    +static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
    +/* A const version, but does not detect erratically being called on pointers. */
    +#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
    +
    +
    +static inline int
    +hb_memcmp (const void *a, const void *b, unsigned int len)
    +{
    +  /* It's illegal to pass NULL to memcmp(), even if len is zero.
    +   * So, wrap it.
    +   * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */
    +  if (unlikely (!len)) return 0;
    +  return memcmp (a, b, len);
    +}
    +
    +static inline void *
    +hb_memset (void *s, int c, unsigned int n)
    +{
    +  /* It's illegal to pass NULL to memset(), even if n is zero. */
    +  if (unlikely (!n)) return 0;
    +  return memset (s, c, n);
    +}
    +
    +static inline unsigned int
    +hb_ceil_to_4 (unsigned int v)
    +{
    +  return ((v - 1) | 3) + 1;
    +}
    +
    +template  static inline bool
    +hb_in_range (T u, T lo, T hi)
    +{
    +  static_assert (!hb_is_signed::value, "");
    +
    +  /* The casts below are important as if T is smaller than int,
    +   * the subtract results will become a signed int! */
    +  return (T)(u - lo) <= (T)(hi - lo);
    +}
    +template  static inline bool
    +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2)
    +{
    +  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
    +}
    +template  static inline bool
    +hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
    +{
    +  return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
    +}
    +
    +
    +/*
    + * Overflow checking.
    + */
    +
    +/* Consider __builtin_mul_overflow use here also */
    +static inline bool
    +hb_unsigned_mul_overflows (unsigned int count, unsigned int size)
    +{
    +  return (size > 0) && (count >= ((unsigned int) -1) / size);
    +}
    +
    +
    +/*
    + * Sort and search.
    + */
    +
    +template 
    +static int
    +_hb_cmp_method (const void *pkey, const void *pval, Ts... ds)
    +{
    +  const K& key = * (const K*) pkey;
    +  const V& val = * (const V*) pval;
    +
    +  return val.cmp (key, ds...);
    +}
    +
    +template 
    +static inline bool
    +hb_bsearch_impl (unsigned *pos, /* Out */
    +                 const K& key,
    +                 V* base, size_t nmemb, size_t stride,
    +                 int (*compar)(const void *_key, const void *_item, Ts... _ds),
    +                 Ts... ds)
    +{
    +  /* This is our *only* bsearch implementation. */
    +
    +  int min = 0, max = (int) nmemb - 1;
    +  while (min <= max)
    +  {
    +    int mid = ((unsigned int) min + (unsigned int) max) / 2;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +    V* p = (V*) (((const char *) base) + (mid * stride));
    +#pragma GCC diagnostic pop
    +    int c = compar ((const void *) hb_addressof (key), (const void *) p, ds...);
    +    if (c < 0)
    +      max = mid - 1;
    +    else if (c > 0)
    +      min = mid + 1;
    +    else
    +    {
    +      *pos = mid;
    +      return true;
    +    }
    +  }
    +  *pos = min;
    +  return false;
    +}
    +
    +template 
    +static inline V*
    +hb_bsearch (const K& key, V* base,
    +            size_t nmemb, size_t stride = sizeof (V),
    +            int (*compar)(const void *_key, const void *_item) = _hb_cmp_method)
    +{
    +  unsigned pos;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +  return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar) ?
    +         (V*) (((const char *) base) + (pos * stride)) : nullptr;
    +#pragma GCC diagnostic pop
    +}
    +template 
    +static inline V*
    +hb_bsearch (const K& key, V* base,
    +            size_t nmemb, size_t stride,
    +            int (*compar)(const void *_key, const void *_item, Ts... _ds),
    +            Ts... ds)
    +{
    +  unsigned pos;
    +#pragma GCC diagnostic push
    +#pragma GCC diagnostic ignored "-Wcast-align"
    +  return hb_bsearch_impl (&pos, key, base, nmemb, stride, compar, ds...) ?
    +         (V*) (((const char *) base) + (pos * stride)) : nullptr;
    +#pragma GCC diagnostic pop
    +}
    +
    +
    +/* From https://github.com/noporpoise/sort_r
    +   Feb 5, 2019 (c8c65c1e)
    +   Modified to support optional argument using templates */
    +
    +/* Isaac Turner 29 April 2014 Public Domain */
    +
    +/*
    +hb_qsort function to be exported.
    +Parameters:
    +  base is the array to be sorted
    +  nel is the number of elements in the array
    +  width is the size in bytes of each element of the array
    +  compar is the comparison function
    +  arg (optional) is a pointer to be passed to the comparison function
    +
    +void hb_qsort(void *base, size_t nel, size_t width,
    +              int (*compar)(const void *_a, const void *_b, [void *_arg]),
    +              [void *arg]);
    +*/
    +
    +#define SORT_R_SWAP(a,b,tmp) ((tmp) = (a), (a) = (b), (b) = (tmp))
    +
    +/* swap a and b */
    +/* a and b must not be equal! */
    +static inline void sort_r_swap(char *__restrict a, char *__restrict b,
    +                               size_t w)
    +{
    +  char tmp, *end = a+w;
    +  for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); }
    +}
    +
    +/* swap a, b iff a>b */
    +/* a and b must not be equal! */
    +/* __restrict is same as restrict but better support on old machines */
    +template 
    +static inline int sort_r_cmpswap(char *__restrict a,
    +                                 char *__restrict b, size_t w,
    +                                 int (*compar)(const void *_a,
    +                                               const void *_b,
    +                                               Ts... _ds),
    +                                 Ts... ds)
    +{
    +  if(compar(a, b, ds...) > 0) {
    +    sort_r_swap(a, b, w);
    +    return 1;
    +  }
    +  return 0;
    +}
    +
    +/*
    +Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr,
    +with the smallest swap so that the blocks are in the opposite order. Blocks may
    +be internally re-ordered e.g.
    +  12345ab  ->   ab34512
    +  123abc   ->   abc123
    +  12abcde  ->   deabc12
    +*/
    +static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb)
    +{
    +  if(na > 0 && nb > 0) {
    +    if(na > nb) { sort_r_swap(ptr, ptr+na, nb); }
    +    else { sort_r_swap(ptr, ptr+nb, na); }
    +  }
    +}
    +
    +/* Implement recursive quicksort ourselves */
    +/* Note: quicksort is not stable, equivalent values may be swapped */
    +template 
    +static inline void sort_r_simple(void *base, size_t nel, size_t w,
    +                                 int (*compar)(const void *_a,
    +                                               const void *_b,
    +                                               Ts... _ds),
    +                                 Ts... ds)
    +{
    +  char *b = (char *)base, *end = b + nel*w;
    +
    +  /* for(size_t i=0; i b && sort_r_cmpswap(pj-w,pj,w,compar,ds...); pj -= w) {}
    +    }
    +  }
    +  else
    +  {
    +    /* nel > 9; Quicksort */
    +
    +    int cmp;
    +    char *pl, *ple, *pr, *pre, *pivot;
    +    char *last = b+w*(nel-1), *tmp;
    +
    +    /*
    +    Use median of second, middle and second-last items as pivot.
    +    First and last may have been swapped with pivot and therefore be extreme
    +    */
    +    char *l[3];
    +    l[0] = b + w;
    +    l[1] = b+w*(nel/2);
    +    l[2] = last - w;
    +
    +    /* printf("pivots: %i, %i, %i\n", *(int*)l[0], *(int*)l[1], *(int*)l[2]); */
    +
    +    if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
    +    if(compar(l[1],l[2],ds...) > 0) {
    +      SORT_R_SWAP(l[1], l[2], tmp);
    +      if(compar(l[0],l[1],ds...) > 0) { SORT_R_SWAP(l[0], l[1], tmp); }
    +    }
    +
    +    /* swap mid value (l[1]), and last element to put pivot as last element */
    +    if(l[1] != last) { sort_r_swap(l[1], last, w); }
    +
    +    /*
    +    pl is the next item on the left to be compared to the pivot
    +    pr is the last item on the right that was compared to the pivot
    +    ple is the left position to put the next item that equals the pivot
    +    ple is the last right position where we put an item that equals the pivot
    +                                           v- end (beyond the array)
    +      EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE.
    +      ^- b  ^- ple  ^- pl   ^- pr  ^- pre ^- last (where the pivot is)
    +    Pivot comparison key:
    +      E = equal, L = less than, u = unknown, G = greater than, E = equal
    +    */
    +    pivot = last;
    +    ple = pl = b;
    +    pre = pr = last;
    +
    +    /*
    +    Strategy:
    +    Loop into the list from the left and right at the same time to find:
    +    - an item on the left that is greater than the pivot
    +    - an item on the right that is less than the pivot
    +    Once found, they are swapped and the loop continues.
    +    Meanwhile items that are equal to the pivot are moved to the edges of the
    +    array.
    +    */
    +    while(pl < pr) {
    +      /* Move left hand items which are equal to the pivot to the far left.
    +         break when we find an item that is greater than the pivot */
    +      for(; pl < pr; pl += w) {
    +        cmp = compar(pl, pivot, ds...);
    +        if(cmp > 0) { break; }
    +        else if(cmp == 0) {
    +          if(ple < pl) { sort_r_swap(ple, pl, w); }
    +          ple += w;
    +        }
    +      }
    +      /* break if last batch of left hand items were equal to pivot */
    +      if(pl >= pr) { break; }
    +      /* Move right hand items which are equal to the pivot to the far right.
    +         break when we find an item that is less than the pivot */
    +      for(; pl < pr; ) {
    +        pr -= w; /* Move right pointer onto an unprocessed item */
    +        cmp = compar(pr, pivot, ds...);
    +        if(cmp == 0) {
    +          pre -= w;
    +          if(pr < pre) { sort_r_swap(pr, pre, w); }
    +        }
    +        else if(cmp < 0) {
    +          if(pl < pr) { sort_r_swap(pl, pr, w); }
    +          pl += w;
    +          break;
    +        }
    +      }
    +    }
    +
    +    pl = pr; /* pr may have gone below pl */
    +
    +    /*
    +    Now we need to go from: EEELLLGGGGEEEE
    +                        to: LLLEEEEEEEGGGG
    +    Pivot comparison key:
    +      E = equal, L = less than, u = unknown, G = greater than, E = equal
    +    */
    +    sort_r_swap_blocks(b, ple-b, pl-ple);
    +    sort_r_swap_blocks(pr, pre-pr, end-pre);
    +
    +    /*for(size_t i=0; i static inline void
    +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
    +{
    +  for (unsigned int i = 1; i < len; i++)
    +  {
    +    unsigned int j = i;
    +    while (j && compar (&array[j - 1], &array[i]) > 0)
    +      j--;
    +    if (i == j)
    +      continue;
    +    /* Move item i to occupy place for item j, shift what's in between. */
    +    {
    +      T t = array[i];
    +      memmove (&array[j + 1], &array[j], (i - j) * sizeof (T));
    +      array[j] = t;
    +    }
    +    if (array2)
    +    {
    +      T3 t = array2[i];
    +      memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T3));
    +      array2[j] = t;
    +    }
    +  }
    +}
    +
    +template  static inline void
    +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
    +{
    +  hb_stable_sort (array, len, compar, (int *) nullptr);
    +}
    +
    +static inline hb_bool_t
    +hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
    +{
    +  unsigned int v;
    +  const char *p = s;
    +  const char *end = p + len;
    +  if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */, base)))
    +    return false;
    +
    +  *out = v;
    +  return true;
    +}
    +
    +
    +/* Operators. */
    +
    +struct hb_bitwise_and
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = false;
    +  static constexpr bool passthru_right = false;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & b)
    +}
    +HB_FUNCOBJ (hb_bitwise_and);
    +struct hb_bitwise_or
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = true;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | b)
    +}
    +HB_FUNCOBJ (hb_bitwise_or);
    +struct hb_bitwise_xor
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = true;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a ^ b)
    +}
    +HB_FUNCOBJ (hb_bitwise_xor);
    +struct hb_bitwise_sub
    +{ HB_PARTIALIZE(2);
    +  static constexpr bool passthru_left = true;
    +  static constexpr bool passthru_right = false;
    +  template  constexpr auto
    +  operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b)
    +}
    +HB_FUNCOBJ (hb_bitwise_sub);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (~a)
    +}
    +HB_FUNCOBJ (hb_bitwise_neg);
    +
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b)
    +}
    +HB_FUNCOBJ (hb_add);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b)
    +}
    +HB_FUNCOBJ (hb_sub);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b)
    +}
    +HB_FUNCOBJ (hb_mul);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b)
    +}
    +HB_FUNCOBJ (hb_div);
    +struct
    +{ HB_PARTIALIZE(2);
    +  template  constexpr auto
    +  operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b)
    +}
    +HB_FUNCOBJ (hb_mod);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (+a)
    +}
    +HB_FUNCOBJ (hb_pos);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (const T &a) const HB_AUTO_RETURN (-a)
    +}
    +HB_FUNCOBJ (hb_neg);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T &a) const HB_AUTO_RETURN (++a)
    +}
    +HB_FUNCOBJ (hb_inc);
    +struct
    +{
    +  template  constexpr auto
    +  operator () (T &a) const HB_AUTO_RETURN (--a)
    +}
    +HB_FUNCOBJ (hb_dec);
    +
    +
    +/* Compiler-assisted vectorization. */
    +
    +/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
    + * basically a fixed-size bitset. */
    +template 
    +struct hb_vector_size_t
    +{
    +  elt_t& operator [] (unsigned int i) { return v[i]; }
    +  const elt_t& operator [] (unsigned int i) const { return v[i]; }
    +
    +  void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
    +
    +  template 
    +  hb_vector_size_t process (const Op& op) const
    +  {
    +    hb_vector_size_t r;
    +    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
    +      r.v[i] = op (v[i]);
    +    return r;
    +  }
    +  template 
    +  hb_vector_size_t process (const Op& op, const hb_vector_size_t &o) const
    +  {
    +    hb_vector_size_t r;
    +    for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
    +      r.v[i] = op (v[i], o.v[i]);
    +    return r;
    +  }
    +  hb_vector_size_t operator | (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_or, o); }
    +  hb_vector_size_t operator & (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_and, o); }
    +  hb_vector_size_t operator ^ (const hb_vector_size_t &o) const
    +  { return process (hb_bitwise_xor, o); }
    +  hb_vector_size_t operator ~ () const
    +  { return process (hb_bitwise_neg); }
    +
    +  private:
    +  static_assert (0 == byte_size % sizeof (elt_t), "");
    +  elt_t v[byte_size / sizeof (elt_t)];
    +};
    +
    +
    +#endif /* HB_ALGS_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    index 4034d92903a81..c6766e61edd49 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-array.hh
    @@ -28,7 +28,7 @@
     #define HB_ARRAY_HH
     
     #include "hb.hh"
    -#include "hb-dsalgs.hh"
    +#include "hb-algs.hh"
     #include "hb-iter.hh"
     #include "hb-null.hh"
     
    @@ -37,22 +37,31 @@ template 
     struct hb_sorted_array_t;
     
     template 
    -struct hb_array_t :
    -        hb_iter_t, Type>,
    -        hb_iter_mixin_t, Type>
    +struct hb_array_t : hb_iter_with_fallback_t, Type&>
     {
       /*
        * Constructors.
        */
    -  hb_array_t () : arrayZ (nullptr), length (0) {}
    -  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
    -  template  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {}
    +  hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
    +  hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
    +  template 
    +  hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}
     
    +  template 
    +  hb_array_t (const hb_array_t &o) :
    +    hb_iter_with_fallback_t (),
    +    arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {}
    +  template 
    +  hb_array_t& operator = (const hb_array_t &o)
    +  { arrayZ = o.arrayZ; length = o.length; backwards_length = o.backwards_length; return *this; }
     
       /*
        * Iterator implementation.
        */
    -  typedef Type __item_type__;
    +  typedef Type& __item_t__;
    +  static constexpr bool is_random_access_iterator = true;
       Type& __item_at__ (unsigned i) const
       {
         if (unlikely (i >= length)) return CrapOrNull (Type);
    @@ -63,16 +72,25 @@ struct hb_array_t :
         if (unlikely (n > length))
           n = length;
         length -= n;
    +    backwards_length += n;
         arrayZ += n;
       }
       void __rewind__ (unsigned n)
       {
    -    if (unlikely (n > length))
    -      n = length;
    -    length -= n;
    +    if (unlikely (n > backwards_length))
    +      n = backwards_length;
    +    length += n;
    +    backwards_length -= n;
    +    arrayZ -= n;
       }
       unsigned __len__ () const { return length; }
    -  bool __random_access__ () const { return true; }
    +  /* Ouch. The operator== compares the contents of the array.  For range-based for loops,
    +   * it's best if we can just compare arrayZ, though comparing contents is still fast,
    +   * but also would require that Type has operator==.  As such, we optimize this operator
    +   * for range-based for loop and just compare arrayZ.  No need to compare length, as we
    +   * assume we're only compared to .end(). */
    +  bool operator != (const hb_array_t& o) const
    +  { return arrayZ != o.arrayZ; }
     
       /* Extra operators.
        */
    @@ -80,70 +98,105 @@ struct hb_array_t :
       operator hb_array_t () { return hb_array_t (arrayZ, length); }
       template  operator T * () const { return arrayZ; }
     
    +  HB_INTERNAL bool operator == (const hb_array_t &o) const;
    +
    +  uint32_t hash () const {
    +    uint32_t current = 0;
    +    for (unsigned int i = 0; i < this->length; i++) {
    +      current = current * 31 + hb_hash (this->arrayZ[i]);
    +    }
    +    return current;
    +  }
    +
       /*
        * Compare, Sort, and Search.
        */
     
       /* Note: our compare is NOT lexicographic; it also does NOT call Type::cmp. */
    -  int cmp (const hb_array_t &a) const
    +  int cmp (const hb_array_t &a) const
       {
         if (length != a.length)
           return (int) a.length - (int) length;
         return hb_memcmp (a.arrayZ, arrayZ, get_size ());
       }
    -  static int cmp (const void *pa, const void *pb)
    +  HB_INTERNAL static int cmp (const void *pa, const void *pb)
       {
    -    hb_array_t *a = (hb_array_t *) pa;
    -    hb_array_t *b = (hb_array_t *) pb;
    +    hb_array_t *a = (hb_array_t *) pa;
    +    hb_array_t *b = (hb_array_t *) pb;
         return b->cmp (*a);
       }
     
       template 
       Type *lsearch (const T &x, Type *not_found = nullptr)
       {
    -    unsigned int count = length;
    -    for (unsigned int i = 0; i < count; i++)
    -      if (!this->arrayZ[i].cmp (x))
    -        return &this->arrayZ[i];
    -    return not_found;
    +    unsigned i;
    +    return lfind (x, &i) ? &this->arrayZ[i] : not_found;
       }
       template 
       const Type *lsearch (const T &x, const Type *not_found = nullptr) const
       {
    -    unsigned int count = length;
    -    for (unsigned int i = 0; i < count; i++)
    +    unsigned i;
    +    return lfind (x, &i) ? &this->arrayZ[i] : not_found;
    +  }
    +  template 
    +  bool lfind (const T &x, unsigned *pos = nullptr) const
    +  {
    +    for (unsigned i = 0; i < length; ++i)
           if (!this->arrayZ[i].cmp (x))
    -        return &this->arrayZ[i];
    -    return not_found;
    +      {
    +        if (pos)
    +          *pos = i;
    +        return true;
    +      }
    +
    +    return false;
       }
     
       hb_sorted_array_t qsort (int (*cmp_)(const void*, const void*))
       {
         if (likely (length))
    -      ::qsort (arrayZ, length, this->item_size, cmp_);
    +      hb_qsort (arrayZ, length, this->get_item_size (), cmp_);
         return hb_sorted_array_t (*this);
       }
       hb_sorted_array_t qsort ()
       {
         if (likely (length))
    -      ::qsort (arrayZ, length, this->item_size, Type::cmp);
    +      hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
         return hb_sorted_array_t (*this);
       }
       void qsort (unsigned int start, unsigned int end)
       {
    -    end = MIN (end, length);
    +    end = hb_min (end, length);
         assert (start <= end);
         if (likely (start < end))
    -      ::qsort (arrayZ + start, end - start, this->item_size, Type::cmp);
    +      hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
       }
     
       /*
        * Other methods.
        */
     
    -  unsigned int get_size () const { return length * this->item_size; }
    +  unsigned int get_size () const { return length * this->get_item_size (); }
     
    -  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
    +  /*
    +   * Reverse the order of items in this array in the range [start, end).
    +   */
    +  void reverse (unsigned start = 0, unsigned end = -1)
    +  {
    +    start = hb_min (start, length);
    +    end = hb_min (end, length);
    +
    +    if (end < start + 2)
    +      return;
    +
    +    for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) {
    +      Type temp = arrayZ[rhs];
    +      arrayZ[rhs] = arrayZ[lhs];
    +      arrayZ[lhs] = temp;
    +    }
    +  }
    +
    +  hb_array_t sub_array (unsigned int start_offset = 0, unsigned int *seg_count = nullptr /* IN/OUT */) const
       {
         if (!start_offset && !seg_count)
           return *this;
    @@ -154,16 +207,45 @@ struct hb_array_t :
         else
           count -= start_offset;
         if (seg_count)
    -      count = *seg_count = MIN (count, *seg_count);
    -    return hb_array_t (arrayZ + start_offset, count);
    +      count = *seg_count = hb_min (count, *seg_count);
    +    return hb_array_t (arrayZ + start_offset, count);
       }
    -  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
    +  hb_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
       { return sub_array (start_offset, &seg_count); }
     
    +  hb_array_t truncate (unsigned length) const { return sub_array (0, length); }
    +
    +  template 
    +  const T *as () const
    +  { return length < hb_null_size (T) ? &Null (T) : reinterpret_cast (arrayZ); }
    +
    +  template 
    +  bool check_range (const T *p, unsigned int size = T::static_size) const
    +  {
    +    return arrayZ <= ((const char *) p)
    +        && ((const char *) p) <= arrayZ + length
    +        && (unsigned int) (arrayZ + length - (const char *) p) >= size;
    +  }
    +
       /* Only call if you allocated the underlying array using malloc() or similar. */
       void free ()
       { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
     
    +  template 
    +  hb_array_t copy (hb_serialize_context_t *c) const
    +  {
    +    TRACE_SERIALIZE (this);
    +    auto* out = c->start_embed (arrayZ);
    +    if (unlikely (!c->extend_size (out, get_size ()))) return_trace (hb_array_t ());
    +    for (unsigned i = 0; i < length; i++)
    +      out[i] = arrayZ[i]; /* TODO: add version that calls c->copy() */
    +    return_trace (hb_array_t (out, length));
    +  }
    +
       template 
       bool sanitize (hb_sanitize_context_t *c) const
       { return c->check_array (arrayZ, length); }
    @@ -175,6 +257,7 @@ struct hb_array_t :
       public:
       Type *arrayZ;
       unsigned int length;
    +  unsigned int backwards_length;
     };
     template  inline hb_array_t
     hb_array (T *array, unsigned int length)
    @@ -183,7 +266,6 @@ template  inline hb_array_t
     hb_array (T (&array_)[length_])
     { return hb_array_t (array_); }
     
    -
     enum hb_bfind_not_found_t
     {
       HB_BFIND_NOT_FOUND_DONT_STORE,
    @@ -193,20 +275,40 @@ enum hb_bfind_not_found_t
     
     template 
     struct hb_sorted_array_t :
    -        hb_sorted_iter_t, Type>,
    -        hb_array_t,
    -        hb_iter_mixin_t, Type>
    +        hb_iter_t, Type&>,
    +        hb_array_t
     {
    +  typedef hb_iter_t iter_base_t;
    +  HB_ITER_USING (iter_base_t);
    +  static constexpr bool is_random_access_iterator = true;
    +  static constexpr bool is_sorted_iterator = true;
    +
       hb_sorted_array_t () : hb_array_t () {}
    -  hb_sorted_array_t (const hb_array_t &o) : hb_array_t (o) {}
       hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t (array_, length_) {}
    -  template  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {}
    +  template 
    +  hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t (array_) {}
    +
    +  template 
    +  hb_sorted_array_t (const hb_array_t &o) :
    +    hb_iter_t (),
    +    hb_array_t (o) {}
    +  template 
    +  hb_sorted_array_t& operator = (const hb_array_t &o)
    +  { hb_array_t (*this) = o; return *this; }
    +
    +  /* Iterator implementation. */
    +  bool operator != (const hb_sorted_array_t& o) const
    +  { return this->arrayZ != o.arrayZ || this->length != o.length; }
     
    -  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
    -  { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); }
    -  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
    +  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
    +  { return hb_sorted_array_t (((const hb_array_t *) (this))->sub_array (start_offset, seg_count)); }
    +  hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int seg_count) const
       { return sub_array (start_offset, &seg_count); }
     
    +  hb_sorted_array_t truncate (unsigned length) const { return sub_array (0, length); }
    +
       template 
       Type *bsearch (const T &x, Type *not_found = nullptr)
       {
    @@ -221,26 +323,18 @@ struct hb_sorted_array_t :
       }
       template 
       bool bfind (const T &x, unsigned int *i = nullptr,
    -                     hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
    -                     unsigned int to_store = (unsigned int) -1) const
    +              hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
    +              unsigned int to_store = (unsigned int) -1) const
       {
    -    int min = 0, max = (int) this->length - 1;
    -    const Type *array = this->arrayZ;
    -    while (min <= max)
    +    unsigned pos;
    +
    +    if (bsearch_impl (x, &pos))
         {
    -      int mid = ((unsigned int) min + (unsigned int) max) / 2;
    -      int c = array[mid].cmp (x);
    -      if (c < 0)
    -        max = mid - 1;
    -      else if (c > 0)
    -        min = mid + 1;
    -      else
    -      {
    -        if (i)
    -          *i = mid;
    -        return true;
    -      }
    +      if (i)
    +        *i = pos;
    +      return true;
         }
    +
         if (i)
         {
           switch (not_found)
    @@ -253,14 +347,22 @@ struct hb_sorted_array_t :
               break;
     
             case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
    -          if (max < 0 || (max < (int) this->length && array[max].cmp (x) > 0))
    -            max++;
    -          *i = max;
    +          *i = pos;
               break;
           }
         }
         return false;
       }
    +  template 
    +  bool bsearch_impl (const T &x, unsigned *pos) const
    +  {
    +    return hb_bsearch_impl (pos,
    +                            x,
    +                            this->arrayZ,
    +                            this->length,
    +                            sizeof (Type),
    +                            _hb_cmp_method);
    +  }
     };
     template  inline hb_sorted_array_t
     hb_sorted_array (T *array, unsigned int length)
    @@ -269,9 +371,38 @@ template  inline hb_sorted_array_t
     hb_sorted_array (T (&array_)[length_])
     { return hb_sorted_array_t (array_); }
     
    +template 
    +bool hb_array_t::operator == (const hb_array_t &o) const
    +{
    +  if (o.length != this->length) return false;
    +  for (unsigned int i = 0; i < this->length; i++) {
    +    if (this->arrayZ[i] != o.arrayZ[i]) return false;
    +  }
    +  return true;
    +}
    +
    +/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
    +
    +template <>
    +inline uint32_t hb_array_t::hash () const {
    +  uint32_t current = 0;
    +  for (unsigned int i = 0; i < this->length; i++)
    +    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
    +  return current;
    +}
    +
    +template <>
    +inline uint32_t hb_array_t::hash () const {
    +  uint32_t current = 0;
    +  for (unsigned int i = 0; i < this->length; i++)
    +    current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
    +  return current;
    +}
    +
     
     typedef hb_array_t hb_bytes_t;
     typedef hb_array_t hb_ubytes_t;
     
     
    +
     #endif /* HB_ARRAY_HH */
    diff --git a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    index d1ff7a722ef9c..a6877f8e67455 100644
    --- a/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    +++ b/src/java.desktop/share/native/libharfbuzz/hb-atomic.hh
    @@ -33,6 +33,7 @@
     #define HB_ATOMIC_HH
     
     #include "hb.hh"
    +#include "hb-meta.hh"
     
     
     /*
    @@ -85,11 +86,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
     #define hb_atomic_int_impl_add(AI, V)           (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
     #define hb_atomic_int_impl_set_relaxed(AI, V)   (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed))
     #define hb_atomic_int_impl_set(AI, V)           (reinterpret_cast *> (AI)->store ((V), std::memory_order_release))
    -#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast *> (AI)->load (std::memory_order_relaxed))
    -#define hb_atomic_int_impl_get(AI)              (reinterpret_cast *> (AI)->load (std::memory_order_acquire))
    +#define hb_atomic_int_impl_get_relaxed(AI)      (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed))
    +#define hb_atomic_int_impl_get(AI)              (reinterpret_cast const *> (AI)->load (std::memory_order_acquire))
     
     #define hb_atomic_ptr_impl_set_relaxed(P, V)    (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed))
    -#define hb_atomic_ptr_impl_get_relaxed(P)       (reinterpret_cast *> (P)->load (std::memory_order_relaxed))
    +#define hb_atomic_ptr_impl_get_relaxed(P)       (reinterpret_cast const *> (P)->load (std::memory_order_relaxed))
     #define hb_atomic_ptr_impl_get(P)               (reinterpret_cast *> (P)->load (std::memory_order_acquire))
     static inline bool
     _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
    @@ -106,7 +107,7 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N)
     
     static inline void _hb_memory_barrier ()
     {
    -#if !defined(MemoryBarrier)
    +#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
       /* MinGW has a convoluted history of supporting MemoryBarrier. */
       LONG dummy = 0;
       InterlockedExchange (&dummy, 1);
    @@ -211,25 +212,19 @@ static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
     static_assert ((sizeof (long) == sizeof (void *)), "");
     
     
    -#elif !defined(HB_NO_MT)
    -
    -#define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
    -
    -#define _hb_memory_barrier()
    +#elif defined(HB_NO_MT)
     
     #define hb_atomic_int_impl_add(AI, V)           ((*(AI) += (V)) - (V))
     
    -#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
    -
    -
    -#else /* HB_NO_MT */
    +#define _hb_memory_barrier()                    do {} while (0)
     
    -#define hb_atomic_int_impl_add(AI, V)           ((*(AI) += (V)) - (V))
    +#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
     
    -#define _hb_memory_barrier()
     
    -#define hb_atomic_ptr_impl_cmpexch(P,O,N)       (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false)
    +#else
     
    +#error "Could not find any system to define atomic_int macros."
    +#error "Check hb-atomic.hh for possible resolutions."
     
     #endif
     
    @@ -282,7 +277,7 @@ struct hb_atomic_int_t
     template 
     struct hb_atomic_ptr_t
     {
    -  typedef typename hb_remove_pointer (P) T;
    +  typedef hb_remove_pointer

    T; void init (T* v_ = nullptr) { set_relaxed (v_); } void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh new file mode 100644 index 0000000000000..ff47c6f2476aa --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-bimap.hh @@ -0,0 +1,166 @@ +/* + * Copyright © 2019 Adobe Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" +#include "hb-map.hh" + +/* Bi-directional map */ +struct hb_bimap_t +{ + hb_bimap_t () { init (); } + ~hb_bimap_t () { fini (); } + + void init () + { + forw_map.init (); + back_map.init (); + } + + void fini () + { + forw_map.fini (); + back_map.fini (); + } + + void reset () + { + forw_map.reset (); + back_map.reset (); + } + + bool in_error () const { return forw_map.in_error () || back_map.in_error (); } + + void set (hb_codepoint_t lhs, hb_codepoint_t rhs) + { + if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return; + if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; } + forw_map.set (lhs, rhs); + back_map.set (rhs, lhs); + } + + hb_codepoint_t get (hb_codepoint_t lhs) const { return forw_map.get (lhs); } + hb_codepoint_t backward (hb_codepoint_t rhs) const { return back_map.get (rhs); } + + hb_codepoint_t operator [] (hb_codepoint_t lhs) const { return get (lhs); } + bool has (hb_codepoint_t lhs, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); } + + void del (hb_codepoint_t lhs) + { + back_map.del (get (lhs)); + forw_map.del (lhs); + } + + void clear () + { + forw_map.clear (); + back_map.clear (); + } + + bool is_empty () const { return get_population () == 0; } + + unsigned int get_population () const { return forw_map.get_population (); } + + protected: + hb_map_t forw_map; + hb_map_t back_map; +}; + +/* Inremental bimap: only lhs is given, rhs is incrementally assigned */ +struct hb_inc_bimap_t : hb_bimap_t +{ + hb_inc_bimap_t () { init (); } + + void init () + { + hb_bimap_t::init (); + next_value = 0; + } + + /* Add a mapping from lhs to rhs with a unique value if lhs is unknown. + * Return the rhs value as the result. + */ + hb_codepoint_t add (hb_codepoint_t lhs) + { + hb_codepoint_t rhs = forw_map[lhs]; + if (rhs == HB_MAP_VALUE_INVALID) + { + rhs = next_value++; + set (lhs, rhs); + } + return rhs; + } + + hb_codepoint_t skip () + { return next_value++; } + + hb_codepoint_t get_next_value () const + { return next_value; } + + void add_set (const hb_set_t *set) + { + hb_codepoint_t i = HB_SET_VALUE_INVALID; + while (hb_set_next (set, &i)) add (i); + } + + /* Create an identity map. */ + bool identity (unsigned int size) + { + clear (); + for (hb_codepoint_t i = 0; i < size; i++) set (i, i); + return !in_error (); + } + + protected: + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + public: + /* Optional: after finished adding all mappings in a random order, + * reassign rhs to lhs so that they are in the same order. */ + void sort () + { + hb_codepoint_t count = get_population (); + hb_vector_t work; + work.resize (count); + + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + work[rhs] = back_map[rhs]; + + work.qsort (cmp_id); + + clear (); + for (hb_codepoint_t rhs = 0; rhs < count; rhs++) + set (work[rhs], rhs); + } + + protected: + unsigned int next_value; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc index ffeecc2a16829..fc18b61c9131b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.cc @@ -25,18 +25,6 @@ * Red Hat Author(s): Behdad Esfahbod */ - -/* https://github.com/harfbuzz/harfbuzz/issues/1308 - * http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html - * https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html - */ -#ifndef _POSIX_C_SOURCE -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-macros" -#define _POSIX_C_SOURCE 200809L -#pragma GCC diagnostic pop -#endif - #include "hb.hh" #include "hb-blob.hh" @@ -48,7 +36,6 @@ #endif /* HAVE_SYS_MMAN_H */ #include -#include #include @@ -155,7 +142,7 @@ hb_blob_create_sub_blob (hb_blob_t *parent, hb_blob_make_immutable (parent); blob = hb_blob_create (parent->data + offset, - MIN (length, parent->length - offset), + hb_min (length, parent->length - offset), HB_MEMORY_MODE_READONLY, hb_blob_reference (parent), _hb_blob_destroy); @@ -202,7 +189,7 @@ hb_blob_copy_writable_or_fail (hb_blob_t *blob) hb_blob_t * hb_blob_get_empty () { - return const_cast (&Null(hb_blob_t)); + return const_cast (&Null (hb_blob_t)); } /** @@ -487,7 +474,11 @@ hb_blob_t::try_make_writable () * Mmap */ +#ifndef HB_NO_OPEN #ifdef HAVE_MMAP +# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) +# include +# endif # include # include # include @@ -532,6 +523,39 @@ _hb_mapped_file_destroy (void *file_) } #endif +#ifdef _PATH_RSRCFORKSPEC +static int +_open_resource_fork (const char *file_name, hb_mapped_file_t *file) +{ + size_t name_len = strlen (file_name); + size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); + + char *rsrc_name = (char *) malloc (len); + if (unlikely (!rsrc_name)) return -1; + + strncpy (rsrc_name, file_name, name_len); + strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, + sizeof (_PATH_RSRCFORKSPEC) - 1); + + int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); + free (rsrc_name); + + if (fd != -1) + { + struct stat st; + if (fstat (fd, &st) != -1) + file->length = (unsigned long) st.st_size; + else + { + close (fd); + fd = -1; + } + } + + return fd; +} +#endif + /** * hb_blob_create_from_file: * @file_name: font filename. @@ -556,6 +580,19 @@ hb_blob_create_from_file (const char *file_name) if (unlikely (fstat (fd, &st) == -1)) goto fail; file->length = (unsigned long) st.st_size; + +#ifdef _PATH_RSRCFORKSPEC + if (unlikely (file->length == 0)) + { + int rfd = _open_resource_fork (file_name, file); + if (rfd != -1) + { + close (fd); + fd = rfd; + } + } +#endif + file->contents = (char *) mmap (nullptr, file->length, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); @@ -579,9 +616,9 @@ hb_blob_create_from_file (const char *file_name) HANDLE fd; unsigned int size = strlen (file_name) + 1; wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size); - if (unlikely (wchar_file_name == nullptr)) goto fail_without_close; + if (unlikely (!wchar_file_name)) goto fail_without_close; mbstowcs (wchar_file_name, file_name, size); -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) { CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); @@ -602,7 +639,7 @@ hb_blob_create_from_file (const char *file_name) if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) { LARGE_INTEGER length; GetFileSizeEx (fd, &length); @@ -613,14 +650,14 @@ hb_blob_create_from_file (const char *file_name) file->length = (unsigned long) GetFileSize (fd, nullptr); file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); #endif - if (unlikely (file->mapping == nullptr)) goto fail; + if (unlikely (!file->mapping)) goto fail; -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); #else file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); #endif - if (unlikely (file->contents == nullptr)) goto fail; + if (unlikely (!file->contents)) goto fail; CloseHandle (fd); return hb_blob_create (file->contents, file->length, @@ -638,10 +675,10 @@ hb_blob_create_from_file (const char *file_name) It's used as a fallback for systems without mmap or to read from pipes */ unsigned long len = 0, allocated = BUFSIZ * 16; char *data = (char *) malloc (allocated); - if (unlikely (data == nullptr)) return hb_blob_get_empty (); + if (unlikely (!data)) return hb_blob_get_empty (); FILE *fp = fopen (file_name, "rb"); - if (unlikely (fp == nullptr)) goto fread_fail_without_close; + if (unlikely (!fp)) goto fread_fail_without_close; while (!feof (fp)) { @@ -652,7 +689,7 @@ hb_blob_create_from_file (const char *file_name) can cover files like that but lets limit our fallback reader */ if (unlikely (allocated > (2 << 28))) goto fread_fail; char *new_data = (char *) realloc (data, allocated); - if (unlikely (new_data == nullptr)) goto fread_fail; + if (unlikely (!new_data)) goto fread_fail; data = new_data; } @@ -666,6 +703,7 @@ hb_blob_create_from_file (const char *file_name) len += addition; } + fclose (fp); return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data, (hb_destroy_func_t) free); @@ -676,3 +714,4 @@ hb_blob_create_from_file (const char *file_name) free (data); return hb_blob_get_empty (); } +#endif /* !HB_NO_OPEN */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.h b/src/java.desktop/share/native/libharfbuzz/hb-blob.h index a714fb2057b28..ddbcd1a999c90 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.h @@ -71,6 +71,9 @@ hb_blob_create (const char *data, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN hb_blob_t * +hb_blob_create_from_file (const char *file_name); + /* Always creates with MEMORY_MODE_READONLY. * Even if the parent blob is writable, we don't * want the user of the sub-blob to be able to @@ -123,9 +126,6 @@ hb_blob_get_data (hb_blob_t *blob, unsigned int *length); HB_EXTERN char * hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length); -HB_EXTERN hb_blob_t * -hb_blob_create_from_file (const char *file_name); - HB_END_DECLS #endif /* HB_BLOB_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh index 4ea13f813701e..d85bd823b0042 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-blob.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-blob.hh @@ -54,13 +54,9 @@ struct hb_blob_t HB_INTERNAL bool try_make_writable_inplace (); HB_INTERNAL bool try_make_writable_inplace_unix (); + hb_bytes_t as_bytes () const { return hb_bytes_t (data, length); } template - const Type* as () const - { - return length < hb_null_size (Type) ? &Null(Type) : reinterpret_cast (data); - } - hb_bytes_t as_bytes () const - { return hb_bytes_t (data, length); } + const Type* as () const { return as_bytes ().as (); } public: hb_object_header_t header; @@ -81,7 +77,7 @@ struct hb_blob_t template struct hb_blob_ptr_t { - typedef typename hb_remove_pointer (P) T; + typedef hb_remove_pointer

    T; hb_blob_ptr_t (hb_blob_t *b_ = nullptr) : b (b_) {} hb_blob_t * operator = (hb_blob_t *b_) { return b = b_; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc index 14a9a568c7e6e..52dbb84bb3f54 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer-serialize.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + #include "hb-buffer.hh" @@ -85,7 +89,7 @@ hb_buffer_serialize_format_from_string (const char *str, int len) const char * hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format) { - switch (format) + switch ((unsigned) format) { case HB_BUFFER_SERIALIZE_FORMAT_TEXT: return serialize_formats[0]; case HB_BUFFER_SERIALIZE_FORMAT_JSON: return serialize_formats[1]; @@ -138,34 +142,34 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, *p++ = '"'; } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", x+pos[i].x_offset, y+pos[i].y_offset)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", pos[i].x_advance, pos[i].y_advance)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"fl\":%u", info[i].mask & HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", extents.x_bearing, extents.y_bearing)); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", extents.width, extents.height)); } @@ -224,37 +228,37 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, p += strlen (p); } else - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) { - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); } if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { if (x+pos[i].x_offset || y+pos[i].y_offset) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) { *p++ = '+'; - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); if (pos[i].y_advance) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); } } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS) { if (info[i].mask & HB_GLYPH_FLAG_DEFINED) - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "#%X", info[i].mask &HB_GLYPH_FLAG_DEFINED)); } if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) { hb_glyph_extents_t extents; hb_font_get_glyph_extents(font, info[i].codepoint, &extents); - p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); } unsigned int l = p - b; @@ -344,8 +348,8 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, if (buf_size) *buf = '\0'; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); if (!buffer->have_positions) flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; @@ -375,43 +379,24 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, } } - -static hb_bool_t -parse_uint (const char *pp, const char *end, uint32_t *pv) +static bool +parse_int (const char *pp, const char *end, int32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - uint32_t v; - - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + int v; + const char *p = pp; + if (unlikely (!hb_parse_int (&p, end, &v, true/* whole buffer */))) return false; *pv = v; return true; } -static hb_bool_t -parse_int (const char *pp, const char *end, int32_t *pv) +static bool +parse_uint (const char *pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp)); - strncpy (buf, pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - int32_t v; - - errno = 0; - v = strtol (p, &pend, 10); - if (errno || p == pend || pend - p != end - pp) + unsigned int v; + const char *p = pp; + if (unlikely (!hb_parse_uint (&p, end, &v, true/* whole buffer */))) return false; *pv = v; @@ -449,8 +434,8 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, end_ptr = &end; *end_ptr = buf; - assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) || - buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS); + assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) || + (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS)); if (buf_len == -1) buf_len = strlen (buf); @@ -484,3 +469,6 @@ hb_buffer_deserialize_glyphs (hb_buffer_t *buffer, } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc index f9a46d12b3c2e..2da3c486e2263 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.cc @@ -324,7 +324,7 @@ hb_buffer_t::clear_positions () out_len = 0; out_info = info; - memset (pos, 0, sizeof (pos[0]) * len); + hb_memset (pos, 0, sizeof (pos[0]) * len); } void @@ -438,13 +438,6 @@ hb_buffer_t::set_masks (hb_mask_t value, if (!mask) return; - if (cluster_start == 0 && cluster_end == (unsigned int)-1) { - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - info[i].mask = (info[i].mask & not_mask) | value; - return; - } - unsigned int count = len; for (unsigned int i = 0; i < count; i++) if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) @@ -455,27 +448,13 @@ void hb_buffer_t::reverse_range (unsigned int start, unsigned int end) { - unsigned int i, j; - if (end - start < 2) return; - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_info_t t; - - t = info[i]; - info[i] = info[j]; - info[j] = t; - } + hb_array_t (info, len).reverse (start, end); if (have_positions) { - for (i = start, j = end - 1; i < j; i++, j--) { - hb_glyph_position_t t; - - t = pos[i]; - pos[i] = pos[j]; - pos[j] = t; - } + hb_array_t (pos, len).reverse (start, end); } } @@ -524,7 +503,7 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int cluster = info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); /* Extend end */ while (end < len && info[end - 1].cluster == info[end].cluster) @@ -555,7 +534,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start, unsigned int cluster = out_info[start].cluster; for (unsigned int i = start + 1; i < end; i++) - cluster = MIN (cluster, out_info[i].cluster); + cluster = hb_min (cluster, out_info[i].cluster); /* Extend start */ while (start && out_info[start - 1].cluster == out_info[start].cluster) @@ -612,7 +591,7 @@ hb_buffer_t::delete_glyph () void hb_buffer_t::unsafe_to_break_impl (unsigned int start, unsigned int end) { - unsigned int cluster = (unsigned int) -1; + unsigned int cluster = UINT_MAX; cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster); _unsafe_to_break_set_mask (info, start, end, cluster); } @@ -628,7 +607,7 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en assert (start <= out_len); assert (idx <= end); - unsigned int cluster = (unsigned int) -1; + unsigned int cluster = UINT_MAX; cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster); cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster); _unsafe_to_break_set_mask (out_info, start, out_len, cluster); @@ -638,8 +617,8 @@ hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int en void hb_buffer_t::guess_segment_properties () { - assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!len && content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); /* If script is set to INVALID, guess from buffer contents */ if (props.script == HB_SCRIPT_INVALID) { @@ -736,7 +715,7 @@ hb_buffer_create () hb_buffer_t * hb_buffer_get_empty () { - return const_cast (&Null(hb_buffer_t)); + return const_cast (&Null (hb_buffer_t)); } /** @@ -776,8 +755,10 @@ hb_buffer_destroy (hb_buffer_t *buffer) free (buffer->info); free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE if (buffer->message_destroy) buffer->message_destroy (buffer->message_data); +#endif free (buffer); } @@ -956,7 +937,7 @@ hb_buffer_get_direction (hb_buffer_t *buffer) * * You can pass one of the predefined #hb_script_t values, or use * hb_script_from_string() or hb_script_from_iso15924_tag() to get the - * corresponding script from an ISO 15924 script tag. + * corresponding script from an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -999,7 +980,7 @@ hb_buffer_get_script (hb_buffer_t *buffer) * are orthogonal to the scripts, and though they are related, they are * different concepts and should not be confused with each other. * - * Use hb_language_from_string() to convert from BCP 47 language tags to + * Use hb_language_from_string() to convert from BCP 47 language tags to * #hb_language_t. * * Since: 0.9.2 @@ -1115,8 +1096,8 @@ hb_buffer_get_flags (hb_buffer_t *buffer) * Since: 0.9.42 **/ void -hb_buffer_set_cluster_level (hb_buffer_t *buffer, - hb_buffer_cluster_level_t cluster_level) +hb_buffer_set_cluster_level (hb_buffer_t *buffer, + hb_buffer_cluster_level_t cluster_level) { if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1532,8 +1513,8 @@ hb_buffer_add_utf (hb_buffer_t *buffer, typedef typename utf_t::codepoint_t T; const hb_codepoint_t replacement = buffer->replacement; - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); if (unlikely (hb_object_is_immutable (buffer))) return; @@ -1736,7 +1717,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer, * @buffer: an #hb_buffer_t. * @source: source #hb_buffer_t. * @start: start index into source buffer to copy. Use 0 to copy from start of buffer. - * @end: end index into source buffer to copy. Use (unsigned int) -1 to copy to end of buffer. + * @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. * * Append (part of) contents of another buffer to this buffer. * @@ -1853,23 +1834,13 @@ void hb_buffer_normalize_glyphs (hb_buffer_t *buffer) { assert (buffer->have_positions); - assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS || - (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)); + assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) || + (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID))); bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - unsigned int count = buffer->len; - if (unlikely (!count)) return; - hb_glyph_info_t *info = buffer->info; - - unsigned int start = 0; - unsigned int end; - for (end = start + 1; end < count; end++) - if (info[start].cluster != info[end].cluster) { - normalize_glyphs_cluster (buffer, start, end, backward); - start = end; - } - normalize_glyphs_cluster (buffer, start, end, backward); + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); } void @@ -1993,6 +1964,7 @@ hb_buffer_diff (hb_buffer_t *buffer, * Debugging. */ +#ifndef HB_NO_BUFFER_MESSAGE /** * hb_buffer_set_message_func: * @buffer: an #hb_buffer_t. @@ -2022,11 +1994,11 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_destroy = nullptr; } } - bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { char buf[100]; - vsnprintf (buf, sizeof (buf), fmt, ap); + vsnprintf (buf, sizeof (buf), fmt, ap); return (bool) this->message_func (this, font, buf, this->message_data); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h index f5a724cfb43fd..1a7ca4069e575 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.h @@ -284,6 +284,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * space glyph and zeroing the advance width.) * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). Since: 2.4 * * Since: 0.9.20 */ @@ -292,7 +296,8 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, - HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u } hb_buffer_flags_t; HB_EXTERN void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh index 6416a5328d9ac..c4ef466b7a728 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-buffer.hh @@ -124,9 +124,11 @@ struct hb_buffer_t unsigned int context_len[2]; /* Debugging API */ +#ifndef HB_NO_BUFFER_MESSAGE hb_buffer_message_func_t message_func; void *message_data; hb_destroy_func_t message_destroy; +#endif /* Internal debugging. */ /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */ @@ -226,10 +228,10 @@ struct hb_buffer_t /* Makes a copy of the glyph at idx to output and replace glyph_index */ hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index) { - if (unlikely (!make_room_for (0, 1))) return Crap(hb_glyph_info_t); + if (unlikely (!make_room_for (0, 1))) return Crap (hb_glyph_info_t); if (unlikely (idx == len && !out_len)) - return Crap(hb_glyph_info_t); + return Crap (hb_glyph_info_t); out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1]; out_info[out_len].codepoint = glyph_index; @@ -316,7 +318,7 @@ struct hb_buffer_t HB_INTERNAL void delete_glyph (); void unsafe_to_break (unsigned int start, - unsigned int end) + unsigned int end) { if (end - start < 2) return; @@ -347,9 +349,19 @@ struct hb_buffer_t HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); - bool messaging () { return unlikely (message_func); } + bool messaging () + { +#ifdef HB_NO_BUFFER_MESSAGE + return false; +#else + return unlikely (message_func); +#endif + } bool message (hb_font_t *font, const char *fmt, ...) HB_PRINTF_FUNC(3, 4) { +#ifdef HB_NO_BUFFER_MESSAGE + return true; +#else if (!messaging ()) return true; va_list ap; @@ -357,6 +369,7 @@ struct hb_buffer_t bool ret = message_impl (font, fmt, ap); va_end (ap); return ret; +#endif } HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); @@ -373,13 +386,13 @@ struct hb_buffer_t inf.cluster = cluster; } - int + unsigned int _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos, unsigned int start, unsigned int end, unsigned int cluster) const { for (unsigned int i = start; i < end; i++) - cluster = MIN (cluster, infos[i].cluster); + cluster = hb_min (cluster, infos[i].cluster); return cluster; } void @@ -395,8 +408,7 @@ struct hb_buffer_t } } - void unsafe_to_break_all () - { unsafe_to_break_impl (0, len); } + void unsafe_to_break_all () { unsafe_to_break_impl (0, len); } void safe_to_break_all () { for (unsigned int i = 0; i < len; i++) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh index a013e96dc4319..3fcd5575122d0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-common.hh @@ -220,32 +220,22 @@ struct number_t void init () { set_real (0.0); } void fini () {} - void set_int (int v) { value = (double) v; } - int to_int () const { return (int) value; } + void set_int (int v) { value = v; } + int to_int () const { return value; } void set_fixed (int32_t v) { value = v / 65536.0; } - int32_t to_fixed () const { return (int32_t) (value * 65536.0); } + int32_t to_fixed () const { return value * 65536.0; } - void set_real (double v) { value = v; } + void set_real (double v) { value = v; } double to_real () const { return value; } - int ceil () const { return (int) ::ceil (value); } - int floor () const { return (int) ::floor (value); } - bool in_int_range () const { return ((double) (int16_t) to_int () == value); } - bool operator > (const number_t &n) const - { return value > n.to_real (); } - - bool operator < (const number_t &n) const - { return n > *this; } - - bool operator >= (const number_t &n) const - { return !(*this < n); } - - bool operator <= (const number_t &n) const - { return !(*this > n); } + bool operator > (const number_t &n) const { return value > n.to_real (); } + bool operator < (const number_t &n) const { return n > *this; } + bool operator >= (const number_t &n) const { return !(*this < n); } + bool operator <= (const number_t &n) const { return !(*this > n); } const number_t &operator += (const number_t &n) { @@ -255,37 +245,34 @@ struct number_t } protected: - double value; + double value; }; /* byte string */ struct UnsizedByteStr : UnsizedArrayOf { // encode 2-byte int (Dict/CharString) or 4-byte int (Dict) - template - static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, int value) + template + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) { TRACE_SERIALIZE (this); - if (unlikely ((value < minVal || value > maxVal))) - return_trace (false); - HBUINT8 *p = c->allocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (intOp); - - INTTYPE *ip = c->allocate_size (INTTYPE::static_size); - if (unlikely (ip == nullptr)) return_trace (false); - ip->set ((unsigned int)value); + if (unlikely (!p)) return_trace (false); + *p = intOp; - return_trace (true); + T *ip = c->allocate_size (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value)); } - static bool serialize_int4 (hb_serialize_context_t *c, int value) - { return serialize_int (c, OpCode_longintdict, value); } + template + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_longintdict, value); } - static bool serialize_int2 (hb_serialize_context_t *c, int value) - { return serialize_int (c, OpCode_shortint, value); } + template + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int (c, OpCode_shortint, value); } /* Defining null_size allows a Null object may be created. Should be safe because: * A descendent struct Dict uses a Null pointer to indicate a missing table, @@ -320,8 +307,7 @@ struct byte_str_t : hb_ubytes_t /* A byte string associated with the current offset and an error condition */ struct byte_str_ref_t { - byte_str_ref_t () - { init (); } + byte_str_ref_t () { init (); } void init () { @@ -343,13 +329,12 @@ struct byte_str_ref_t } const unsigned char& operator [] (int i) { - if (unlikely ((unsigned int)(offset + i) >= str.length)) + if (unlikely ((unsigned int) (offset + i) >= str.length)) { set_error (); - return Null(unsigned char); + return Null (unsigned char); } - else - return str[offset + i]; + return str[offset + i]; } /* Conversion to byte_str_t */ @@ -359,9 +344,7 @@ struct byte_str_ref_t { return str.sub_str (offset_, len_); } bool avail (unsigned int count=1) const - { - return (!in_error () && str.check_limit (offset, count)); - } + { return (!in_error () && str.check_limit (offset, count)); } void inc (unsigned int count=1) { if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length))) @@ -389,7 +372,7 @@ typedef hb_vector_t byte_str_array_t; /* stack */ template -struct stack_t +struct cff_stack_t { void init () { @@ -400,11 +383,7 @@ struct stack_t for (unsigned int i = 0; i < elements.length; i++) elements[i].init (); } - - void fini () - { - elements.fini_deep (); - } + void fini () { elements.fini_deep (); } ELEM& operator [] (unsigned int i) { @@ -419,7 +398,6 @@ struct stack_t else set_error (); } - ELEM &push () { if (likely (count < elements.length)) @@ -427,7 +405,7 @@ struct stack_t else { set_error (); - return Crap(ELEM); + return Crap (ELEM); } } @@ -438,10 +416,9 @@ struct stack_t else { set_error (); - return Crap(ELEM); + return Crap (ELEM); } } - void pop (unsigned int n) { if (likely (count >= n)) @@ -452,13 +429,12 @@ struct stack_t const ELEM& peek () { - if (likely (count > 0)) - return elements[count-1]; - else + if (unlikely (count < 0)) { set_error (); - return Null(ELEM); + return Null (ELEM); } + return elements[count - 1]; } void unpop () @@ -475,7 +451,7 @@ struct stack_t void set_error () { error = true; } unsigned int get_count () const { return count; } - bool is_empty () const { return count == 0; } + bool is_empty () const { return !count; } static constexpr unsigned kSizeLimit = LIMIT; @@ -487,7 +463,7 @@ struct stack_t /* argument stack */ template -struct arg_stack_t : stack_t +struct arg_stack_t : cff_stack_t { void push_int (int v) { @@ -519,7 +495,7 @@ struct arg_stack_t : stack_t i = 0; S::set_error (); } - return (unsigned)i; + return (unsigned) i; } void push_longint_from_substr (byte_str_ref_t& str_ref) @@ -538,12 +514,10 @@ struct arg_stack_t : stack_t } hb_array_t get_subarray (unsigned int start) const - { - return S::elements.sub_array (start); - } + { return S::elements.sub_array (start); } private: - typedef stack_t S; + typedef cff_stack_t S; }; /* an operator prefixed by its operands in a byte string */ @@ -565,7 +539,7 @@ struct op_serializer_t TRACE_SERIALIZE (this); HBUINT8 *d = c->allocate_size (opstr.str.length); - if (unlikely (d == nullptr)) return_trace (false); + if (unlikely (!d)) return_trace (false); memcpy (d, &opstr.str[0], opstr.str.length); return_trace (true); } @@ -605,7 +579,7 @@ struct parsed_values_t } unsigned get_count () const { return values.length; } - const VAL &get_value (unsigned int i) const { return values[i]; } + const VAL &get_value (unsigned int i) const { return values[i]; } const VAL &operator [] (unsigned int i) const { return get_value (i); } unsigned int opStart; @@ -644,30 +618,19 @@ struct interp_env_t return op; } - const ARG& eval_arg (unsigned int i) - { - return argStack[i]; - } + const ARG& eval_arg (unsigned int i) { return argStack[i]; } - ARG& pop_arg () - { - return argStack.pop (); - } + ARG& pop_arg () { return argStack.pop (); } + void pop_n_args (unsigned int n) { argStack.pop (n); } - void pop_n_args (unsigned int n) - { - argStack.pop (n); - } - - void clear_args () - { - pop_n_args (argStack.get_count ()); - } + void clear_args () { pop_n_args (argStack.get_count ()); } - byte_str_ref_t str_ref; - arg_stack_t argStack; + byte_str_ref_t + str_ref; + arg_stack_t + argStack; protected: - bool error; + bool error; }; typedef interp_env_t<> num_interp_env_t; @@ -691,7 +654,7 @@ struct opset_t case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: - env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); + env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); env.str_ref.inc (); break; @@ -711,8 +674,8 @@ struct opset_t }; template -struct interpreter_t { - +struct interpreter_t +{ ~interpreter_t() { fini (); } void fini () { env.fini (); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh index d6d7f857ecb06..1b0d795c7cb2c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-cs-common.hh @@ -57,14 +57,14 @@ struct call_context_t /* call stack */ const unsigned int kMaxCallLimit = 10; -struct call_stack_t : stack_t {}; +struct call_stack_t : cff_stack_t {}; template struct biased_subrs_t { - void init (const SUBRS &subrs_) + void init (const SUBRS *subrs_) { - subrs = &subrs_; + subrs = subrs_; unsigned int nSubrs = get_count (); if (nSubrs < 1240) bias = 107; @@ -76,13 +76,13 @@ struct biased_subrs_t void fini () {} - unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; } - unsigned int get_bias () const { return bias; } + unsigned int get_count () const { return subrs ? subrs->count : 0; } + unsigned int get_bias () const { return bias; } byte_str_t operator [] (unsigned int index) const { - if (unlikely ((subrs == nullptr) || index >= subrs->count)) - return Null(byte_str_t); + if (unlikely (!subrs || index >= subrs->count)) + return Null (byte_str_t); else return (*subrs)[index]; } @@ -118,7 +118,7 @@ struct point_t template struct cs_interp_env_t : interp_env_t { - void init (const byte_str_t &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_) + void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) { interp_env_t::init (str); @@ -147,8 +147,9 @@ struct cs_interp_env_t : interp_env_t return callStack.in_error () || SUPER::in_error (); } - bool popSubrNum (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) + bool pop_subr_num (const biased_subrs_t& biasedSubrs, unsigned int &subr_num) { + subr_num = 0; int n = SUPER::argStack.pop_int (); n += biasedSubrs.get_bias (); if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ()))) @@ -158,11 +159,11 @@ struct cs_interp_env_t : interp_env_t return true; } - void callSubr (const biased_subrs_t& biasedSubrs, cs_type_t type) + void call_subr (const biased_subrs_t& biasedSubrs, cs_type_t type) { - unsigned int subr_num; + unsigned int subr_num = 0; - if (unlikely (!popSubrNum (biasedSubrs, subr_num) + if (unlikely (!pop_subr_num (biasedSubrs, subr_num) || callStack.get_count () >= kMaxCallLimit)) { SUPER::set_error (); @@ -175,7 +176,7 @@ struct cs_interp_env_t : interp_env_t SUPER::str_ref = context.str_ref; } - void returnFromSubr () + void return_from_subr () { if (unlikely (SUPER::str_ref.in_error ())) SUPER::set_error (); @@ -246,7 +247,7 @@ struct path_procs_null_t static void flex1 (ENV &env, PARAM& param) {} }; -template > +template > struct cs_opset_t : opset_t { static void process_op (op_code_t op, ENV &env, PARAM& param) @@ -254,7 +255,7 @@ struct cs_opset_t : opset_t switch (op) { case OpCode_return: - env.returnFromSubr (); + env.return_from_subr (); break; case OpCode_endchar: OPSET::check_width (op, env, param); @@ -267,11 +268,11 @@ struct cs_opset_t : opset_t break; case OpCode_callsubr: - env.callSubr (env.localSubrs, CSType_LocalSubr); + env.call_subr (env.localSubrs, CSType_LocalSubr); break; case OpCode_callgsubr: - env.callSubr (env.globalSubrs, CSType_GlobalSubr); + env.call_subr (env.globalSubrs, CSType_GlobalSubr); break; case OpCode_hstem: @@ -550,8 +551,13 @@ struct path_procs_t static void rcurveline (ENV &env, PARAM& param) { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + unsigned int i = 0; - for (; i + 6 <= env.argStack.get_count (); i += 6) + unsigned int curve_limit = arg_count - 2; + for (; i + 6 <= curve_limit; i += 6) { point_t pt1 = env.get_pt (); pt1.move (env.eval_arg (i), env.eval_arg (i+1)); @@ -561,34 +567,34 @@ struct path_procs_t pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); PATH::curve (env, param, pt1, pt2, pt3); } - for (; i + 2 <= env.argStack.get_count (); i += 2) - { - point_t pt1 = env.get_pt (); - pt1.move (env.eval_arg (i), env.eval_arg (i+1)); - PATH::line (env, param, pt1); - } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + PATH::line (env, param, pt1); } static void rlinecurve (ENV &env, PARAM& param) { + unsigned int arg_count = env.argStack.get_count (); + if (unlikely (arg_count < 8)) + return; + unsigned int i = 0; - unsigned int line_limit = (env.argStack.get_count () % 6); + unsigned int line_limit = arg_count - 6; for (; i + 2 <= line_limit; i += 2) { point_t pt1 = env.get_pt (); pt1.move (env.eval_arg (i), env.eval_arg (i+1)); PATH::line (env, param, pt1); } - for (; i + 6 <= env.argStack.get_count (); i += 6) - { - point_t pt1 = env.get_pt (); - pt1.move (env.eval_arg (i), env.eval_arg (i+1)); - point_t pt2 = pt1; - pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); - point_t pt3 = pt2; - pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); - PATH::curve (env, param, pt1, pt2, pt3); - } + + point_t pt1 = env.get_pt (); + pt1.move (env.eval_arg (i), env.eval_arg (i+1)); + point_t pt2 = pt1; + pt2.move (env.eval_arg (i+2), env.eval_arg (i+3)); + point_t pt3 = pt2; + pt3.move (env.eval_arg (i+4), env.eval_arg (i+5)); + PATH::curve (env, param, pt1, pt2, pt3); } static void vvcurveto (ENV &env, PARAM& param) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh index 256c96c046208..a0a9429bf9bf0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff-interp-dict-common.hh @@ -27,8 +27,6 @@ #define HB_CFF_INTERP_DICT_COMMON_HH #include "hb-cff-interp-common.hh" -#include -#include namespace CFF { @@ -58,19 +56,6 @@ struct top_dict_values_t : dict_values_t } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_op_size (const OPSTR& opstr) const - { - switch (opstr.op) - { - case OpCode_CharStrings: - case OpCode_FDArray: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return opstr.str.length; - } - } - unsigned int charStringsOffset; unsigned int FDArrayOffset; }; @@ -94,130 +79,52 @@ struct dict_opset_t : opset_t } } + /* Turns CFF's BCD format into strtod understandable string */ static double parse_bcd (byte_str_ref_t& str_ref) { - bool neg = false; - double int_part = 0; - uint64_t frac_part = 0; - uint32_t frac_count = 0; - bool exp_neg = false; - uint32_t exp_part = 0; - bool exp_overflow = false; - enum Part { INT_PART=0, FRAC_PART, EXP_PART } part = INT_PART; + if (unlikely (str_ref.in_error ())) return .0; + enum Nibble { DECIMAL=10, EXP_POS, EXP_NEG, RESERVED, NEG, END }; - const uint64_t MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 1^52-1 */ - const uint32_t MAX_EXP = 0x7FFu; /* 1^11-1 */ - double value = 0.0; + char buf[32]; unsigned char byte = 0; - for (uint32_t i = 0;; i++) + for (unsigned i = 0, count = 0; count < ARRAY_LENGTH (buf); ++i, ++count) { - char d; - if ((i & 1) == 0) + unsigned nibble; + if (!(i & 1)) { - if (!str_ref.avail ()) - { - str_ref.set_error (); - return 0.0; - } + if (unlikely (!str_ref.avail ())) break; + byte = str_ref[0]; str_ref.inc (); - d = byte >> 4; + nibble = byte >> 4; } else - d = byte & 0x0F; + nibble = byte & 0x0F; - switch (d) + if (unlikely (nibble == RESERVED)) break; + else if (nibble == END) { - case RESERVED: - str_ref.set_error (); - return value; - - case END: - value = (double)(neg? -int_part: int_part); - if (frac_count > 0) - { - double frac = (frac_part / pow (10.0, (double)frac_count)); - if (neg) frac = -frac; - value += frac; - } - if (unlikely (exp_overflow)) - { - if (value == 0.0) - return value; - if (exp_neg) - return neg? -DBL_MIN: DBL_MIN; - else - return neg? -DBL_MAX: DBL_MAX; - } - if (exp_part != 0) - { - if (exp_neg) - value /= pow (10.0, (double)exp_part); - else - value *= pow (10.0, (double)exp_part); - } - return value; - - case NEG: - if (i != 0) - { - str_ref.set_error (); - return 0.0; - } - neg = true; - break; - - case DECIMAL: - if (part != INT_PART) - { - str_ref.set_error (); - return value; - } - part = FRAC_PART; + const char *p = buf; + double pv; + if (unlikely (!hb_parse_double (&p, p + count, &pv, true/* whole buffer */))) break; - - case EXP_NEG: - exp_neg = true; - HB_FALLTHROUGH; - - case EXP_POS: - if (part == EXP_PART) - { - str_ref.set_error (); - return value; - } - part = EXP_PART; - break; - - default: - switch (part) { - default: - case INT_PART: - int_part = (int_part * 10) + d; - break; - - case FRAC_PART: - if (likely (frac_part <= MAX_FRACT / 10)) - { - frac_part = (frac_part * 10) + (unsigned)d; - frac_count++; - } - break; - - case EXP_PART: - if (likely (exp_part * 10 + d <= MAX_EXP)) - { - exp_part = (exp_part * 10) + d; - } - else - exp_overflow = true; - break; - } + return pv; + } + else + { + buf[count] = "0123456789.EE?-?"[nibble]; + if (nibble == EXP_NEG) + { + ++count; + if (unlikely (count == ARRAY_LENGTH (buf))) break; + buf[count] = '-'; + } } } - return value; + str_ref.set_error (); + return .0; } static bool is_hint_op (op_code_t op) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh index a8208a3d1968b..96718f4a6986e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff1-interp-cs.hh @@ -40,7 +40,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t template void init (const byte_str_t &str, ACC &acc, unsigned int fd) { - SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); processed_width = false; has_width = false; arg_start = 0; @@ -81,7 +81,7 @@ struct cff1_cs_interp_env_t : cs_interp_env_t typedef cs_interp_env_t SUPER; }; -template > +template > struct cff1_cs_opset_t : cs_opset_t { /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh index 6971c2eec55f7..f1de1e32981ab 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-cff2-interp-cs.hh @@ -52,7 +52,7 @@ struct blend_arg_t : number_t void set_real (double v) { reset_blends (); number_t::set_real (v); } void set_blends (unsigned int numValues_, unsigned int valueIndex_, - unsigned int numBlends, hb_array_t blends_) + unsigned int numBlends, hb_array_t blends_) { numValues = numValues_; valueIndex = valueIndex_; @@ -80,9 +80,9 @@ struct cff2_cs_interp_env_t : cs_interp_env_t { template void init (const byte_str_t &str, ACC &acc, unsigned int fd, - const int *coords_=nullptr, unsigned int num_coords_=0) + const int *coords_=nullptr, unsigned int num_coords_=0) { - SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); + SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs); coords = coords_; num_coords = num_coords_; @@ -90,7 +90,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t seen_blend = false; seen_vsindex_ = false; scalars.init (); - do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore)); + do_blend = num_coords && coords && varStore->size; set_ivs (acc.privateDicts[fd].ivs); } @@ -133,10 +133,11 @@ struct cff2_cs_interp_env_t : cs_interp_env_t region_count = varStore->varStore.get_region_index_count (get_ivs ()); if (do_blend) { - scalars.resize (region_count); - varStore->varStore.get_scalars (get_ivs (), - (int *)coords, num_coords, - &scalars[0], region_count); + if (unlikely (!scalars.resize (region_count))) + set_error (); + else + varStore->varStore.get_scalars (get_ivs (), coords, num_coords, + &scalars[0], region_count); } seen_blend = true; } @@ -193,7 +194,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t typedef cs_interp_env_t SUPER; }; -template > +template > struct cff2_cs_opset_t : cs_opset_t { static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-common.cc index 890697cee1bc7..4dd2479a0e60e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.cc @@ -27,14 +27,13 @@ */ #include "hb.hh" - #include "hb-machinery.hh" #include -#ifdef HAVE_XLOCALE_H -#include -#endif +#ifdef HB_NO_SETLOCALE +#define setlocale(Category, Locale) "C" +#endif /** * SECTION:hb-common @@ -67,10 +66,9 @@ _hb_options_init () p = c + strlen (c); #define OPTION(name, symbol) \ - if (0 == strncmp (c, name, p - c) && strlen (name) == p - c) u.opts.symbol = true; + if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast(p - c)) do { u.opts.symbol = true; } while (0) OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); - OPTION ("aat", aat); #undef OPTION @@ -334,14 +332,14 @@ lang_find_or_insert (const char *key) /** * hb_language_from_string: * @str: (array length=len) (element-type uint8_t): a string representing - * a BCP 47 language tag + * a BCP 47 language tag * @len: length of the @str, or -1 if it is %NULL-terminated. * - * Converts @str representing a BCP 47 language tag to the corresponding + * Converts @str representing a BCP 47 language tag to the corresponding * #hb_language_t. * * Return value: (transfer none): - * The #hb_language_t corresponding to the BCP 47 language tag. + * The #hb_language_t corresponding to the BCP 47 language tag. * * Since: 0.9.2 **/ @@ -356,7 +354,7 @@ hb_language_from_string (const char *str, int len) { /* NUL-terminate it. */ char strbuf[64]; - len = MIN (len, (int) sizeof (strbuf) - 1); + len = hb_min (len, (int) sizeof (strbuf) - 1); memcpy (strbuf, str, len); strbuf[len] = '\0'; item = lang_find_or_insert (strbuf); @@ -382,7 +380,8 @@ hb_language_from_string (const char *str, int len) const char * hb_language_to_string (hb_language_t language) { - /* This is actually nullptr-safe! */ + if (unlikely (!language)) return nullptr; + return language->s; } @@ -422,12 +421,12 @@ hb_language_get_default () /** * hb_script_from_iso15924_tag: - * @tag: an #hb_tag_t representing an ISO 15924 tag. + * @tag: an #hb_tag_t representing an ISO 15924 tag. * - * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. * * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -468,15 +467,15 @@ hb_script_from_iso15924_tag (hb_tag_t tag) /** * hb_script_from_string: * @str: (array length=len) (element-type uint8_t): a string representing an - * ISO 15924 tag. + * ISO 15924 tag. * @len: length of the @str, or -1 if it is %NULL-terminated. * - * Converts a string @str representing an ISO 15924 script tag to a + * Converts a string @str representing an ISO 15924 script tag to a * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then * hb_script_from_iso15924_tag(). * * Return value: - * An #hb_script_t corresponding to the ISO 15924 tag. + * An #hb_script_t corresponding to the ISO 15924 tag. * * Since: 0.9.2 **/ @@ -488,12 +487,12 @@ hb_script_from_string (const char *str, int len) /** * hb_script_to_iso15924_tag: - * @script: an #hb_script_ to convert. + * @script: an #hb_script_t to convert. * * See hb_script_from_iso15924_tag(). * * Return value: - * An #hb_tag_t representing an ISO 15924 script tag. + * An #hb_tag_t representing an ISO 15924 script tag. * * Since: 0.9.2 **/ @@ -575,6 +574,13 @@ hb_script_get_horizontal_direction (hb_script_t script) case HB_SCRIPT_OLD_SOGDIAN: case HB_SCRIPT_SOGDIAN: + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_YEZIDI: + return HB_DIRECTION_RTL; @@ -590,38 +596,6 @@ hb_script_get_horizontal_direction (hb_script_t script) } -/* hb_user_data_array_t */ - -bool -hb_user_data_array_t::set (hb_user_data_key_t *key, - void * data, - hb_destroy_func_t destroy, - hb_bool_t replace) -{ - if (!key) - return false; - - if (replace) { - if (!data && !destroy) { - items.remove (key, lock); - return true; - } - } - hb_user_data_item_t item = {key, data, destroy}; - bool ret = !!items.replace_or_insert (item, lock, (bool) replace); - - return ret; -} - -void * -hb_user_data_array_t::get (hb_user_data_key_t *key) -{ - hb_user_data_item_t item = {nullptr, nullptr, nullptr}; - - return items.find (key, &item, lock) ? item.data : nullptr; -} - - /* hb_version */ @@ -719,131 +693,24 @@ parse_char (const char **pp, const char *end, char c) static bool parse_uint (const char **pp, const char *end, unsigned int *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; return true; } static bool parse_uint32 (const char **pp, const char *end, uint32_t *pv) { - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - unsigned int v; - - /* Intentionally use strtol instead of strtoul, such that - * -1 turns into "big number"... */ - errno = 0; - v = strtol (p, &pend, 0); - if (errno || p == pend) - return false; + /* Intentionally use hb_parse_int inside instead of hb_parse_uint, + * such that -1 turns into "big number"... */ + int v; + if (unlikely (!hb_parse_int (pp, end, &v))) return false; *pv = v; - *pp += pend - p; - return true; -} - -#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L) -#define USE_XLOCALE 1 -#define HB_LOCALE_T locale_t -#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr) -#define HB_FREE_LOCALE(loc) freelocale (loc) -#elif defined(_MSC_VER) -#define USE_XLOCALE 1 -#define HB_LOCALE_T _locale_t -#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName) -#define HB_FREE_LOCALE(loc) _free_locale (loc) -#define strtod_l(a, b, c) _strtod_l ((a), (b), (c)) -#endif - -#ifdef USE_XLOCALE - -#if HB_USE_ATEXIT -static void free_static_C_locale (); -#endif - -static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t -{ - static HB_LOCALE_T create () - { - HB_LOCALE_T C_locale = HB_CREATE_LOCALE ("C"); - -#if HB_USE_ATEXIT - atexit (free_static_C_locale); -#endif - - return C_locale; - } - static void destroy (HB_LOCALE_T p) - { - HB_FREE_LOCALE (p); - } - static HB_LOCALE_T get_null () - { - return nullptr; - } -} static_C_locale; - -#if HB_USE_ATEXIT -static -void free_static_C_locale () -{ - static_C_locale.free_instance (); -} -#endif - -static HB_LOCALE_T -get_C_locale () -{ - return static_C_locale.get_unconst (); -} -#endif /* USE_XLOCALE */ - -static bool -parse_float (const char **pp, const char *end, float *pv) -{ - char buf[32]; - unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); - strncpy (buf, *pp, len); - buf[len] = '\0'; - - char *p = buf; - char *pend = p; - float v; - - errno = 0; -#ifdef USE_XLOCALE - v = strtod_l (p, &pend, get_C_locale ()); -#else - v = strtod (p, &pend); -#endif - if (errno || p == pend) - return false; - - *pv = v; - *pp += pend - p; return true; } @@ -857,9 +724,14 @@ parse_bool (const char **pp, const char *end, uint32_t *pv) (*pp)++; /* CSS allows on/off as aliases 1/0. */ - if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') *pv = 1; - else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') *pv = 0; else return false; @@ -974,7 +846,41 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) * * Parses a string into a #hb_feature_t. * - * TODO: document the syntax here. + * The format for specifying feature strings follows. All valid CSS + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. + * + * The range indices refer to the positions between Unicode characters. The + * position before the first character is always 0. + * + * The format is Python-esque. Here is how it all works: + * + * + * + * + * Syntax Value Start End + * + * + * Setting value: + * kern 1 0 Turn feature on + * +kern 1 0 Turn feature on + * -kern 0 0 Turn feature off + * kern=0 0 0 Turn feature off + * kern=1 1 0 Turn feature on + * aalt=2 2 0 Choose 2nd alternate + * Setting index: + * kern[] 1 0 Turn feature on + * kern[:] 1 0 Turn feature on + * kern[5:] 1 5 Turn feature on, partial + * kern[:5] 1 0 5 Turn feature on, partial + * kern[3:5] 1 3 5 Turn feature on, range + * kern[3] 1 3 3+1 Turn feature on, single char + * Mixing it all: + * aalt[3:5]=2 2 3 5 Turn 2nd alternate on for range + * + * + * * * Return value: * %true if @str is successfully parsed, %false otherwise. @@ -1028,25 +934,25 @@ hb_feature_to_string (hb_feature_t *feature, len += 4; while (len && s[len - 1] == ' ') len--; - if (feature->start != 0 || feature->end != (unsigned int) -1) + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) { s[len++] = '['; if (feature->start) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); if (feature->end != feature->start + 1) { s[len++] = ':'; - if (feature->end != (unsigned int) -1) - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); + if (feature->end != HB_FEATURE_GLOBAL_END) + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); } s[len++] = ']'; } if (feature->value > 1) { s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); } assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); + len = hb_min (len, size - 1); memcpy (buf, s, len); buf[len] = '\0'; } @@ -1057,7 +963,11 @@ static bool parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) { parse_char (pp, end, '='); /* Optional. */ - return parse_float (pp, end, &variation->value); + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; } static bool @@ -1113,14 +1023,71 @@ hb_variation_to_string (hb_variation_t *variation, while (len && s[len - 1] == ' ') len--; s[len++] = '='; - len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); + len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value)); assert (len < ARRAY_LENGTH (s)); - len = MIN (len, size - 1); + len = hb_min (len, size - 1); memcpy (buf, s, len); buf[len] = '\0'; } +/** + * hb_color_get_alpha: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Alpha channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_alpha) (hb_color_t color) +{ + return hb_color_get_alpha (color); +} + +/** + * hb_color_get_red: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Red channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_red) (hb_color_t color) +{ + return hb_color_get_red (color); +} + +/** + * hb_color_get_green: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Green channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_green) (hb_color_t color) +{ + return hb_color_get_green (color); +} + +/** + * hb_color_get_blue: + * color: a #hb_color_t we are interested in its channels. + * + * Return value: Blue channel value of the given color + * + * Since: 2.1.0 + */ +uint8_t +(hb_color_get_blue) (hb_color_t color) +{ + return hb_color_get_blue (color); +} + + /* If there is no visibility control, then hb-static.cc will NOT * define anything. Instead, we get it to define one set in here * only, so only libharfbuzz.so defines them, not other libs. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-common.h b/src/java.desktop/share/native/libharfbuzz/hb-common.h index fea193ada71ba..9614e720b3265 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-common.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-common.h @@ -63,6 +63,8 @@ typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; +#elif defined (__KERNEL__) +# include #else # include #endif @@ -357,6 +359,22 @@ typedef enum /*11.0*/HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), + /* + * Since 2.4.0 + */ + /*12.0*/HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), + /*12.0*/HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), + /*12.0*/HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), + /*12.0*/HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), + + /* + * Since 2.6.7 + */ + /*13.0*/HB_SCRIPT_CHORASMIAN = HB_TAG ('C','h','r','s'), + /*13.0*/HB_SCRIPT_DIVES_AKURU = HB_TAG ('D','i','a','k'), + /*13.0*/HB_SCRIPT_KHITAN_SMALL_SCRIPT = HB_TAG ('K','i','t','s'), + /*13.0*/HB_SCRIPT_YEZIDI = HB_TAG ('Y','e','z','i'), + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, @@ -415,6 +433,21 @@ typedef void (*hb_destroy_func_t) (void *user_data); */ #define HB_FEATURE_GLOBAL_END ((unsigned int) -1) +/** + * hb_feature_t: + * @tag: a feature tag + * @value: 0 disables the feature, non-zero (usually 1) enables the feature. + * For features implemented as lookup type 3 (like 'salt') the @value is a one + * based index into the alternates. + * @start: the cluster to start applying this feature setting (inclusive). + * @end: the cluster to end applying this feature setting (exclusive). + * + * The #hb_feature_t is the structure that holds information about requested + * feature application. The feature will be applied with the given value to all + * glyphs which are in clusters between @start (inclusive) and @end (exclusive). + * Setting start to @HB_FEATURE_GLOBAL_START and end to @HB_FEATURE_GLOBAL_END + * specifies that the feature always applies to the entire buffer. + */ typedef struct hb_feature_t { hb_tag_t tag; uint32_t value; @@ -459,39 +492,21 @@ typedef uint32_t hb_color_t; #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) -/** - * hb_color_get_alpha: - * - * - * - * Since: 2.1.0 - */ +HB_EXTERN uint8_t +hb_color_get_alpha (hb_color_t color); #define hb_color_get_alpha(color) ((color) & 0xFF) -/** - * hb_color_get_red: - * - * - * - * Since: 2.1.0 - */ + +HB_EXTERN uint8_t +hb_color_get_red (hb_color_t color); #define hb_color_get_red(color) (((color) >> 8) & 0xFF) -/** - * hb_color_get_green: - * - * - * - * Since: 2.1.0 - */ + +HB_EXTERN uint8_t +hb_color_get_green (hb_color_t color); #define hb_color_get_green(color) (((color) >> 16) & 0xFF) -/** - * hb_color_get_blue: - * - * - * - * Since: 2.1.0 - */ -#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) +HB_EXTERN uint8_t +hb_color_get_blue (hb_color_t color); +#define hb_color_get_blue(color) (((color) >> 24) & 0xFF) HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-config.hh b/src/java.desktop/share/native/libharfbuzz/hb-config.hh new file mode 100644 index 0000000000000..fc8d424bfb0b5 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-config.hh @@ -0,0 +1,163 @@ +/* + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_CONFIG_HH +#define HB_CONFIG_HH + +#if 0 /* Make test happy. */ +#include "hb.hh" +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#ifdef HB_TINY +#define HB_LEAN +#define HB_MINI +#define HB_NO_MT +#define HB_NO_UCD_UNASSIGNED +#ifndef NDEBUG +#define NDEBUG +#endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif +#endif + +#ifdef HB_LEAN +#define HB_DISABLE_DEPRECATED +#define HB_NDEBUG +#define HB_NO_ATEXIT +#define HB_NO_BUFFER_MESSAGE +#define HB_NO_BUFFER_SERIALIZE +#define HB_NO_BITMAP +#define HB_NO_CFF +#define HB_NO_COLOR +#define HB_NO_DRAW +#define HB_NO_ERRNO +#define HB_NO_FACE_COLLECT_UNICODES +#define HB_NO_GETENV +#define HB_NO_HINTING +#define HB_NO_LANGUAGE_PRIVATE_SUBTAG +#define HB_NO_LAYOUT_FEATURE_PARAMS +#define HB_NO_LAYOUT_COLLECT_GLYPHS +#define HB_NO_LAYOUT_UNUSED +#define HB_NO_MATH +#define HB_NO_META +#define HB_NO_METRICS +#define HB_NO_MMAP +#define HB_NO_NAME +#define HB_NO_OPEN +#define HB_NO_SETLOCALE +#define HB_NO_OT_FONT_GLYPH_NAMES +#define HB_NO_OT_SHAPE_FRACTIONS +#define HB_NO_STYLE +#define HB_NO_SUBSET_LAYOUT +#define HB_NO_VAR +#endif + +#ifdef HB_MINI +#define HB_NO_AAT +#define HB_NO_LEGACY +#endif + + +/* Closure of options. */ + +#ifdef HB_DISABLE_DEPRECATED +#define HB_IF_NOT_DEPRECATED(x) +#else +#define HB_IF_NOT_DEPRECATED(x) x +#endif + +#ifdef HB_NO_AAT +#define HB_NO_OT_NAME_LANGUAGE_AAT +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_BITMAP +#define HB_NO_OT_FONT_BITMAP +#endif + +#ifdef HB_NO_CFF +#define HB_NO_OT_FONT_CFF +#define HB_NO_SUBSET_CFF +#endif + +#ifdef HB_NO_GETENV +#define HB_NO_UNISCRIBE_BUG_COMPATIBLE +#endif + +#ifdef HB_NO_LEGACY +#define HB_NO_CMAP_LEGACY_SUBTABLES +#define HB_NO_FALLBACK_SHAPE +#define HB_NO_OT_KERN +#define HB_NO_OT_LAYOUT_BLACKLIST +#define HB_NO_OT_SHAPE_FALLBACK +#endif + +#ifdef HB_NO_NAME +#define HB_NO_OT_NAME_LANGUAGE +#endif + +#ifdef HB_NO_OT +#define HB_NO_OT_FONT +#define HB_NO_OT_LAYOUT +#define HB_NO_OT_TAG +#define HB_NO_OT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE +#define HB_NO_AAT_SHAPE +#endif + +#ifdef HB_NO_OT_SHAPE_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK +#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS +#endif + +#ifdef NDEBUG +#ifndef HB_NDEBUG +#define HB_NDEBUG +#endif +#endif + +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif + +#ifdef HAVE_CONFIG_OVERRIDE_H +#include "config-override.h" +#endif + + +#endif /* HB_CONFIG_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc b/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc index f8d03085bd90a..a382228f20d84 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-coretext.cc @@ -27,6 +27,9 @@ */ #include "hb.hh" + +#ifdef HAVE_CORETEXT + #include "hb-shaper-impl.hh" #include "hb-coretext.h" @@ -46,24 +49,6 @@ /* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */ #define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f -static CGFloat -coretext_font_size_from_ptem (float ptem) -{ - /* CoreText points are CSS pixels (96 per inch), - * NOT typographic points (72 per inch). - * - * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html - */ - ptem *= 96.f / 72.f; - return ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : ptem; -} -static float -coretext_font_size_to_ptem (CGFloat size) -{ - size *= 72.f / 96.f; - return size <= 0.f ? 0 : size; -} - static void release_table_data (void *user_data) { @@ -72,7 +57,7 @@ release_table_data (void *user_data) } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { CGFontRef cg_font = reinterpret_cast (user_data); CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); @@ -171,7 +156,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) if (CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSText")) || CFStringHasPrefix (cg_postscript_name, CFSTR (".SFNSDisplay"))) { -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1080 # define kCTFontUIFontSystem kCTFontSystemFontType # define kCTFontUIFontEmphasizedSystem kCTFontEmphasizedSystemFontType #endif @@ -214,7 +199,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) } CFURLRef original_url = nullptr; -#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 ATSFontRef atsFont; FSRef fsref; OSStatus status; @@ -244,7 +229,7 @@ create_ct_font (CGFontRef cg_font, CGFloat font_size) * process in Blink. This can be detected by the new file URL location * that the newly found font points to. */ CFURLRef new_url = nullptr; -#if TARGET_OS_OSX && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 atsFont = CTFontGetPlatformFont (new_ct_font, NULL); status = ATSFontGetFileReference (atsFont, &fsref); if (status == noErr) @@ -293,13 +278,32 @@ _hb_coretext_shaper_face_data_destroy (hb_coretext_face_data_t *data) CFRelease ((CGFontRef) data); } +/** + * hb_coretext_face_create: + * @cg_font: The CGFontRef to work upon + * + * Creates an #hb_face_t face object from the specified + * CGFontRef. + * + * Return value: the new #hb_face_t face object + * + * Since: 0.9.10 + */ hb_face_t * hb_coretext_face_create (CGFontRef cg_font) { - return hb_face_create_for_tables (reference_table, CGFontRetain (cg_font), _hb_cg_font_release); + return hb_face_create_for_tables (_hb_cg_reference_table, CGFontRetain (cg_font), _hb_cg_font_release); } -/* +/** + * hb_coretext_face_get_cg_font: + * @face: The #hb_face_t to work upon + * + * Fetches the CGFontRef associated with an #hb_face_t + * face object + * + * Return value: the CGFontRef found + * * Since: 0.9.10 */ CGFontRef @@ -317,7 +321,8 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font) if (unlikely (!face_data)) return nullptr; CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; - CTFontRef ct_font = create_ct_font (cg_font, coretext_font_size_from_ptem (font->ptem)); + CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); + CTFontRef ct_font = create_ct_font (cg_font, font_size); if (unlikely (!ct_font)) { @@ -341,7 +346,7 @@ hb_coretext_font_data_sync (hb_font_t *font) const hb_coretext_font_data_t *data = font->data.coretext; if (unlikely (!data)) return nullptr; - if (fabs (CTFontGetSize((CTFontRef) data) - coretext_font_size_from_ptem (font->ptem)) > .5) + if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5) { /* XXX-MT-bug * Note that evaluating condition above can be dangerous if another thread @@ -365,10 +370,17 @@ hb_coretext_font_data_sync (hb_font_t *font) return font->data.coretext; } - -/* +/** + * hb_coretext_font_create: + * @ct_font: The CTFontRef to work upon + * + * Creates an #hb_font_t font object from the specified + * CTFontRef. + * + * Return value: the new #hb_font_t font object + * * Since: 1.7.2 - */ + **/ hb_font_t * hb_coretext_font_create (CTFontRef ct_font) { @@ -381,7 +393,7 @@ hb_coretext_font_create (CTFontRef ct_font) if (unlikely (hb_object_is_immutable (font))) return font; - hb_font_set_ptem (font, coretext_font_size_to_ptem (CTFontGetSize(ct_font))); + hb_font_set_ptem (font, CTFontGetSize (ct_font)); /* Let there be dragons here... */ font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); @@ -389,6 +401,17 @@ hb_coretext_font_create (CTFontRef ct_font) return font; } +/** + * hb_coretext_face_get_ct_font: + * @font: #hb_font_t to work upon + * + * Fetches the CTFontRef associated with the specified + * #hb_font_t font object. + * + * Return value: the CTFontRef found + * + * Since: 0.9.10 + */ CTFontRef hb_coretext_font_get_ct_font (hb_font_t *font) { @@ -410,7 +433,7 @@ struct active_feature_t { feature_record_t rec; unsigned int order; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const active_feature_t *a = (const active_feature_t *) pa; const active_feature_t *b = (const active_feature_t *) pb; return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 : @@ -428,7 +451,7 @@ struct feature_event_t { bool start; active_feature_t feature; - static int cmp (const void *pa, const void *pb) { + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_event_t *a = (const feature_event_t *) pa; const feature_event_t *b = (const feature_event_t *) pb; return a->index < b->index ? -1 : a->index > b->index ? 1 : @@ -489,13 +512,19 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, hb_vector_t feature_events; for (unsigned int i = 0; i < num_features; i++) { + active_feature_t feature; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 const hb_aat_feature_mapping_t * mapping = hb_aat_layout_find_feature_mapping (features[i].tag); if (!mapping) continue; - active_feature_t feature; feature.rec.feature = mapping->aatFeatureType; feature.rec.setting = features[i].value ? mapping->selectorToEnable : mapping->selectorToDisable; +#else + feature.rec.feature = features[i].tag; + feature.rec.setting = features[i].value; +#endif feature.order = i; feature_event_t *event; @@ -544,6 +573,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, /* active_features.qsort (); */ for (unsigned int j = 0; j < active_features.length; j++) { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 CFStringRef keys[] = { kCTFontFeatureTypeIdentifierKey, kCTFontFeatureSelectorIdentifierKey @@ -552,6 +582,17 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature), CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) }; +#else + char tag[5] = {HB_UNTAG (active_features[j].rec.feature)}; + CFTypeRef keys[] = { + kCTFontOpenTypeFeatureTag, + kCTFontOpenTypeFeatureValue + }; + CFTypeRef values[] = { + CFStringCreateWithCString (kCFAllocatorDefault, tag, kCFStringEncodingASCII), + CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting) + }; +#endif static_assert ((ARRAY_LENGTH_CONST (keys) == ARRAY_LENGTH_CONST (values)), ""); CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault, (const void **) keys, @@ -598,7 +639,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } else { active_feature_t *feature = active_features.find (&event->feature); if (feature) - active_features.remove (feature - active_features.arrayZ ()); + active_features.remove (feature - active_features.arrayZ); } } } @@ -608,7 +649,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, #define ALLOCATE_ARRAY(Type, name, len, on_no_room) \ Type *name = (Type *) scratch; \ - { \ + do { \ unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \ if (unlikely (_consumed > scratch_size)) \ { \ @@ -617,9 +658,9 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } \ scratch += _consumed; \ scratch_size -= _consumed; \ - } + } while (0) - ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/); + ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, ((void)nullptr) /*nothing*/); unsigned int chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { hb_codepoint_t c = buffer->info[i].codepoint; @@ -633,7 +674,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } } - ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/); + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); chars_len = 0; for (unsigned int i = 0; i < buffer->len; i++) { @@ -649,7 +690,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ ret = false; \ goto fail; \ - } HB_STMT_END; + } HB_STMT_END bool ret = true; CFStringRef string_ref = nullptr; @@ -711,7 +752,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, /* What's the iOS equivalent of this check? * The symbols was introduced in iOS 7.0. * At any rate, our fallback is safe and works fine. */ -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 # define kCTLanguageAttributeName CFSTR ("NSLanguage") #endif CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, @@ -771,7 +812,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, feature.start < chars_len && feature.start < feature.end) { CFRange feature_range = CFRangeMake (feature.start, - MIN (feature.end, chars_len) - feature.start); + hb_min (feature.end, chars_len) - feature.start); if (feature.value) CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); else @@ -783,7 +824,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level); -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +#if !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; #endif CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault, @@ -977,7 +1018,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, #define SCRATCH_RESTORE() \ scratch_size = scratch_size_saved; \ - scratch = scratch_saved; + scratch = scratch_saved { /* Setup glyphs */ SCRATCH_SAVE(); @@ -1069,7 +1110,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, if (false) { /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + HB_UNUSED bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); assert (bool (status_and & kCTRunStatusRightToLeft) == backward); assert (bool (status_or & kCTRunStatusRightToLeft) == backward); } @@ -1116,7 +1157,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, unsigned int cluster = info[count - 1].cluster; for (unsigned int i = count - 1; i > 0; i--) { - cluster = MIN (cluster, info[i - 1].cluster); + cluster = hb_min (cluster, info[i - 1].cluster); info[i - 1].cluster = cluster; } } @@ -1125,7 +1166,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, unsigned int cluster = info[0].cluster; for (unsigned int i = 1; i < count; i++) { - cluster = MIN (cluster, info[i].cluster); + cluster = hb_min (cluster, info[i].cluster); info[i].cluster = cluster; } } @@ -1150,57 +1191,4 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, } -/* - * AAT shaper - */ - -/* - * shaper face data - */ - -struct hb_coretext_aat_face_data_t {}; - -hb_coretext_aat_face_data_t * -_hb_coretext_aat_shaper_face_data_create (hb_face_t *face) -{ - return hb_aat_layout_has_substitution (face) || hb_aat_layout_has_positioning (face) ? - (hb_coretext_aat_face_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_face_data_destroy (hb_coretext_aat_face_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper font data - */ - -struct hb_coretext_aat_font_data_t {}; - -hb_coretext_aat_font_data_t * -_hb_coretext_aat_shaper_font_data_create (hb_font_t *font) -{ - return font->data.coretext ? (hb_coretext_aat_font_data_t *) HB_SHAPER_DATA_SUCCEEDED : nullptr; -} - -void -_hb_coretext_aat_shaper_font_data_destroy (hb_coretext_aat_font_data_t *data HB_UNUSED) -{ -} - - -/* - * shaper - */ - -hb_bool_t -_hb_coretext_aat_shape (hb_shape_plan_t *shape_plan, - hb_font_t *font, - hb_buffer_t *buffer, - const hb_feature_t *features, - unsigned int num_features) -{ - return _hb_coretext_shape (shape_plan, font, buffer, features, num_features); -} +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-coretext.h b/src/java.desktop/share/native/libharfbuzz/hb-coretext.h index 4b0a6f01b6f67..55cac7e294a67 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-coretext.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-coretext.h @@ -40,8 +40,40 @@ HB_BEGIN_DECLS +/** + * HB_CORETEXT_TAG_MORT: + * + * The #hb_tag_t tag for the `mort` (glyph metamorphosis) table, + * which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6mort.html + * + **/ #define HB_CORETEXT_TAG_MORT HB_TAG('m','o','r','t') + +/** + * HB_CORETEXT_TAG_MORX: + * + * The #hb_tag_t tag for the `morx` (extended glyph metamorphosis) + * table, which holds AAT features. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6morx.html + * + **/ #define HB_CORETEXT_TAG_MORX HB_TAG('m','o','r','x') + +/** + * HB_CORETEXT_TAG_KERX: + * + * The #hb_tag_t tag for the `kerx` (extended kerning) table, which + * holds AAT kerning information. + * + * For more information, see + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html + * + **/ #define HB_CORETEXT_TAG_KERX HB_TAG('k','e','r','x') diff --git a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh index d5ec94f4e4711..db60837748fad 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-debug.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-debug.hh @@ -29,7 +29,7 @@ #include "hb.hh" #include "hb-atomic.hh" -#include "hb-dsalgs.hh" +#include "hb-algs.hh" #ifndef HB_DEBUG @@ -46,7 +46,6 @@ struct hb_options_t bool unused : 1; /* In-case sign bit is here. */ bool initialized : 1; bool uniscribe_bug_compatible : 1; - bool aat : 1; }; union hb_options_union_t { @@ -63,6 +62,9 @@ extern HB_INTERNAL hb_atomic_int_t _hb_options; static inline hb_options_t hb_options () { +#ifdef HB_NO_GETENV + return hb_options_t (); +#endif /* Make a local copy, so we can access bitfield threadsafely. */ hb_options_union_t u; u.i = _hb_options.get_relaxed (); @@ -158,7 +160,7 @@ _hb_debug_msg_va (const char *what, VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; fprintf (stderr, "%2u %s" VRBAR "%s", level, - bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), + bars + sizeof (bars) - 1 - hb_min ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level), level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR); } else fprintf (stderr, " " VRBAR LBAR); @@ -246,8 +248,8 @@ struct hb_printer_t { }; template <> -struct hb_printer_t { - const char *print (hb_void_t) { return ""; } +struct hb_printer_t { + const char *print (hb_empty_t) { return ""; } }; @@ -263,7 +265,7 @@ static inline void _hb_warn_no_return (bool returned) } } template <> -/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) +/*static*/ inline void _hb_warn_no_return (bool returned HB_UNUSED) {} template @@ -293,22 +295,23 @@ struct hb_auto_trace_t if (plevel) --*plevel; } - ret_t ret (ret_t v, - const char *func = "", - unsigned int line = 0) + template + T ret (T&& v, + const char *func = "", + unsigned int line = 0) { if (unlikely (returned)) { fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n"); - return v; + return hb_forward (v); } _hb_debug_msg (what, obj, func, true, plevel ? *plevel : 1, -1, "return %s (line %d)", - hb_printer_t().print (v), line); + hb_printer_t().print (v), line); if (plevel) --*plevel; plevel = nullptr; returned = true; - return v; + return hb_forward (v); } private: @@ -327,18 +330,20 @@ struct hb_auto_trace_t<0, ret_t> const char *message, ...) HB_PRINTF_FUNC(6, 7) {} - ret_t ret (ret_t v, - const char *func HB_UNUSED = nullptr, - unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } }; /* For disabled tracing; optimize out everything. * https://github.com/harfbuzz/harfbuzz/pull/605 */ template struct hb_no_trace_t { - ret_t ret (ret_t v, - const char *func HB_UNUSED = "", - unsigned int line HB_UNUSED = 0) { return v; } + template + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return hb_forward (v); } }; #define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) @@ -368,10 +373,6 @@ struct hb_no_trace_t { #define HB_DEBUG_FT (HB_DEBUG+0) #endif -#ifndef HB_DEBUG_GET_COVERAGE -#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0) -#endif - #ifndef HB_DEBUG_OBJECT #define HB_DEBUG_OBJECT (HB_DEBUG+0) #endif @@ -408,7 +409,7 @@ struct hb_no_trace_t { #define TRACE_SANITIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SANITIZE(this) hb_no_trace_t trace #endif @@ -420,7 +421,7 @@ struct hb_no_trace_t { #define TRACE_SERIALIZE(this) \ hb_auto_trace_t trace \ (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ - " "); + " ") #else #define TRACE_SERIALIZE(this) hb_no_trace_t trace #endif @@ -432,37 +433,24 @@ struct hb_no_trace_t { #define TRACE_SUBSET(this) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - " "); + " ") #else #define TRACE_SUBSET(this) hb_no_trace_t trace #endif -#ifndef HB_DEBUG_WOULD_APPLY -#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0) -#endif -#if HB_DEBUG_WOULD_APPLY -#define TRACE_WOULD_APPLY(this) \ - hb_auto_trace_t trace \ - (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "%d glyphs", c->len); -#else -#define TRACE_WOULD_APPLY(this) hb_no_trace_t trace -#endif - #ifndef HB_DEBUG_DISPATCH #define HB_DEBUG_DISPATCH ( \ HB_DEBUG_APPLY + \ HB_DEBUG_SANITIZE + \ HB_DEBUG_SERIALIZE + \ - HB_DEBUG_SUBSET + \ - HB_DEBUG_WOULD_APPLY + \ + HB_DEBUG_SUBSET + \ 0) #endif #if HB_DEBUG_DISPATCH #define TRACE_DISPATCH(this, format) \ hb_auto_trace_t trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "format %d", (int) format); + "format %d", (int) format) #else #define TRACE_DISPATCH(this, format) hb_no_trace_t trace #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h index 9409f320781f2..5dd04050b8e38 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-deprecated.h @@ -63,7 +63,7 @@ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t *glyph, void *user_data); -HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func or hb_font_funcs_set_variation_glyph_func) void +HB_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy); @@ -165,29 +165,8 @@ hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, hb_codepoint_t *decomposed); -typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - void *user_data); -typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_v_kerning_func_t; -/** - * hb_font_funcs_set_glyph_h_kerning_func: - * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: - * - * - * - * Since: 0.9.2 - * Deprecated: 2.0.0 - **/ -HB_EXTERN void -hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, - hb_font_get_glyph_h_kerning_func_t func, - void *user_data, hb_destroy_func_t destroy); - /** * hb_font_funcs_set_glyph_v_kerning_func: * @ffuncs: font functions. @@ -206,19 +185,9 @@ hb_font_funcs_set_glyph_v_kerning_func (hb_font_funcs_t *ffuncs, void *user_data, hb_destroy_func_t destroy); HB_EXTERN hb_position_t -hb_font_get_glyph_h_kerning (hb_font_t *font, - hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); -HB_EXTERN hb_position_t hb_font_get_glyph_v_kerning (hb_font_t *font, hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph); -HB_EXTERN void -hb_font_get_glyph_kerning_for_direction (hb_font_t *font, - hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, - hb_direction_t direction, - hb_position_t *x, hb_position_t *y); - - #endif HB_END_DECLS diff --git a/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh b/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh new file mode 100644 index 0000000000000..e946f21c114ba --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-dispatch.hh @@ -0,0 +1,61 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_DISPATCH_HH +#define HB_DISPATCH_HH + +#include "hb.hh" + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + hb_dispatch_context_t () : debug_depth (0) {} + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast (this); } + Context* thiz () { return static_cast< Context *> (this); } + public: + const char *get_name () { return "UNKNOWN"; } + static constexpr unsigned max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), hb_forward (ds)...); } + static return_t no_dispatch_return_value () { return Context::default_return_value (); } + static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } + unsigned debug_depth; +}; + + +#endif /* HB_DISPATCH_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.cc b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc new file mode 100644 index 0000000000000..72444a94c8a90 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.cc @@ -0,0 +1,261 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_DRAW +#ifdef HB_EXPERIMENTAL_API + +#include "hb-draw.hh" +#include "hb-ot.h" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" + +/** + * hb_draw_funcs_set_move_to_func: + * @funcs: draw functions object + * @move_to: move-to callback + * + * Sets move-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->move_to = move_to; +} + +/** + * hb_draw_funcs_set_line_to_func: + * @funcs: draw functions object + * @line_to: line-to callback + * + * Sets line-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->line_to = line_to; +} + +/** + * hb_draw_funcs_set_quadratic_to_func: + * @funcs: draw functions object + * @move_to: quadratic-to callback + * + * Sets quadratic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->quadratic_to = quadratic_to; + funcs->is_quadratic_to_set = true; +} + +/** + * hb_draw_funcs_set_cubic_to_func: + * @funcs: draw functions + * @cubic_to: cubic-to callback + * + * Sets cubic-to callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->cubic_to = cubic_to; +} + +/** + * hb_draw_funcs_set_close_path_func: + * @funcs: draw functions object + * @close_path: close-path callback + * + * Sets close-path callback to the draw functions object. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path) +{ + if (unlikely (hb_object_is_immutable (funcs))) return; + funcs->close_path = close_path; +} + +static void +_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {} + +static void +_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED, + hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED, + hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, + void *user_data HB_UNUSED) {} + +static void +_close_path_nil (void *user_data HB_UNUSED) {} + +/** + * hb_draw_funcs_create: + * + * Creates a new draw callbacks object. + * + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_create () +{ + hb_draw_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create ()))) + return const_cast (&Null (hb_draw_funcs_t)); + + funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil; + funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil; + funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil; + funcs->is_quadratic_to_set = false; + funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil; + funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil; + return funcs; +} + +/** + * hb_draw_funcs_reference: + * @funcs: draw functions + * + * Add to callbacks object refcount. + * + * Returns: The same object. + * Since: EXPERIMENTAL + **/ +hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_draw_funcs_destroy: + * @funcs: draw functions + * + * Decreases refcount of callbacks object and deletes the object if it reaches + * to zero. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + free (funcs); +} + +/** + * hb_draw_funcs_make_immutable: + * @funcs: draw functions + * + * Makes funcs object immutable. + * + * Since: EXPERIMENTAL + **/ +void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs) +{ + if (hb_object_is_immutable (funcs)) + return; + + hb_object_make_immutable (funcs); +} + +/** + * hb_draw_funcs_is_immutable: + * @funcs: draw functions + * + * Checks whether funcs is immutable. + * + * Returns: If is immutable. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs) +{ + return hb_object_is_immutable (funcs); +} + +/** + * hb_font_draw_glyph: + * @font: a font object + * @glyph: a glyph id + * @funcs: draw callbacks object + * @user_data: parameter you like be passed to the callbacks when are called + * + * Draw a glyph. + * + * Returns: Whether the font had the glyph and the operation completed successfully. + * Since: EXPERIMENTAL + **/ +hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, + void *user_data) +{ + if (unlikely (funcs == &Null (hb_draw_funcs_t) || + glyph >= font->face->get_num_glyphs ())) + return false; + + draw_helper_t draw_helper (funcs, user_data); + if (font->face->table.glyf->get_path (font, glyph, draw_helper)) return true; +#ifndef HB_NO_CFF + if (font->face->table.cff1->get_path (font, glyph, draw_helper)) return true; + if (font->face->table.cff2->get_path (font, glyph, draw_helper)) return true; +#endif + + return false; +} + +#endif +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.h b/src/java.desktop/share/native/libharfbuzz/hb-draw.h new file mode 100644 index 0000000000000..d5eb6ecc65b14 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.h @@ -0,0 +1,98 @@ +/* + * Copyright © 2019-2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_DRAW_H +#define HB_DRAW_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +typedef void (*hb_draw_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data); +typedef void (*hb_draw_quadratic_to_func_t) (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y, + void *user_data); +typedef void (*hb_draw_close_path_func_t) (void *user_data); + +/** + * hb_draw_funcs_t: + * + * Glyph draw callbacks. + * + * _move_to, _line_to and _cubic_to calls are nessecary to be defined but we + * translate _quadratic_to calls to _cubic_to if the callback isn't defined. + * + * Since: EXPERIMENTAL + **/ +typedef struct hb_draw_funcs_t hb_draw_funcs_t; + +HB_EXTERN void +hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs, + hb_draw_move_to_func_t move_to); + +HB_EXTERN void +hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs, + hb_draw_line_to_func_t line_to); + +HB_EXTERN void +hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs, + hb_draw_quadratic_to_func_t quadratic_to); + +HB_EXTERN void +hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs, + hb_draw_cubic_to_func_t cubic_to); + +HB_EXTERN void +hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs, + hb_draw_close_path_func_t close_path); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_create (void); + +HB_EXTERN hb_draw_funcs_t * +hb_draw_funcs_reference (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_destroy (hb_draw_funcs_t *funcs); + +HB_EXTERN void +hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs); +#endif + +HB_END_DECLS + +#endif /* HB_DRAW_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-draw.hh b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh new file mode 100644 index 0000000000000..0e5101f9cd24c --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-draw.hh @@ -0,0 +1,139 @@ +/* + * Copyright © 2020 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_DRAW_HH +#define HB_DRAW_HH + +#include "hb.hh" + +#ifdef HB_EXPERIMENTAL_API +struct hb_draw_funcs_t +{ + hb_object_header_t header; + + hb_draw_move_to_func_t move_to; + hb_draw_line_to_func_t line_to; + hb_draw_quadratic_to_func_t quadratic_to; + bool is_quadratic_to_set; + hb_draw_cubic_to_func_t cubic_to; + hb_draw_close_path_func_t close_path; +}; + +struct draw_helper_t +{ + draw_helper_t (const hb_draw_funcs_t *funcs_, void *user_data_) + { + funcs = funcs_; + user_data = user_data_; + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + ~draw_helper_t () { end_path (); } + + void move_to (hb_position_t x, hb_position_t y) + { + if (path_open) end_path (); + current_x = path_start_x = x; + current_y = path_start_y = y; + } + + void line_to (hb_position_t x, hb_position_t y) + { + if (equal_to_current (x, y)) return; + if (!path_open) start_path (); + funcs->line_to (x, y, user_data); + current_x = x; + current_y = y; + } + + void + quadratic_to (hb_position_t control_x, hb_position_t control_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control_x, control_y) && equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + if (funcs->is_quadratic_to_set) + funcs->quadratic_to (control_x, control_y, to_x, to_y, user_data); + else + funcs->cubic_to (roundf ((current_x + 2.f * control_x) / 3.f), + roundf ((current_y + 2.f * control_y) / 3.f), + roundf ((to_x + 2.f * control_x) / 3.f), + roundf ((to_y + 2.f * control_y) / 3.f), + to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void + cubic_to (hb_position_t control1_x, hb_position_t control1_y, + hb_position_t control2_x, hb_position_t control2_y, + hb_position_t to_x, hb_position_t to_y) + { + if (equal_to_current (control1_x, control1_y) && + equal_to_current (control2_x, control2_y) && + equal_to_current (to_x, to_y)) + return; + if (!path_open) start_path (); + funcs->cubic_to (control1_x, control1_y, control2_x, control2_y, to_x, to_y, user_data); + current_x = to_x; + current_y = to_y; + } + + void end_path () + { + if (path_open) + { + if ((path_start_x != current_x) || (path_start_y != current_y)) + funcs->line_to (path_start_x, path_start_y, user_data); + funcs->close_path (user_data); + } + path_open = false; + path_start_x = current_x = path_start_y = current_y = 0; + } + + protected: + bool equal_to_current (hb_position_t x, hb_position_t y) + { return current_x == x && current_y == y; } + + void start_path () + { + if (path_open) end_path (); + path_open = true; + funcs->move_to (path_start_x, path_start_y, user_data); + } + + hb_position_t path_start_x; + hb_position_t path_start_y; + + hb_position_t current_x; + hb_position_t current_y; + + bool path_open; + const hb_draw_funcs_t *funcs; + void *user_data; +}; +#endif + +#endif /* HB_DRAW_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh b/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh deleted file mode 100644 index f5f286883272d..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-dsalgs.hh +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright © 2017 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifndef HB_DSALGS_HH -#define HB_DSALGS_HH - -#include "hb.hh" -#include "hb-null.hh" - - -/* Void! For when we need a expression-type of void. */ -typedef const struct _hb_void_t *hb_void_t; -#define HB_VOID ((const _hb_void_t *) nullptr) - - -/* - * Bithacks. - */ - -/* Return the number of 1 bits in v. */ -template -static inline HB_CONST_FUNC unsigned int -hb_popcount (T v) -{ -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return __builtin_popcount (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return __builtin_popcountl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return __builtin_popcountll (v); -#endif - - if (sizeof (T) <= 4) - { - /* "HACKMEM 169" */ - uint32_t y; - y = (v >> 1) &033333333333; - y = v - y - ((y >>1) & 033333333333); - return (((y + (y >> 3)) & 030707070707) % 077); - } - - if (sizeof (T) == 8) - { - unsigned int shift = 32; - return hb_popcount ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift)); - } - - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return hb_popcount ((uint64_t) v) + hb_popcount ((uint64_t) (v >> shift)); - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - -/* Returns the number of bits needed to store number */ -template -static inline HB_CONST_FUNC unsigned int -hb_bit_storage (T v) -{ - if (unlikely (!v)) return 0; - -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return sizeof (unsigned int) * 8 - __builtin_clz (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return sizeof (unsigned long) * 8 - __builtin_clzl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return sizeof (unsigned long long) * 8 - __builtin_clzll (v); -#endif - -#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) - if (sizeof (T) <= sizeof (unsigned int)) - { - unsigned long where; - _BitScanReverse (&where, v); - return 1 + where; - } -# if defined(_WIN64) - if (sizeof (T) <= 8) - { - unsigned long where; - _BitScanReverse64 (&where, v); - return 1 + where; - } -# endif -#endif - - if (sizeof (T) <= 4) - { - /* "bithacks" */ - const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000}; - const unsigned int S[] = {1, 2, 4, 8, 16}; - unsigned int r = 0; - for (int i = 4; i >= 0; i--) - if (v & b[i]) - { - v >>= S[i]; - r |= S[i]; - } - return r + 1; - } - if (sizeof (T) <= 8) - { - /* "bithacks" */ - const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; - const unsigned int S[] = {1, 2, 4, 8, 16, 32}; - unsigned int r = 0; - for (int i = 5; i >= 0; i--) - if (v & b[i]) - { - v >>= S[i]; - r |= S[i]; - } - return r + 1; - } - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return (v >> shift) ? hb_bit_storage ((uint64_t) (v >> shift)) + shift : - hb_bit_storage ((uint64_t) v); - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - -/* Returns the number of zero bits in the least significant side of v */ -template -static inline HB_CONST_FUNC unsigned int -hb_ctz (T v) -{ - if (unlikely (!v)) return 0; - -#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) - if (sizeof (T) <= sizeof (unsigned int)) - return __builtin_ctz (v); - - if (sizeof (T) <= sizeof (unsigned long)) - return __builtin_ctzl (v); - - if (sizeof (T) <= sizeof (unsigned long long)) - return __builtin_ctzll (v); -#endif - -#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) - if (sizeof (T) <= sizeof (unsigned int)) - { - unsigned long where; - _BitScanForward (&where, v); - return where; - } -# if defined(_WIN64) - if (sizeof (T) <= 8) - { - unsigned long where; - _BitScanForward64 (&where, v); - return where; - } -# endif -#endif - - if (sizeof (T) <= 4) - { - /* "bithacks" */ - unsigned int c = 32; - v &= - (int32_t) v; - if (v) c--; - if (v & 0x0000FFFF) c -= 16; - if (v & 0x00FF00FF) c -= 8; - if (v & 0x0F0F0F0F) c -= 4; - if (v & 0x33333333) c -= 2; - if (v & 0x55555555) c -= 1; - return c; - } - if (sizeof (T) <= 8) - { - /* "bithacks" */ - unsigned int c = 64; - v &= - (int64_t) (v); - if (v) c--; - if (v & 0x00000000FFFFFFFFULL) c -= 32; - if (v & 0x0000FFFF0000FFFFULL) c -= 16; - if (v & 0x00FF00FF00FF00FFULL) c -= 8; - if (v & 0x0F0F0F0F0F0F0F0FULL) c -= 4; - if (v & 0x3333333333333333ULL) c -= 2; - if (v & 0x5555555555555555ULL) c -= 1; - return c; - } - if (sizeof (T) == 16) - { - unsigned int shift = 64; - return (uint64_t) v ? hb_bit_storage ((uint64_t) v) : - hb_bit_storage ((uint64_t) (v >> shift)) + shift; - } - - assert (0); - return 0; /* Shut up stupid compiler. */ -} - - -/* - * Tiny stuff. - */ - -template -static inline T* hb_addressof (T& arg) -{ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-align" - /* https://en.cppreference.com/w/cpp/memory/addressof */ - return reinterpret_cast( - &const_cast( - reinterpret_cast(arg))); -#pragma GCC diagnostic pop -} - -/* ASCII tag/character handling */ -static inline bool ISALPHA (unsigned char c) -{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -static inline bool ISALNUM (unsigned char c) -{ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } -static inline bool ISSPACE (unsigned char c) -{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; } -static inline unsigned char TOUPPER (unsigned char c) -{ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } -static inline unsigned char TOLOWER (unsigned char c) -{ return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } - -#undef MIN -template -static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; } - -#undef MAX -template -static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; } - -static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b) -{ return (a + (b - 1)) / b; } - - -#undef ARRAY_LENGTH -template -static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; } -/* A const version, but does not detect erratically being called on pointers. */ -#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) - - -static inline int -hb_memcmp (const void *a, const void *b, unsigned int len) -{ - /* It's illegal to pass NULL to memcmp(), even if len is zero. - * So, wrap it. - * https://sourceware.org/bugzilla/show_bug.cgi?id=23878 */ - if (!len) return 0; - return memcmp (a, b, len); -} - -static inline bool -hb_unsigned_mul_overflows (unsigned int count, unsigned int size) -{ - return (size > 0) && (count >= ((unsigned int) -1) / size); -} - -static inline unsigned int -hb_ceil_to_4 (unsigned int v) -{ - return ((v - 1) | 3) + 1; -} - -template struct hb_is_signed; -/* https://github.com/harfbuzz/harfbuzz/issues/1535 */ -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = true }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; -template <> struct hb_is_signed { enum { value = false }; }; - -template static inline bool -hb_in_range (T u, T lo, T hi) -{ - /* The sizeof() is here to force template instantiation. - * I'm sure there are better ways to do this but can't think of - * one right now. Declaring a variable won't work as HB_UNUSED - * is unusable on some platforms and unused types are less likely - * to generate a warning than unused variables. */ - static_assert (!hb_is_signed::value, ""); - - /* The casts below are important as if T is smaller than int, - * the subtract results will become a signed int! */ - return (T)(u - lo) <= (T)(hi - lo); -} -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2); -} -template static inline bool -hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3) -{ - return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3); -} - - -/* - * Sort and search. - */ - -static inline void * -hb_bsearch (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item)) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = (min + max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return nullptr; -} - -static inline void * -hb_bsearch_r (const void *key, const void *base, - size_t nmemb, size_t size, - int (*compar)(const void *_key, const void *_item, void *_arg), - void *arg) -{ - int min = 0, max = (int) nmemb - 1; - while (min <= max) - { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const void *p = (const void *) (((const char *) base) + (mid * size)); - int c = compar (key, p, arg); - if (c < 0) - max = mid - 1; - else if (c > 0) - min = mid + 1; - else - return (void *) p; - } - return nullptr; -} - - -/* From https://github.com/noporpoise/sort_r - * With following modifications: - * - * 10 November 2018: - * https://github.com/noporpoise/sort_r/issues/7 - */ - -/* Isaac Turner 29 April 2014 Public Domain */ - -/* - -hb_sort_r function to be exported. - -Parameters: - base is the array to be sorted - nel is the number of elements in the array - width is the size in bytes of each element of the array - compar is the comparison function - arg is a pointer to be passed to the comparison function - -void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg); -*/ - - -/* swap a, b iff a>b */ -/* __restrict is same as restrict but better support on old machines */ -static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char tmp, *end = a+w; - if(compar(a, b, arg) > 0) { - for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; } - return 1; - } - return 0; -} - -/* Note: quicksort is not stable, equivalent values may be swapped */ -static inline void sort_r_simple(void *base, size_t nel, size_t w, - int (*compar)(const void *_a, const void *_b, - void *_arg), - void *arg) -{ - char *b = (char *)base, *end = b + nel*w; - if(nel < 7) { - /* Insertion sort for arbitrarily small inputs */ - char *pi, *pj; - for(pi = b+w; pi < end; pi += w) { - for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {} - } - } - else - { - /* nel > 6; Quicksort */ - - /* Use median of first, middle and last items as pivot */ - char *x, *y, *xend, ch; - char *pl, *pm, *pr; - char *last = b+w*(nel-1), *tmp; - char *l[3]; - l[0] = b; - l[1] = b+w*(nel/2); - l[2] = last; - - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - if(compar(l[1],l[2],arg) > 0) { - tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */ - if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; } - } - - /* swap l[id], l[2] to put pivot as last element */ - for(x = l[1], y = last, xend = x+w; x>1); - for(; pl < pm; pl += w) { - if(sort_r_cmpswap(pl, pr, w, compar, arg)) { - pr -= w; /* pivot now at pl */ - break; - } - } - pm = pl+((pr-pl)>>1); - for(; pm < pr; pr -= w) { - if(sort_r_cmpswap(pl, pr, w, compar, arg)) { - pl += w; /* pivot now at pr */ - break; - } - } - } - - sort_r_simple(b, (pl-b)/w, w, compar, arg); - sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg); - } -} - -static inline void hb_sort_r(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, void *_arg), - void *arg) -{ - sort_r_simple(base, nel, width, compar, arg); -} - - -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) -{ - for (unsigned int i = 1; i < len; i++) - { - unsigned int j = i; - while (j && compar (&array[j - 1], &array[i]) > 0) - j--; - if (i == j) - continue; - /* Move item i to occupy place for item j, shift what's in between. */ - { - T t = array[i]; - memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); - array[j] = t; - } - if (array2) - { - T2 t = array2[i]; - memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2)); - array2[j] = t; - } - } -} - -template static inline void -hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) -{ - hb_stable_sort (array, len, compar, (int *) nullptr); -} - -static inline hb_bool_t -hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) -{ - /* Pain because we don't know whether s is nul-terminated. */ - char buf[64]; - len = MIN (ARRAY_LENGTH (buf) - 1, len); - strncpy (buf, s, len); - buf[len] = '\0'; - - char *end; - errno = 0; - unsigned long v = strtoul (buf, &end, base); - if (errno) return false; - if (*end) return false; - *out = v; - return true; -} - - -struct HbOpOr -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = true; - template static void process (T &o, const T &a, const T &b) { o = a | b; } -}; -struct HbOpAnd -{ - static constexpr bool passthru_left = false; - static constexpr bool passthru_right = false; - template static void process (T &o, const T &a, const T &b) { o = a & b; } -}; -struct HbOpMinus -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = false; - template static void process (T &o, const T &a, const T &b) { o = a & ~b; } -}; -struct HbOpXor -{ - static constexpr bool passthru_left = true; - static constexpr bool passthru_right = true; - template static void process (T &o, const T &a, const T &b) { o = a ^ b; } -}; - - -/* Compiler-assisted vectorization. */ - -/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))), - * using vectorized operations if HB_VECTOR_SIZE is set to **bit** numbers (eg 128). - * Define that to 0 to disable. */ -template -struct hb_vector_size_t -{ - elt_t& operator [] (unsigned int i) { return u.v[i]; } - const elt_t& operator [] (unsigned int i) const { return u.v[i]; } - - void clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); } - - template - hb_vector_size_t process (const hb_vector_size_t &o) const - { - hb_vector_size_t r; -#if HB_VECTOR_SIZE - if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE) - for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++) - Op::process (r.u.vec[i], u.vec[i], o.u.vec[i]); - else -#endif - for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++) - Op::process (r.u.v[i], u.v[i], o.u.v[i]); - return r; - } - hb_vector_size_t operator | (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator & (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator ^ (const hb_vector_size_t &o) const - { return process (o); } - hb_vector_size_t operator ~ () const - { - hb_vector_size_t r; -#if HB_VECTOR_SIZE && 0 - if (HB_VECTOR_SIZE && 0 == (byte_size * 8) % HB_VECTOR_SIZE) - for (unsigned int i = 0; i < ARRAY_LENGTH (u.vec); i++) - r.u.vec[i] = ~u.vec[i]; - else -#endif - for (unsigned int i = 0; i < ARRAY_LENGTH (u.v); i++) - r.u.v[i] = ~u.v[i]; - return r; - } - - private: - static_assert (byte_size / sizeof (elt_t) * sizeof (elt_t) == byte_size, ""); - union { - elt_t v[byte_size / sizeof (elt_t)]; -#if HB_VECTOR_SIZE - hb_vector_size_impl_t vec[byte_size / sizeof (hb_vector_size_impl_t)]; -#endif - } u; -}; - - -#endif /* HB_DSALGS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-face.cc index e3dc46922f538..6d96dcc38485a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.cc @@ -200,10 +200,15 @@ hb_face_create (hb_blob_t *blob, if (unlikely (!blob)) blob = hb_blob_get_empty (); - hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)), index); + blob = hb_sanitize_context_t ().sanitize_blob (hb_blob_reference (blob)); + + hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (blob, index); if (unlikely (!closure)) + { + hb_blob_destroy (blob); return hb_face_get_empty (); + } face = hb_face_create_for_tables (_hb_face_for_data_reference_table, closure, @@ -226,7 +231,7 @@ hb_face_create (hb_blob_t *blob, hb_face_t * hb_face_get_empty () { - return const_cast (&Null(hb_face_t)); + return const_cast (&Null (hb_face_t)); } @@ -367,6 +372,9 @@ hb_blob_t * hb_face_reference_table (const hb_face_t *face, hb_tag_t tag) { + if (unlikely (tag == HB_TAG_NONE)) + return hb_blob_get_empty (); + return face->reference_table (tag); } @@ -531,6 +539,7 @@ hb_face_get_table_tags (const hb_face_t *face, */ +#ifndef HB_NO_FACE_COLLECT_UNICODES /** * hb_face_collect_unicodes: * @face: font face. @@ -542,9 +551,8 @@ void hb_face_collect_unicodes (hb_face_t *face, hb_set_t *out) { - face->table.cmap->collect_unicodes (out); + face->table.cmap->collect_unicodes (out, face->get_num_glyphs ()); } - /** * hb_face_collect_variation_selectors: * @face: font face. @@ -560,7 +568,6 @@ hb_face_collect_variation_selectors (hb_face_t *face, { face->table.cmap->collect_variation_selectors (out); } - /** * hb_face_collect_variation_unicodes: * @face: font face. @@ -577,7 +584,7 @@ hb_face_collect_variation_unicodes (hb_face_t *face, { face->table.cmap->collect_variation_unicodes (variation_selector, out); } - +#endif /* @@ -714,7 +721,10 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return false; hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data; + hb_face_builder_data_t::table_entry_t *entry = data->tables.push (); + if (data->tables.in_error()) + return false; entry->tag = tag; entry->blob = hb_blob_reference (blob); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-face.hh index b2730eb0a42ca..010eaba9e6f68 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-face.hh @@ -94,7 +94,7 @@ struct hb_face_t unsigned int get_num_glyphs () const { unsigned int ret = num_glyphs.get_relaxed (); - if (unlikely (ret == (unsigned int) -1)) + if (unlikely (ret == UINT_MAX)) return load_num_glyphs (); return ret; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc index df30871ecde24..d1d26b6a9f2f6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-fallback-shape.cc @@ -26,6 +26,7 @@ #include "hb-shaper-impl.hh" +#ifndef HB_NO_FALLBACK_SHAPE /* * shaper face data @@ -120,3 +121,5 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, return true; } + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-font.cc index 855c99833285f..42bf3e948d237 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.cc @@ -33,6 +33,9 @@ #include "hb-ot.h" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" + /** * SECTION:hb-font @@ -355,6 +358,7 @@ hb_font_get_glyph_h_kerning_default (hb_font_t *font, return font->parent_scale_x_distance (font->parent->get_glyph_h_kerning (left_glyph, right_glyph)); } +#ifndef HB_DISABLE_DEPRECATED static hb_position_t hb_font_get_glyph_v_kerning_nil (hb_font_t *font HB_UNUSED, void *font_data HB_UNUSED, @@ -373,6 +377,7 @@ hb_font_get_glyph_v_kerning_default (hb_font_t *font, { return font->parent_scale_y_distance (font->parent->get_glyph_v_kerning (top_glyph, bottom_glyph)); } +#endif static hb_bool_t hb_font_get_glyph_extents_nil (hb_font_t *font HB_UNUSED, @@ -672,7 +677,8 @@ hb_font_funcs_set_##name##_func (hb_font_funcs_t *ffuncs, \ void *user_data, \ hb_destroy_func_t destroy) \ { \ - if (hb_object_is_immutable (ffuncs)) { \ + if (hb_object_is_immutable (ffuncs)) \ + { \ if (destroy) \ destroy (user_data); \ return; \ @@ -789,6 +795,29 @@ hb_font_get_nominal_glyph (hb_font_t *font, return font->get_nominal_glyph (unicode, glyph); } +/** + * hb_font_get_nominal_glyphs: + * @font: a font. + * + * + * + * Return value: + * + * Since: 2.6.3 + **/ +unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride) +{ + return font->get_nominal_glyphs (count, + first_unicode, unicode_stride, + first_glyph, glyph_stride); +} + /** * hb_font_get_variation_glyph: * @font: a font. @@ -936,7 +965,6 @@ hb_font_get_glyph_v_origin (hb_font_t *font, * Return value: * * Since: 0.9.2 - * Deprecated: 2.0.0 **/ hb_position_t hb_font_get_glyph_h_kerning (hb_font_t *font, @@ -945,6 +973,7 @@ hb_font_get_glyph_h_kerning (hb_font_t *font, return font->get_glyph_h_kerning (left_glyph, right_glyph); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_font_get_glyph_v_kerning: * @font: a font. @@ -964,6 +993,7 @@ hb_font_get_glyph_v_kerning (hb_font_t *font, { return font->get_glyph_v_kerning (top_glyph, bottom_glyph); } +#endif /** * hb_font_get_glyph_extents: @@ -1185,7 +1215,6 @@ hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, * * * Since: 0.9.2 - * Deprecated: 2.0.0 **/ void hb_font_get_glyph_kerning_for_direction (hb_font_t *font, @@ -1298,6 +1327,8 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 1000, /* x_scale */ 1000, /* y_scale */ + 1<<16, /* x_mult */ + 1<<16, /* y_mult */ 0, /* x_ppem */ 0, /* y_ppem */ @@ -1305,6 +1336,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) = 0, /* num_coords */ nullptr, /* coords */ + nullptr, /* design_coords */ const_cast (&_hb_Null_hb_font_funcs_t), @@ -1328,6 +1360,7 @@ _hb_font_create (hb_face_t *face) font->klass = hb_font_funcs_get_empty (); font->data.init0 (font); font->x_scale = font->y_scale = hb_face_get_upem (face); + font->x_mult = font->y_mult = 1 << 16; return font; } @@ -1347,12 +1380,28 @@ hb_font_create (hb_face_t *face) { hb_font_t *font = _hb_font_create (face); +#ifndef HB_NO_OT_FONT /* Install our in-house, very lightweight, funcs. */ hb_ot_font_set_funcs (font); +#endif return font; } +static void +_hb_font_adopt_var_coords (hb_font_t *font, + int *coords, /* 2.14 normalized */ + float *design_coords, + unsigned int coords_length) +{ + free (font->coords); + free (font->design_coords); + + font->coords = coords; + font->design_coords = design_coords; + font->num_coords = coords_length; +} + /** * hb_font_create_sub_font: * @parent: parent font. @@ -1378,21 +1427,27 @@ hb_font_create_sub_font (hb_font_t *parent) font->x_scale = parent->x_scale; font->y_scale = parent->y_scale; + font->mults_changed (); font->x_ppem = parent->x_ppem; font->y_ppem = parent->y_ppem; font->ptem = parent->ptem; - font->num_coords = parent->num_coords; - if (!font->num_coords) - font->coords = nullptr; - else + unsigned int num_coords = parent->num_coords; + if (num_coords) { - unsigned int size = parent->num_coords * sizeof (parent->coords[0]); - font->coords = (int *) malloc (size); - if (unlikely (!font->coords)) - font->num_coords = 0; + int *coords = (int *) calloc (num_coords, sizeof (parent->coords[0])); + float *design_coords = (float *) calloc (num_coords, sizeof (parent->design_coords[0])); + if (likely (coords && design_coords)) + { + memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0])); + memcpy (design_coords, parent->design_coords, num_coords * sizeof (parent->design_coords[0])); + _hb_font_adopt_var_coords (font, coords, design_coords, num_coords); + } else - memcpy (font->coords, parent->coords, size); + { + free (coords); + free (design_coords); + } } return font; @@ -1410,7 +1465,7 @@ hb_font_create_sub_font (hb_font_t *parent) hb_font_t * hb_font_get_empty () { - return const_cast (&Null(hb_font_t)); + return const_cast (&Null (hb_font_t)); } /** @@ -1452,6 +1507,7 @@ hb_font_destroy (hb_font_t *font) hb_font_funcs_destroy (font->klass); free (font->coords); + free (font->design_coords); free (font); } @@ -1597,7 +1653,9 @@ hb_font_set_face (hb_font_t *font, hb_face_t *old = font->face; + hb_face_make_immutable (face); font->face = hb_face_reference (face); + font->mults_changed (); hb_face_destroy (old); } @@ -1707,6 +1765,7 @@ hb_font_set_scale (hb_font_t *font, font->x_scale = x_scale; font->y_scale = y_scale; + font->mults_changed (); } /** @@ -1805,21 +1864,11 @@ hb_font_get_ptem (hb_font_t *font) return font->ptem; } +#ifndef HB_NO_VAR /* * Variations */ -static void -_hb_font_adopt_var_coords_normalized (hb_font_t *font, - int *coords, /* 2.14 normalized */ - unsigned int coords_length) -{ - free (font->coords); - - font->coords = coords; - font->num_coords = coords_length; -} - /** * hb_font_set_variations: * @@ -1842,13 +1891,30 @@ hb_font_set_variations (hb_font_t *font, unsigned int coords_length = hb_ot_var_get_axis_count (font->face); int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } - hb_ot_var_normalize_variations (font->face, - variations, variations_length, - normalized, coords_length); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + const OT::fvar &fvar = *font->face->table.fvar; + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (font->face, variations[i].tag, &info) && + info.axis_index < coords_length) + { + float v = variations[i].value; + design_coords[info.axis_index] = v; + normalized[info.axis_index] = fvar.normalize_axis_value (info.axis_index, v); + } + } + font->face->table.avar->map_coords (normalized, coords_length); + + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); } /** @@ -1865,11 +1931,47 @@ hb_font_set_var_coords_design (hb_font_t *font, return; int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr; - if (unlikely (coords_length && !normalized)) + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + + if (unlikely (coords_length && !(normalized && design_coords))) + { + free (normalized); + free (design_coords); return; + } + + if (coords_length) + memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0])); hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized); - _hb_font_adopt_var_coords_normalized (font, normalized, coords_length); + _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length); +} + +/** + * hb_font_set_var_named_instance: + * @font: a font. + * @instance_index: named instance index. + * + * Sets design coords of a font from a named instance index. + * + * Since: 2.6.0 + */ +void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index) +{ + if (hb_object_is_immutable (font)) + return; + + unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr); + + float *coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr; + if (unlikely (coords_length && !coords)) + return; + + hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords); + hb_font_set_var_coords_design (font, coords, coords_length); + free (coords); } /** @@ -1886,13 +1988,30 @@ hb_font_set_var_coords_normalized (hb_font_t *font, return; int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; - if (unlikely (coords_length && !copy)) + int *unmapped = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr; + float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (design_coords[0])) : nullptr; + + if (unlikely (coords_length && !(copy && unmapped && design_coords))) + { + free (copy); + free (unmapped); + free (design_coords); return; + } if (coords_length) + { memcpy (copy, coords, coords_length * sizeof (coords[0])); + memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* Best effort design coords simulation */ + font->face->table.avar->unmap_coords (unmapped, coords_length); + for (unsigned int i = 0; i < coords_length; ++i) + design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]); + free (unmapped); - _hb_font_adopt_var_coords_normalized (font, copy, coords_length); + _hb_font_adopt_var_coords (font, copy, design_coords, coords_length); } /** @@ -1913,7 +2032,28 @@ hb_font_get_var_coords_normalized (hb_font_t *font, return font->coords; } +#ifdef HB_EXPERIMENTAL_API +/** + * hb_font_get_var_coords_design: + * + * Return value is valid as long as variation coordinates of the font + * are not modified. + * + * Since: EXPERIMENTAL + */ +const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + return font->design_coords; +} +#endif +#endif +#ifndef HB_DISABLE_DEPRECATED /* * Deprecated get_glyph_func(): */ @@ -2015,6 +2155,13 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_func_t func, void *user_data, hb_destroy_func_t destroy) { + if (hb_object_is_immutable (ffuncs)) + { + if (destroy) + destroy (user_data); + return; + } + hb_font_get_glyph_trampoline_t *trampoline; trampoline = trampoline_create (func, user_data, destroy); @@ -2036,3 +2183,4 @@ hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs, trampoline, trampoline_destroy); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.h b/src/java.desktop/share/native/libharfbuzz/hb-font.h index 85893f97c63f7..217bf33f4406a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.h @@ -33,6 +33,7 @@ #include "hb-common.h" #include "hb-face.h" +#include "hb-draw.h" HB_BEGIN_DECLS @@ -157,6 +158,11 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t; typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t; +typedef hb_position_t (*hb_font_get_glyph_kerning_func_t) (hb_font_t *font, void *font_data, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + void *user_data); +typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t; + typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t glyph, @@ -356,6 +362,22 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, hb_font_get_glyph_v_origin_func_t func, void *user_data, hb_destroy_func_t destroy); +/** + * hb_font_funcs_set_glyph_h_kerning_func: + * @ffuncs: font functions. + * @func: (closure user_data) (destroy destroy) (scope notified): + * @user_data: + * @destroy: + * + * + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_kerning_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_kerning_func_t func, + void *user_data, hb_destroy_func_t destroy); + /** * hb_font_funcs_set_glyph_extents_func: * @ffuncs: font functions. @@ -438,6 +460,14 @@ hb_font_get_variation_glyph (hb_font_t *font, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph); +HB_EXTERN unsigned int +hb_font_get_nominal_glyphs (hb_font_t *font, + unsigned int count, + const hb_codepoint_t *first_unicode, + unsigned int unicode_stride, + hb_codepoint_t *first_glyph, + unsigned int glyph_stride); + HB_EXTERN hb_position_t hb_font_get_glyph_h_advance (hb_font_t *font, hb_codepoint_t glyph); @@ -469,6 +499,10 @@ hb_font_get_glyph_v_origin (hb_font_t *font, hb_codepoint_t glyph, hb_position_t *x, hb_position_t *y); +HB_EXTERN hb_position_t +hb_font_get_glyph_h_kerning (hb_font_t *font, + hb_codepoint_t left_glyph, hb_codepoint_t right_glyph); + HB_EXTERN hb_bool_t hb_font_get_glyph_extents (hb_font_t *font, hb_codepoint_t glyph, @@ -531,6 +565,12 @@ hb_font_subtract_glyph_origin_for_direction (hb_font_t *font, hb_direction_t direction, hb_position_t *x, hb_position_t *y); +HB_EXTERN void +hb_font_get_glyph_kerning_for_direction (hb_font_t *font, + hb_codepoint_t first_glyph, hb_codepoint_t second_glyph, + hb_direction_t direction, + hb_position_t *x, hb_position_t *y); + HB_EXTERN hb_bool_t hb_font_get_glyph_extents_for_origin (hb_font_t *font, hb_codepoint_t glyph, @@ -665,6 +705,12 @@ hb_font_set_var_coords_design (hb_font_t *font, const float *coords, unsigned int coords_length); +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN const float * +hb_font_get_var_coords_design (hb_font_t *font, + unsigned int *length); +#endif + HB_EXTERN void hb_font_set_var_coords_normalized (hb_font_t *font, const int *coords, /* 2.14 normalized */ @@ -674,6 +720,16 @@ HB_EXTERN const int * hb_font_get_var_coords_normalized (hb_font_t *font, unsigned int *length); +HB_EXTERN void +hb_font_set_var_named_instance (hb_font_t *font, + unsigned instance_index); + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph, + const hb_draw_funcs_t *funcs, void *user_data); +#endif + HB_END_DECLS #endif /* HB_FONT_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-font.hh b/src/java.desktop/share/native/libharfbuzz/hb-font.hh index dd33d2f7d7cdc..2fa9cea73889e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-font.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-font.hh @@ -52,7 +52,7 @@ HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \ HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \ HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \ - HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning) \ + HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \ HB_FONT_FUNC_IMPLEMENT (glyph_extents) \ HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \ HB_FONT_FUNC_IMPLEMENT (glyph_name) \ @@ -107,8 +107,10 @@ struct hb_font_t hb_font_t *parent; hb_face_t *face; - int x_scale; - int y_scale; + int32_t x_scale; + int32_t y_scale; + int64_t x_mult; + int64_t y_mult; unsigned int x_ppem; unsigned int y_ppem; @@ -118,6 +120,7 @@ struct hb_font_t /* Font variation coordinates. */ unsigned int num_coords; int *coords; + float *design_coords; hb_font_funcs_t *klass; void *user_data; @@ -127,16 +130,16 @@ struct hb_font_t /* Convert from font-space to user-space */ - int dir_scale (hb_direction_t direction) - { return HB_DIRECTION_IS_VERTICAL(direction) ? y_scale : x_scale; } - hb_position_t em_scale_x (int16_t v) { return em_scale (v, x_scale); } - hb_position_t em_scale_y (int16_t v) { return em_scale (v, y_scale); } - hb_position_t em_scalef_x (float v) { return em_scalef (v, this->x_scale); } - hb_position_t em_scalef_y (float v) { return em_scalef (v, this->y_scale); } + int64_t dir_mult (hb_direction_t direction) + { return HB_DIRECTION_IS_VERTICAL(direction) ? y_mult : x_mult; } + hb_position_t em_scale_x (int16_t v) { return em_mult (v, x_mult); } + hb_position_t em_scale_y (int16_t v) { return em_mult (v, y_mult); } + hb_position_t em_scalef_x (float v) { return em_scalef (v, x_scale); } + hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); } float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); } float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); } hb_position_t em_scale_dir (int16_t v, hb_direction_t direction) - { return em_scale (v, dir_scale (direction)); } + { return em_mult (v, dir_mult (direction)); } /* Convert from parent-font user-space to our user-space */ hb_position_t parent_scale_x_distance (hb_position_t v) @@ -214,7 +217,7 @@ struct hb_font_t } hb_bool_t get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) + hb_codepoint_t *glyph) { *glyph = 0; return klass->get.f.nominal_glyph (this, user_data, @@ -284,7 +287,7 @@ struct hb_font_t } hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y) { *x = *y = 0; return klass->get.f.glyph_h_origin (this, user_data, @@ -304,21 +307,29 @@ struct hb_font_t hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph, hb_codepoint_t right_glyph) { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else return klass->get.f.glyph_h_kerning (this, user_data, left_glyph, right_glyph, klass->user_data.glyph_h_kerning); +#endif } hb_position_t get_glyph_v_kerning (hb_codepoint_t top_glyph, hb_codepoint_t bottom_glyph) { +#ifdef HB_DISABLE_DEPRECATED + return 0; +#else return klass->get.f.glyph_v_kerning (this, user_data, top_glyph, bottom_glyph, klass->user_data.glyph_v_kerning); +#endif } hb_bool_t get_glyph_extents (hb_codepoint_t glyph, - hb_glyph_extents_t *extents) + hb_glyph_extents_t *extents) { memset (extents, 0, sizeof (*extents)); return klass->get.f.glyph_extents (this, user_data, @@ -328,7 +339,7 @@ struct hb_font_t } hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index, - hb_position_t *x, hb_position_t *y) + hb_position_t *x, hb_position_t *y) { *x = *y = 0; return klass->get.f.glyph_contour_point (this, user_data, @@ -599,15 +610,19 @@ struct hb_font_t return false; } - hb_position_t em_scale (int16_t v, int scale) + void mults_changed () + { + signed upem = face->get_upem (); + x_mult = ((int64_t) x_scale << 16) / upem; + y_mult = ((int64_t) y_scale << 16) / upem; + } + + hb_position_t em_mult (int16_t v, int64_t mult) { - int upem = face->get_upem (); - int64_t scaled = v * (int64_t) scale; - scaled += scaled >= 0 ? upem/2 : -upem/2; /* Round. */ - return (hb_position_t) (scaled / upem); + return (hb_position_t) ((v * mult) >> 16); } hb_position_t em_scalef (float v, int scale) - { return (hb_position_t) round (v * scale / face->get_upem ()); } + { return (hb_position_t) roundf (v * scale / face->get_upem ()); } float em_fscale (int16_t v, int scale) { return (float) v * scale / face->get_upem (); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc index d73c4aaac737d..6b7b9e087711a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.cc @@ -29,6 +29,8 @@ #include "hb.hh" +#ifdef HAVE_FREETYPE + #include "hb-ft.h" #include "hb-font.hh" @@ -46,8 +48,13 @@ * @short_description: FreeType integration * @include: hb-ft.h * - * Functions for using HarfBuzz with the FreeType library to provide face and + * Functions for using HarfBuzz with the FreeType library. + * + * HarfBuzz supports using FreeType to provide face and * font data. + * + * Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either. **/ @@ -85,9 +92,7 @@ static hb_ft_font_t * _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) { hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t)); - - if (unlikely (!ft_font)) - return nullptr; + if (unlikely (!ft_font)) return nullptr; ft_font->lock.init (); ft_font->ft_face = ft_face; @@ -96,7 +101,7 @@ _hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref) ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; - ft_font->cached_x_scale.set (0); + ft_font->cached_x_scale.set_relaxed (0); ft_font->advance_cache.init (); return ft_font; @@ -125,10 +130,13 @@ _hb_ft_font_destroy (void *data) /** * hb_ft_font_set_load_flags: - * @font: - * @load_flags: + * @font: #hb_font_t to work upon + * @load_flags: The FreeType load flags to set * + * Sets the FT_Load_Glyph load flags for the specified #hb_font_t. * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx * * Since: 1.0.5 **/ @@ -138,7 +146,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) if (hb_object_is_immutable (font)) return; - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return; hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; @@ -148,17 +156,21 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags) /** * hb_ft_font_get_load_flags: - * @font: + * @font: #hb_font_t to work upon * + * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. * + * For more information, see + * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx + * + * Return value: FT_Load_Glyph flags found * - * Return value: * Since: 1.0.5 **/ int hb_ft_font_get_load_flags (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return 0; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; @@ -166,17 +178,69 @@ hb_ft_font_get_load_flags (hb_font_t *font) return ft_font->load_flags; } +/** + * hb_ft_get_face: + * @font: #hb_font_t to work upon + * + * Fetches the FT_Face associated with the specified #hb_font_t + * font object. + * + * Return value: the FT_Face found + * + * Since: 0.9.2 + **/ FT_Face hb_ft_font_get_face (hb_font_t *font) { - if (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy) + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return nullptr; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + return ft_font->ft_face; +} + +/** + * hb_ft_font_lock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +FT_Face +hb_ft_font_lock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) return nullptr; const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + ft_font->lock.lock (); + return ft_font->ft_face; } +/** + * hb_ft_font_unlock_face: + * @font: + * + * + * + * Return value: + * Since: 2.6.5 + **/ +void +hb_ft_font_unlock_face (hb_font_t *font) +{ + if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy)) + return; + + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font->user_data; + + ft_font->lock.unlock (); +} static hb_bool_t @@ -346,6 +410,25 @@ hb_ft_get_glyph_v_origin (hb_font_t *font, return true; } +#ifndef HB_NO_OT_SHAPE_FALLBACK +static hb_position_t +hb_ft_get_glyph_h_kerning (hb_font_t *font, + void *font_data, + hb_codepoint_t left_glyph, + hb_codepoint_t right_glyph, + void *user_data HB_UNUSED) +{ + const hb_ft_font_t *ft_font = (const hb_ft_font_t *) font_data; + FT_Vector kerningv; + + FT_Kerning_Mode mode = font->x_ppem ? FT_KERNING_DEFAULT : FT_KERNING_UNFITTED; + if (FT_Get_Kerning (ft_font->ft_face, left_glyph, right_glyph, mode, &kerningv)) + return 0; + + return kerningv.x; +} +#endif + static hb_bool_t hb_ft_get_glyph_extents (hb_font_t *font, void *font_data, @@ -439,7 +522,7 @@ hb_ft_get_glyph_from_name (hb_font_t *font HB_UNUSED, else { /* Make a nul-terminated version. */ char buf[128]; - len = MIN (len, (int) sizeof (buf) - 1); + len = hb_min (len, (int) sizeof (buf) - 1); strncpy (buf, name, len); buf[len] = '\0'; *glyph = FT_Get_Name_Index (ft_face, buf); @@ -497,6 +580,10 @@ static struct hb_ft_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tcharmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL; + hb_ft_font_t *ft_font = _hb_ft_font_create (ft_face, symbol, unref); + if (unlikely (!ft_font)) return; + hb_font_set_funcs (font, _hb_ft_get_font_funcs (), - _hb_ft_font_create (ft_face, symbol, unref), + ft_font, _hb_ft_font_destroy); } static hb_blob_t * -reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +_hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { FT_Face ft_face = (FT_Face) user_data; FT_Byte *buffer; @@ -570,12 +660,22 @@ reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) /** * hb_ft_face_create: - * @ft_face: (destroy destroy) (scope notified): - * @destroy: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: A callback to call when the face object is not needed anymore + * + * Creates an #hb_face_t face object from the specified FT_Face. * + * This variant of the function does not provide any life-cycle management. * + * Most client programs should use hb_ft_face_create_referenced() + * (or, perhaps, hb_ft_face_create_cached()) instead. + * + * If you know you have valid reasons not to use hb_ft_face_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_face_t face object has been destroyed. + * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_face_t * @@ -594,7 +694,7 @@ hb_ft_face_create (FT_Face ft_face, face = hb_face_create (blob, ft_face->face_index); hb_blob_destroy (blob); } else { - face = hb_face_create_for_tables (reference_table, ft_face, destroy); + face = hb_face_create_for_tables (_hb_ft_reference_table, ft_face, destroy); } hb_face_set_index (face, ft_face->face_index); @@ -605,11 +705,20 @@ hb_ft_face_create (FT_Face ft_face, /** * hb_ft_face_create_referenced: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. * + * This is the preferred variant of the hb_ft_face_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_face_t face object remains alive. Also calls FT_Done_Face() + * when the #hb_face_t face object is destroyed. * + * Use this version unless you know you have good reasons not to. + * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.38 **/ hb_face_t * @@ -627,11 +736,21 @@ hb_ft_face_finalize (FT_Face ft_face) /** * hb_ft_face_create_cached: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_face_t face object from the specified FT_Face. + * + * This variant of the function caches the newly created #hb_face_t + * face object, using the @generic pointer of @ft_face. Subsequent function + * calls that are passed the same @ft_face parameter will have the same + * #hb_face_t returned to them, and that #hb_face_t will be correctly + * reference counted. * + * However, client programs are still responsible for destroying + * @ft_face after the last #hb_face_t face object has been destroyed. * + * Return value: (transfer full): the new #hb_face_t face object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_face_t * @@ -649,15 +768,34 @@ hb_ft_face_create_cached (FT_Face ft_face) return hb_face_reference ((hb_face_t *) ft_face->generic.data); } - /** * hb_ft_font_create: - * @ft_face: (destroy destroy) (scope notified): - * @destroy: + * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon + * @destroy: (optional): A callback to call when the font object is not needed anymore + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create() on it. Otherwise, HarfBuzz will not pick up + * the face size. * + * This variant of the function does not provide any life-cycle management. * + * Most client programs should use hb_ft_font_create_referenced() + * instead. + * + * If you know you have valid reasons not to use hb_ft_font_create_referenced(), + * then it is the client program's responsibility to destroy @ft_face + * after the #hb_font_t font object has been destroyed. + * + * HarfBuzz will use the @destroy callback on the #hb_font_t font object + * if it is supplied when you use this function. However, even if @destroy + * is provided, it is the client program's responsibility to destroy @ft_face, + * and it is the client program's responsibility to ensure that @ft_face is + * destroyed only after the #hb_font_t font object has been destroyed. + * + * Return value: (transfer full): the new #hb_font_t font object * - * Return value: (transfer full): * Since: 0.9.2 **/ hb_font_t * @@ -675,6 +813,16 @@ hb_ft_font_create (FT_Face ft_face, return font; } +/** + * hb_ft_font_has_changed: + * @font: #hb_font_t to work upon + * + * Refreshes the state of @font when the underlying FT_Face has changed. + * This function should be called after changing the size or + * variation-axis settings on the FT_Face. + * + * Since: 1.0.5 + **/ void hb_ft_font_changed (hb_font_t *font) { @@ -682,6 +830,7 @@ hb_ft_font_changed (hb_font_t *font) return; hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data; + FT_Face ft_face = ft_font->ft_face; hb_font_set_scale (font, @@ -693,7 +842,7 @@ hb_ft_font_changed (hb_font_t *font) ft_face->size->metrics.y_ppem); #endif -#ifdef HAVE_FT_GET_VAR_BLEND_COORDINATES +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) FT_MM_Var *mm_var = nullptr; if (!FT_Get_MM_Var (ft_face, &mm_var)) { @@ -730,11 +879,23 @@ hb_ft_font_changed (hb_font_t *font) /** * hb_ft_font_create_referenced: - * @ft_face: + * @ft_face: FT_Face to work upon + * + * Creates an #hb_font_t font object from the specified FT_Face. + * + * Note: You must set the face size on @ft_face before calling + * hb_ft_font_create_references() on it. Otherwise, HarfBuzz will not pick up + * the face size. + * + * This is the preferred variant of the hb_ft_font_create* + * function family, because it calls FT_Reference_Face() on @ft_face, + * ensuring that @ft_face remains alive as long as the resulting + * #hb_font_t font object remains alive. * + * Use this version unless you know you have good reasons not to. * + * Return value: (transfer full): the new #hb_font_t font object * - * Return value: (transfer full): * Since: 0.9.38 **/ hb_font_t * @@ -748,7 +909,7 @@ hb_ft_font_create_referenced (FT_Face ft_face) static void free_static_ft_library (); #endif -static struct hb_ft_library_lazy_loader_t : hb_lazy_loader_t, hb_ft_library_lazy_loader_t> { static FT_Library create () @@ -793,6 +954,28 @@ _release_blob (FT_Face ft_face) hb_blob_destroy ((hb_blob_t *) ft_face->generic.data); } +/** + * hb_ft_font_set_funcs: + * @font: #hb_font_t to work upon + * + * Configures the font-functions structure of the specified + * #hb_font_t font object to use FreeType font functions. + * + * In particular, you can use this function to configure an + * existing #hb_face_t face object for use with FreeType font + * functions even if that #hb_face_t face object was initially + * created with hb_face_create(), and therefore was not + * initially configured to use FreeType font functions. + * + * An #hb_face_t face object created with hb_ft_face_create() + * is preconfigured for FreeType font functions and does not + * require this function to be used. + * + * Note: Internally, this function creates an FT_Face. +* + * + * Since: 1.0.5 + **/ void hb_ft_font_set_funcs (hb_font_t *font) { @@ -815,8 +998,8 @@ hb_ft_font_set_funcs (hb_font_t *font) return; } - if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE)) - FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL); + if (FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL)) + FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE); FT_Set_Char_Size (ft_face, abs (font->x_scale), abs (font->y_scale), @@ -832,7 +1015,7 @@ hb_ft_font_set_funcs (hb_font_t *font) FT_Set_Transform (ft_face, &matrix, nullptr); } -#ifdef HAVE_FT_SET_VAR_BLEND_COORDINATES +#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR) unsigned int num_coords; const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); if (num_coords) @@ -841,7 +1024,7 @@ hb_ft_font_set_funcs (hb_font_t *font) if (ft_coords) { for (unsigned int i = 0; i < num_coords; i++) - ft_coords[i] = coords[i] << 2; + ft_coords[i] = coords[i] * 4; FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords); free (ft_coords); } @@ -854,3 +1037,6 @@ hb_ft_font_set_funcs (hb_font_t *font) _hb_ft_font_set_funcs (font, ft_face, true); hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ft.h b/src/java.desktop/share/native/libharfbuzz/hb-ft.h index dda30d3815748..4962eaf38d4c5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ft.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ft.h @@ -110,6 +110,12 @@ hb_ft_font_create_referenced (FT_Face ft_face); HB_EXTERN FT_Face hb_ft_font_get_face (hb_font_t *font); +HB_EXTERN FT_Face +hb_ft_font_lock_face (hb_font_t *font); + +HB_EXTERN void +hb_ft_font_unlock_face (hb_font_t *font); + HB_EXTERN void hb_ft_font_set_load_flags (hb_font_t *font, int load_flags); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh index c4ab26dc06302..99a441af44f2e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-iter.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-iter.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Google, Inc. + * Copyright © 2019 Facebook, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -22,13 +23,15 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod */ #ifndef HB_ITER_HH #define HB_ITER_HH #include "hb.hh" -#include "hb-null.hh" +#include "hb-algs.hh" +#include "hb-meta.hh" /* Unified iterator object. @@ -39,16 +42,32 @@ * copied by value. If the collection / object being iterated on * is writable, then the iterator returns lvalues, otherwise it * returns rvalues. + * + * TODO Document more. + * + * If iterator implementation implements operator!=, then can be + * used in range-based for loop. That comes free if the iterator + * is random-access. Otherwise, the range-based for loop incurs + * one traversal to find end(), which can be avoided if written + * as a while-style for loop, or if iterator implements a faster + * __end__() method. + * TODO When opting in for C++17, address this by changing return + * type of .end()? + */ + +/* + * Base classes for iterators. */ /* Base class for all iterators. */ -template +template struct hb_iter_t { - typedef Iter iter_t; - typedef iter_t const_iter_t; typedef Item item_t; - static constexpr unsigned item_size = hb_static_size (Item); + constexpr unsigned get_item_size () const { return hb_static_size (Item); } + static constexpr bool is_iterator = true; + static constexpr bool is_random_access_iterator = false; + static constexpr bool is_sorted_iterator = false; private: /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ @@ -56,53 +75,119 @@ struct hb_iter_t iter_t* thiz () { return static_cast< iter_t *> (this); } public: - /* Operators. */ - operator iter_t () { return iter(); } - explicit_operator bool () const { return more (); } - item_t& operator * () const { return item (); } - item_t& operator [] (signed i) const { return item_at ((unsigned) i); } - iter_t& operator += (unsigned count) { forward (count); return *thiz(); } - iter_t& operator ++ () { next (); return *thiz(); } - iter_t& operator -= (unsigned count) { rewind (count); return *thiz(); } - iter_t& operator -- () { prev (); return *thiz(); } - iter_t operator + (unsigned count) { iter_t c (*thiz()); c += count; return c; } - iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } - iter_t operator - (unsigned count) { iter_t c (*thiz()); c -= count; return c; } - iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + /* TODO: + * Port operators below to use hb_enable_if to sniff which method implements + * an operator and use it, and remove hb_iter_fallback_mixin_t completely. */ - /* Methods. */ + /* Operators. */ iter_t iter () const { return *thiz(); } - const_iter_t const_iter () const { return iter (); } - item_t& item () const { return thiz()->__item__ (); } - item_t& item_at (unsigned i) const { return thiz()->__item_at__ (i); } - bool more () const { return thiz()->__more__ (); } + iter_t operator + () const { return *thiz(); } + iter_t begin () const { return *thiz(); } + iter_t end () const { return thiz()->__end__ (); } + explicit operator bool () const { return thiz()->__more__ (); } unsigned len () const { return thiz()->__len__ (); } - void next () { thiz()->__next__ (); } - void forward (unsigned n) { thiz()->__forward__ (n); } - void prev () { thiz()->__prev__ (); } - void rewind (unsigned n) { thiz()->__rewind__ (n); } - bool random_access () const { return thiz()->__random_access__ (); } + /* The following can only be enabled if item_t is reference type. Otherwise + * it will be returning pointer to temporary rvalue. + * TODO Use a wrapper return type to fix for non-reference type. */ + template + hb_remove_reference* operator -> () const { return hb_addressof (**thiz()); } + item_t operator * () const { return thiz()->__item__ (); } + item_t operator * () { return thiz()->__item__ (); } + item_t operator [] (unsigned i) const { return thiz()->__item_at__ (i); } + item_t operator [] (unsigned i) { return thiz()->__item_at__ (i); } + iter_t& operator += (unsigned count) & { thiz()->__forward__ (count); return *thiz(); } + iter_t operator += (unsigned count) && { thiz()->__forward__ (count); return *thiz(); } + iter_t& operator ++ () & { thiz()->__next__ (); return *thiz(); } + iter_t operator ++ () && { thiz()->__next__ (); return *thiz(); } + iter_t& operator -= (unsigned count) & { thiz()->__rewind__ (count); return *thiz(); } + iter_t operator -= (unsigned count) && { thiz()->__rewind__ (count); return *thiz(); } + iter_t& operator -- () & { thiz()->__prev__ (); return *thiz(); } + iter_t operator -- () && { thiz()->__prev__ (); return *thiz(); } + iter_t operator + (unsigned count) const { auto c = thiz()->iter (); c += count; return c; } + friend iter_t operator + (unsigned count, const iter_t &it) { return it + count; } + iter_t operator ++ (int) { iter_t c (*thiz()); ++*thiz(); return c; } + iter_t operator - (unsigned count) const { auto c = thiz()->iter (); c -= count; return c; } + iter_t operator -- (int) { iter_t c (*thiz()); --*thiz(); return c; } + template + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template + iter_t operator << (const T v) && { **thiz() = v; ++*thiz(); return *thiz(); } protected: - hb_iter_t () {} - hb_iter_t (const hb_iter_t &o HB_UNUSED) {} - void operator = (const hb_iter_t &o HB_UNUSED) {} + hb_iter_t () = default; + hb_iter_t (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t (hb_iter_t &&o HB_UNUSED) = default; + hb_iter_t& operator = (const hb_iter_t &o HB_UNUSED) = default; + hb_iter_t& operator = (hb_iter_t &&o HB_UNUSED) = default; }; -/* Base class for sorted iterators. Does not enforce anything. - * Just for class taxonomy and requirements. */ -template -struct hb_sorted_iter_t : hb_iter_t +#define HB_ITER_USING(Name) \ + using item_t = typename Name::item_t; \ + using Name::begin; \ + using Name::end; \ + using Name::get_item_size; \ + using Name::is_iterator; \ + using Name::iter; \ + using Name::operator bool; \ + using Name::len; \ + using Name::operator ->; \ + using Name::operator *; \ + using Name::operator []; \ + using Name::operator +=; \ + using Name::operator ++; \ + using Name::operator -=; \ + using Name::operator --; \ + using Name::operator +; \ + using Name::operator -; \ + using Name::operator >>; \ + using Name::operator <<; \ + static_assert (true, "") + +/* Returns iterator / item type of a type. */ +template +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template struct hb_array_t; +template struct hb_sorted_array_t; + +struct { - protected: - hb_sorted_iter_t () {} - hb_sorted_iter_t (const hb_sorted_iter_t &o) : hb_iter_t (o) {} - void operator = (const hb_sorted_iter_t &o HB_UNUSED) {} -}; + template hb_iter_type + operator () (T&& c) const + { return hb_deref (hb_forward (c)).iter (); } + + /* Specialization for C arrays. */ + + template inline hb_array_t + operator () (Type *array, unsigned int length) const + { return hb_array_t (array, length); } + + template hb_array_t + operator () (Type (&array)[length]) const + { return hb_array_t (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template unsigned + operator () (T&& c) const + { return c.len (); } + +} +HB_FUNCOBJ (hb_len); /* Mixin to fill in what the subclass doesn't provide. */ -template -struct hb_iter_mixin_t +template +struct hb_iter_fallback_mixin_t { private: /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ @@ -111,42 +196,743 @@ struct hb_iter_mixin_t public: /* Access: Implement __item__(), or __item_at__() if random-access. */ - item_t& __item__ () const { return thiz()->item_at (0); } - item_t& __item_at__ (unsigned i) const { return *(thiz() + i); } + item_t __item__ () const { return (*thiz())[0]; } + item_t __item_at__ (unsigned i) const { return *(*thiz() + i); } /* Termination: Implement __more__(), or __len__() if random-access. */ - bool __more__ () const { return thiz()->__len__ (); } + bool __more__ () const { return bool (thiz()->len ()); } unsigned __len__ () const - { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; }; return l; } + { iter_t c (*thiz()); unsigned l = 0; while (c) { c++; l++; } return l; } /* Advancing: Implement __next__(), or __forward__() if random-access. */ - void __next__ () { thiz()->forward (1); } - void __forward__ (unsigned n) { while (n--) thiz()->next (); } + void __next__ () { *thiz() += 1; } + void __forward__ (unsigned n) { while (*thiz() && n--) ++*thiz(); } /* Rewinding: Implement __prev__() or __rewind__() if bidirectional. */ - void __prev__ () { thiz()->rewind (1); } - void __rewind__ (unsigned n) { while (n--) thiz()->prev (); } + void __prev__ () { *thiz() -= 1; } + void __rewind__ (unsigned n) { while (*thiz() && n--) --*thiz(); } + + /* Range-based for: Implement __end__() if can be done faster, + * and operator!=. */ + iter_t __end__ () const + { + if (thiz()->is_random_access_iterator) + return *thiz() + thiz()->len (); + /* Above expression loops twice. Following loops once. */ + auto it = *thiz(); + while (it) ++it; + return it; + } + + protected: + hb_iter_fallback_mixin_t () = default; + hb_iter_fallback_mixin_t (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (const hb_iter_fallback_mixin_t &o HB_UNUSED) = default; + hb_iter_fallback_mixin_t& operator = (hb_iter_fallback_mixin_t &&o HB_UNUSED) = default; +}; + +template +struct hb_iter_with_fallback_t : + hb_iter_t, + hb_iter_fallback_mixin_t +{ + protected: + hb_iter_with_fallback_t () = default; + hb_iter_with_fallback_t (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t (hb_iter_with_fallback_t &&o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (const hb_iter_with_fallback_t &o HB_UNUSED) = default; + hb_iter_with_fallback_t& operator = (hb_iter_with_fallback_t &&o HB_UNUSED) = default; +}; + +/* + * Meta-programming predicates. + */ + +/* hb_is_iterator() / hb_is_iterator_of() */ + +template +struct hb_is_iterator_of +{ + template + static hb_true_type impl (hb_priority<2>, hb_iter_t> *); + static hb_false_type impl (hb_priority<0>, const void *); + + public: + static constexpr bool value = decltype (impl (hb_prioritize, hb_declval (Iter*)))::value; +}; +#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of::value +#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) + +/* hb_is_iterable() */ + +template +struct hb_is_iterable +{ + private: + + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template +struct hb_is_source_of +{ + private: + template >))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) >> hb_declval (Item &), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_source_of(Iter, Item) hb_is_source_of::value + +template +struct hb_is_sink_of +{ + private: + template ))> + static hb_true_type impl (hb_priority<2>); + template + static auto impl (hb_priority<1>) -> decltype (hb_declval (Iter2) << hb_declval (Item), hb_true_type ()); + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl (hb_prioritize))::value; +}; +#define hb_is_sink_of(Iter, Item) hb_is_sink_of::value + +/* This is commonly used, so define: */ +#define hb_is_sorted_source_of(Iter, Item) \ + (hb_is_source_of(Iter, Item) && Iter::is_sorted_iterator) + + +/* Range-based 'for' for iterables. */ + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +/* begin()/end() are NOT looked up non-ADL. So each namespace must declare them. + * Do it for namespace OT. */ +namespace OT { + +template +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (hb_forward (rhs) (hb_forward (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template +struct hb_map_iter_t : + hb_iter_t, + decltype (hb_get (hb_declval (Proj), *hb_declval (Iter)))> +{ + hb_map_iter_t (const Iter& it, Proj f_) : it (it), f (f_) {} + + typedef decltype (hb_get (hb_declval (Proj), *hb_declval (Iter))) __item_t__; + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = + Sorted == hb_function_sortedness_t::SORTED ? true : + Sorted == hb_function_sortedness_t::RETAINS_SORTING ? Iter::is_sorted_iterator : + false; + __item_t__ __item__ () const { return hb_get (f.get (), *it); } + __item_t__ __item_at__ (unsigned i) const { return hb_get (f.get (), it[i]); } + bool __more__ () const { return bool (it); } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + hb_map_iter_t __end__ () const { return hb_map_iter_t (it.end (), f); } + bool operator != (const hb_map_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper f; +}; + +template +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template + hb_map_iter_t + operator () (Iter it) + { return hb_map_iter_t (it, f); } + + private: + Proj f; +}; +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template + hb_map_iter_factory_t + operator () (Proj&& f) const + { return hb_map_iter_factory_t (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template +struct hb_filter_iter_t : + hb_iter_with_fallback_t, + typename Iter::item_t> +{ + hb_filter_iter_t (const Iter& it_, Pred p_, Proj f_) : it (it_), p (p_), f (f_) + { while (it && !hb_has (p.get (), hb_get (f.get (), *it))) ++it; } + + typedef typename Iter::item_t __item_t__; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + __item_t__ __item__ () const { return *it; } + bool __more__ () const { return bool (it); } + void __next__ () { do ++it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + void __prev__ () { do --it; while (it && !hb_has (p.get (), hb_get (f.get (), *it))); } + hb_filter_iter_t __end__ () const { return hb_filter_iter_t (it.end (), p, f); } + bool operator != (const hb_filter_iter_t& o) const + { return it != o.it; } + + private: + Iter it; + hb_reference_wrapper p; + hb_reference_wrapper f; +}; +template +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template + hb_filter_iter_t + operator () (Iter it) + { return hb_filter_iter_t (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template + hb_filter_iter_factory_t + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template > + AccuT + operator () (Iter it) + { + AccuT value = init_value; + for (; it; ++it) + value = r (value, *it); + return value; + } + + private: + Redu r; + InitT init_value; +}; +struct +{ + template + hb_reduce_t + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ - /* Random access: Return true if item_at(), len(), forward() are fast. */ - bool __random_access__ () const { return false; } +template +struct hb_zip_iter_t : + hb_iter_t, + hb_pair_t> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t __item_t__; + static constexpr bool is_random_access_iterator = + A::is_random_access_iterator && + B::is_random_access_iterator; + /* Note. The following categorization is only valid if A is strictly sorted, + * ie. does NOT have duplicates. Previously I tried to categorize sortedness + * more granularly, see commits: + * + * 513762849a683914fc266a17ddf38f133cccf072 + * 4d3cf2adb669c345cc43832d11689271995e160a + * + * However, that was not enough, since hb_sorted_array_t, hb_sorted_vector_t, + * SortedArrayOf, etc all needed to be updated to add more variants. At that + * point I saw it not worth the effort, and instead we now deem all sorted + * collections as essentially strictly-sorted for the purposes of zip. + * + * The above assumption is not as bad as it sounds. Our "sorted" comes with + * no guarantees. It's just a contract, put in place to help you remember, + * and think about, whether an iterator you receive is expected to be + * sorted or not. As such, it's not perfect by definition, and should not + * be treated so. The inaccuracy here just errs in the direction of being + * more permissive, so your code compiles instead of erring on the side of + * marking your zipped iterator unsorted in which case your code won't + * compile. + * + * This semantical limitation does NOT affect logic in any other place I + * know of as of this writing. + */ + static constexpr bool is_sorted_iterator = A::is_sorted_iterator; + + __item_t__ __item__ () const { return __item_t__ (*a, *b); } + __item_t__ __item_at__ (unsigned i) const { return __item_t__ (a[i], b[i]); } + bool __more__ () const { return bool (a) && bool (b); } + unsigned __len__ () const { return hb_min (a.len (), b.len ()); } + void __next__ () { ++a; ++b; } + void __forward__ (unsigned n) { a += n; b += n; } + void __prev__ () { --a; --b; } + void __rewind__ (unsigned n) { a -= n; b -= n; } + hb_zip_iter_t __end__ () const { return hb_zip_iter_t (a.end (), b.end ()); } + /* Note, we should stop if ANY of the iters reaches end. As such two compare + * unequal if both items are unequal, NOT if either is unequal. */ + bool operator != (const hb_zip_iter_t& o) const + { return a != o.a && b != o.b; } + + private: + A a; + B b; +}; +struct +{ HB_PARTIALIZE(2); + template + hb_zip_iter_t, hb_iter_type> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t, hb_iter_type> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_apply() */ + +template +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; }; +struct +{ + template hb_apply_t + operator () (Appl&& a) const + { return hb_apply_t (a); } + template hb_apply_t + operator () (Appl *a) const + { return hb_apply_t (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template +struct hb_range_iter_t : + hb_iter_t, T> +{ + hb_range_iter_t (T start, T end_, S step) : v (start), end_ (end_for (start, end_, step)), step (step) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + __item_t__ __item_at__ (unsigned j) const { return v + j * step; } + bool __more__ () const { return v != end_; } + unsigned __len__ () const { return !step ? UINT_MAX : (end_ - v) / step; } + void __next__ () { v += step; } + void __forward__ (unsigned n) { v += n * step; } + void __prev__ () { v -= step; } + void __rewind__ (unsigned n) { v -= n * step; } + hb_range_iter_t __end__ () const { return hb_range_iter_t (end_, end_, step); } + bool operator != (const hb_range_iter_t& o) const + { return v != o.v; } + + private: + static inline T end_for (T start, T end_, S step) + { + if (!step) + return end_; + auto res = (end_ - start) % step; + if (!res) + return end_; + end_ += step - res; + return end_; + } + + private: + T v; + T end_; + S step; +}; +struct +{ + template hb_range_iter_t + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t (0, end, 1u); } + + template hb_range_iter_t + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template +struct hb_iota_iter_t : + hb_iter_with_fallback_t, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template + auto + inc (hb_type_identity s, hb_priority<1>) + -> hb_void_t (s), hb_declval ()))> + { v = hb_invoke (hb_forward (s), v); } + + void + inc (S s, hb_priority<0>) + { v += s; } + + public: + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return hb_ridentity (v); } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () { inc (step, hb_prioritize); } + void __prev__ () { v -= step; } + hb_iota_iter_t __end__ () const { return *this; } + bool operator != (const hb_iota_iter_t& o) const { return true; } + + private: + T v; + S step; +}; +struct +{ + template hb_iota_iter_t + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t (start, step); } +} +HB_FUNCOBJ (hb_iota); -/* Functions operating on iterators or iteratables. */ +template +struct hb_repeat_iter_t : + hb_iter_t, T> +{ + hb_repeat_iter_t (T value) : v (value) {} + + typedef T __item_t__; + static constexpr bool is_random_access_iterator = true; + static constexpr bool is_sorted_iterator = true; + __item_t__ __item__ () const { return v; } + __item_t__ __item_at__ (unsigned j) const { return v; } + bool __more__ () const { return true; } + unsigned __len__ () const { return UINT_MAX; } + void __next__ () {} + void __forward__ (unsigned) {} + void __prev__ () {} + void __rewind__ (unsigned) {} + hb_repeat_iter_t __end__ () const { return *this; } + bool operator != (const hb_repeat_iter_t& o) const { return true; } + + private: + T v; +}; +struct +{ + template hb_repeat_iter_t + operator () (T value) const + { return hb_repeat_iter_t (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template + auto operator () (Iterable&& it, Index start = 0u) const HB_AUTO_RETURN + ( hb_zip (hb_iota (start), it) ) +} +HB_FUNCOBJ (hb_enumerate); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iterable&& it, unsigned count) const HB_AUTO_RETURN + ( hb_zip (hb_range (count), it) | hb_map (hb_second) ) + + /* Specialization arrays. */ + + template inline hb_array_t + operator () (hb_array_t array, unsigned count) const + { return array.sub_array (0, count); } + + template inline hb_sorted_array_t + operator () (hb_sorted_array_t array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template + auto operator () (Iter it, unsigned count) const HB_AUTO_RETURN + ( + + hb_iota (it, hb_add (count)) + | hb_map (hb_take (count)) + | hb_take ((hb_len (it) + count - 1) / count) + ) +} +HB_FUNCOBJ (hb_chop); + +/* hb_sink() */ + +template +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template hb_sink_t + operator () (Sink&& s) const + { return hb_sink_t (s); } + + template hb_sink_t + operator () (Sink *s) const + { return hb_sink_t (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template hb_unzip_t + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t (s1, s2); } + + template hb_unzip_t + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (!hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template + bool operator () (Iterable&& c, + Pred&& p = hb_identity, + Proj&& f = hb_identity) const + { + for (auto it = hb_iter (c); it; ++it) + if (hb_match (hb_forward (p), hb_get (hb_forward (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ -template inline void -hb_fill (const C& c, const V &v) +template +inline void +hb_fill (C& c, const V &v) { - for (typename C::iter_t i (c); i; i++) - hb_assign (*i, v); + for (auto i = hb_iter (c); i; i++) + *i = v; } -template inline bool -hb_copy (hb_iter_t &id, hb_iter_t &is) +template +inline void +hb_copy (S&& is, D&& id) { - for (; id && is; ++id, ++is) - *id = *is; - return !is; + hb_iter (is) | hb_sink (id); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh index 43d70d7f22166..42e5493641054 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-kern.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-kern.hh @@ -52,8 +52,7 @@ struct hb_kern_machine_t OT::hb_ot_apply_context_t c (1, font, buffer); c.set_lookup_mask (kern_mask); c.set_lookup_props (OT::LookupFlag::IgnoreMarks); - OT::hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c.iter_input; - skippy_iter.init (&c); + auto &skippy_iter = c.iter_input; bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); unsigned int count = buffer->len; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh index 2ae288494f445..0c820e7429fcc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-machinery.hh @@ -32,30 +32,15 @@ #include "hb.hh" #include "hb-blob.hh" -#include "hb-array.hh" -#include "hb-vector.hh" +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" +#include "hb-serialize.hh" /* * Casts */ -/* Cast to struct T, reference to reference */ -template -static inline const Type& CastR(const TObject &X) -{ return reinterpret_cast (X); } -template -static inline Type& CastR(TObject &X) -{ return reinterpret_cast (X); } - -/* Cast to struct T, pointer to pointer */ -template -static inline const Type* CastP(const TObject *X) -{ return reinterpret_cast (X); } -template -static inline Type* CastP(TObject *X) -{ return reinterpret_cast (X); } - /* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory * location pointed to by P plus Ofs bytes. */ template @@ -69,7 +54,7 @@ static inline const Type& StructAtOffsetUnaligned(const void *P, unsigned int of { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" - return * reinterpret_cast ((char *) P + offset); + return * reinterpret_cast ((const char *) P + offset); #pragma GCC diagnostic pop } template @@ -134,7 +119,7 @@ static inline Type& StructAfter(TObject &X) #define DEFINE_SIZE_ARRAY(size, array) \ DEFINE_COMPILES_ASSERTION ((void) (array)[0].static_size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + VAR * sizeof ((array)[0])) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + (HB_VAR_ARRAY+0) * sizeof ((array)[0])) \ static constexpr unsigned null_size = (size); \ static constexpr unsigned min_size = (size) @@ -143,615 +128,6 @@ static inline Type& StructAfter(TObject &X) DEFINE_SIZE_ARRAY(size, array) -/* - * Dispatch - */ - -template -struct hb_dispatch_context_t -{ - static constexpr unsigned max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template - bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } - static return_t no_dispatch_return_value () { return Context::default_return_value (); } - static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } -}; - - -/* - * Sanitize - * - * - * === Introduction === - * - * The sanitize machinery is at the core of our zero-cost font loading. We - * mmap() font file into memory and create a blob out of it. Font subtables - * are returned as a readonly sub-blob of the main font blob. These table - * blobs are then sanitized before use, to ensure invalid memory access does - * not happen. The toplevel sanitize API use is like, eg. to load the 'head' - * table: - * - * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (face); - * - * The blob then can be converted to a head table struct with: - * - * const head *head_table = head_blob->as (); - * - * What the reference_table does is, to call hb_face_reference_table() to load - * the table blob, sanitize it and return either the sanitized blob, or empty - * blob if sanitization failed. The blob->as() function returns the null - * object of its template type argument if the blob is empty. Otherwise, it - * just casts the blob contents to the desired type. - * - * Sanitizing a blob of data with a type T works as follows (with minor - * simplification): - * - * - Cast blob content to T*, call sanitize() method of it, - * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. - * - Return empty blob otherwise. - * - * - * === The sanitize() contract === - * - * The sanitize() method of each object type shall return true if it's safe to - * call other methods of the object, and false otherwise. - * - * Note that what sanitize() checks for might align with what the specification - * describes as valid table data, but does not have to be. In particular, we - * do NOT want to be pedantic and concern ourselves with validity checks that - * are irrelevant to our use of the table. On the contrary, we want to be - * lenient with error handling and accept invalid data to the extent that it - * does not impose extra burden on us. - * - * Based on the sanitize contract, one can see that what we check for depends - * on how we use the data in other table methods. Ie. if other table methods - * assume that offsets do NOT point out of the table data block, then that's - * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On - * the other hand, if other methods do such checks themselves, then sanitize() - * does not have to bother with them (glyf/local work this way). The choice - * depends on the table structure and sanitize() performance. For example, to - * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard - * to avoid such costs during font loading. By postponing such checks to the - * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime - * cost to O(used-glyphs). As such, this is preferred. - * - * The same argument can be made re GSUB/GPOS/GDEF, but there, the table - * structure is so complicated that by checking all offsets at sanitize() time, - * we make the code much simpler in other methods, as offsets and referenced - * objects do not need to be validated at each use site. - */ - -/* This limits sanitizing time on really broken fonts. */ -#ifndef HB_SANITIZE_MAX_EDITS -#define HB_SANITIZE_MAX_EDITS 32 -#endif -#ifndef HB_SANITIZE_MAX_OPS_FACTOR -#define HB_SANITIZE_MAX_OPS_FACTOR 8 -#endif -#ifndef HB_SANITIZE_MAX_OPS_MIN -#define HB_SANITIZE_MAX_OPS_MIN 16384 -#endif -#ifndef HB_SANITIZE_MAX_OPS_MAX -#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t -{ - hb_sanitize_context_t () : - debug_depth (0), - start (nullptr), end (nullptr), - max_ops (0), - writable (false), edit_count (0), - blob (nullptr), - num_glyphs (65536), - num_glyphs_set (false) {} - - const char *get_name () { return "SANITIZE"; } - template - bool may_dispatch (const T *obj HB_UNUSED, const F *format) - { return format->sanitize (this); } - template - return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value () { return true; } - static return_t no_dispatch_return_value () { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - void set_num_glyphs (unsigned int num_glyphs_) - { - num_glyphs = num_glyphs_; - num_glyphs_set = true; - } - unsigned int get_num_glyphs () { return num_glyphs; } - - void set_max_ops (int max_ops_) { max_ops = max_ops_; } - - template - void set_object (const T *obj) - { - reset_object (); - - if (!obj) return; - - const char *obj_start = (const char *) obj; - if (unlikely (obj_start < this->start || this->end <= obj_start)) - this->start = this->end = nullptr; - else - { - this->start = obj_start; - this->end = obj_start + MIN (this->end - obj_start, obj->get_size ()); - } - } - - void reset_object () - { - this->start = this->blob->data; - this->end = this->start + this->blob->length; - assert (this->start <= this->end); /* Must not overflow. */ - } - - void start_processing () - { - reset_object (); - this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, - (unsigned) HB_SANITIZE_MAX_OPS_MIN); - this->edit_count = 0; - this->debug_depth = 0; - - DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - } - - void end_processing () - { - DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); - - hb_blob_destroy (this->blob); - this->blob = nullptr; - this->start = this->end = nullptr; - } - - bool check_range (const void *base, - unsigned int len) const - { - const char *p = (const char *) base; - bool ok = this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len && - this->max_ops-- > 0; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - template - bool check_range (const T *base, - unsigned int a, - unsigned int b) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b); - } - - template - bool check_range (const T *base, - unsigned int a, - unsigned int b, - unsigned int c) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b, c); - } - - template - bool check_array (const T *base, unsigned int len) const - { - return this->check_range (base, len, hb_static_size (T)); - } - - template - bool check_array (const T *base, - unsigned int a, - unsigned int b) const - { - return this->check_range (base, a, b, hb_static_size (T)); - } - - template - bool check_struct (const Type *obj) const - { return likely (this->check_range (obj, obj->min_size)); } - - bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - hb_assign (* const_cast (obj), v); - return true; - } - return false; - } - - template - hb_blob_t *sanitize_blob (hb_blob_t *blob) - { - bool sane; - - init (blob); - - retry: - DEBUG_MSG_FUNC (SANITIZE, start, "start"); - - start_processing (); - - if (unlikely (!start)) - { - end_processing (); - return blob; - } - - Type *t = CastP (const_cast (start)); - - sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) { - start = hb_blob_get_data_writable (blob, nullptr); - end = start + blob->length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } - - end_processing (); - - DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); - if (sane) - { - hb_blob_make_immutable (blob); - return blob; - } - else - { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - template - hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) - { - if (!num_glyphs_set) - set_num_glyphs (hb_face_get_glyph_count (face)); - return sanitize_blob (hb_face_reference_table (face, tableTag)); - } - - mutable unsigned int debug_depth; - const char *start, *end; - mutable int max_ops; - private: - bool writable; - unsigned int edit_count; - hb_blob_t *blob; - unsigned int num_glyphs; - bool num_glyphs_set; -}; - -struct hb_sanitize_with_object_t -{ - template - hb_sanitize_with_object_t (hb_sanitize_context_t *c, - const T& obj) : c (c) - { c->set_object (obj); } - ~hb_sanitize_with_object_t () - { c->reset_object (); } - - private: - hb_sanitize_context_t *c; -}; - - -/* - * Serialize - */ - -struct hb_serialize_context_t -{ - hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - reset (); - } - - bool in_error () const { return !this->successful; } - - void reset () - { - this->successful = true; - this->head = this->start; - this->debug_depth = 0; - } - - bool propagate_error (bool e) - { return this->successful = this->successful && e; } - template bool propagate_error (const T &obj) - { return this->successful = this->successful && !obj.in_error (); } - template bool propagate_error (const T *obj) - { return this->successful = this->successful && !obj->in_error (); } - template bool propagate_error (T1 &o1, T2 &o2) - { return propagate_error (o1) && propagate_error (o2); } - template bool propagate_error (T1 *o1, T2 *o2) - { return propagate_error (o1) && propagate_error (o2); } - template - bool propagate_error (T1 &o1, T2 &o2, T3 &o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - template - bool propagate_error (T1 *o1, T2 *o2, T3 *o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - - /* To be called around main operation. */ - template - Type *start_serialize () - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - - return start_embed (); - } - void end_serialize () - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", - this->start, this->end, - (int) (this->head - this->start), - this->successful ? "successful" : "UNSUCCESSFUL"); - } - - unsigned int length () const { return this->head - this->start; } - - void align (unsigned int alignment) - { - unsigned int l = length () % alignment; - if (l) - allocate_size (alignment - l); - } - - template - Type *start_embed (const Type *_ HB_UNUSED = nullptr) const - { - Type *ret = reinterpret_cast (this->head); - return ret; - } - - template - Type *allocate_size (unsigned int size) - { - if (unlikely (!this->successful || this->end - this->head < ptrdiff_t (size))) { - this->successful = false; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast (ret); - } - - template - Type *allocate_min () - { - return this->allocate_size (Type::min_size); - } - - template - Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, &obj, size); - return ret; - } - template - hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; } - - template - Type *extend_size (Type &obj, unsigned int size) - { - assert (this->start <= (char *) &obj); - assert ((char *) &obj <= this->head); - assert ((char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - template - Type *extend_min (Type &obj) { return extend_size (obj, obj.min_size); } - - template - Type *extend (Type &obj) { return extend_size (obj, obj.get_size ()); } - - /* Output routines. */ - template - Type *copy () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast (p); - } - hb_bytes_t copy_bytes () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - else - return hb_bytes_t (); - return hb_bytes_t ((char *) p, len); - } - hb_blob_t *copy_blob () const - { - assert (this->successful); - return hb_blob_create (this->start, - this->head - this->start, - HB_MEMORY_MODE_DUPLICATE, - nullptr, nullptr); - } - - public: - unsigned int debug_depth; - char *start, *end, *head; - bool successful; -}; - - - -/* - * Big-endian integers. - */ - -template struct BEInt; - -template -struct BEInt -{ - public: - void set (Type V) { v = V; } - operator Type () const { return v; } - private: uint8_t v; -}; -template -struct BEInt -{ - public: - void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - operator Type () const - { -#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ - defined(__BYTE_ORDER) && \ - (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) - /* Spoon-feed the compiler a big-endian integer with alignment 1. - * https://github.com/harfbuzz/harfbuzz/pull/1398 */ - struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) this)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) this)->v; -#endif -#endif - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template -struct BEInt -{ - public: - void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template -struct BEInt -{ - public: - typedef Type type; - void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - /* * Lazy loaders. @@ -814,7 +190,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t const Returned * operator -> () const { return get (); } const Returned & operator * () const { return *get (); } - explicit_operator bool () const + explicit operator bool () const { return get_stored () != Funcs::get_null (); } template operator const C * () const { return get (); } @@ -858,7 +234,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t static Returned* convert (Stored *p) { return p; } /* By default null/init/fini the object. */ - static const Stored* get_null () { return &Null(Stored); } + static const Stored* get_null () { return &Null (Stored); } static Stored *create (Data *data) { Stored *p = (Stored *) calloc (1, sizeof (Stored)); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-map.cc index 3fccfa0fa01b2..114efcb3d75de 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.cc @@ -69,7 +69,7 @@ hb_map_create () hb_map_t * hb_map_get_empty () { - return const_cast (&Null(hb_map_t)); + return const_cast (&Null (hb_map_t)); } /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-map.hh index c04a8dff96636..a5c997c32e837 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-map.hh @@ -30,31 +30,36 @@ #include "hb.hh" -template -inline uint32_t Hash (const T &v) -{ - /* Knuth's multiplicative method: */ - return (uint32_t) v * 2654435761u; -} - - /* - * hb_map_t + * hb_hashmap_t */ -struct hb_map_t +template +struct hb_hashmap_t { - HB_NO_COPY_ASSIGN (hb_map_t); - hb_map_t () { init (); } - ~hb_map_t () { fini (); } + HB_DELETE_COPY_ASSIGN (hb_hashmap_t); + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + static_assert (hb_is_integral (K) || hb_is_pointer (K), ""); + static_assert (hb_is_integral (V) || hb_is_pointer (V), ""); struct item_t { - hb_codepoint_t key; - hb_codepoint_t value; - - bool is_unused () const { return key == INVALID; } - bool is_tombstone () const { return key != INVALID && value == INVALID; } + K key; + V value; + uint32_t hash; + + void clear () { key = kINVALID; value = vINVALID; hash = 0; } + + bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); } + bool operator == (const item_t &o) { return *this == o.key; } + bool is_unused () const { return key == kINVALID; } + bool is_tombstone () const { return key != kINVALID && value == vINVALID; } + bool is_real () const { return key != kINVALID && value != vINVALID; } + hb_pair_t get_pair() const { return hb_pair_t (key, value); } }; hb_object_header_t header; @@ -82,14 +87,22 @@ struct hb_map_t { free (items); items = nullptr; + population = occupancy = 0; } void fini () { - population = occupancy = 0; hb_object_fini (this); fini_shallow (); } + void reset () + { + if (unlikely (hb_object_is_immutable (this))) + return; + successful = true; + clear (); + } + bool in_error () const { return !successful; } bool resize () @@ -104,7 +117,8 @@ struct hb_map_t successful = false; return false; } - memset (new_items, 0xFF, (size_t) new_size * sizeof (item_t)); + for (auto &_ : hb_iter (new_items, new_size)) + _.clear (); unsigned int old_size = mask + 1; item_t *old_items = items; @@ -118,22 +132,96 @@ struct hb_map_t /* Insert back old items. */ if (old_items) for (unsigned int i = 0; i < old_size; i++) - if (old_items[i].key != INVALID && old_items[i].value != INVALID) - set (old_items[i].key, old_items[i].value); + if (old_items[i].is_real ()) + set_with_hash (old_items[i].key, + old_items[i].hash, + old_items[i].value); free (old_items); return true; } - void set (hb_codepoint_t key, hb_codepoint_t value) + void set (K key, V value) + { + set_with_hash (key, hb_hash (key), value); + } + + V get (K key) const + { + if (unlikely (!items)) return vINVALID; + unsigned int i = bucket_for (key); + return items[i].is_real () && items[i] == key ? items[i].value : vINVALID; + } + + void del (K key) { set (key, vINVALID); } + + /* Has interface. */ + static constexpr V SENTINEL = vINVALID; + typedef V value_t; + value_t operator [] (K k) const { return get (k); } + bool has (K k, V *vp = nullptr) const + { + V v = (*this)[k]; + if (vp) *vp = v; + return v != SENTINEL; + } + /* Projection. */ + V operator () (K k) const { return get (k); } + + void clear () + { + if (unlikely (hb_object_is_immutable (this))) + return; + if (items) + for (auto &_ : hb_iter (items, mask + 1)) + _.clear (); + + population = occupancy = 0; + } + + bool is_empty () const { return population == 0; } + + unsigned int get_population () const { return population; } + + /* + * Iterator + */ + auto iter () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::get_pair) + ) + auto keys () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::key) + | hb_map (hb_ridentity) + ) + auto values () const HB_AUTO_RETURN + ( + + hb_array (items, mask ? mask + 1 : 0) + | hb_filter (&item_t::is_real) + | hb_map (&item_t::value) + | hb_map (hb_ridentity) + ) + + /* Sink interface. */ + hb_hashmap_t& operator << (const hb_pair_t& v) + { set (v.first, v.second); return *this; } + + protected: + + void set_with_hash (K key, uint32_t hash, V value) { if (unlikely (!successful)) return; - if (unlikely (key == INVALID)) return; + if (unlikely (key == kINVALID)) return; if ((occupancy + occupancy / 2) >= mask && !resize ()) return; - unsigned int i = bucket_for (key); + unsigned int i = bucket_for_hash (key, hash); - if (value == INVALID && items[i].key != key) + if (value == vINVALID && items[i].key != key) return; /* Trying to delete non-existent key. */ if (!items[i].is_unused ()) @@ -145,55 +233,32 @@ struct hb_map_t items[i].key = key; items[i].value = value; + items[i].hash = hash; occupancy++; if (!items[i].is_tombstone ()) population++; - - } - hb_codepoint_t get (hb_codepoint_t key) const - { - if (unlikely (!items)) return INVALID; - unsigned int i = bucket_for (key); - return items[i].key == key ? items[i].value : INVALID; } - void del (hb_codepoint_t key) { set (key, INVALID); } - - bool has (hb_codepoint_t key) const - { return get (key) != INVALID; } - - hb_codepoint_t operator [] (unsigned int key) const - { return get (key); } - - static constexpr hb_codepoint_t INVALID = HB_MAP_VALUE_INVALID; - - void clear () + unsigned int bucket_for (K key) const { - memset (items, 0xFF, ((size_t) mask + 1) * sizeof (item_t)); - population = occupancy = 0; + return bucket_for_hash (key, hb_hash (key)); } - bool is_empty () const { return population == 0; } - - unsigned int get_population () const { return population; } - - protected: - - unsigned int bucket_for (hb_codepoint_t key) const + unsigned int bucket_for_hash (K key, uint32_t hash) const { - unsigned int i = Hash (key) % prime; + unsigned int i = hash % prime; unsigned int step = 0; - unsigned int tombstone = INVALID; + unsigned int tombstone = (unsigned) -1; while (!items[i].is_unused ()) { - if (items[i].key == key) + if (items[i].hash == hash && items[i] == key) return i; - if (tombstone == INVALID && items[i].is_tombstone ()) + if (tombstone == (unsigned) -1 && items[i].is_tombstone ()) tombstone = i; i = (i + ++step) & mask; } - return tombstone == INVALID ? i : tombstone; + return tombstone == (unsigned) -1 ? i : tombstone; } static unsigned int prime_for (unsigned int shift) @@ -248,5 +313,14 @@ struct hb_map_t } }; +/* + * hb_map_t + */ + +struct hb_map_t : hb_hashmap_t {}; + #endif /* HB_MAP_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-meta.hh b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh new file mode 100644 index 0000000000000..ea64416f0d292 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-meta.hh @@ -0,0 +1,410 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_META_HH +#define HB_META_HH + +#include "hb.hh" + + +/* + * C++ template meta-programming & fundamentals used with them. + */ + +/* Void! For when we need a expression-type of void. */ +struct hb_empty_t {}; + +/* https://en.cppreference.com/w/cpp/types/void_t */ +template struct _hb_void_t { typedef void type; }; +template using hb_void_t = typename _hb_void_t::type; + +template struct _hb_head_t { typedef Head type; }; +template using hb_head_t = typename _hb_head_t::type; + +template struct hb_integral_constant { static constexpr T value = v; }; +template using hb_bool_constant = hb_integral_constant; +using hb_true_type = hb_bool_constant; +using hb_false_type = hb_bool_constant; + + +/* Basic type SFINAE. */ + +template struct hb_enable_if {}; +template struct hb_enable_if { typedef T type; }; +#define hb_enable_if(Cond) typename hb_enable_if<(Cond)>::type* = nullptr +/* Concepts/Requires alias: */ +#define hb_requires(Cond) hb_enable_if((Cond)) + +template struct hb_is_same : hb_false_type {}; +template struct hb_is_same : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t { (E); } + +template struct hb_priority : hb_priority {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template struct hb_type_identity_t { typedef T type; }; +template using hb_type_identity = typename hb_type_identity_t::type; + +struct +{ + template constexpr T* + operator () (T& arg) const + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + /* https://en.cppreference.com/w/cpp/memory/addressof */ + return reinterpret_cast ( + &const_cast ( + reinterpret_cast (arg))); +#pragma GCC diagnostic pop + } +} +HB_FUNCOBJ (hb_addressof); + +template static inline T hb_declval (); +#define hb_declval(T) (hb_declval ()) + +template struct hb_match_const : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_const : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_const = typename hb_match_const::type; +template using hb_add_const = const T; +#define hb_is_const(T) hb_match_const::value +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template struct hb_match_reference : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_reference = typename hb_match_reference::type; +template auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference (hb_prioritize)); +template auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity; +template auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity; +template using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference (hb_prioritize)); +#define hb_is_reference(T) hb_match_reference::value +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant{}; +template struct hb_match_pointer : hb_type_identity_t, hb_bool_constant {}; +template using hb_remove_pointer = typename hb_match_pointer::type; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity*>; +template auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity; +template using hb_add_pointer = decltype (_hb_try_add_pointer (hb_prioritize)); +#define hb_is_pointer(T) hb_match_pointer::value + + +/* TODO Add feature-parity to std::decay. */ +template using hb_decay = hb_remove_const>; + + +template +struct _hb_conditional { typedef T type; }; +template +struct _hb_conditional { typedef F type; }; +template +using hb_conditional = typename _hb_conditional::type; + + +template +struct hb_is_convertible +{ + private: + static constexpr bool from_void = hb_is_same (void, hb_decay); + static constexpr bool to_void = hb_is_same (void, hb_decay ); + static constexpr bool either_void = from_void || to_void; + static constexpr bool both_void = from_void && to_void; + + static hb_true_type impl2 (hb_conditional); + + template + static auto impl (hb_priority<1>) -> decltype (impl2 (hb_declval (T))); + template + static hb_false_type impl (hb_priority<0>); + public: + static constexpr bool value = both_void || + (!either_void && + decltype (impl> (hb_prioritize))::value); +}; +#define hb_is_convertible(From,To) hb_is_convertible::value + +template +using hb_is_base_of = hb_is_convertible *, hb_decay *>; +#define hb_is_base_of(Base,Derived) hb_is_base_of::value + +template +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay, hb_decay) && + (!hb_is_const (From) || hb_is_const (To)) && + (!hb_is_reference (To) || hb_is_const (To) || hb_is_reference (To)) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible::value + +/* std::move and std::forward */ + +template +static constexpr hb_remove_reference&& hb_move (T&& t) { return (hb_remove_reference&&) (t); } + +template +static constexpr T&& hb_forward (hb_remove_reference& t) { return (T&&) t; } +template +static constexpr T&& hb_forward (hb_remove_reference&& t) { return (T&&) t; } + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +struct +{ + template constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (hb_forward (v)) + + template constexpr auto + operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v)) +} +HB_FUNCOBJ (hb_ref); + +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T v) : v (v) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T () const { return v; } + T get () const { return v; } + T v; +}; +template +struct hb_reference_wrapper +{ + hb_reference_wrapper (T& v) : v (hb_addressof (v)) {} + bool operator == (const hb_reference_wrapper& o) const { return v == o.v; } + bool operator != (const hb_reference_wrapper& o) const { return v != o.v; } + operator T& () const { return *v; } + T& get () const { return *v; } + T* v; +}; + + +template +using hb_is_integral = hb_bool_constant< + hb_is_same (hb_decay, char) || + hb_is_same (hb_decay, signed char) || + hb_is_same (hb_decay, unsigned char) || + hb_is_same (hb_decay, signed int) || + hb_is_same (hb_decay, unsigned int) || + hb_is_same (hb_decay, signed short) || + hb_is_same (hb_decay, unsigned short) || + hb_is_same (hb_decay, signed long) || + hb_is_same (hb_decay, unsigned long) || + hb_is_same (hb_decay, signed long long) || + hb_is_same (hb_decay, unsigned long long) || + false +>; +#define hb_is_integral(T) hb_is_integral::value +template +using hb_is_floating_point = hb_bool_constant< + hb_is_same (hb_decay, float) || + hb_is_same (hb_decay, double) || + hb_is_same (hb_decay, long double) || + false +>; +#define hb_is_floating_point(T) hb_is_floating_point::value +template +using hb_is_arithmetic = hb_bool_constant< + hb_is_integral (T) || + hb_is_floating_point (T) || + false +>; +#define hb_is_arithmetic(T) hb_is_arithmetic::value + + +template +using hb_is_signed = hb_conditional, + hb_false_type>; +#define hb_is_signed(T) hb_is_signed::value +template +using hb_is_unsigned = hb_conditional, + hb_false_type>; +#define hb_is_unsigned(T) hb_is_unsigned::value + +template struct hb_int_min; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +template <> struct hb_int_min : hb_integral_constant {}; +#define hb_int_min(T) hb_int_min::value +template struct hb_int_max; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +template <> struct hb_int_max : hb_integral_constant {}; +#define hb_int_max(T) hb_int_max::value + + + +template +struct _hb_is_destructible : hb_false_type {}; +template +struct _hb_is_destructible> : hb_true_type {}; +template +using hb_is_destructible = _hb_is_destructible; +#define hb_is_destructible(T) hb_is_destructible::value + +template +struct _hb_is_constructible : hb_false_type {}; +template +struct _hb_is_constructible, Ts...> : hb_true_type {}; +template +using hb_is_constructible = _hb_is_constructible; +#define hb_is_constructible(...) hb_is_constructible<__VA_ARGS__>::value + +template +using hb_is_default_constructible = hb_is_constructible; +#define hb_is_default_constructible(T) hb_is_default_constructible::value + +template +using hb_is_copy_constructible = hb_is_constructible>>; +#define hb_is_copy_constructible(T) hb_is_copy_constructible::value + +template +using hb_is_move_constructible = hb_is_constructible>>; +#define hb_is_move_constructible(T) hb_is_move_constructible::value + +template +struct _hb_is_assignable : hb_false_type {}; +template +struct _hb_is_assignable> : hb_true_type {}; +template +using hb_is_assignable = _hb_is_assignable; +#define hb_is_assignable(T,U) hb_is_assignable::value + +template +using hb_is_copy_assignable = hb_is_assignable, + hb_add_lvalue_reference>>; +#define hb_is_copy_assignable(T) hb_is_copy_assignable::value + +template +using hb_is_move_assignable = hb_is_assignable, + hb_add_rvalue_reference>; +#define hb_is_move_assignable(T) hb_is_move_assignable::value + +/* Trivial versions. */ + +template union hb_trivial { T value; }; + +template +using hb_is_trivially_destructible= hb_is_destructible>; +#define hb_is_trivially_destructible(T) hb_is_trivially_destructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_constructible= hb_is_constructible, hb_trivial...>; +//#define hb_is_trivially_constructible(...) hb_is_trivially_constructible<__VA_ARGS__>::value + +template +using hb_is_trivially_default_constructible= hb_is_default_constructible>; +#define hb_is_trivially_default_constructible(T) hb_is_trivially_default_constructible::value + +template +using hb_is_trivially_copy_constructible= hb_is_copy_constructible>; +#define hb_is_trivially_copy_constructible(T) hb_is_trivially_copy_constructible::value + +template +using hb_is_trivially_move_constructible= hb_is_move_constructible>; +#define hb_is_trivially_move_constructible(T) hb_is_trivially_move_constructible::value + +/* Don't know how to do the following. */ +//template +//using hb_is_trivially_assignable= hb_is_assignable, hb_trivial>; +//#define hb_is_trivially_assignable(T,U) hb_is_trivially_assignable::value + +template +using hb_is_trivially_copy_assignable= hb_is_copy_assignable>; +#define hb_is_trivially_copy_assignable(T) hb_is_trivially_copy_assignable::value + +template +using hb_is_trivially_move_assignable= hb_is_move_assignable>; +#define hb_is_trivially_move_assignable(T) hb_is_trivially_move_assignable::value + +template +using hb_is_trivially_copyable= hb_bool_constant< + hb_is_trivially_destructible (T) && + (!hb_is_move_assignable (T) || hb_is_trivially_move_assignable (T)) && + (!hb_is_move_constructible (T) || hb_is_trivially_move_constructible (T)) && + (!hb_is_copy_assignable (T) || hb_is_trivially_copy_assignable (T)) && + (!hb_is_copy_constructible (T) || hb_is_trivially_copy_constructible (T)) && + true +>; +#define hb_is_trivially_copyable(T) hb_is_trivially_copyable::value + +template +using hb_is_trivial= hb_bool_constant< + hb_is_trivially_copyable (T) && + hb_is_trivially_default_constructible (T) +>; +#define hb_is_trivial(T) hb_is_trivial::value + +/* hb_unwrap_type (T) + * If T has no T::type, returns T. Otherwise calls itself on T::type recursively. + */ + +template +struct _hb_unwrap_type : hb_type_identity_t {}; +template +struct _hb_unwrap_type> : _hb_unwrap_type {}; +template +using hb_unwrap_type = _hb_unwrap_type; +#define hb_unwrap_type(T) typename hb_unwrap_type::type + +#endif /* HB_META_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh index 1582b40cb048b..f2d2962b7138b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-mutex.hh @@ -48,12 +48,22 @@ /* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ +#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include +typedef pthread_mutex_t hb_mutex_impl_t; +#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER +#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) +#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) +#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) +#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) + + #elif !defined(HB_NO_MT) && defined(_WIN32) -#include typedef CRITICAL_SECTION hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT {0} -#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) #define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) #else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) @@ -63,17 +73,6 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #define hb_mutex_impl_finish(M) DeleteCriticalSection (M) -#elif !defined(HB_NO_MT) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) - -#include -typedef pthread_mutex_t hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER -#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr) -#define hb_mutex_impl_lock(M) pthread_mutex_lock (M) -#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M) -#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M) - - #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES) #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) @@ -92,25 +91,7 @@ typedef volatile int hb_mutex_impl_t; #define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END -#elif !defined(HB_NO_MT) - -#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD) -# include -# define HB_SCHED_YIELD() sched_yield () -#else -# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END -#endif - -#define HB_MUTEX_INT_NIL 1 /* Warn that fallback implementation is in use. */ -typedef volatile int hb_mutex_impl_t; -#define HB_MUTEX_IMPL_INIT 0 -#define hb_mutex_impl_init(M) *(M) = 0 -#define hb_mutex_impl_lock(M) HB_STMT_START { while (*(M)) HB_SCHED_YIELD (); (*(M))++; } HB_STMT_END -#define hb_mutex_impl_unlock(M) (*(M))--; -#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END - - -#else /* HB_NO_MT */ +#elif defined(HB_NO_MT) typedef int hb_mutex_impl_t; #define HB_MUTEX_IMPL_INIT 0 @@ -120,6 +101,11 @@ typedef int hb_mutex_impl_t; #define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END +#else + +#error "Could not find any system to define mutex macros." +#error "Check hb-mutex.hh for possible resolutions." + #endif @@ -127,8 +113,6 @@ typedef int hb_mutex_impl_t; struct hb_mutex_t { - /* TODO Add tracing. */ - hb_mutex_impl_t m; void init () { hb_mutex_impl_init (&m); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-null.hh b/src/java.desktop/share/native/libharfbuzz/hb-null.hh index 8a0e2d7dd05f1..d2bc3322e53c4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-null.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-null.hh @@ -28,6 +28,7 @@ #define HB_NULL_HH #include "hb.hh" +#include "hb-meta.hh" /* @@ -36,7 +37,7 @@ /* Global nul-content Null pool. Enlarge as necessary. */ -#define HB_NULL_POOL_SIZE 9880 +#define HB_NULL_POOL_SIZE 384 /* Use SFINAE to sniff whether T has min_size; in which case return T::null_size, * otherwise return sizeof(T). */ @@ -45,18 +46,13 @@ * https://stackoverflow.com/questions/7776448/sfinae-tried-with-bool-gives-compiler-error-template-argument-tvalue-invol */ -template struct _hb_bool_type {}; - -template -struct _hb_null_size -{ enum { value = sizeof (T) }; }; +template +struct _hb_null_size : hb_integral_constant {}; template -struct _hb_null_size > -{ enum { value = T::null_size }; }; +struct _hb_null_size> : hb_integral_constant {}; template -struct hb_null_size -{ enum { value = _hb_null_size >::value }; }; +using hb_null_size = _hb_null_size; #define hb_null_size(T) hb_null_size::value /* These doesn't belong here, but since is copy/paste from above, put it here. */ @@ -64,56 +60,36 @@ struct hb_null_size /* hb_static_size (T) * Returns T::static_size if T::min_size is defined, or sizeof (T) otherwise. */ -template -struct _hb_static_size -{ enum { value = sizeof (T) }; }; +template +struct _hb_static_size : hb_integral_constant {}; template -struct _hb_static_size > -{ enum { value = T::static_size }; }; - +struct _hb_static_size> : hb_integral_constant {}; template -struct hb_static_size -{ enum { value = _hb_static_size >::value }; }; +using hb_static_size = _hb_static_size; #define hb_static_size(T) hb_static_size::value -/* hb_assign (obj, value) - * Calls obj.set (value) if obj.min_size is defined and value has different type - * from obj, or obj = v otherwise. */ - -template -struct _hb_assign -{ static inline void value (T &o, const V v) { o = v; } }; -template -struct _hb_assign > -{ static inline void value (T &o, const V v) { o.set (v); } }; -template -struct _hb_assign > -{ static inline void value (T &o, const T v) { o = v; } }; - -template -static inline void hb_assign (T &o, const V v) -{ _hb_assign >::value (o, v); } - - /* * Null() */ extern HB_INTERNAL -hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)]; +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; /* Generic nul-content Null objects. */ template -static inline Type const & Null () { - static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); - return *reinterpret_cast (_hb_NullPool); -} +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; template struct NullHelper { - typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type; - static const Type & get_null () { return Null (); } + typedef hb_remove_const> Type; + static const Type & get_null () { return Null::get_null (); } }; #define Null(Type) NullHelper::get_null () @@ -122,11 +98,13 @@ struct NullHelper } /* Close namespace. */ \ extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \ template <> \ - /*static*/ inline const Namespace::Type& Null () { \ - return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ - } \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ namespace Namespace { \ - static_assert (true, "Just so we take semicolon after.") + static_assert (true, "") /* Require semicolon after. */ #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size] @@ -134,10 +112,12 @@ struct NullHelper #define DECLARE_NULL_INSTANCE(Type) \ extern HB_INTERNAL const Type _hb_Null_##Type; \ template <> \ - /*static*/ inline const Type& Null () { \ - return _hb_Null_##Type; \ - } \ -static_assert (true, "Just so we take semicolon after.") + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "") /* Require semicolon after. */ #define DEFINE_NULL_INSTANCE(Type) \ const Type _hb_Null_##Type @@ -148,31 +128,31 @@ static_assert (true, "Just so we take semicolon after.") * causing bad memory access. So, races there are not actually introducing incorrectness * in the code. Has ~12kb binary size overhead to have it, also clang build fails with it. */ extern HB_INTERNAL -/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)]; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)]; /* CRAP pool: Common Region for Access Protection. */ template static inline Type& Crap () { static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); Type *obj = reinterpret_cast (_hb_CrapPool); - memcpy (obj, &Null(Type), sizeof (*obj)); + memcpy (obj, &Null (Type), sizeof (*obj)); return *obj; } template struct CrapHelper { - typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type; + typedef hb_remove_const> Type; static Type & get_crap () { return Crap (); } }; #define Crap(Type) CrapHelper::get_crap () template struct CrapOrNullHelper { - static Type & get () { return Crap(Type); } + static Type & get () { return Crap (Type); } }; template struct CrapOrNullHelper { - static const Type & get () { return Null(Type); } + static const Type & get () { return Null (Type); } }; #define CrapOrNull(Type) CrapOrNullHelper::get () @@ -184,7 +164,7 @@ struct CrapOrNullHelper { template struct hb_nonnull_ptr_t { - typedef typename hb_remove_pointer (P) T; + typedef hb_remove_pointer

    T; hb_nonnull_ptr_t (T *v_ = nullptr) : v (v_) {} T * operator = (T *v_) { return v = v_; } @@ -194,7 +174,7 @@ struct hb_nonnull_ptr_t /* Only auto-cast to const types. */ template operator const C * () const { return get (); } operator const char * () const { return (const char *) get (); } - T * get () const { return v ? v : const_cast (&Null(T)); } + T * get () const { return v ? v : const_cast (&Null (T)); } T * get_raw () const { return v; } T *v; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh new file mode 100644 index 0000000000000..9d2867e483550 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number-parser.hh @@ -0,0 +1,237 @@ + +#line 1 "hb-number-parser.rl" +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_PARSER_HH +#define HB_NUMBER_PARSER_HH + +#include "hb.hh" + + +#line 35 "hb-number-parser.hh" +static const unsigned char _double_parser_trans_keys[] = { + 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, + 46u, 101u, 0 +}; + +static const char _double_parser_key_spans[] = { + 0, 15, 12, 10, 15, 10, 54, 10, + 56 +}; + +static const unsigned char _double_parser_index_offsets[] = { + 0, 0, 16, 29, 40, 56, 67, 122, + 133 +}; + +static const char _double_parser_indicies[] = { + 0, 1, 2, 3, 1, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 1, 3, 1, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 1, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 1, 6, 1, 7, 1, 1, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 1, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 1, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 1, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 1, 3, 1, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 9, 1, 0 +}; + +static const char _double_parser_trans_targs[] = { + 2, 0, 2, 3, 8, 6, 5, 5, + 7, 4 +}; + +static const char _double_parser_trans_actions[] = { + 0, 0, 1, 0, 2, 3, 0, 4, + 5, 0 +}; + +static const int double_parser_start = 1; +static const int double_parser_first_final = 6; +static const int double_parser_error = 0; + +static const int double_parser_en_main = 1; + + +#line 68 "hb-number-parser.rl" + + +/* Works only for n < 512 */ +static inline double +_pow10 (unsigned exponent) +{ + static const double _powers_of_10[] = + { + 1.0e+256, + 1.0e+128, + 1.0e+64, + 1.0e+32, + 1.0e+16, + 1.0e+8, + 10000., + 100., + 10. + }; + unsigned mask = 1 << (ARRAY_LENGTH (_powers_of_10) - 1); + double result = 1; + for (const double *power = _powers_of_10; mask; ++power, mask >>= 1) + if (exponent & mask) result *= *power; + return result; +} + +/* a variant of strtod that also gets end of buffer in its second argument */ +static inline double +strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) +{ + double value = 0; + double frac = 0; + double frac_count = 0; + unsigned exp = 0; + bool neg = false, exp_neg = false, exp_overflow = false; + const unsigned long long MAX_FRACT = 0xFFFFFFFFFFFFFull; /* 2^52-1 */ + const unsigned MAX_EXP = 0x7FFu; /* 2^11-1 */ + + const char *pe = *end_ptr; + while (p < pe && ISSPACE (*p)) + p++; + + int cs; + +#line 139 "hb-number-parser.hh" + { + cs = double_parser_start; + } + +#line 144 "hb-number-parser.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _keys = _double_parser_trans_keys + (cs<<1); + _inds = _double_parser_indicies + _double_parser_index_offsets[cs]; + + _slen = _double_parser_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _double_parser_trans_targs[_trans]; + + if ( _double_parser_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _double_parser_trans_actions[_trans] ) { + case 1: +#line 37 "hb-number-parser.rl" + { neg = true; } + break; + case 4: +#line 38 "hb-number-parser.rl" + { exp_neg = true; } + break; + case 2: +#line 40 "hb-number-parser.rl" + { + value = value * 10. + ((*p) - '0'); +} + break; + case 3: +#line 43 "hb-number-parser.rl" + { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + ((*p) - '0'); + ++frac_count; + } +} + break; + case 5: +#line 50 "hb-number-parser.rl" + { + if (likely (exp * 10 + ((*p) - '0') <= MAX_EXP)) + exp = exp * 10 + ((*p) - '0'); + else + exp_overflow = true; +} + break; +#line 202 "hb-number-parser.hh" + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 113 "hb-number-parser.rl" + + + *end_ptr = p; + + if (frac_count) value += frac / _pow10 (frac_count); + if (neg) value *= -1.; + + if (unlikely (exp_overflow)) + { + if (value == 0) return value; + if (exp_neg) return neg ? -DBL_MIN : DBL_MIN; + else return neg ? -DBL_MAX : DBL_MAX; + } + + if (exp) + { + if (exp_neg) value /= _pow10 (exp); + else value *= _pow10 (exp); + } + + return value; +} + +#endif /* HB_NUMBER_PARSER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.cc b/src/java.desktop/share/native/libharfbuzz/hb-number.cc new file mode 100644 index 0000000000000..f4ce693d8ed88 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number.cc @@ -0,0 +1,80 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#include "hb.hh" +#include "hb-machinery.hh" +#include "hb-number.hh" +#include "hb-number-parser.hh" + +template +static bool +_parse_number (const char **pp, const char *end, T *pv, + bool whole_buffer, Func f) +{ + char buf[32]; + unsigned len = hb_min (ARRAY_LENGTH (buf) - 1, (unsigned) (end - *pp)); + strncpy (buf, *pp, len); + buf[len] = '\0'; + + char *p = buf; + char *pend = p; + + errno = 0; + *pv = f (p, &pend); + if (unlikely (errno || p == pend || + /* Check if consumed whole buffer if is requested */ + (whole_buffer && pend - p != end - *pp))) + return false; + + *pp += pend - p; + return true; +} + +bool +hb_parse_int (const char **pp, const char *end, int *pv, bool whole_buffer) +{ + return _parse_number (pp, end, pv, whole_buffer, + [] (const char *p, char **end) + { return strtol (p, end, 10); }); +} + +bool +hb_parse_uint (const char **pp, const char *end, unsigned *pv, + bool whole_buffer, int base) +{ + return _parse_number (pp, end, pv, whole_buffer, + [base] (const char *p, char **end) + { return strtoul (p, end, base); }); +} + +bool +hb_parse_double (const char **pp, const char *end, double *pv, bool whole_buffer) +{ + const char *pend = end; + *pv = strtod_rl (*pp, &pend); + if (unlikely (*pp == pend)) return false; + *pp = pend; + return !whole_buffer || end == pend; +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-number.hh b/src/java.desktop/share/native/libharfbuzz/hb-number.hh new file mode 100644 index 0000000000000..47d902cf3e740 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-number.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + */ + +#ifndef HB_NUMBER_HH +#define HB_NUMBER_HH + +HB_INTERNAL bool +hb_parse_int (const char **pp, const char *end, int *pv, + bool whole_buffer = false); + +HB_INTERNAL bool +hb_parse_uint (const char **pp, const char *end, unsigned int *pv, + bool whole_buffer = false, int base = 10); + +HB_INTERNAL bool +hb_parse_double (const char **pp, const char *end, double *pv, + bool whole_buffer = false); + +#endif /* HB_NUMBER_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-object.hh b/src/java.desktop/share/native/libharfbuzz/hb-object.hh index 1d7b6bf4f0b94..f01508ed40baa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-object.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-object.hh @@ -168,8 +168,8 @@ struct hb_user_data_array_t void *data; hb_destroy_func_t destroy; - bool operator == (hb_user_data_key_t *other_key) const { return key == other_key; } - bool operator == (hb_user_data_item_t &other) const { return key == other.key; } + bool operator == (const hb_user_data_key_t *other_key) const { return key == other_key; } + bool operator == (const hb_user_data_item_t &other) const { return key == other.key; } void fini () { if (destroy) destroy (data); } }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh index 72b203041d697..95a8f75ff2052 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-file.hh @@ -56,7 +56,7 @@ typedef struct TableRecord { int cmp (Tag t) const { return -t.cmp (tag); } - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const TableRecord *a = (const TableRecord *) pa; const TableRecord *b = (const TableRecord *) pb; @@ -86,27 +86,22 @@ typedef struct OffsetTable const TableRecord& get_table (unsigned int i) const { return tables[i]; } unsigned int get_table_tags (unsigned int start_offset, - unsigned int *table_count, /* IN/OUT */ - hb_tag_t *table_tags /* OUT */) const + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) const { if (table_count) { - if (start_offset >= tables.len) - *table_count = 0; - else - *table_count = MIN (*table_count, tables.len - start_offset); - - const TableRecord *sub_tables = tables.arrayZ + start_offset; - unsigned int count = *table_count; - for (unsigned int i = 0; i < count; i++) - table_tags[i] = sub_tables[i].tag; + + tables.sub_array (start_offset, table_count) + | hb_map (&TableRecord::tag) + | hb_sink (hb_array (table_tags, *table_count)) + ; } return tables.len; } bool find_table_index (hb_tag_t tag, unsigned int *table_index) const { Tag t; - t.set (tag); + t = tag; return tables.bfind (t, table_index, HB_BFIND_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); } const TableRecord& get_table_by_tag (hb_tag_t tag) const @@ -127,7 +122,7 @@ typedef struct OffsetTable /* Alloc 12 for the OTHeader. */ if (unlikely (!c->extend_min (*this))) return_trace (false); /* Write sfntVersion (bytes 0..3). */ - sfnt_version.set (sfnt_tag); + sfnt_version = sfnt_tag; /* Take space for numTables, searchRange, entrySelector, RangeShift * and the TableRecords themselves. */ if (unlikely (!tables.serialize (c, items.length))) return_trace (false); @@ -140,15 +135,16 @@ typedef struct OffsetTable { TableRecord &rec = tables.arrayZ[i]; hb_blob_t *blob = items[i].blob; - rec.tag.set (items[i].tag); - rec.length.set (hb_blob_get_length (blob)); + rec.tag = items[i].tag; + rec.length = blob->length; rec.offset.serialize (c, this); /* Allocate room for the table and copy it. */ char *start = (char *) c->allocate_size (rec.length); - if (unlikely (!start)) {return false;} + if (unlikely (!start)) return false; - memcpy (start, hb_blob_get_data (blob, nullptr), rec.length); + if (likely (rec.length)) + memcpy (start, blob->data, rec.length); /* 4-byte alignment. */ c->align (4); @@ -159,7 +155,7 @@ typedef struct OffsetTable { head *h = (head *) start; checksum_adjustment = &h->checkSumAdjustment; - checksum_adjustment->set (0); + *checksum_adjustment = 0; } rec.checkSum.set_for_data (start, end - start); @@ -177,10 +173,10 @@ typedef struct OffsetTable for (unsigned int i = 0; i < items.length; i++) { TableRecord &rec = tables.arrayZ[i]; - checksum.set (checksum + rec.checkSum); + checksum = checksum + rec.checkSum; } - checksum_adjustment->set (0xB1B0AFBAu - checksum); + *checksum_adjustment = 0xB1B0AFBAu - checksum; } return_trace (true); @@ -222,7 +218,7 @@ struct TTCHeaderVersion1 Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */ FixedVersion<>version; /* Version of the TTC Header (1.0), * 0x00010000u */ - LArrayOf > + LArrayOf> table; /* Array of offsets to the OffsetTable for each font * from the beginning of the file */ public: @@ -248,7 +244,7 @@ struct TTCHeader switch (u.header.version.major) { case 2: /* version 2 is compatible with version 1 */ case 1: return u.version1.get_face (i); - default:return Null(OpenTypeFontFace); + default:return Null (OpenTypeFontFace); } } @@ -283,10 +279,10 @@ struct TTCHeader struct ResourceRecord { const OpenTypeFontFace & get_face (const void *data_base) const - { return CastR ((data_base+offset).arrayZ); } + { return * reinterpret_cast ((data_base+offset).arrayZ); } bool sanitize (hb_sanitize_context_t *c, - const void *data_base) const + const void *data_base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && @@ -334,7 +330,7 @@ struct ResourceTypeRecord protected: Tag tag; /* Resource type. */ HBUINT16 resCountM1; /* Number of resources minus 1. */ - NNOffsetTo > + NNOffsetTo> resourcesZ; /* Offset from beginning of resource type list * to reference item list for this type. */ public: @@ -390,7 +386,7 @@ struct ResourceMap HBUINT32 reserved1; /* Reserved for handle to next resource map */ HBUINT16 resreved2; /* Reserved for file reference number */ HBUINT16 attrs; /* Resource fork attribute */ - NNOffsetTo > + NNOffsetTo> typeList; /* Offset from beginning of map to * resource type list */ Offset16 nameList; /* Offset from beginning of map to @@ -422,7 +418,7 @@ struct ResourceForkHeader } protected: - LNNOffsetTo > + LNNOffsetTo> data; /* Offset from beginning of resource fork * to resource data */ LNNOffsetTo @@ -477,7 +473,7 @@ struct OpenTypeFontFile case TrueTypeTag: return u.fontFace; case TTCTag: return u.ttcHeader.get_face (i); case DFontTag: return u.rfHeader.get_face (i, base_offset); - default: return Null(OpenTypeFontFace); + default: return Null (OpenTypeFontFace); } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh index 596099305c16c..624194651d3a6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-open-type.hh @@ -52,22 +52,34 @@ namespace OT { * Int types */ -template struct hb_signedness_int; -template <> struct hb_signedness_int { typedef unsigned int value; }; -template <> struct hb_signedness_int { typedef signed int value; }; - /* Integer types in big-endian order and no alignment requirement */ template struct IntType { typedef Type type; - typedef typename hb_signedness_int::value>::value wide_type; + typedef hb_conditional wide_type; - void set (wide_type i) { v.set (i); } + IntType& operator = (wide_type i) { v = i; return *this; } operator wide_type () const { return v; } - bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } - bool operator != (const IntType &o) const { return !(*this == o); } - static int cmp (const IntType *a, const IntType *b) { return b->cmp (*a); } + bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; } + bool operator != (const IntType &o) const { return !(*this == o); } + + IntType& operator += (unsigned count) { *this = *this + count; return *this; } + IntType& operator -= (unsigned count) { *this = *this - count; return *this; } + IntType& operator ++ () { *this += 1; return *this; } + IntType& operator -- () { *this -= 1; return *this; } + IntType operator ++ (int) { IntType c (*this); ++*this; return c; } + IntType operator -- (int) { IntType c (*this); --*this; return c; } + + HB_INTERNAL static int cmp (const IntType *a, const IntType *b) + { return b->cmp (*a); } + HB_INTERNAL static int cmp (const void *a, const void *b) + { + IntType *pa = (IntType *) a; + IntType *pb = (IntType *) b; + + return pb->cmp (*pa); + } template int cmp (Type2 a) const { @@ -110,19 +122,21 @@ typedef HBUINT16 UFWORD; /* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */ struct F2DOT14 : HBINT16 { + F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; } // 16384 means 1<<14 float to_float () const { return ((int32_t) v) / 16384.f; } - void set_float (float f) { v.set (round (f * 16384.f)); } + void set_float (float f) { v = roundf (f * 16384.f); } public: DEFINE_SIZE_STATIC (2); }; /* 32-bit signed fixed-point number (16.16). */ -struct Fixed : HBINT32 +struct HBFixed : HBINT32 { + HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; } // 65536 means 1<<16 float to_float () const { return ((int32_t) v) / 65536.f; } - void set_float (float f) { v.set (round (f * 65536.f)); } + void set_float (float f) { v = roundf (f * 65536.f); } public: DEFINE_SIZE_STATIC (4); }; @@ -147,6 +161,7 @@ struct LONGDATETIME * system, feature, or baseline */ struct Tag : HBUINT32 { + Tag& operator = (hb_tag_t i) { HBUINT32::operator= (i); return *this; } /* What the char* converters return is NOT nul-terminated. Print using "%.4s" */ operator const char* () const { return reinterpret_cast (&this->v); } operator char* () { return reinterpret_cast (&this->v); } @@ -155,11 +170,15 @@ struct Tag : HBUINT32 }; /* Glyph index number, same as uint16 (length = 16 bits) */ -typedef HBUINT16 GlyphID; +struct HBGlyphID : HBUINT16 +{ + HBGlyphID& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } +}; /* Script/language-system/feature index */ struct Index : HBUINT16 { static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFu; + Index& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } }; DECLARE_NULL_NAMESPACE_BYTES (OT, Index); @@ -169,6 +188,8 @@ typedef Index NameID; template struct Offset : Type { + Offset& operator = (typename Type::type i) { Type::operator= (i); return *this; } + typedef Type type; bool is_null () const { return has_null && 0 == *this; } @@ -176,7 +197,7 @@ struct Offset : Type void *serialize (hb_serialize_context_t *c, const void *base) { void *t = c->start_embed (); - this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */ + c->check_assign (*this, (unsigned) ((char *) t - (char *) base)); return t; } @@ -191,6 +212,8 @@ typedef Offset Offset32; /* CheckSum */ struct CheckSum : HBUINT32 { + CheckSum& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } + /* This is reference implementation from the spec. */ static uint32_t CalcTableChecksum (const HBUINT32 *Table, uint32_t Length) { @@ -205,7 +228,7 @@ struct CheckSum : HBUINT32 /* Note: data should be 4byte aligned and have 4byte padding at the end. */ void set_for_data (const void *data, unsigned int length) - { set (CalcTableChecksum ((const HBUINT32 *) data, length)); } + { *this = CalcTableChecksum ((const HBUINT32 *) data, length); } public: DEFINE_SIZE_STATIC (4); @@ -248,13 +271,18 @@ struct _hb_has_null template struct _hb_has_null { - static const Type *get_null () { return &Null(Type); } - static Type *get_crap () { return &Crap(Type); } + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } }; template struct OffsetTo : Offset { + HB_DELETE_COPY_ASSIGN (OffsetTo); + OffsetTo () = default; + + OffsetTo& operator = (typename OffsetType::type i) { OffsetType::operator= (i); return *this; } + const Type& operator () (const void *base) const { if (unlikely (this->is_null ())) return *_hb_has_null::get_null (); @@ -266,24 +294,73 @@ struct OffsetTo : Offset return StructAtOffset (base, *this); } + template + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + Type& serialize (hb_serialize_context_t *c, const void *base) { return * (Type *) Offset::serialize (c, base); } - template - void serialize_subset (hb_subset_context_t *c, const T &src, const void *base) + template + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const void *src_base, Ts&&... ds) { - if (&src == &Null (T)) - { - this->set (0); - return; - } - serialize (c->serializer, base); - if (!src.subset (c)) - this->set (0); + *this = 0; + if (src.is_null ()) + return false; + + auto *s = c->serializer; + + s->push (); + + bool ret = c->dispatch (src_base+src, hb_forward (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + /* TODO: Somehow merge this with previous function into a serialize_dispatch(). */ + /* Workaround clang bug: https://bugs.llvm.org/show_bug.cgi?id=23029 + * Can't compile: whence = hb_serialize_context_t::Head followed by Ts&&... + */ + template + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias, + hb_serialize_context_t::whence_t whence, + Ts&&... ds) + { + *this = 0; + if (src.is_null ()) + return false; + + c->push (); + + bool ret = c->copy (src_base+src, hb_forward (ds)...); + + c->add_link (*this, c->pop_pack (), whence, dst_bias); + + return ret; } + bool serialize_copy (hb_serialize_context_t *c, const OffsetTo& src, + const void *src_base, unsigned dst_bias = 0) + { return serialize_copy (c, src, src_base, dst_bias, hb_serialize_context_t::Head); } + bool sanitize_shallow (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -293,39 +370,13 @@ struct OffsetTo : Offset return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, const void *base) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const - { - TRACE_SANITIZE (this); - return_trace (sanitize_shallow (c, base) && - (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1, d2) || - neuter (c))); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const + template + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const { TRACE_SANITIZE (this); return_trace (sanitize_shallow (c, base) && (this->is_null () || - StructAtOffset (base, *this).sanitize (c, d1, d2, d3) || + c->dispatch (StructAtOffset (base, *this), hb_forward (ds)...) || neuter (c))); } @@ -338,14 +389,12 @@ struct OffsetTo : Offset DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ -template struct LOffsetTo : OffsetTo {}; -template struct NNOffsetTo : OffsetTo {}; -template struct LNNOffsetTo : OffsetTo {}; - -template -static inline const Type& operator + (const Base &base, const OffsetTo &offset) { return offset (base); } -template -static inline Type& operator + (Base &base, OffsetTo &offset) { return offset (base); } +template +using LOffsetTo = OffsetTo; +template +using NNOffsetTo = OffsetTo; +template +using LNNOffsetTo = LOffsetTo; /* @@ -358,7 +407,7 @@ struct UnsizedArrayOf typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (UnsizedArrayOf, Type); + HB_DELETE_CREATE_COPY_ASSIGN (UnsizedArrayOf); const Type& operator [] (int i_) const { @@ -384,7 +433,7 @@ struct UnsizedArrayOf { return hb_array (arrayZ, len); } hb_array_t as_array (unsigned int len) const { return hb_array (arrayZ, len); } - operator hb_array_t () { return as_array (); } + operator hb_array_t< Type> () { return as_array (); } operator hb_array_t () const { return as_array (); } template @@ -393,42 +442,49 @@ struct UnsizedArrayOf template const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const { return *as_array (len).lsearch (x, ¬_found); } + template + bool lfind (unsigned int len, const T &x, unsigned *pos = nullptr) const + { return as_array (len).lfind (x, pos); } void qsort (unsigned int len, unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array (len).qsort (start, end); } - bool sanitize (hb_sanitize_context_t *c, unsigned int count) const + bool serialize (hb_serialize_context_t *c, unsigned int items_len) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c, count))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - + TRACE_SERIALIZE (this); + if (unlikely (!c->extend (*this, items_len))) return_trace (false); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const + template + bool serialize (hb_serialize_context_t *c, Iterator items) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c, count))) return_trace (false); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base))) - return_trace (false); + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - template - bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const + + UnsizedArrayOf* copy (hb_serialize_context_t *c, unsigned count) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!as_array (count).copy (c))) return_trace (nullptr); + return_trace (out); + } + + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c, count))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -440,14 +496,14 @@ struct UnsizedArrayOf } public: - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_UNBOUNDED (0); }; /* Unsized array of offset's */ template -struct UnsizedOffsetArrayOf : UnsizedArrayOf > {}; +using UnsizedOffsetArrayOf = UnsizedArrayOf>; /* Unsized array of offsets relative to the beginning of the array itself. */ template @@ -468,17 +524,12 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf return this+*p; } - - bool sanitize (hb_sanitize_context_t *c, unsigned int count) const - { - TRACE_SANITIZE (this); - return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this))); - } - template - bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace ((UnsizedOffsetArrayOf::sanitize (c, count, this, user_data))); + return_trace ((UnsizedOffsetArrayOf + ::sanitize (c, count, this, hb_forward (ds)...))); } }; @@ -501,8 +552,8 @@ struct SortedUnsizedArrayOf : UnsizedArrayOf { return *as_array (len).bsearch (x, ¬_found); } template bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const { return as_array (len).bfind (x, i, not_found, to_store); } }; @@ -514,7 +565,7 @@ struct ArrayOf typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOf, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOf); const Type& operator [] (int i_) const { @@ -532,74 +583,83 @@ struct ArrayOf unsigned int get_size () const { return len.static_size + len * Type::static_size; } - hb_array_t as_array () - { return hb_array (arrayZ, len); } - hb_array_t as_array () const - { return hb_array (arrayZ, len); } - operator hb_array_t (void) { return as_array (); } - operator hb_array_t (void) const { return as_array (); } + explicit operator bool () const { return len; } + + void pop () { len--; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, len); } + hb_array_t as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } - bool serialize (hb_serialize_context_t *c, unsigned int items_len) + hb_success_t serialize (hb_serialize_context_t *c, unsigned items_len) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - len.set (items_len); /* TODO(serialize) Overflow? */ + c->check_assign (len, items_len); if (unlikely (!c->extend (*this))) return_trace (false); return_trace (true); } - template - bool serialize (hb_serialize_context_t *c, hb_array_t items) + template + hb_success_t serialize (hb_serialize_context_t *c, Iterator items) { TRACE_SERIALIZE (this); - if (unlikely (!serialize (c, items.length))) return_trace (false); - for (unsigned int i = 0; i < items.length; i++) - hb_assign (arrayZ[i], items[i]); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const + Type* serialize_append (hb_serialize_context_t *c) { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - - return_trace (true); + TRACE_SERIALIZE (this); + len++; + if (unlikely (!len || !c->extend (*this))) + { + len--; + return_trace (nullptr); + } + return_trace (&arrayZ[len - 1]); } - bool sanitize (hb_sanitize_context_t *c, const void *base) const + + ArrayOf* copy (hb_serialize_context_t *c) const { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = len; - for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base))) - return_trace (false); - return_trace (true); + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (nullptr); + c->check_assign (out->len, len); + if (unlikely (!as_array ().copy (c))) return_trace (nullptr); + return_trace (out); } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); unsigned int count = len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -610,6 +670,9 @@ struct ArrayOf template const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const { return *as_array ().lsearch (x, ¬_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1) { as_array ().qsort (start, end); } @@ -622,20 +685,21 @@ struct ArrayOf public: LenType len; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; -template struct LArrayOf : ArrayOf {}; -typedef ArrayOf PString; +template +using LArrayOf = ArrayOf; +using PString = ArrayOf; /* Array of Offset's */ template -struct OffsetArrayOf : ArrayOf > {}; +using OffsetArrayOf = ArrayOf>; template -struct LOffsetArrayOf : ArrayOf > {}; +using LOffsetArrayOf = ArrayOf>; template -struct LOffsetLArrayOf : ArrayOf, HBUINT32> {}; +using LOffsetLArrayOf = ArrayOf, HBUINT32>; /* Array of offsets relative to the beginning of the array itself. */ template @@ -661,20 +725,15 @@ struct OffsetListOf : OffsetArrayOf if (unlikely (!out)) return_trace (false); unsigned int count = this->len; for (unsigned int i = 0; i < count; i++) - out->arrayZ[i].serialize_subset (c, (*this)[i], out); + out->arrayZ[i].serialize_subset (c, this->arrayZ[i], this, out); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this)); - } - template - bool sanitize (hb_sanitize_context_t *c, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); - return_trace (OffsetArrayOf::sanitize (c, this, user_data)); + return_trace (OffsetArrayOf::sanitize (c, this, hb_forward (ds)...)); } }; @@ -684,7 +743,7 @@ struct HeadlessArrayOf { static constexpr unsigned item_size = Type::static_size; - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (HeadlessArrayOf, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (HeadlessArrayOf); const Type& operator [] (int i_) const { @@ -699,34 +758,53 @@ struct HeadlessArrayOf return arrayZ[i-1]; } unsigned int get_size () const - { return lenP1.static_size + (lenP1 ? lenP1 - 1 : 0) * Type::static_size; } + { return lenP1.static_size + get_length () * Type::static_size; } - bool serialize (hb_serialize_context_t *c, - hb_array_t items) + unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } + + hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } + hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + bool serialize (hb_serialize_context_t *c, unsigned int items_len) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - lenP1.set (items.length + 1); /* TODO(serialize) Overflow? */ + c->check_assign (lenP1, items_len + 1); if (unlikely (!c->extend (*this))) return_trace (false); - for (unsigned int i = 0; i < items.length; i++) - arrayZ[i] = items[i]; + return_trace (true); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + unsigned count = items.len (); + if (unlikely (!serialize (c, count))) return_trace (false); + /* TODO Umm. Just exhaust the iterator instead? Being extra + * cautious right now.. */ + for (unsigned i = 0; i < count; i++, ++items) + arrayZ[i] = *items; return_trace (true); } - bool sanitize (hb_sanitize_context_t *c) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && arrayZ[0].sanitize (c)); - + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) + return_trace (false); return_trace (true); } @@ -740,7 +818,7 @@ struct HeadlessArrayOf public: LenType lenP1; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; @@ -749,7 +827,7 @@ struct HeadlessArrayOf template struct ArrayOfM1 { - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2 (ArrayOfM1, Type, LenType); + HB_DELETE_CREATE_COPY_ASSIGN (ArrayOfM1); const Type& operator [] (int i_) const { @@ -766,14 +844,14 @@ struct ArrayOfM1 unsigned int get_size () const { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); unsigned int count = lenM1 + 1; for (unsigned int i = 0; i < count; i++) - if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) + if (unlikely (!c->dispatch (arrayZ[i], hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -788,7 +866,7 @@ struct ArrayOfM1 public: LenType lenM1; - Type arrayZ[VAR]; + Type arrayZ[HB_VAR_ARRAY]; public: DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; @@ -797,21 +875,40 @@ struct ArrayOfM1 template struct SortedArrayOf : ArrayOf { - hb_sorted_array_t as_array () - { return hb_sorted_array (this->arrayZ, this->len); } - hb_sorted_array_t as_array () const - { return hb_sorted_array (this->arrayZ, this->len); } - operator hb_sorted_array_t () { return as_array (); } - operator hb_sorted_array_t () const { return as_array (); } + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t iter_t; + typedef hb_sorted_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } + + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int count) + { return as_array ().sub_array (start_offset, count); } + hb_sorted_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) + { return as_array ().sub_array (start_offset, count); } - hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} - hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + bool serialize (hb_serialize_context_t *c, unsigned int items_len) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items_len); + return_trace (ret); + } + template + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf::serialize (c, items); + return_trace (ret); + } template Type &bsearch (const T &x, Type ¬_found = Crap (Type)) @@ -821,8 +918,8 @@ struct SortedArrayOf : ArrayOf { return *as_array ().bsearch (x, ¬_found); } template bool bfind (const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const { return as_array ().bfind (x, i, not_found, to_store); } }; @@ -841,15 +938,16 @@ struct BinSearchHeader return_trace (c->check_struct (this)); } - void set (unsigned int v) + BinSearchHeader& operator = (unsigned int v) { - len.set (v); + len = v; assert (len == v); - entrySelector.set (MAX (1u, hb_bit_storage (v)) - 1); - searchRange.set (16 * (1u << entrySelector)); - rangeShift.set (v * 16 > searchRange - ? 16 * v - searchRange - : 0); + entrySelector = hb_max (1u, hb_bit_storage (v)) - 1; + searchRange = 16 * (1u << entrySelector); + rangeShift = v * 16 > searchRange + ? 16 * v - searchRange + : 0; + return *this; } protected: @@ -863,7 +961,7 @@ struct BinSearchHeader }; template -struct BinSearchArrayOf : SortedArrayOf > {}; +using BinSearchArrayOf = SortedArrayOf>; struct VarSizedBinSearchHeader @@ -893,7 +991,7 @@ struct VarSizedBinSearchArrayOf { static constexpr unsigned item_size = Type::static_size; - HB_NO_CREATE_COPY_ASSIGN_TEMPLATE (VarSizedBinSearchArrayOf, Type); + HB_DELETE_CREATE_COPY_ASSIGN (VarSizedBinSearchArrayOf); bool last_is_terminator () const { @@ -928,40 +1026,15 @@ struct VarSizedBinSearchArrayOf unsigned int get_size () const { return header.static_size + header.nUnits * header.unitSize; } - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - - /* Note: for structs that do not reference other structs, - * we do not need to call their sanitize() as we already did - * a bound check on the aggregate array size. We just include - * a small unreachable expression to make sure the structs - * pointed to do have a simple sanitize(), ie. they do not - * reference other structs via offsets. - */ - (void) (false && StructAtOffset (&bytesZ, 0).sanitize (c)); - - return_trace (true); - } - bool sanitize (hb_sanitize_context_t *c, const void *base) const + template + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { TRACE_SANITIZE (this); if (unlikely (!sanitize_shallow (c))) return_trace (false); + if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true); unsigned int count = get_length (); for (unsigned int i = 0; i < count; i++) - if (unlikely (!(*this)[i].sanitize (c, base))) - return_trace (false); - return_trace (true); - } - template - bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const - { - TRACE_SANITIZE (this); - if (unlikely (!sanitize_shallow (c))) return_trace (false); - unsigned int count = get_length (); - for (unsigned int i = 0; i < count; i++) - if (unlikely (!(*this)[i].sanitize (c, base, user_data))) + if (unlikely (!(*this)[i].sanitize (c, hb_forward (ds)...))) return_trace (false); return_trace (true); } @@ -969,18 +1042,15 @@ struct VarSizedBinSearchArrayOf template const Type *bsearch (const T &key) const { - unsigned int size = header.unitSize; - int min = 0, max = (int) get_length () - 1; - while (min <= max) - { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const Type *p = (const Type *) (((const char *) &bytesZ) + (mid * size)); - int c = p->cmp (key); - if (c < 0) max = mid - 1; - else if (c > 0) min = mid + 1; - else return p; - } - return nullptr; + unsigned pos; + return hb_bsearch_impl (&pos, + key, + (const void *) bytesZ, + get_length (), + header.unitSize, + _hb_cmp_method) + ? (const Type *) (((const char *) &bytesZ) + (pos * header.unitSize)) + : nullptr; } private: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh index d278e03d930a5..28073724efc9f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff-common.hh @@ -27,6 +27,7 @@ #define HB_OT_CFF_COMMON_HH #include "hb-open-type.hh" +#include "hb-bimap.hh" #include "hb-ot-layout-common.hh" #include "hb-cff-interp-dict-common.hh" #include "hb-subset-plan.hh" @@ -37,16 +38,19 @@ using namespace OT; #define CFF_UNDEF_CODE 0xFFFFFFFF +using objidx_t = hb_serialize_context_t::objidx_t; +using whence_t = hb_serialize_context_t::whence_t; + /* utility macro */ template -static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset) -{ return offset? (* reinterpret_cast ((const char *) P + offset)): Null(Type); } +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset (P, offset) : Null (Type); } -inline unsigned int calcOffSize(unsigned int dataSize) +inline unsigned int calcOffSize (unsigned int dataSize) { unsigned int size = 1; unsigned int offset = dataSize + 1; - while ((offset & ~0xFF) != 0) + while (offset & ~0xFF) { size++; offset >>= 8; @@ -57,8 +61,8 @@ inline unsigned int calcOffSize(unsigned int dataSize) struct code_pair_t { - hb_codepoint_t code; - hb_codepoint_t glyph; + hb_codepoint_t code; + hb_codepoint_t glyph; }; typedef hb_vector_t str_buff_t; @@ -82,27 +86,20 @@ struct str_buff_vec_t : hb_vector_t template struct CFFIndex { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */ - (c->check_struct (this) && offSize >= 1 && offSize <= 4 && - c->check_array (offsets, offSize, count + 1) && - c->check_array ((const HBUINT8*)data_base (), 1, max_offset () - 1)))); - } - static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count) { return offSize * (count + 1); } unsigned int offset_array_size () const { return calculate_offset_array_size (offSize, count); } - static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize) + CFFIndex *copy (hb_serialize_context_t *c) const { - if (count == 0) - return COUNT::static_size; - else - return min_size + calculate_offset_array_size (offSize, count) + dataSize; + TRACE_SERIALIZE (this); + unsigned int size = get_size (); + CFFIndex *out = c->allocate_size (size); + if (likely (out)) + memcpy (out, this, size); + return_trace (out); } bool serialize (hb_serialize_context_t *c, const CFFIndex &src) @@ -110,7 +107,7 @@ struct CFFIndex TRACE_SERIALIZE (this); unsigned int size = src.get_size (); CFFIndex *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -123,16 +120,16 @@ struct CFFIndex if (byteArray.length == 0) { COUNT *dest = c->allocate_min (); - if (unlikely (dest == nullptr)) return_trace (false); - dest->set (0); + if (unlikely (!dest)) return_trace (false); + *dest = 0; } else { /* serialize CFFIndex header */ if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (byteArray.length); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (byteArray.length + 1)))) + this->count = byteArray.length; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) return_trace (false); /* serialize indices */ @@ -149,9 +146,8 @@ struct CFFIndex for (unsigned int i = 0; i < byteArray.length; i++) { const byte_str_t &bs = byteArray[i]; - unsigned char *dest = c->allocate_size (bs.length); - if (unlikely (dest == nullptr)) - return_trace (false); + unsigned char *dest = c->allocate_size (bs.length); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &bs[0], bs.length); } } @@ -166,14 +162,77 @@ struct CFFIndex byteArray.init (); byteArray.resize (buffArray.length); for (unsigned int i = 0; i < byteArray.length; i++) - { - byteArray[i] = byte_str_t (buffArray[i].arrayZ (), buffArray[i].length); - } + byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length); bool result = this->serialize (c, offSize_, byteArray); byteArray.fini (); return result; } + template + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (it.len () == 0) + { + COUNT *dest = c->allocate_min (); + if (unlikely (!dest)) return_trace (false); + *dest = 0; + } + else + { + serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; })); + for (const byte_str_t &_ : +it) + _.copy (c); + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const byte_str_array_t &byteArray) + { return serialize (c, + hb_iter (byteArray)); } + + bool serialize (hb_serialize_context_t *c, + const str_buff_vec_t &buffArray) + { + auto it = + + hb_iter (buffArray) + | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); }) + ; + return serialize (c, it); + } + + template + bool serialize_header (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + + unsigned total = + it | hb_reduce (hb_add, 0); + unsigned off_size = calcOffSize (total); + + /* serialize CFFIndex header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count = it.len (); + this->offSize = off_size; + if (unlikely (!c->allocate_size (off_size * (it.len () + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (unsigned _ : +it) + { + CFFIndex::set_offset_at (i++, offset); + offset += _; + } + CFFIndex::set_offset_at (i, offset); + + return_trace (true); + } + void set_offset_at (unsigned int index, unsigned int offset) { HBUINT8 *p = offsets + offSize * index + offSize; @@ -181,7 +240,7 @@ struct CFFIndex for (; size; size--) { --p; - p->set (offset & 0xFF); + *p = offset & 0xFF; offset >>= 8; } } @@ -199,37 +258,38 @@ struct CFFIndex unsigned int length_at (unsigned int index) const { - if (likely ((offset_at (index + 1) >= offset_at (index)) && - (offset_at (index + 1) <= offset_at (count)))) - return offset_at (index + 1) - offset_at (index); - else - return 0; + if (unlikely ((offset_at (index + 1) < offset_at (index)) || + (offset_at (index + 1) > offset_at (count)))) + return 0; + return offset_at (index + 1) - offset_at (index); } const unsigned char *data_base () const - { return (const unsigned char *)this + min_size + offset_array_size (); } + { return (const unsigned char *) this + min_size + offset_array_size (); } unsigned int data_size () const { return HBINT8::static_size; } byte_str_t operator [] (unsigned int index) const { - if (likely (index < count)) - return byte_str_t (data_base () + offset_at (index) - 1, length_at (index)); - else - return Null(byte_str_t); + if (unlikely (index >= count)) return Null (byte_str_t); + return byte_str_t (data_base () + offset_at (index) - 1, length_at (index)); } unsigned int get_size () const { - if (this != &Null(CFFIndex)) - { - if (count > 0) - return min_size + offset_array_size () + (offset_at (count) - 1); - else - return count.static_size; /* empty CFFIndex contains count only */ - } - else - return 0; + if (this == &Null (CFFIndex)) return 0; + if (count > 0) + return min_size + offset_array_size () + (offset_at (count) - 1); + return count.static_size; /* empty CFFIndex contains count only */ + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */ + (c->check_struct (this) && offSize >= 1 && offSize <= 4 && + c->check_array (offsets, offSize, count + 1) && + c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1)))); } protected: @@ -245,10 +305,11 @@ struct CFFIndex } public: - COUNT count; /* Number of object data. Note there are (count+1) offsets */ - HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ - HBUINT8 offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */ - /* HBUINT8 data[VAR]; Object data */ + COUNT count; /* Number of object data. Note there are (count+1) offsets */ + HBUINT8 offSize; /* The byte size of each offset in the offsets array. */ + HBUINT8 offsets[HB_VAR_ARRAY]; + /* The array of (count + 1) offsets into objects array (1-base). */ + /* HBUINT8 data[HB_VAR_ARRAY]; Object data */ public: DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets); }; @@ -260,7 +321,7 @@ struct CFFIndexOf : CFFIndex { if (likely (index < CFFIndex::count)) return byte_str_t (CFFIndex::data_base () + CFFIndex::offset_at (index) - 1, CFFIndex::length_at (index)); - return Null(byte_str_t); + return Null (byte_str_t); } template @@ -275,9 +336,9 @@ struct CFFIndexOf : CFFIndex TRACE_SERIALIZE (this); /* serialize CFFIndex header */ if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (dataArrayLen); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (dataArrayLen + 1)))) + this->count = dataArrayLen; + this->offSize = offSize_; + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) return_trace (false); /* serialize indices */ @@ -293,112 +354,74 @@ struct CFFIndexOf : CFFIndex /* serialize data */ for (unsigned int i = 0; i < dataArrayLen; i++) { - TYPE *dest = c->start_embed (); - if (unlikely (dest == nullptr || - !dest->serialize (c, dataArray[i], param1, param2))) + TYPE *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2))) return_trace (false); } return_trace (true); } - - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, - const DATA *dataArray, - unsigned int dataArrayLen, - hb_vector_t &dataSizeArray, /* OUT */ - const PARAM ¶m) - { - /* determine offset size */ - unsigned int totalDataSize = 0; - for (unsigned int i = 0; i < dataArrayLen; i++) - { - unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param); - dataSizeArray[i] = dataSize; - totalDataSize += dataSize; - } - offSize_ = calcOffSize (totalDataSize); - - return CFFIndex::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize); - } }; /* Top Dict, Font Dict, Private Dict */ struct Dict : UnsizedByteStr { - template + template bool serialize (hb_serialize_context_t *c, const DICTVAL &dictval, OP_SERIALIZER& opszr, - PARAM& param) + Ts&&... ds) { TRACE_SERIALIZE (this); for (unsigned int i = 0; i < dictval.get_count (); i++) - { - if (unlikely (!opszr.serialize (c, dictval[i], param))) + if (unlikely (!opszr.serialize (c, dictval[i], hb_forward (ds)...))) return_trace (false); - } - return_trace (true); - } - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (const DICTVAL &dictval, - OP_SERIALIZER& opszr, - PARAM& param) - { - unsigned int size = 0; - for (unsigned int i = 0; i < dictval.get_count (); i++) - size += opszr.calculate_serialized_size (dictval[i], param); - return size; - } - - template - static unsigned int calculate_serialized_size (const DICTVAL &dictval, - OP_SERIALIZER& opszr) - { - unsigned int size = 0; - for (unsigned int i = 0; i < dictval.get_count (); i++) - size += opszr.calculate_serialized_size (dictval[i]); - return size; + return_trace (true); } - template - static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, int value, op_code_t intOp) + template + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) { // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation - if (/*unlikely*/ (!serialize_int (c, intOp, value))) + if (/*unlikely*/ (!serialize_int (c, intOp, value))) return false; TRACE_SERIALIZE (this); /* serialize the opcode */ HBUINT8 *p = c->allocate_size (OpCode_Size (op)); - if (unlikely (p == nullptr)) return_trace (false); + if (unlikely (!p)) return_trace (false); if (Is_OpCode_ESC (op)) { - p->set (OpCode_escape); + *p = OpCode_escape; op = Unmake_OpCode_ESC (op); p++; } - p->set (op); + *p = op; return_trace (true); } - static bool serialize_uint4_op (hb_serialize_context_t *c, op_code_t op, int value) - { return serialize_int_op (c, op, value, OpCode_longintdict); } + template + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_longintdict); } - static bool serialize_uint2_op (hb_serialize_context_t *c, op_code_t op, int value) - { return serialize_int_op (c, op, value, OpCode_shortint); } + template + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op (c, op, value, OpCode_shortint); } - static bool serialize_offset4_op (hb_serialize_context_t *c, op_code_t op, int value) + template + static bool serialize_link_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence) { - return serialize_uint4_op (c, op, value); + T &ofs = *(T *) (c->head + OpCode_Size (int_op)); + if (unlikely (!serialize_int_op (c, op, 0, int_op))) return false; + c->add_link (ofs, link, whence); + return true; } - static bool serialize_offset2_op (hb_serialize_context_t *c, op_code_t op, int value) - { - return serialize_uint2_op (c, op, value); - } + static bool serialize_link4_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } + + static bool serialize_link2_op (hb_serialize_context_t *c, op_code_t op, objidx_t link, whence_t whence = whence_t::Head) + { return serialize_link_op (c, op, link, whence); } }; struct TopDict : Dict {}; @@ -407,155 +430,39 @@ struct PrivateDict : Dict {}; struct table_info_t { - void init () { offSize = offset = size = 0; } + void init () { offset = size = 0; link = 0; } unsigned int offset; unsigned int size; - unsigned int offSize; -}; - -/* used to remap font index or SID from fullset to subset. - * set to CFF_UNDEF_CODE if excluded from subset */ -struct remap_t : hb_vector_t -{ - void init () { SUPER::init (); } - - void fini () { SUPER::fini (); } - - bool reset (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - for (unsigned int i = 0; i < length; i++) - (*this)[i] = CFF_UNDEF_CODE; - count = 0; - return true; - } - - bool identity (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - unsigned int i; - for (i = 0; i < length; i++) - (*this)[i] = i; - count = i; - return true; - } - - bool excludes (hb_codepoint_t id) const - { return (id < length) && ((*this)[id] == CFF_UNDEF_CODE); } - - bool includes (hb_codepoint_t id) const - { return !excludes (id); } - - unsigned int add (unsigned int i) - { - if ((*this)[i] == CFF_UNDEF_CODE) - (*this)[i] = count++; - return (*this)[i]; - } - - hb_codepoint_t get_count () const { return count; } - - protected: - hb_codepoint_t count; - - private: - typedef hb_vector_t SUPER; + objidx_t link; }; template struct FDArray : CFFIndexOf { - /* used by CFF1 */ - template + template bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const hb_vector_t &fontDicts, + Iterator it, OP_SERIALIZER& opszr) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (fontDicts.length); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (fontDicts.length + 1)))) - return_trace (false); - - /* serialize font dict offsets */ - unsigned int offset = 1; - unsigned int fid = 0; - for (; fid < fontDicts.length; fid++) - { - CFFIndexOf::set_offset_at (fid, offset); - offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr); - } - CFFIndexOf::set_offset_at (fid, offset); - /* serialize font dicts */ - for (unsigned int i = 0; i < fontDicts.length; i++) + /* serialize INDEX data */ + hb_vector_t sizes; + c->push (); + + it + | hb_map ([&] (const hb_pair_t &_) { FontDict *dict = c->start_embed (); - if (unlikely (!dict->serialize (c, fontDicts[i], opszr, fontDicts[i]))) - return_trace (false); - } - return_trace (true); - } - - /* used by CFF2 */ - template - bool serialize (hb_serialize_context_t *c, - unsigned int offSize_, - const hb_vector_t &fontDicts, - unsigned int fdCount, - const remap_t &fdmap, - OP_SERIALIZER& opszr, - const hb_vector_t &privateInfos) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (fdCount); - this->offSize.set (offSize_); - if (!unlikely (c->allocate_size (offSize_ * (fdCount + 1)))) - return_trace (false); - - /* serialize font dict offsets */ - unsigned int offset = 1; - unsigned int fid = 0; - for (unsigned i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) - { - CFFIndexOf::set_offset_at (fid++, offset); - offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); - } - CFFIndexOf::set_offset_at (fid, offset); + dict->serialize (c, _.first, opszr, _.second); + return c->head - (const char*)dict; + }) + | hb_sink (sizes) + ; + c->pop_pack (false); - /* serialize font dicts */ - for (unsigned int i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) - { - FontDict *dict = c->start_embed (); - if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]]))) - return_trace (false); - } - return_trace (true); - } - - /* in parallel to above */ - template - static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, - const hb_vector_t &fontDicts, - unsigned int fdCount, - const remap_t &fdmap, - OP_SERIALIZER& opszr) - { - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < fontDicts.len; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr); - - offSize_ = calcOffSize (dictsSize); - return CFFIndex::calculate_serialized_size (offSize_, fdCount, dictsSize); + /* serialize INDEX header */ + return_trace (CFFIndex::serialize_header (c, hb_iter (sizes))); } }; @@ -574,21 +481,20 @@ struct FDSelect0 { } hb_codepoint_t get_fd (hb_codepoint_t glyph) const - { - return (hb_codepoint_t)fds[glyph]; - } + { return (hb_codepoint_t) fds[glyph]; } unsigned int get_size (unsigned int num_glyphs) const { return HBUINT8::static_size * num_glyphs; } - HBUINT8 fds[VAR]; + HBUINT8 fds[HB_VAR_ARRAY]; - DEFINE_SIZE_MIN (1); + DEFINE_SIZE_MIN (0); }; template -struct FDSelect3_4_Range { - bool sanitize (hb_sanitize_context_t *c, const void */*nullptr*/, unsigned int fdcount) const +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const { TRACE_SANITIZE (this); return_trace (first < c->get_num_glyphs () && (fd < fdcount)); @@ -596,12 +502,13 @@ struct FDSelect3_4_Range { GID_TYPE first; FD_TYPE fd; - + public: DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size); }; template -struct FDSelect3_4 { +struct FDSelect3_4 +{ unsigned int get_size () const { return GID_TYPE::static_size * 2 + ranges.get_size (); } @@ -613,10 +520,8 @@ struct FDSelect3_4 { return_trace (false); for (unsigned int i = 1; i < nRanges (); i++) - { if (unlikely (ranges[i - 1].first >= ranges[i].first)) - return_trace (false); - } + return_trace (false); if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ()))) return_trace (false); @@ -631,13 +536,13 @@ struct FDSelect3_4 { if (glyph < ranges[i].first) break; - return (hb_codepoint_t)ranges[i - 1].fd; + return (hb_codepoint_t) ranges[i - 1].fd; } - GID_TYPE &nRanges () { return ranges.len; } - GID_TYPE nRanges () const { return ranges.len; } - GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } - const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } + GID_TYPE &nRanges () { return ranges.len; } + GID_TYPE nRanges () const { return ranges.len; } + GID_TYPE &sentinel () { return StructAfter (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter (ranges[nRanges () - 1]); } ArrayOf, GID_TYPE> ranges; /* GID_TYPE sentinel */ @@ -648,56 +553,60 @@ struct FDSelect3_4 { typedef FDSelect3_4 FDSelect3; typedef FDSelect3_4_Range FDSelect3_Range; -struct FDSelect { - bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const - { - TRACE_SANITIZE (this); - - return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) && - (format == 0)? - u.format0.sanitize (c, fdcount): - u.format3.sanitize (c, fdcount))); - } - +struct FDSelect +{ bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); FDSelect *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } - unsigned int calculate_serialized_size (unsigned int num_glyphs) const - { return get_size (num_glyphs); } - unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = format.static_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else - size += u.format3.get_size (); - return size; + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + default:return 0; + } } hb_codepoint_t get_fd (hb_codepoint_t glyph) const { - if (this == &Null(FDSelect)) - return 0; - if (format == 0) - return u.format0.get_fd (glyph); - else - return u.format3.get_fd (glyph); + if (this == &Null (FDSelect)) return 0; + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + default:return_trace (false); + } } HBUINT8 format; union { - FDSelect0 format0; - FDSelect3 format3; + FDSelect0 format0; + FDSelect3 format3; } u; - + public: DEFINE_SIZE_MIN (1); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh new file mode 100644 index 0000000000000..65d56ae18b5d8 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-std-str.hh @@ -0,0 +1,425 @@ +/* + * Copyright © 2019 Adobe, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_CFF1_STD_STR_HH +#if 0 /* Make checks happy. */ +#define HB_OT_CFF1_STD_STR_HH +#include "hb.hh" +#endif + +_S(".notdef") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quoteright") +_S("parenleft") +_S("parenright") +_S("asterisk") +_S("plus") +_S("comma") +_S("hyphen") +_S("period") +_S("slash") +_S("zero") +_S("one") +_S("two") +_S("three") +_S("four") +_S("five") +_S("six") +_S("seven") +_S("eight") +_S("nine") +_S("colon") +_S("semicolon") +_S("less") +_S("equal") +_S("greater") +_S("question") +_S("at") +_S("A") +_S("B") +_S("C") +_S("D") +_S("E") +_S("F") +_S("G") +_S("H") +_S("I") +_S("J") +_S("K") +_S("L") +_S("M") +_S("N") +_S("O") +_S("P") +_S("Q") +_S("R") +_S("S") +_S("T") +_S("U") +_S("V") +_S("W") +_S("X") +_S("Y") +_S("Z") +_S("bracketleft") +_S("backslash") +_S("bracketright") +_S("asciicircum") +_S("underscore") +_S("quoteleft") +_S("a") +_S("b") +_S("c") +_S("d") +_S("e") +_S("f") +_S("g") +_S("h") +_S("i") +_S("j") +_S("k") +_S("l") +_S("m") +_S("n") +_S("o") +_S("p") +_S("q") +_S("r") +_S("s") +_S("t") +_S("u") +_S("v") +_S("w") +_S("x") +_S("y") +_S("z") +_S("braceleft") +_S("bar") +_S("braceright") +_S("asciitilde") +_S("exclamdown") +_S("cent") +_S("sterling") +_S("fraction") +_S("yen") +_S("florin") +_S("section") +_S("currency") +_S("quotesingle") +_S("quotedblleft") +_S("guillemotleft") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("endash") +_S("dagger") +_S("daggerdbl") +_S("periodcentered") +_S("paragraph") +_S("bullet") +_S("quotesinglbase") +_S("quotedblbase") +_S("quotedblright") +_S("guillemotright") +_S("ellipsis") +_S("perthousand") +_S("questiondown") +_S("grave") +_S("acute") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("dieresis") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("emdash") +_S("AE") +_S("ordfeminine") +_S("Lslash") +_S("Oslash") +_S("OE") +_S("ordmasculine") +_S("ae") +_S("dotlessi") +_S("lslash") +_S("oslash") +_S("oe") +_S("germandbls") +_S("onesuperior") +_S("logicalnot") +_S("mu") +_S("trademark") +_S("Eth") +_S("onehalf") +_S("plusminus") +_S("Thorn") +_S("onequarter") +_S("divide") +_S("brokenbar") +_S("degree") +_S("thorn") +_S("threequarters") +_S("twosuperior") +_S("registered") +_S("minus") +_S("eth") +_S("multiply") +_S("threesuperior") +_S("copyright") +_S("Aacute") +_S("Acircumflex") +_S("Adieresis") +_S("Agrave") +_S("Aring") +_S("Atilde") +_S("Ccedilla") +_S("Eacute") +_S("Ecircumflex") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Ntilde") +_S("Oacute") +_S("Ocircumflex") +_S("Odieresis") +_S("Ograve") +_S("Otilde") +_S("Scaron") +_S("Uacute") +_S("Ucircumflex") +_S("Udieresis") +_S("Ugrave") +_S("Yacute") +_S("Ydieresis") +_S("Zcaron") +_S("aacute") +_S("acircumflex") +_S("adieresis") +_S("agrave") +_S("aring") +_S("atilde") +_S("ccedilla") +_S("eacute") +_S("ecircumflex") +_S("edieresis") +_S("egrave") +_S("iacute") +_S("icircumflex") +_S("idieresis") +_S("igrave") +_S("ntilde") +_S("oacute") +_S("ocircumflex") +_S("odieresis") +_S("ograve") +_S("otilde") +_S("scaron") +_S("uacute") +_S("ucircumflex") +_S("udieresis") +_S("ugrave") +_S("yacute") +_S("ydieresis") +_S("zcaron") +_S("exclamsmall") +_S("Hungarumlautsmall") +_S("dollaroldstyle") +_S("dollarsuperior") +_S("ampersandsmall") +_S("Acutesmall") +_S("parenleftsuperior") +_S("parenrightsuperior") +_S("twodotenleader") +_S("onedotenleader") +_S("zerooldstyle") +_S("oneoldstyle") +_S("twooldstyle") +_S("threeoldstyle") +_S("fouroldstyle") +_S("fiveoldstyle") +_S("sixoldstyle") +_S("sevenoldstyle") +_S("eightoldstyle") +_S("nineoldstyle") +_S("commasuperior") +_S("threequartersemdash") +_S("periodsuperior") +_S("questionsmall") +_S("asuperior") +_S("bsuperior") +_S("centsuperior") +_S("dsuperior") +_S("esuperior") +_S("isuperior") +_S("lsuperior") +_S("msuperior") +_S("nsuperior") +_S("osuperior") +_S("rsuperior") +_S("ssuperior") +_S("tsuperior") +_S("ff") +_S("ffi") +_S("ffl") +_S("parenleftinferior") +_S("parenrightinferior") +_S("Circumflexsmall") +_S("hyphensuperior") +_S("Gravesmall") +_S("Asmall") +_S("Bsmall") +_S("Csmall") +_S("Dsmall") +_S("Esmall") +_S("Fsmall") +_S("Gsmall") +_S("Hsmall") +_S("Ismall") +_S("Jsmall") +_S("Ksmall") +_S("Lsmall") +_S("Msmall") +_S("Nsmall") +_S("Osmall") +_S("Psmall") +_S("Qsmall") +_S("Rsmall") +_S("Ssmall") +_S("Tsmall") +_S("Usmall") +_S("Vsmall") +_S("Wsmall") +_S("Xsmall") +_S("Ysmall") +_S("Zsmall") +_S("colonmonetary") +_S("onefitted") +_S("rupiah") +_S("Tildesmall") +_S("exclamdownsmall") +_S("centoldstyle") +_S("Lslashsmall") +_S("Scaronsmall") +_S("Zcaronsmall") +_S("Dieresissmall") +_S("Brevesmall") +_S("Caronsmall") +_S("Dotaccentsmall") +_S("Macronsmall") +_S("figuredash") +_S("hypheninferior") +_S("Ogoneksmall") +_S("Ringsmall") +_S("Cedillasmall") +_S("questiondownsmall") +_S("oneeighth") +_S("threeeighths") +_S("fiveeighths") +_S("seveneighths") +_S("onethird") +_S("twothirds") +_S("zerosuperior") +_S("foursuperior") +_S("fivesuperior") +_S("sixsuperior") +_S("sevensuperior") +_S("eightsuperior") +_S("ninesuperior") +_S("zeroinferior") +_S("oneinferior") +_S("twoinferior") +_S("threeinferior") +_S("fourinferior") +_S("fiveinferior") +_S("sixinferior") +_S("seveninferior") +_S("eightinferior") +_S("nineinferior") +_S("centinferior") +_S("dollarinferior") +_S("periodinferior") +_S("commainferior") +_S("Agravesmall") +_S("Aacutesmall") +_S("Acircumflexsmall") +_S("Atildesmall") +_S("Adieresissmall") +_S("Aringsmall") +_S("AEsmall") +_S("Ccedillasmall") +_S("Egravesmall") +_S("Eacutesmall") +_S("Ecircumflexsmall") +_S("Edieresissmall") +_S("Igravesmall") +_S("Iacutesmall") +_S("Icircumflexsmall") +_S("Idieresissmall") +_S("Ethsmall") +_S("Ntildesmall") +_S("Ogravesmall") +_S("Oacutesmall") +_S("Ocircumflexsmall") +_S("Otildesmall") +_S("Odieresissmall") +_S("OEsmall") +_S("Oslashsmall") +_S("Ugravesmall") +_S("Uacutesmall") +_S("Ucircumflexsmall") +_S("Udieresissmall") +_S("Yacutesmall") +_S("Thornsmall") +_S("Ydieresissmall") +_S("001.000") +_S("001.001") +_S("001.002") +_S("001.003") +_S("Black") +_S("Bold") +_S("Book") +_S("Light") +_S("Medium") +_S("Regular") +_S("Roman") +_S("Semibold") + +#endif /* HB_OT_CFF1_STD_STR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc index a5ee1f31f6f93..24287364ba2c8 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.cc @@ -24,11 +24,29 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_CFF + +#include "hb-draw.hh" +#include "hb-algs.hh" #include "hb-ot-cff1-table.hh" #include "hb-cff1-interp-cs.hh" using namespace CFF; +struct sid_to_gid_t +{ + uint16_t sid; + uint8_t gid; + + int cmp (uint16_t a) const + { + if (a == sid) return 0; + return (a < sid) ? -1 : 1; + } +}; + /* SID to code */ static const uint8_t standard_encoding_to_code [] = { @@ -100,6 +118,80 @@ static const uint16_t expert_subset_charset_to_sid [] = 340, 341, 342, 343, 344, 345, 346 }; +/* SID to glyph ID */ +static const sid_to_gid_t expert_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 12 }, { 14, 13 }, { 15, 14 }, + { 27, 26 }, { 28, 27 }, { 99, 15 }, { 109, 46 }, + { 110, 47 }, { 150, 111 }, { 155, 101 }, { 158, 100 }, + { 163, 102 }, { 164, 112 }, { 169, 113 }, { 229, 2 }, + { 230, 3 }, { 231, 4 }, { 232, 5 }, { 233, 6 }, + { 234, 7 }, { 235, 8 }, { 236, 9 }, { 237, 10 }, + { 238, 11 }, { 239, 16 }, { 240, 17 }, { 241, 18 }, + { 242, 19 }, { 243, 20 }, { 244, 21 }, { 245, 22 }, + { 246, 23 }, { 247, 24 }, { 248, 25 }, { 249, 28 }, + { 250, 29 }, { 251, 30 }, { 252, 31 }, { 253, 32 }, + { 254, 33 }, { 255, 34 }, { 256, 35 }, { 257, 36 }, + { 258, 37 }, { 259, 38 }, { 260, 39 }, { 261, 40 }, + { 262, 41 }, { 263, 42 }, { 264, 43 }, { 265, 44 }, + { 266, 45 }, { 267, 48 }, { 268, 49 }, { 269, 50 }, + { 270, 51 }, { 271, 52 }, { 272, 53 }, { 273, 54 }, + { 274, 55 }, { 275, 56 }, { 276, 57 }, { 277, 58 }, + { 278, 59 }, { 279, 60 }, { 280, 61 }, { 281, 62 }, + { 282, 63 }, { 283, 64 }, { 284, 65 }, { 285, 66 }, + { 286, 67 }, { 287, 68 }, { 288, 69 }, { 289, 70 }, + { 290, 71 }, { 291, 72 }, { 292, 73 }, { 293, 74 }, + { 294, 75 }, { 295, 76 }, { 296, 77 }, { 297, 78 }, + { 298, 79 }, { 299, 80 }, { 300, 81 }, { 301, 82 }, + { 302, 83 }, { 303, 84 }, { 304, 85 }, { 305, 86 }, + { 306, 87 }, { 307, 88 }, { 308, 89 }, { 309, 90 }, + { 310, 91 }, { 311, 92 }, { 312, 93 }, { 313, 94 }, + { 314, 95 }, { 315, 96 }, { 316, 97 }, { 317, 98 }, + { 318, 99 }, { 319, 103 }, { 320, 104 }, { 321, 105 }, + { 322, 106 }, { 323, 107 }, { 324, 108 }, { 325, 109 }, + { 326, 110 }, { 327, 114 }, { 328, 115 }, { 329, 116 }, + { 330, 117 }, { 331, 118 }, { 332, 119 }, { 333, 120 }, + { 334, 121 }, { 335, 122 }, { 336, 123 }, { 337, 124 }, + { 338, 125 }, { 339, 126 }, { 340, 127 }, { 341, 128 }, + { 342, 129 }, { 343, 130 }, { 344, 131 }, { 345, 132 }, + { 346, 133 }, { 347, 134 }, { 348, 135 }, { 349, 136 }, + { 350, 137 }, { 351, 138 }, { 352, 139 }, { 353, 140 }, + { 354, 141 }, { 355, 142 }, { 356, 143 }, { 357, 144 }, + { 358, 145 }, { 359, 146 }, { 360, 147 }, { 361, 148 }, + { 362, 149 }, { 363, 150 }, { 364, 151 }, { 365, 152 }, + { 366, 153 }, { 367, 154 }, { 368, 155 }, { 369, 156 }, + { 370, 157 }, { 371, 158 }, { 372, 159 }, { 373, 160 }, + { 374, 161 }, { 375, 162 }, { 376, 163 }, { 377, 164 }, + { 378, 165 } +}; + +/* SID to glyph ID */ +static const sid_to_gid_t expert_subset_charset_sid_to_gid [] = +{ + { 1, 1 }, { 13, 8 }, { 14, 9 }, { 15, 10 }, + { 27, 22 }, { 28, 23 }, { 99, 11 }, { 109, 41 }, + { 110, 42 }, { 150, 64 }, { 155, 55 }, { 158, 54 }, + { 163, 56 }, { 164, 65 }, { 169, 66 }, { 231, 2 }, + { 232, 3 }, { 235, 4 }, { 236, 5 }, { 237, 6 }, + { 238, 7 }, { 239, 12 }, { 240, 13 }, { 241, 14 }, + { 242, 15 }, { 243, 16 }, { 244, 17 }, { 245, 18 }, + { 246, 19 }, { 247, 20 }, { 248, 21 }, { 249, 24 }, + { 250, 25 }, { 251, 26 }, { 253, 27 }, { 254, 28 }, + { 255, 29 }, { 256, 30 }, { 257, 31 }, { 258, 32 }, + { 259, 33 }, { 260, 34 }, { 261, 35 }, { 262, 36 }, + { 263, 37 }, { 264, 38 }, { 265, 39 }, { 266, 40 }, + { 267, 43 }, { 268, 44 }, { 269, 45 }, { 270, 46 }, + { 272, 47 }, { 300, 48 }, { 301, 49 }, { 302, 50 }, + { 305, 51 }, { 314, 52 }, { 315, 53 }, { 320, 57 }, + { 321, 58 }, { 322, 59 }, { 323, 60 }, { 324, 61 }, + { 325, 62 }, { 326, 63 }, { 327, 67 }, { 328, 68 }, + { 329, 69 }, { 330, 70 }, { 331, 71 }, { 332, 72 }, + { 333, 73 }, { 334, 74 }, { 335, 75 }, { 336, 76 }, + { 337, 77 }, { 338, 78 }, { 339, 79 }, { 340, 80 }, + { 341, 81 }, { 342, 82 }, { 343, 83 }, { 344, 84 }, + { 345, 85 }, { 346, 86 } +}; + /* code to SID */ static const uint8_t standard_encoding_to_sid [] = { @@ -153,6 +245,18 @@ hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_sid (hb_codepoint_t gl return 0; } +hb_codepoint_t OT::cff1::lookup_expert_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + +hb_codepoint_t OT::cff1::lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid) +{ + const auto *pair = hb_sorted_array (expert_subset_charset_sid_to_gid).bsearch (sid); + return pair ? pair->gid : 0; +} + hb_codepoint_t OT::cff1::lookup_standard_encoding_for_sid (hb_codepoint_t code) { if (code < ARRAY_LENGTH (standard_encoding_to_sid)) @@ -165,8 +269,8 @@ struct bounds_t { void init () { - min.set_int (0x7FFFFFFF, 0x7FFFFFFF); - max.set_int (-0x80000000, -0x80000000); + min.set_int (INT_MAX, INT_MAX); + max.set_int (INT_MIN, INT_MIN); } void update (const point_t &pt) @@ -199,14 +303,13 @@ struct bounds_t } } - bool empty () const - { return (min.x >= max.x) || (min.y >= max.y); } + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } point_t min; point_t max; }; -struct extents_param_t +struct cff1_extents_param_t { void init (const OT::cff1::accelerator_t *_cff) { @@ -215,25 +318,25 @@ struct extents_param_t bounds.init (); } - void start_path () { path_open = true; } - void end_path () { path_open = false; } + void start_path () { path_open = true; } + void end_path () { path_open = false; } bool is_path_open () const { return path_open; } - bool path_open; - bounds_t bounds; + bool path_open; + bounds_t bounds; const OT::cff1::accelerator_t *cff; }; -struct cff1_path_procs_extents_t : path_procs_t +struct cff1_path_procs_extents_t : path_procs_t { - static void moveto (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt) + static void moveto (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt) { param.end_path (); env.moveto (pt); } - static void line (cff1_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1) + static void line (cff1_cs_interp_env_t &env, cff1_extents_param_t& param, const point_t &pt1) { if (!param.is_path_open ()) { @@ -244,7 +347,7 @@ struct cff1_path_procs_extents_t : path_procs_t +struct cff1_cs_opset_extents_t : cff1_cs_opset_t { - static void process_seac (cff1_cs_interp_env_t &env, extents_param_t& param) + static void process_seac (cff1_cs_interp_env_t &env, cff1_extents_param_t& param) { unsigned int n = env.argStack.get_count (); point_t delta; @@ -292,20 +395,25 @@ bool _get_bounds (const OT::cff1::accelerator_t *cff, hb_codepoint_t glyph, boun if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; unsigned int fd = cff->fdSelect->get_fd (glyph); - cff1_cs_interpreter_t interp; + cff1_cs_interpreter_t interp; const byte_str_t str = (*cff->charStrings)[glyph]; interp.env.init (str, *cff, fd); interp.env.set_in_seac (in_seac); - extents_param_t param; + cff1_extents_param_t param; param.init (cff); if (unlikely (!interp.interpret (param))) return false; bounds = param.bounds; return true; } -bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const +bool OT::cff1::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { - bounds_t bounds; +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + bounds_t bounds; if (!_get_bounds (this, glyph, bounds)) return false; @@ -317,8 +425,8 @@ bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extent } else { - extents->x_bearing = (int32_t)bounds.min.x.floor (); - extents->width = (int32_t)bounds.max.x.ceil () - extents->x_bearing; + extents->x_bearing = font->em_scalef_x (bounds.min.x.to_real ()); + extents->width = font->em_scalef_x (bounds.max.x.to_real () - bounds.min.x.to_real ()); } if (bounds.min.y >= bounds.max.y) { @@ -327,13 +435,137 @@ bool OT::cff1::accelerator_t::get_extents (hb_codepoint_t glyph, hb_glyph_extent } else { - extents->y_bearing = (int32_t)bounds.max.y.ceil (); - extents->height = (int32_t)bounds.min.y.floor () - extents->y_bearing; + extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ()); + extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ()); } return true; } +#ifdef HB_EXPERIMENTAL_API +struct cff1_path_param_t +{ + cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_, + draw_helper_t &draw_helper_, point_t *delta_) + { + draw_helper = &draw_helper_; + cff = cff_; + font = font_; + delta = delta_; + } + + void move_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void line_to (const point_t &p) + { + point_t point = p; + if (delta) point.move (*delta); + draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ())); + } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + point_t point1 = p1, point2 = p2, point3 = p3; + if (delta) + { + point1.move (*delta); + point2.move (*delta); + point3.move (*delta); + } + draw_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()), + font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()), + font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ())); + } + + void end_path () { draw_helper->end_path (); } + + hb_font_t *font; + draw_helper_t *draw_helper; + point_t *delta; + + const OT::cff1::accelerator_t *cff; +}; + +struct cff1_path_procs_path_t : path_procs_t +{ + static void moveto (cff1_cs_interp_env_t &env, cff1_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); + } + + static void curve (cff1_cs_interp_env_t &env, cff1_path_param_t ¶m, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac = false, point_t *delta = nullptr); + +struct cff1_cs_opset_path_t : cff1_cs_opset_t +{ + static void process_seac (cff1_cs_interp_env_t &env, cff1_path_param_t& param) + { + /* End previous path */ + param.end_path (); + + unsigned int n = env.argStack.get_count (); + point_t delta; + delta.x = env.argStack[n-4]; + delta.y = env.argStack[n-3]; + hb_codepoint_t base = param.cff->std_code_to_glyph (env.argStack[n-2].to_int ()); + hb_codepoint_t accent = param.cff->std_code_to_glyph (env.argStack[n-1].to_int ()); + + if (unlikely (!(!env.in_seac && base && accent + && _get_path (param.cff, param.font, base, *param.draw_helper, true) + && _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta)))) + env.set_error (); + } +}; + +bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph, + draw_helper_t &draw_helper, bool in_seac, point_t *delta) +{ + if (unlikely (!cff->is_valid () || (glyph >= cff->num_glyphs))) return false; + + unsigned int fd = cff->fdSelect->get_fd (glyph); + cff1_cs_interpreter_t interp; + const byte_str_t str = (*cff->charStrings)[glyph]; + interp.env.init (str, *cff, fd); + interp.env.set_in_seac (in_seac); + cff1_path_param_t param (cff, font, draw_helper, delta); + if (unlikely (!interp.interpret (param))) return false; + + /* Let's end the path specially since it is called inside seac also */ + param.end_path (); + + return true; +} + +bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + return _get_path (this, font, glyph, draw_helper); +} +#endif + struct get_seac_param_t { void init (const OT::cff1::accelerator_t *_cff) @@ -383,3 +615,6 @@ bool OT::cff1::accelerator_t::get_seac_components (hb_codepoint_t glyph, hb_code } return false; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh index 73258e79007c2..6768e4ea10772 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff1-table.hh @@ -27,15 +27,21 @@ #ifndef HB_OT_CFF1_TABLE_HH #define HB_OT_CFF1_TABLE_HH -#include "hb-ot-head-table.hh" #include "hb-ot-cff-common.hh" #include "hb-subset-cff1.hh" +#include "hb-draw.hh" + +#define HB_STRING_ARRAY_NAME cff1_std_strings +#define HB_STRING_ARRAY_LIST "hb-ot-cff1-std-str.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME namespace CFF { /* * CFF -- Compact Font Format (CFF) - * http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf + * https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf */ #define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ') @@ -49,7 +55,6 @@ template struct CFF1IndexOf : CFFIndexOf {}; typedef CFFIndex CFF1Index; typedef CFF1Index CFF1CharStrings; -typedef FDArray CFF1FDArray; typedef Subrs CFF1Subrs; struct CFF1FDSelect : FDSelect {}; @@ -59,14 +64,14 @@ struct Encoding0 { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c)); + return_trace (codes.sanitize (c)); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; - if (glyph < nCodes) + if (glyph < nCodes ()) { return (hb_codepoint_t)codes[glyph]; } @@ -74,13 +79,12 @@ struct Encoding0 { return CFF_UNDEF_CODE; } - unsigned int get_size () const - { return HBUINT8::static_size * (nCodes + 1); } + HBUINT8 &nCodes () { return codes.len; } + HBUINT8 nCodes () const { return codes.len; } - HBUINT8 nCodes; - HBUINT8 codes[VAR]; + ArrayOf codes; - DEFINE_SIZE_ARRAY(1, codes); + DEFINE_SIZE_ARRAY_SIZED (1, codes); }; struct Encoding1_Range { @@ -97,34 +101,34 @@ struct Encoding1_Range { }; struct Encoding1 { - unsigned int get_size () const - { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; } - bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c))); + return_trace (ranges.sanitize (c)); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; - for (unsigned int i = 0; i < nRanges; i++) + for (unsigned int i = 0; i < nRanges (); i++) { if (glyph <= ranges[i].nLeft) { - return (hb_codepoint_t)ranges[i].first + glyph; + hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph; + return (likely (code < 0x100) ? code: CFF_UNDEF_CODE); } glyph -= (ranges[i].nLeft + 1); } return CFF_UNDEF_CODE; } - HBUINT8 nRanges; - Encoding1_Range ranges[VAR]; + HBUINT8 &nRanges () { return ranges.len; } + HBUINT8 nRanges () const { return ranges.len; } + + ArrayOf ranges; - DEFINE_SIZE_ARRAY (1, ranges); + DEFINE_SIZE_ARRAY_SIZED (1, ranges); }; struct SuppEncoding { @@ -144,47 +148,33 @@ struct CFF1SuppEncData { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c))); + return_trace (supps.sanitize (c)); } void get_codes (hb_codepoint_t sid, hb_vector_t &codes) const { - for (unsigned int i = 0; i < nSups; i++) + for (unsigned int i = 0; i < nSups (); i++) if (sid == supps[i].glyph) codes.push (supps[i].code); } - unsigned int get_size () const - { return HBUINT8::static_size + SuppEncoding::static_size * nSups; } + HBUINT8 &nSups () { return supps.len; } + HBUINT8 nSups () const { return supps.len; } - HBUINT8 nSups; - SuppEncoding supps[VAR]; + ArrayOf supps; - DEFINE_SIZE_ARRAY (1, supps); + DEFINE_SIZE_ARRAY_SIZED (1, supps); }; -struct Encoding { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - - if (unlikely (!c->check_struct (this))) - return_trace (false); - unsigned int fmt = format & 0x7F; - if (unlikely (fmt > 1)) - return_trace (false); - if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c)))) - return_trace (false); - return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c)); - } - +struct Encoding +{ /* serialize a fullset Encoding */ bool serialize (hb_serialize_context_t *c, const Encoding &src) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (); Encoding *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -198,72 +188,66 @@ struct Encoding { { TRACE_SERIALIZE (this); Encoding *dest = c->extend_min (*this); - if (unlikely (dest == nullptr)) return_trace (false); - dest->format.set (format | ((supp_codes.length > 0)? 0x80: 0)); - if (format == 0) + if (unlikely (!dest)) return_trace (false); + dest->format = format | ((supp_codes.length > 0) ? 0x80 : 0); + switch (format) { + case 0: { Encoding0 *fmt0 = c->allocate_size (Encoding0::min_size + HBUINT8::static_size * enc_count); - if (unlikely (fmt0 == nullptr)) return_trace (false); - fmt0->nCodes.set (enc_count); + if (unlikely (!fmt0)) return_trace (false); + fmt0->nCodes () = enc_count; unsigned int glyph = 0; for (unsigned int i = 0; i < code_ranges.length; i++) { hb_codepoint_t code = code_ranges[i].code; for (int left = (int)code_ranges[i].glyph; left >= 0; left--) - fmt0->codes[glyph++].set (code++); + fmt0->codes[glyph++] = code++; if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) return_trace (false); } } - else + break; + + case 1: { Encoding1 *fmt1 = c->allocate_size (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.length); - if (unlikely (fmt1 == nullptr)) return_trace (false); - fmt1->nRanges.set (code_ranges.length); + if (unlikely (!fmt1)) return_trace (false); + fmt1->nRanges () = code_ranges.length; for (unsigned int i = 0; i < code_ranges.length; i++) { if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) return_trace (false); - fmt1->ranges[i].first.set (code_ranges[i].code); - fmt1->ranges[i].nLeft.set (code_ranges[i].glyph); + fmt1->ranges[i].first = code_ranges[i].code; + fmt1->ranges[i].nLeft = code_ranges[i].glyph; } } - if (supp_codes.length > 0) + break; + + } + + if (supp_codes.length) { CFF1SuppEncData *suppData = c->allocate_size (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.length); - if (unlikely (suppData == nullptr)) return_trace (false); - suppData->nSups.set (supp_codes.length); + if (unlikely (!suppData)) return_trace (false); + suppData->nSups () = supp_codes.length; for (unsigned int i = 0; i < supp_codes.length; i++) { - suppData->supps[i].code.set (supp_codes[i].code); - suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */ + suppData->supps[i].code = supp_codes[i].code; + suppData->supps[i].glyph = supp_codes[i].glyph; /* actually SID */ } } - return_trace (true); - } - /* parallel to above: calculate the size of a subset Encoding */ - static unsigned int calculate_serialized_size (uint8_t format, - unsigned int enc_count, - unsigned int supp_count) - { - unsigned int size = min_size; - if (format == 0) - size += Encoding0::min_size + HBUINT8::static_size * enc_count; - else - size += Encoding1::min_size + Encoding1_Range::static_size * enc_count; - if (supp_count > 0) - size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count; - return size; + return_trace (true); } unsigned int get_size () const { unsigned int size = min_size; - if (table_format () == 0) - size += u.format0.get_size (); - else - size += u.format1.get_size (); + switch (table_format ()) + { + case 0: size += u.format0.get_size (); break; + case 1: size += u.format1.get_size (); break; + } if (has_supplement ()) size += suppEncData ().get_size (); return size; @@ -271,14 +255,16 @@ struct Encoding { hb_codepoint_t get_code (hb_codepoint_t glyph) const { - if (table_format () == 0) - return u.format0.get_code (glyph); - else - return u.format1.get_code (glyph); + switch (table_format ()) + { + case 0: return u.format0.get_code (glyph); + case 1: return u.format1.get_code (glyph); + default:return 0; + } } - uint8_t table_format () const { return (format & 0x7F); } - bool has_supplement () const { return (format & 0x80) != 0; } + uint8_t table_format () const { return format & 0x7F; } + bool has_supplement () const { return format & 0x80; } void get_supplement_codes (hb_codepoint_t sid, hb_vector_t &codes) const { @@ -287,21 +273,37 @@ struct Encoding { suppEncData().get_codes (sid, codes); } + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (table_format ()) + { + case 0: if (unlikely (!u.format0.sanitize (c))) { return_trace (false); } break; + case 1: if (unlikely (!u.format1.sanitize (c))) { return_trace (false); } break; + default:return_trace (false); + } + return_trace (likely (!has_supplement () || suppEncData ().sanitize (c))); + } + protected: const CFF1SuppEncData &suppEncData () const { - if ((format & 0x7F) == 0) - return StructAfter (u.format0.codes[u.format0.nCodes-1]); - else - return StructAfter (u.format1.ranges[u.format1.nRanges-1]); + switch (table_format ()) + { + case 0: return StructAfter (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter (u.format1.ranges[u.format1.nRanges ()-1]); + default:return Null (CFF1SuppEncData); + } } public: HBUINT8 format; - union { - Encoding0 format0; - Encoding1 format1; + Encoding0 format0; + Encoding1 format1; } u; /* CFF1SuppEncData suppEncData; */ @@ -343,7 +345,7 @@ struct Charset0 { return HBUINT16::static_size * (num_glyphs - 1); } - HBUINT16 sids[VAR]; + HBUINT16 sids[HB_VAR_ARRAY]; DEFINE_SIZE_ARRAY(0, sids); }; @@ -425,7 +427,7 @@ struct Charset1_2 { return size; } - Charset_Range ranges[VAR]; + Charset_Range ranges[HB_VAR_ARRAY]; DEFINE_SIZE_ARRAY (0, ranges); }; @@ -435,30 +437,15 @@ typedef Charset1_2 Charset2; typedef Charset_Range Charset1_Range; typedef Charset_Range Charset2_Range; -struct Charset { - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - - if (unlikely (!c->check_struct (this))) - return_trace (false); - if (format == 0) - return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); - else if (format == 1) - return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); - else if (likely (format == 2)) - return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); - else - return_trace (false); - } - +struct Charset +{ /* serialize a fullset Charset */ bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); Charset *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } @@ -471,93 +458,103 @@ struct Charset { { TRACE_SERIALIZE (this); Charset *dest = c->extend_min (*this); - if (unlikely (dest == nullptr)) return_trace (false); - dest->format.set (format); - if (format == 0) + if (unlikely (!dest)) return_trace (false); + dest->format = format; + switch (format) + { + case 0: { Charset0 *fmt0 = c->allocate_size (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); - if (unlikely (fmt0 == nullptr)) return_trace (false); + if (unlikely (!fmt0)) return_trace (false); unsigned int glyph = 0; for (unsigned int i = 0; i < sid_ranges.length; i++) { hb_codepoint_t sid = sid_ranges[i].code; for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) - fmt0->sids[glyph++].set (sid++); + fmt0->sids[glyph++] = sid++; } } - else if (format == 1) + break; + + case 1: { Charset1 *fmt1 = c->allocate_size (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length); - if (unlikely (fmt1 == nullptr)) return_trace (false); + if (unlikely (!fmt1)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.length; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) return_trace (false); - fmt1->ranges[i].first.set (sid_ranges[i].code); - fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph); + fmt1->ranges[i].first = sid_ranges[i].code; + fmt1->ranges[i].nLeft = sid_ranges[i].glyph; } } - else /* format 2 */ + break; + + case 2: { Charset2 *fmt2 = c->allocate_size (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length); - if (unlikely (fmt2 == nullptr)) return_trace (false); + if (unlikely (!fmt2)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.length; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) return_trace (false); - fmt2->ranges[i].first.set (sid_ranges[i].code); - fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph); + fmt2->ranges[i].first = sid_ranges[i].code; + fmt2->ranges[i].nLeft = sid_ranges[i].glyph; } + } + break; + } return_trace (true); } - /* parallel to above: calculate the size of a subset Charset */ - static unsigned int calculate_serialized_size ( - uint8_t format, - unsigned int count) + unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = min_size; - if (format == 0) - size += Charset0::min_size + HBUINT16::static_size * (count - 1); - else if (format == 1) - size += Charset1::min_size + Charset1_Range::static_size * count; - else - size += Charset2::min_size + Charset2_Range::static_size * count; - - return size; + switch (format) + { + case 0: return min_size + u.format0.get_size (num_glyphs); + case 1: return min_size + u.format1.get_size (num_glyphs); + case 2: return min_size + u.format2.get_size (num_glyphs); + default:return 0; + } } - unsigned int get_size (unsigned int num_glyphs) const + hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs) const { - unsigned int size = min_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else if (format == 1) - size += u.format1.get_size (num_glyphs); - else - size += u.format2.get_size (num_glyphs); - return size; + if (unlikely (glyph >= num_glyphs)) return 0; + switch (format) + { + case 0: return u.format0.get_sid (glyph); + case 1: return u.format1.get_sid (glyph); + case 2: return u.format2.get_sid (glyph); + default:return 0; + } } - hb_codepoint_t get_sid (hb_codepoint_t glyph) const + hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const { - if (format == 0) - return u.format0.get_sid (glyph); - else if (format == 1) - return u.format1.get_sid (glyph); - else - return u.format2.get_sid (glyph); + switch (format) + { + case 0: return u.format0.get_glyph (sid, num_glyphs); + case 1: return u.format1.get_glyph (sid, num_glyphs); + case 2: return u.format2.get_glyph (sid, num_glyphs); + default:return 0; + } } - hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const + bool sanitize (hb_sanitize_context_t *c) const { - if (format == 0) - return u.format0.get_glyph (sid, num_glyphs); - else if (format == 1) - return u.format1.get_glyph (sid, num_glyphs); - else - return u.format2.get_glyph (sid, num_glyphs); + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ())); + case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ())); + case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); + default:return_trace (false); + } } HBUINT8 format; @@ -573,48 +570,32 @@ struct Charset { struct CFF1StringIndex : CFF1Index { bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, - unsigned int offSize_, const remap_t &sidmap) + const hb_inc_bimap_t &sidmap) { TRACE_SERIALIZE (this); - if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) + if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0))) { - if (!unlikely (c->extend_min (this->count))) + if (unlikely (!c->extend_min (this->count))) return_trace (false); - count.set (0); + count = 0; return_trace (true); } byte_str_array_t bytesArray; bytesArray.init (); - if (!bytesArray.resize (sidmap.get_count ())) + if (!bytesArray.resize (sidmap.get_population ())) return_trace (false); for (unsigned int i = 0; i < strings.count; i++) { hb_codepoint_t j = sidmap[i]; - if (j != CFF_UNDEF_CODE) + if (j != HB_MAP_VALUE_INVALID) bytesArray[j] = strings[i]; } - bool result = CFF1Index::serialize (c, offSize_, bytesArray); + bool result = CFF1Index::serialize (c, bytesArray); bytesArray.fini (); return_trace (result); } - - /* in parallel to above */ - unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const remap_t &sidmap) const - { - offSize = 0; - if ((count == 0) || (sidmap.get_count () == 0)) - return count.static_size; - - unsigned int dataSize = 0; - for (unsigned int i = 0; i < count; i++) - if (sidmap[i] != CFF_UNDEF_CODE) - dataSize += length_at (i); - - offSize = calcOffSize(dataSize); - return CFF1Index::calculate_serialized_size (offSize, sidmap.get_count (), dataSize); - } }; struct cff1_top_dict_interp_env_t : num_interp_env_t @@ -717,7 +698,7 @@ struct cff1_top_dict_values_t : top_dict_values_t unsigned int EncodingOffset; unsigned int CharsetOffset; unsigned int FDSelectOffset; - table_info_t privateDictInfo; + table_info_t privateDictInfo; }; struct cff1_top_dict_opset_t : top_dict_opset_t @@ -859,21 +840,10 @@ struct cff1_private_dict_values_base_t : dict_values_t { dict_values_t::init (); subrsOffset = 0; - localSubrs = &Null(CFF1Subrs); + localSubrs = &Null (CFF1Subrs); } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < dict_values_t::get_count; i++) - if (dict_values_t::get_value (i).op == OpCode_Subrs) - size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); - else - size += dict_values_t::get_value (i).str.length; - return size; - } - unsigned int subrsOffset; const CFF1Subrs *localSubrs; }; @@ -976,6 +946,37 @@ typedef dict_interpreter_t cff1 typedef CFF1Index CFF1NameIndex; typedef CFF1IndexOf CFF1TopDictIndex; +struct cff1_font_dict_values_mod_t +{ + cff1_font_dict_values_mod_t() { init (); } + + void init () { init ( &Null (cff1_font_dict_values_t), CFF_UNDEF_SID ); } + + void init (const cff1_font_dict_values_t *base_, + unsigned int fontName_) + { + base = base_; + fontName = fontName_; + privateDictInfo.init (); + } + + unsigned get_count () const { return base->get_count (); } + + const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } + + const cff1_font_dict_values_t *base; + table_info_t privateDictInfo; + unsigned int fontName; +}; + +struct CFF1FDArray : FDArray +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + } /* namespace CFF */ namespace OT { @@ -1010,7 +1011,7 @@ struct cff1 const OT::cff1 *cff = this->blob->template as (); - if (cff == &Null(OT::cff1)) + if (cff == &Null (OT::cff1)) { fini (); return; } nameIndex = &cff->nameIndex (cff); @@ -1031,7 +1032,7 @@ struct cff1 } if (is_predef_charset ()) - charset = &Null(Charset); + charset = &Null (Charset); else { charset = &StructAtOffsetOrNull (cff, topDict.CharsetOffset); @@ -1043,16 +1044,30 @@ struct cff1 { fdArray = &StructAtOffsetOrNull (cff, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull (cff, topDict.FDSelectOffset); - if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) || - (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) { fini (); return; } fdCount = fdArray->count; } else { - fdArray = &Null(CFF1FDArray); - fdSelect = &Null(CFF1FDSelect); + fdArray = &Null (CFF1FDArray); + fdSelect = &Null (CFF1FDSelect); + } + + encoding = &Null (Encoding); + if (is_CID ()) + { + if (unlikely (charset == &Null (Charset))) { fini (); return; } + } + else + { + if (!is_predef_encoding ()) + { + encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + } } stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); @@ -1065,14 +1080,15 @@ struct cff1 charStrings = &StructAtOffsetOrNull (cff, topDict.charStringsOffset); - if ((charStrings == &Null(CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) { fini (); return; } num_glyphs = charStrings->count; if (num_glyphs != sc.get_num_glyphs ()) { fini (); return; } - privateDicts.resize (fdCount); + if (unlikely (!privateDicts.resize (fdCount))) + { fini (); return; } for (unsigned int i = 0; i < fdCount; i++) privateDicts[i].init (); @@ -1083,14 +1099,14 @@ struct cff1 { byte_str_t fontDictStr = (*fdArray)[i]; if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } - cff1_font_dict_values_t *font; + cff1_font_dict_values_t *font; cff1_font_dict_interpreter_t font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); - if (unlikely (font == &Crap(cff1_font_dict_values_t))) { fini (); return; } + if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; } font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } - PRIVDICTVAL *priv = &privateDicts[i]; + PRIVDICTVAL *priv = &privateDicts[i]; const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } dict_interpreter_t priv_interp; @@ -1099,15 +1115,15 @@ struct cff1 if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null(CFF1Subrs) && + if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } } else /* non-CID */ { - cff1_top_dict_values_t *font = &topDict; - PRIVDICTVAL *priv = &privateDicts[0]; + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; const byte_str_t privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } @@ -1117,7 +1133,7 @@ struct cff1 if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (&privDictStr, priv->subrsOffset); - if (priv->localSubrs != &Null(CFF1Subrs) && + if (priv->localSubrs != &Null (CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } @@ -1133,8 +1149,8 @@ struct cff1 blob = nullptr; } - bool is_valid () const { return blob != nullptr; } - bool is_CID () const { return topDict.is_CID (); } + bool is_valid () const { return blob; } + bool is_CID () const { return topDict.is_CID (); } bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } @@ -1144,144 +1160,232 @@ struct cff1 if (unlikely (sid == CFF_UNDEF_SID)) return 0; - if (charset != &Null(Charset)) + if (charset != &Null (Charset)) return charset->get_glyph (sid, num_glyphs); else if ((topDict.CharsetOffset == ISOAdobeCharset) && (code <= 228 /*zcaron*/)) return sid; return 0; } - protected: - hb_blob_t *blob; - hb_sanitize_context_t sc; - - public: - const Charset *charset; - const CFF1NameIndex *nameIndex; - const CFF1TopDictIndex *topDictIndex; - const CFF1StringIndex *stringIndex; - const CFF1Subrs *globalSubrs; - const CFF1CharStrings *charStrings; - const CFF1FDArray *fdArray; - const CFF1FDSelect *fdSelect; - unsigned int fdCount; - - cff1_top_dict_values_t topDict; - hb_vector_t fontDicts; - hb_vector_t privateDicts; - - unsigned int num_glyphs; - }; - - struct accelerator_t : accelerator_templ_t - { - HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; - HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; - }; + bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } - struct accelerator_subset_t : accelerator_templ_t - { - void init (hb_face_t *face) + hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const { - SUPER::init (face); - if (blob == nullptr) return; - - const OT::cff1 *cff = this->blob->as (); - encoding = &Null(Encoding); - if (is_CID ()) - { - if (unlikely (charset == &Null(Charset))) { fini (); return; } - } + if (encoding != &Null (Encoding)) + return encoding->get_code (glyph); else { - if (!is_predef_encoding ()) + hb_codepoint_t sid = glyph_to_sid (glyph); + if (sid == 0) return 0; + hb_codepoint_t code = 0; + switch (topDict.EncodingOffset) { - encoding = &StructAtOffsetOrNull (cff, topDict.EncodingOffset); - if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } + case StandardEncoding: + code = lookup_standard_encoding_for_code (sid); + break; + case ExpertEncoding: + code = lookup_expert_encoding_for_code (sid); + break; + default: + break; } + return code; } } - bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } - - hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const + hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const { - if (encoding != &Null(Encoding)) - return encoding->get_code (glyph); + if (charset != &Null (Charset)) + return charset->get_sid (glyph, num_glyphs); else { - hb_codepoint_t sid = glyph_to_sid (glyph); - if (sid == 0) return 0; - hb_codepoint_t code = 0; - switch (topDict.EncodingOffset) + hb_codepoint_t sid = 0; + switch (topDict.CharsetOffset) { - case StandardEncoding: - code = lookup_standard_encoding_for_code (sid); + case ISOAdobeCharset: + if (glyph <= 228 /*zcaron*/) sid = glyph; + break; + case ExpertCharset: + sid = lookup_expert_charset_for_sid (glyph); break; - case ExpertEncoding: - code = lookup_expert_encoding_for_code (sid); + case ExpertSubsetCharset: + sid = lookup_expert_subset_charset_for_sid (glyph); break; default: break; } - return code; + return sid; } } - hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const + hb_codepoint_t sid_to_glyph (hb_codepoint_t sid) const { - if (charset != &Null(Charset)) - return charset->get_sid (glyph); + if (charset != &Null (Charset)) + return charset->get_glyph (sid, num_glyphs); else { - hb_codepoint_t sid = 0; + hb_codepoint_t glyph = 0; switch (topDict.CharsetOffset) { - case ISOAdobeCharset: - if (glyph <= 228 /*zcaron*/) sid = glyph; + case ISOAdobeCharset: + if (sid <= 228 /*zcaron*/) glyph = sid; break; - case ExpertCharset: - sid = lookup_expert_charset_for_sid (glyph); + case ExpertCharset: + glyph = lookup_expert_charset_for_glyph (sid); break; - case ExpertSubsetCharset: - sid = lookup_expert_subset_charset_for_sid (glyph); + case ExpertSubsetCharset: + glyph = lookup_expert_subset_charset_for_glyph (sid); break; default: break; } - return sid; + return glyph; } } - const Encoding *encoding; + protected: + hb_blob_t *blob; + hb_sanitize_context_t sc; - private: - typedef accelerator_templ_t SUPER; + public: + const Encoding *encoding; + const Charset *charset; + const CFF1NameIndex *nameIndex; + const CFF1TopDictIndex *topDictIndex; + const CFF1StringIndex *stringIndex; + const CFF1Subrs *globalSubrs; + const CFF1CharStrings *charStrings; + const CFF1FDArray *fdArray; + const CFF1FDSelect *fdSelect; + unsigned int fdCount; + + cff1_top_dict_values_t topDict; + hb_vector_t + fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs; }; - bool subset (hb_subset_plan_t *plan) const + struct accelerator_t : accelerator_templ_t { - hb_blob_t *cff_prime = nullptr; - - bool success = true; - if (hb_subset_cff1 (plan, &cff_prime)) { - success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime); - hb_blob_t *head_blob = hb_sanitize_context_t().reference_table (plan->source); - success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); - hb_blob_destroy (head_blob); - } else { - success = false; + void init (hb_face_t *face) + { + SUPER::init (face); + + if (!is_valid ()) return; + if (is_CID ()) return; + + /* fill glyph_names */ + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t sid = glyph_to_sid (gid); + gname_t gname; + gname.sid = sid; + if (sid < cff1_std_strings_length) + gname.name = cff1_std_strings (sid); + else + { + byte_str_t ustr = (*stringIndex)[sid - cff1_std_strings_length]; + gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length); + } + if (unlikely (!gname.name.arrayZ)) { fini (); return; } + glyph_names.push (gname); + } + glyph_names.qsort (); } - hb_blob_destroy (cff_prime); - return success; - } + void fini () + { + glyph_names.fini (); + + SUPER::fini (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + if (!buf) return true; + if (unlikely (!is_valid ())) return false; + if (is_CID()) return false; + hb_codepoint_t sid = glyph_to_sid (glyph); + const char *str; + size_t str_len; + if (sid < cff1_std_strings_length) + { + hb_bytes_t byte_str = cff1_std_strings (sid); + str = byte_str.arrayZ; + str_len = byte_str.length; + } + else + { + byte_str_t ubyte_str = (*stringIndex)[sid - cff1_std_strings_length]; + str = (const char *)ubyte_str.arrayZ; + str_len = ubyte_str.length; + } + if (!str_len) return false; + unsigned int len = hb_min (buf_len - 1, str_len); + strncpy (buf, (const char*)str, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + if (len < 0) len = strlen (name); + if (unlikely (!len)) return false; + + gname_t key = { hb_bytes_t (name, len), 0 }; + const gname_t *gname = glyph_names.bsearch (key); + if (!gname) return false; + hb_codepoint_t gid = sid_to_glyph (gname->sid); + if (!gid && gname->sid) return false; + *glyph = gid; + return true; + } + + HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; + HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif + + private: + struct gname_t + { + hb_bytes_t name; + uint16_t sid; + + static int cmp (const void *a_, const void *b_) + { + const gname_t *a = (const gname_t *)a_; + const gname_t *b = (const gname_t *)b_; + int minlen = hb_min (a->name.length, b->name.length); + int ret = strncmp (a->name.arrayZ, b->name.arrayZ, minlen); + if (ret) return ret; + return a->name.length - b->name.length; + } + + int cmp (const gname_t &a) const { return cmp (&a, this); } + }; + + hb_sorted_vector_t glyph_names; + + typedef accelerator_templ_t SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t {}; + + bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); } protected: HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); + HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_glyph (hb_codepoint_t sid); + HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_glyph (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); public: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc index 45eb8bd276109..56f331ed44761 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.cc @@ -24,24 +24,29 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_OT_FONT_CFF + #include "hb-ot-cff2-table.hh" #include "hb-cff2-interp-cs.hh" +#include "hb-draw.hh" using namespace CFF; -struct extents_param_t +struct cff2_extents_param_t { void init () { path_open = false; - min_x.set_int (0x7FFFFFFF); - min_y.set_int (0x7FFFFFFF); - max_x.set_int (-0x80000000); - max_y.set_int (-0x80000000); + min_x.set_int (INT_MAX); + min_y.set_int (INT_MAX); + max_x.set_int (INT_MIN); + max_y.set_int (INT_MIN); } - void start_path () { path_open = true; } - void end_path () { path_open = false; } + void start_path () { path_open = true; } + void end_path () { path_open = false; } bool is_path_open () const { return path_open; } void update_bounds (const point_t &pt) @@ -59,15 +64,15 @@ struct extents_param_t number_t max_y; }; -struct cff2_path_procs_extents_t : path_procs_t +struct cff2_path_procs_extents_t : path_procs_t { - static void moveto (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt) + static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) { param.end_path (); env.moveto (pt); } - static void line (cff2_cs_interp_env_t &env, extents_param_t& param, const point_t &pt1) + static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) { if (!param.is_path_open ()) { @@ -78,7 +83,7 @@ struct cff2_path_procs_extents_t : path_procs_t {}; +struct cff2_cs_opset_extents_t : cff2_cs_opset_t {}; bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; - unsigned int num_coords; - const int *coords = hb_font_get_var_coords_normalized (font, &num_coords); unsigned int fd = fdSelect->get_fd (glyph); - cff2_cs_interpreter_t interp; + cff2_cs_interpreter_t interp; const byte_str_t str = (*charStrings)[glyph]; - interp.env.init (str, *this, fd, coords, num_coords); - extents_param_t param; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_extents_param_t param; param.init (); if (unlikely (!interp.interpret (param))) return false; @@ -118,8 +126,8 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->x_bearing = (int32_t)param.min_x.floor (); - extents->width = (int32_t)param.max_x.ceil () - extents->x_bearing; + extents->x_bearing = font->em_scalef_x (param.min_x.to_real ()); + extents->width = font->em_scalef_x (param.max_x.to_real () - param.min_x.to_real ()); } if (param.min_y >= param.max_y) { @@ -128,9 +136,80 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, } else { - extents->y_bearing = (int32_t)param.max_y.ceil (); - extents->height = (int32_t)param.min_y.floor () - extents->y_bearing; + extents->y_bearing = font->em_scalef_y (param.max_y.to_real ()); + extents->height = font->em_scalef_y (param.min_y.to_real () - param.max_y.to_real ()); + } + + return true; +} + +#ifdef HB_EXPERIMENTAL_API +struct cff2_path_param_t +{ + cff2_path_param_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + draw_helper = &draw_helper_; + font = font_; + } + + void move_to (const point_t &p) + { draw_helper->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void line_to (const point_t &p) + { draw_helper->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); } + + void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3) + { + draw_helper->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()), + font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()), + font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ())); + } + + protected: + draw_helper_t *draw_helper; + hb_font_t *font; +}; + +struct cff2_path_procs_path_t : path_procs_t +{ + static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + { + param.move_to (pt); + env.moveto (pt); + } + + static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + { + param.line_to (pt1); + env.moveto (pt1); } + static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + { + param.cubic_to (pt1, pt2, pt3); + env.moveto (pt3); + } +}; + +struct cff2_cs_opset_path_t : cff2_cs_opset_t {}; + +bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const +{ +#ifdef HB_NO_OT_FONT_CFF + /* XXX Remove check when this code moves to .hh file. */ + return true; +#endif + + if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; + + unsigned int fd = fdSelect->get_fd (glyph); + cff2_cs_interpreter_t interp; + const byte_str_t str = (*charStrings)[glyph]; + interp.env.init (str, *this, fd, font->coords, font->num_coords); + cff2_path_param_t param (font, draw_helper); + if (unlikely (!interp.interpret (param))) return false; return true; } +#endif + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh index 8111d503844cd..90c0b5b9cf684 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cff2-table.hh @@ -27,9 +27,9 @@ #ifndef HB_OT_CFF2_TABLE_HH #define HB_OT_CFF2_TABLE_HH -#include "hb-ot-head-table.hh" #include "hb-ot-cff-common.hh" #include "hb-subset-cff2.hh" +#include "hb-draw.hh" namespace CFF { @@ -43,7 +43,6 @@ typedef CFFIndex CFF2Index; template struct CFF2IndexOf : CFFIndexOf {}; typedef CFF2Index CFF2CharStrings; -typedef FDArray CFF2FDArray; typedef Subrs CFF2Subrs; typedef FDSelect3_4 FDSelect4; @@ -51,62 +50,63 @@ typedef FDSelect3_4_Range FDSelect4_Range; struct CFF2FDSelect { - bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const - { - TRACE_SANITIZE (this); - - return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) && - (format == 0)? - u.format0.sanitize (c, fdcount): - ((format == 3)? - u.format3.sanitize (c, fdcount): - u.format4.sanitize (c, fdcount)))); - } - bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (num_glyphs); CFF2FDSelect *dest = c->allocate_size (size); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } - unsigned int calculate_serialized_size (unsigned int num_glyphs) const - { return get_size (num_glyphs); } - unsigned int get_size (unsigned int num_glyphs) const { - unsigned int size = format.static_size; - if (format == 0) - size += u.format0.get_size (num_glyphs); - else if (format == 3) - size += u.format3.get_size (); - else - size += u.format4.get_size (); - return size; + switch (format) + { + case 0: return format.static_size + u.format0.get_size (num_glyphs); + case 3: return format.static_size + u.format3.get_size (); + case 4: return format.static_size + u.format4.get_size (); + default:return 0; + } } hb_codepoint_t get_fd (hb_codepoint_t glyph) const { - if (this == &Null(CFF2FDSelect)) + if (this == &Null (CFF2FDSelect)) return 0; - if (format == 0) - return u.format0.get_fd (glyph); - else if (format == 3) - return u.format3.get_fd (glyph); - else - return u.format4.get_fd (glyph); + + switch (format) + { + case 0: return u.format0.get_fd (glyph); + case 3: return u.format3.get_fd (glyph); + case 4: return u.format4.get_fd (glyph); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + + switch (format) + { + case 0: return_trace (u.format0.sanitize (c, fdcount)); + case 3: return_trace (u.format3.sanitize (c, fdcount)); + case 4: return_trace (u.format4.sanitize (c, fdcount)); + default:return_trace (false); + } } HBUINT8 format; union { - FDSelect0 format0; - FDSelect3 format3; - FDSelect4 format4; + FDSelect0 format0; + FDSelect3 format3; + FDSelect4 format4; } u; - + public: DEFINE_SIZE_MIN (2); }; @@ -123,7 +123,7 @@ struct CFF2VariationStore TRACE_SERIALIZE (this); unsigned int size_ = varStore->get_size (); CFF2VariationStore *dest = c->allocate_size (size_); - if (unlikely (dest == nullptr)) return_trace (false); + if (unlikely (!dest)) return_trace (false); memcpy (dest, varStore, size_); return_trace (true); } @@ -146,26 +146,6 @@ struct cff2_top_dict_values_t : top_dict_values_t<> } void fini () { top_dict_values_t<>::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < get_count (); i++) - { - op_code_t op = get_value (i).op; - switch (op) - { - case OpCode_vstore: - case OpCode_FDSelect: - size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); - break; - default: - size += top_dict_values_t<>::calculate_serialized_op_size (get_value (i)); - break; - } - } - return size; - } - unsigned int vstoreOffset; unsigned int FDSelectOffset; }; @@ -252,22 +232,11 @@ struct cff2_private_dict_values_base_t : dict_values_t { dict_values_t::init (); subrsOffset = 0; - localSubrs = &Null(CFF2Subrs); + localSubrs = &Null (CFF2Subrs); ivs = 0; } void fini () { dict_values_t::fini (); } - unsigned int calculate_serialized_size () const - { - unsigned int size = 0; - for (unsigned int i = 0; i < dict_values_t::get_count; i++) - if (dict_values_t::get_value (i).op == OpCode_Subrs) - size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); - else - size += dict_values_t::get_value (i).str.length; - return size; - } - unsigned int subrsOffset; const CFF2Subrs *localSubrs; unsigned int ivs; @@ -400,6 +369,14 @@ struct cff2_private_dict_opset_subset_t : dict_opset_t typedef dict_interpreter_t cff2_top_dict_interpreter_t; typedef dict_interpreter_t cff2_font_dict_interpreter_t; +struct CFF2FDArray : FDArray +{ + /* FDArray::serialize does not compile without this partial specialization */ + template + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray::serialize (c, it, opszr); } +}; + } /* namespace CFF */ namespace OT { @@ -434,7 +411,7 @@ struct cff2 const OT::cff2 *cff2 = this->blob->template as (); - if (cff2 == &Null(OT::cff2)) + if (cff2 == &Null (OT::cff2)) { fini (); return; } { /* parse top dict */ @@ -452,11 +429,11 @@ struct cff2 fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); - if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || - (charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || - (globalSubrs == &Null(CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || - (fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || - (((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) + if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || + (charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || + (globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || + (fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) || + (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count))))) { fini (); return; } num_glyphs = charStrings->count; @@ -464,7 +441,8 @@ struct cff2 { fini (); return; } fdCount = fdArray->count; - privateDicts.resize (fdCount); + if (!privateDicts.resize (fdCount)) + { fini (); return; } /* parse font dicts and gather private dicts */ for (unsigned int i = 0; i < fdCount; i++) @@ -475,7 +453,7 @@ struct cff2 cff2_font_dict_interpreter_t font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); - if (unlikely (font == &Crap(cff2_font_dict_values_t))) { fini (); return; } + if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; } font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } @@ -487,7 +465,7 @@ struct cff2 if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } privateDicts[i].localSubrs = &StructAtOffsetOrNull (&privDictStr[0], privateDicts[i].subrsOffset); - if (privateDicts[i].localSubrs != &Null(CFF2Subrs) && + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) { fini (); return; } } @@ -503,7 +481,7 @@ struct cff2 blob = nullptr; } - bool is_valid () const { return blob != nullptr; } + bool is_valid () const { return blob; } protected: hb_blob_t *blob; @@ -529,27 +507,14 @@ struct cff2 HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; +#ifdef HB_EXPERIMENTAL_API + HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const; +#endif }; typedef accelerator_templ_t accelerator_subset_t; - bool subset (hb_subset_plan_t *plan) const - { - hb_blob_t *cff2_prime = nullptr; - - bool success = true; - if (hb_subset_cff2 (plan, &cff2_prime)) { - success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime); - hb_blob_t *head_blob = hb_sanitize_context_t().reference_table (plan->source); - success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); - hb_blob_destroy (head_blob); - } else { - success = false; - } - hb_blob_destroy (cff2_prime); - - return success; - } + bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); } public: FixedVersion version; /* Version of CFF2 table. set to 0x0200u */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh index 9e2ada67c4a5e..7160b168a7a90 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-cmap-table.hh @@ -56,6 +56,18 @@ struct CmapSubtableFormat0 out->add (i); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + for (unsigned i = 0; i < 256; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t glyph = glyphIdArray[i]; + unicodes->add (i); + mapping->set (i, glyph); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -74,154 +86,203 @@ struct CmapSubtableFormat0 struct CmapSubtableFormat4 { - struct segment_plan - { - HBUINT16 start_code; - HBUINT16 end_code; - bool use_delta; - }; - bool serialize (hb_serialize_context_t *c, - const hb_subset_plan_t *plan, - const hb_vector_t &segments) + template + HBUINT16* serialize_endcode_array (hb_serialize_context_t *c, + Iterator it) { - TRACE_SERIALIZE (this); - - if (unlikely (!c->extend_min (*this))) return_trace (false); + HBUINT16 *endCode = c->start_embed (); + hb_codepoint_t prev_endcp = 0xFFFF; - this->format.set (4); - this->length.set (get_sub_table_size (segments)); - - this->segCountX2.set (segments.length * 2); - this->entrySelector.set (MAX (1u, hb_bit_storage (segments.length)) - 1); - this->searchRange.set (2 * (1u << this->entrySelector)); - this->rangeShift.set (segments.length * 2 > this->searchRange - ? 2 * segments.length - this->searchRange - : 0); - - HBUINT16 *end_count = c->allocate_size (HBUINT16::static_size * segments.length); - c->allocate_size (HBUINT16::static_size); // 2 bytes of padding. - HBUINT16 *start_count = c->allocate_size (HBUINT16::static_size * segments.length); - HBINT16 *id_delta = c->allocate_size (HBUINT16::static_size * segments.length); - HBUINT16 *id_range_offset = c->allocate_size (HBUINT16::static_size * segments.length); - - if (id_range_offset == nullptr) - return_trace (false); + for (const hb_item_type _ : +it) + { + if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first) + { + HBUINT16 end_code; + end_code = prev_endcp; + c->copy (end_code); + } + prev_endcp = _.first; + } - for (unsigned int i = 0; i < segments.length; i++) { - end_count[i].set (segments[i].end_code); - start_count[i].set (segments[i].start_code); - if (segments[i].use_delta) + // last endCode + HBUINT16 endcode; + endcode = prev_endcp; + if (unlikely (!c->copy (endcode))) return nullptr; + // There must be a final entry with end_code == 0xFFFF. + if (prev_endcp != 0xFFFF) { - hb_codepoint_t cp = segments[i].start_code; - hb_codepoint_t start_gid = 0; - if (unlikely (!plan->new_gid_for_codepoint (cp, &start_gid) && cp != 0xFFFF)) - return_trace (false); - id_delta[i].set (start_gid - segments[i].start_code); - } else { - id_delta[i].set (0); - unsigned int num_codepoints = segments[i].end_code - segments[i].start_code + 1; - HBUINT16 *glyph_id_array = c->allocate_size (HBUINT16::static_size * num_codepoints); - if (glyph_id_array == nullptr) - return_trace (false); - // From the cmap spec: - // - // id_range_offset[i]/2 - // + (cp - segments[i].start_code) - // + (id_range_offset + i) - // = - // glyph_id_array + (cp - segments[i].start_code) - // - // So, solve for id_range_offset[i]: - // - // id_range_offset[i] - // = - // 2 * (glyph_id_array - id_range_offset - i) - id_range_offset[i].set (2 * ( - glyph_id_array - id_range_offset - i)); - for (unsigned int j = 0; j < num_codepoints; j++) - { - hb_codepoint_t cp = segments[i].start_code + j; - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) - return_trace (false); - glyph_id_array[j].set (new_gid); - } + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; } } - return_trace (true); + return endCode; } - static size_t get_sub_table_size (const hb_vector_t &segments) + template + HBUINT16* serialize_startcode_array (hb_serialize_context_t *c, + Iterator it) { - size_t segment_size = 0; - for (unsigned int i = 0; i < segments.length; i++) + HBUINT16 *startCode = c->start_embed (); + hb_codepoint_t prev_cp = 0xFFFF; + + for (const hb_item_type _ : +it) { - // Parallel array entries - segment_size += - 2 // end count - + 2 // start count - + 2 // delta - + 2; // range offset - - if (!segments[i].use_delta) - // Add bytes for the glyph index array entries for this segment. - segment_size += (segments[i].end_code - segments[i].start_code + 1) * 2; + if (prev_cp == 0xFFFF || prev_cp + 1u != _.first) + { + HBUINT16 start_code; + start_code = _.first; + c->copy (start_code); + } + + prev_cp = _.first; } - return min_size - + 2 // Padding - + segment_size; + // There must be a final entry with end_code == 0xFFFF. + if (it.len () == 0 || prev_cp != 0xFFFF) + { + HBUINT16 finalcode; + finalcode = 0xFFFF; + if (unlikely (!c->copy (finalcode))) return nullptr; + } + + return startCode; } - static bool create_sub_table_plan (const hb_subset_plan_t *plan, - hb_vector_t *segments) + template + HBINT16* serialize_idDelta_array (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + unsigned segcount) { - segment_plan *segment = nullptr; - hb_codepoint_t last_gid = 0; + unsigned i = 0; + hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF; + bool use_delta = true; - hb_codepoint_t cp = HB_SET_VALUE_INVALID; - while (plan->unicodes->next (&cp)) { - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) + HBINT16 *idDelta = c->start_embed (); + if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size) + return nullptr; + + for (const hb_item_type _ : +it) + { + if (_.first == startCode[i]) { - DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp); - return false; + use_delta = true; + start_gid = _.second; } + else if (_.second != last_gid + 1) use_delta = false; - /* Stop adding to cmap if we are now outside of unicode BMP. */ - if (cp > 0xFFFF) break; - - if (!segment || - cp != segment->end_code + 1u) + if (_.first == endCode[i]) { - segment = segments->push (); - segment->start_code.set (cp); - segment->end_code.set (cp); - segment->use_delta = true; - } else { - segment->end_code.set (cp); - if (last_gid + 1u != new_gid) - // gid's are not consecutive in this segment so delta - // cannot be used. - segment->use_delta = false; + HBINT16 delta; + if (use_delta) delta = (int)start_gid - (int)startCode[i]; + else delta = 0; + c->copy (delta); + + i++; } - last_gid = new_gid; + last_gid = _.second; + last_cp = _.first; } - // There must be a final entry with end_code == 0xFFFF. Check if we need to add one. - if (segment == nullptr || segment->end_code != 0xFFFF) + if (it.len () == 0 || last_cp != 0xFFFF) { - segment = segments->push (); - segment->start_code.set (0xFFFF); - segment->end_code.set (0xFFFF); - segment->use_delta = true; + HBINT16 delta; + delta = 1; + if (unlikely (!c->copy (delta))) return nullptr; } - return true; + return idDelta; + } + + template + HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, + Iterator it, + HBUINT16 *endCode, + HBUINT16 *startCode, + HBINT16 *idDelta, + unsigned segcount) + { + HBUINT16 *idRangeOffset = c->allocate_size (HBUINT16::static_size * segcount); + if (unlikely (!c->check_success (idRangeOffset))) return nullptr; + if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; + + + hb_range (segcount) + | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }) + | hb_apply ([&] (const unsigned i) + { + idRangeOffset[i] = 2 * (c->start_embed () - idRangeOffset - i); + + + it + | hb_filter ([&] (const hb_item_type _) { return _.first >= startCode[i] && _.first <= endCode[i]; }) + | hb_apply ([&] (const hb_item_type _) + { + HBUINT16 glyID; + glyID = _.second; + c->copy (glyID); + }) + ; + + + }) + ; + + return idRangeOffset; + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it) + { + auto format4_iter = + + it + | hb_filter ([&] (const hb_pair_t _) + { return _.first <= 0xFFFF; }) + ; + + if (format4_iter.len () == 0) return; + + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; + this->format = 4; + + //serialize endCode[] + HBUINT16 *endCode = serialize_endcode_array (c, format4_iter); + if (unlikely (!endCode)) return; + + unsigned segcount = (c->length () - min_size) / HBUINT16::static_size; + + // 2 bytes of padding. + if (unlikely (!c->allocate_size (HBUINT16::static_size))) return; // 2 bytes of padding. + + // serialize startCode[] + HBUINT16 *startCode = serialize_startcode_array (c, format4_iter); + if (unlikely (!startCode)) return; + + //serialize idDelta[] + HBINT16 *idDelta = serialize_idDelta_array (c, format4_iter, endCode, startCode, segcount); + if (unlikely (!idDelta)) return; + + HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount); + if (unlikely (!c->check_success (idRangeOffset))) return; + + if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) return; + this->segCountX2 = segcount * 2; + this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; + this->searchRange = 2 * (1u << this->entrySelector); + this->rangeShift = segcount * 2 > this->searchRange + ? 2 * segcount - this->searchRange + : 0; } struct accelerator_t @@ -244,27 +305,28 @@ struct CmapSubtableFormat4 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - /* Custom two-array bsearch. */ - int min = 0, max = (int) this->segCount - 1; - const HBUINT16 *startCount = this->startCount; - const HBUINT16 *endCount = this->endCount; - unsigned int i; - while (min <= max) + struct CustomRange { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - if (codepoint < startCount[mid]) - max = mid - 1; - else if (codepoint > endCount[mid]) - min = mid + 1; - else + int cmp (hb_codepoint_t k, + unsigned distance) const { - i = mid; - goto found; + if (k > last) return +1; + if (k < (&last)[distance]) return -1; + return 0; } - } - return false; + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + 2, + _hb_cmp_method, + this->segCount + 1); + if (!found) + return false; + unsigned int i = found - endCount; - found: hb_codepoint_t gid; unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) @@ -286,10 +348,10 @@ struct CmapSubtableFormat4 *glyph = gid; return true; } - static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) - { - return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); - } + + HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) + { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } + void collect_unicodes (hb_set_t *out) const { unsigned int count = this->segCount; @@ -297,14 +359,22 @@ struct CmapSubtableFormat4 count--; /* Skip sentinel segment. */ for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; unsigned int rangeOffset = this->idRangeOffset[i]; if (rangeOffset == 0) - out->add_range (this->startCount[i], this->endCount[i]); + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + out->add (codepoint); + } + } else { - for (hb_codepoint_t codepoint = this->startCount[i]; - codepoint <= this->endCount[i]; - codepoint++) + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) { unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; if (unlikely (index >= this->glyphIdArrayLength)) @@ -318,6 +388,45 @@ struct CmapSubtableFormat4 } } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = this->segCount; + if (count && this->startCount[count - 1] == 0xFFFFu) + count--; /* Skip sentinel segment. */ + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->startCount[i]; + hb_codepoint_t end = this->endCount[i]; + unsigned rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + else + { + for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) + { + unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + break; + hb_codepoint_t gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + continue; + unicodes->add (codepoint); + mapping->set (codepoint, gid); + } + } + } + } + const HBUINT16 *endCount; const HBUINT16 *startCount; const HBUINT16 *idDelta; @@ -338,6 +447,13 @@ struct CmapSubtableFormat4 accel.collect_unicodes (out); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + accelerator_t accel (this); + accel.collect_mapping (unicodes, mapping); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -349,9 +465,9 @@ struct CmapSubtableFormat4 /* Some broken fonts have too long of a "length" value. * If that is the case, just change the value to truncate * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) MIN ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); + uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); if (!c->try_set (&length, new_length)) return_trace (false); } @@ -440,6 +556,21 @@ struct CmapSubtableTrimmed out->add (start + i); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + hb_codepoint_t start_cp = startCharCode; + unsigned count = glyphIdArray.len; + for (unsigned i = 0; i < count; i++) + if (glyphIdArray[i]) + { + hb_codepoint_t unicode = start_cp + i; + hb_codepoint_t glyphid = glyphIdArray[i]; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -451,7 +582,7 @@ struct CmapSubtableTrimmed UINT length; /* Byte length of this subtable. */ UINT language; /* Ignore. */ UINT startCharCode; /* First character code covered. */ - ArrayOf + ArrayOf glyphIdArray; /* Array of glyph index values for character * codes in the range. */ public: @@ -475,12 +606,56 @@ struct CmapSubtableLongSegmented return true; } - void collect_unicodes (hb_set_t *out) const + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { + for (unsigned int i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + out->add_range (start, end); + } + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs) const { - for (unsigned int i = 0; i < this->groups.len; i++) { - out->add_range (this->groups[i].startCharCode, - MIN ((hb_codepoint_t) this->groups[i].endCharCode, - (hb_codepoint_t) HB_UNICODE_MAX)); + for (unsigned i = 0; i < this->groups.len; i++) + { + hb_codepoint_t start = this->groups[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, + (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t gid = this->groups[i].glyphID; + if (!gid) + { + /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */ + if (! T::group_get_glyph (this->groups[i], end)) continue; + start++; + gid++; + } + if (unlikely ((unsigned int) gid >= num_glyphs)) continue; + if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs)) + end = start + (hb_codepoint_t) num_glyphs - gid; + + for (unsigned cp = start; cp <= end; cp++) + { + unicodes->add (cp); + mapping->set (cp, gid); + gid++; + } } } @@ -490,15 +665,6 @@ struct CmapSubtableLongSegmented return_trace (c->check_struct (this) && groups.sanitize (c)); } - bool serialize (hb_serialize_context_t *c, - const hb_vector_t &group_data) - { - TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!groups.serialize (c, group_data.as_array ()))) return_trace (false); - return true; - } - protected: HBUINT16 format; /* Subtable format; set to 12. */ HBUINT16 reserved; /* Reserved; set to 0. */ @@ -518,63 +684,66 @@ struct CmapSubtableFormat12 : CmapSubtableLongSegmented group.glyphID + (u - group.startCharCode) : 0; } - bool serialize (hb_serialize_context_t *c, - const hb_vector_t &groups) + template + void serialize (hb_serialize_context_t *c, + Iterator it) { - if (unlikely (!c->extend_min (*this))) return false; - - this->format.set (12); - this->reserved.set (0); - this->length.set (get_sub_table_size (groups)); + if (it.len () == 0) return; + unsigned table_initpos = c->length (); + if (unlikely (!c->extend_min (*this))) return; - return CmapSubtableLongSegmented::serialize (c, groups); - } + hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF; + hb_codepoint_t glyphID = 0; - static size_t get_sub_table_size (const hb_vector_t &groups) - { - return 16 + 12 * groups.length; - } - - static bool create_sub_table_plan (const hb_subset_plan_t *plan, - hb_vector_t *groups) - { - CmapSubtableLongGroup *group = nullptr; - - hb_codepoint_t cp = HB_SET_VALUE_INVALID; - while (plan->unicodes->next (&cp)) { - hb_codepoint_t new_gid; - if (unlikely (!plan->new_gid_for_codepoint (cp, &new_gid))) + for (const hb_item_type _ : +it) + { + if (startCharCode == 0xFFFF) { - DEBUG_MSG(SUBSET, nullptr, "Unable to find new gid for %04x", cp); - return false; + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; } - - if (!group || !_is_gid_consecutive (group, cp, new_gid)) + else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) { - group = groups->push (); - group->startCharCode.set (cp); - group->endCharCode.set (cp); - group->glyphID.set (new_gid); + CmapSubtableLongGroup grouprecord; + grouprecord.startCharCode = startCharCode; + grouprecord.endCharCode = endCharCode; + grouprecord.glyphID = glyphID; + c->copy (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; } - else group->endCharCode.set (cp); + else + endCharCode = _.first; } - DEBUG_MSG(SUBSET, nullptr, "cmap"); - for (unsigned int i = 0; i < groups->length; i++) { - CmapSubtableLongGroup& group = (*groups)[i]; - DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode)); - } + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy (record); - return true; + this->format = 12; + this->reserved = 0; + this->length = c->length () - table_initpos; + this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size; } - private: - static bool _is_gid_consecutive (CmapSubtableLongGroup *group, + static size_t get_sub_table_size (const hb_sorted_vector_t &groups_data) + { return 16 + 12 * groups_data.length; } + + private: + static bool _is_gid_consecutive (hb_codepoint_t endCharCode, + hb_codepoint_t startCharCode, + hb_codepoint_t glyphID, hb_codepoint_t cp, hb_codepoint_t new_gid) { - return (cp - 1 == group->endCharCode) && - new_gid == group->glyphID + (cp - group->startCharCode); + return (cp - 1 == endCharCode) && + new_gid == glyphID + (cp - startCharCode); } }; @@ -623,12 +792,69 @@ struct DefaultUVS : SortedArrayOf for (unsigned int i = 0; i < count; i++) { hb_codepoint_t first = arrayZ[i].startUnicodeValue; - hb_codepoint_t last = MIN ((hb_codepoint_t) (first + arrayZ[i].additionalCount), - (hb_codepoint_t) HB_UNICODE_MAX); + hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), + (hb_codepoint_t) HB_UNICODE_MAX); out->add_range (first, last); } } + DefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes) const + { + DefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy (len))) return nullptr; + unsigned init_len = c->length (); + + hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; + int count = -1; + + for (const UnicodeValueRange& _ : as_array ()) + { + for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) + { + unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; + if (!unicodes->has (curEntry)) continue; + count += 1; + if (lastCode == HB_MAP_VALUE_INVALID) + lastCode = curEntry; + else if (lastCode + count != curEntry) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count - 1; + c->copy (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy (rec); + } + + if (c->length () - init_len == 0) + { + c->revert (snap); + return nullptr; + } + else + { + if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr; + return out; + } + } + public: DEFINE_SIZE_ARRAY (4, *this); }; @@ -636,9 +862,7 @@ struct DefaultUVS : SortedArrayOf struct UVSMapping { int cmp (const hb_codepoint_t &codepoint) const - { - return unicodeValue.cmp (codepoint); - } + { return unicodeValue.cmp (codepoint); } bool sanitize (hb_sanitize_context_t *c) const { @@ -647,7 +871,7 @@ struct UVSMapping } HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ - GlyphID glyphID; /* Glyph ID of the UVS */ + HBGlyphID glyphID; /* Glyph ID of the UVS */ public: DEFINE_SIZE_STATIC (5); }; @@ -658,7 +882,63 @@ struct NonDefaultUVS : SortedArrayOf { unsigned int count = len; for (unsigned int i = 0; i < count; i++) - out->add (arrayZ[i].glyphID); + out->add (arrayZ[i].unicodeValue); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t unicode = arrayZ[i].unicodeValue; + hb_codepoint_t glyphid = arrayZ[i].glyphID; + unicodes->add (unicode); + mapping->set (unicode, glyphid); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + as_array () + | hb_filter (unicodes, &UVSMapping::unicodeValue) + | hb_map (&UVSMapping::glyphID) + | hb_sink (glyphset) + ; + } + + NonDefaultUVS* copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map) const + { + NonDefaultUVS *out = c->start_embed (); + if (unlikely (!out)) return nullptr; + + auto it = + + as_array () + | hb_filter ([&] (const UVSMapping& _) + { + return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID); + }) + ; + + if (!it) return nullptr; + + HBUINT32 len; + len = it.len (); + if (unlikely (!c->copy (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy (mapping); + } + + return out; } public: @@ -682,17 +962,37 @@ struct VariationSelectorRecord return GLYPH_VARIANT_NOT_FOUND; } + VariationSelectorRecord(const VariationSelectorRecord& other) + { + *this = other; + } + + void operator= (const VariationSelectorRecord& other) + { + varSelector = other.varSelector; + HBUINT32 offset = other.defaultUVS; + defaultUVS = offset; + offset = other.nonDefaultUVS; + nonDefaultUVS = offset; + } + void collect_unicodes (hb_set_t *out, const void *base) const { (base+defaultUVS).collect_unicodes (out); (base+nonDefaultUVS).collect_unicodes (out); } - int cmp (const hb_codepoint_t &variation_selector) const + void collect_mapping (const void *base, + hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const { - return varSelector.cmp (variation_selector); + (base+defaultUVS).collect_unicodes (unicodes); + (base+nonDefaultUVS).collect_mapping (unicodes, mapping); } + int cmp (const hb_codepoint_t &variation_selector) const + { return varSelector.cmp (variation_selector); } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -701,6 +1001,45 @@ struct VariationSelectorRecord nonDefaultUVS.sanitize (c, base)); } + hb_pair_t + copy (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) const + { + auto snap = c->snapshot (); + auto *out = c->embed (*this); + if (unlikely (!out)) return hb_pair (0, 0); + + out->defaultUVS = 0; + out->nonDefaultUVS = 0; + + unsigned non_default_uvs_objidx = 0; + if (nonDefaultUVS != 0) + { + c->push (); + if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map)) + non_default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + unsigned default_uvs_objidx = 0; + if (defaultUVS != 0) + { + c->push (); + if (c->copy (base+defaultUVS, unicodes)) + default_uvs_objidx = c->pop_pack (); + else c->pop_discard (); + } + + + if (!default_uvs_objidx && !non_default_uvs_objidx) + c->revert (snap); + + return hb_pair (default_uvs_objidx, non_default_uvs_objidx); + } + HBUINT24 varSelector; /* Variation selector. */ LOffsetTo defaultUVS; /* Offset to Default UVS Table. May be 0. */ @@ -715,9 +1054,7 @@ struct CmapSubtableFormat14 glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, hb_codepoint_t variation_selector, hb_codepoint_t *glyph) const - { - return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); - } + { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } void collect_variation_selectors (hb_set_t *out) const { @@ -727,8 +1064,110 @@ struct CmapSubtableFormat14 } void collect_variation_unicodes (hb_codepoint_t variation_selector, hb_set_t *out) const + { record.bsearch (variation_selector).collect_unicodes (out, this); } + + void serialize (hb_serialize_context_t *c, + const hb_set_t *unicodes, + const hb_set_t *glyphs_requested, + const hb_map_t *glyph_map, + const void *base) + { + auto snap = c->snapshot (); + unsigned table_initpos = c->length (); + const char* init_tail = c->tail; + + if (unlikely (!c->extend_min (*this))) return; + this->format = 14; + + auto src_tbl = reinterpret_cast (base); + + /* + * Some versions of OTS require that offsets are in order. Due to the use + * of push()/pop_pack() serializing the variation records in order results + * in the offsets being in reverse order (first record has the largest + * offset). While this is perfectly valid, it will cause some versions of + * OTS to consider this table bad. + * + * So to prevent this issue we serialize the variation records in reverse + * order, so that the offsets are ordered from small to large. Since + * variation records are supposed to be in increasing order of varSelector + * we then have to reverse the order of the written variation selector + * records after everything is finalized. + */ + hb_vector_t> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base); + if (result.first || result.second) + obj_indices.push (result); + } + + if (c->length () - table_initpos == CmapSubtableFormat14::min_size) + { + c->revert (snap); + return; + } + + if (unlikely (!c->check_success (!obj_indices.in_error ()))) + return; + + int tail_len = init_tail - c->tail; + c->check_assign (this->length, c->length () - table_initpos + tail_len); + c->check_assign (this->record.len, + (c->length () - table_initpos - CmapSubtableFormat14::min_size) / + VariationSelectorRecord::static_size); + + /* Correct the incorrect write order by reversing the order of the variation + records array. */ + _reverse_variation_records (); + + /* Now that records are in the right order, we can set up the offsets. */ + _add_links_to_variation_records (c, obj_indices); + } + + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t>& obj_indices) + { + for (unsigned i = 0; i < obj_indices.length; i++) + { + /* + * Since the record array has been reversed (see comments in copy()) + * but obj_indices has not been, the indices at obj_indices[i] + * are for the variation record at record[j]. + */ + int j = obj_indices.length - 1 - i; + c->add_link (record[j].defaultUVS, obj_indices[i].first); + c->add_link (record[j].nonDefaultUVS, obj_indices[i].second); + } + } + + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const + { + + hb_iter (record) + | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) + | hb_map (&VariationSelectorRecord::nonDefaultUVS) + | hb_map (hb_add (this)) + | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) + ; + } + + void collect_unicodes (hb_set_t *out) const + { + for (const VariationSelectorRecord& _ : record) + _.collect_unicodes (out, this); + } + + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const { - record.bsearch (variation_selector).collect_unicodes (out, this); + for (const VariationSelectorRecord& _ : record) + _.collect_mapping (this, unicodes, mapping); } bool sanitize (hb_sanitize_context_t *c) const @@ -766,20 +1205,52 @@ struct CmapSubtable default: return false; } } - void collect_unicodes (hb_set_t *out) const + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { switch (u.format) { case 0: u.format0 .collect_unicodes (out); return; case 4: u.format4 .collect_unicodes (out); return; case 6: u.format6 .collect_unicodes (out); return; case 10: u.format10.collect_unicodes (out); return; - case 12: u.format12.collect_unicodes (out); return; - case 13: u.format13.collect_unicodes (out); return; + case 12: u.format12.collect_unicodes (out, num_glyphs); return; + case 13: u.format13.collect_unicodes (out, num_glyphs); return; case 14: default: return; } } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping, /* OUT */ + unsigned num_glyphs = UINT_MAX) const + { + switch (u.format) { + case 0: u.format0 .collect_mapping (unicodes, mapping); return; + case 4: u.format4 .collect_mapping (unicodes, mapping); return; + case 6: u.format6 .collect_mapping (unicodes, mapping); return; + case 10: u.format10.collect_mapping (unicodes, mapping); return; + case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; + case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return; + case 14: + default: return; + } + } + + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const hb_subset_plan_t *plan, + const void *base) + { + switch (format) { + case 4: return u.format4.serialize (c, it); + case 12: return u.format12.serialize (c, it); + case 14: return u.format14.serialize (c, plan->unicodes, plan->glyphs_requested, plan->glyph_map, base); + default: return; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -831,6 +1302,40 @@ struct EncodingRecord subtable.sanitize (c, base)); } + template + EncodingRecord* copy (hb_serialize_context_t *c, + Iterator it, + unsigned format, + const void *base, + const hb_subset_plan_t *plan, + /* INOUT */ unsigned *objidx) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->subtable = 0; + + if (*objidx == 0) + { + CmapSubtable *cmapsubtable = c->push (); + unsigned origin_length = c->length (); + cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); + if (c->length () - origin_length > 0) *objidx = c->pop_pack (); + else c->pop_discard (); + } + + if (*objidx == 0) + { + c->revert (snap); + return_trace (nullptr); + } + + c->add_link (out->subtable, *objidx); + return_trace (out); + } + HBUINT16 platformID; /* Platform ID. */ HBUINT16 encodingID; /* Platform-specific encoding ID. */ LOffsetTo @@ -843,124 +1348,128 @@ struct cmap { static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; - struct subset_plan + template + void serialize (hb_serialize_context_t *c, + Iterator it, + EncodingRecIter encodingrec_iter, + const void *base, + const hb_subset_plan_t *plan) { - size_t final_size () const + if (unlikely (!c->extend_min ((*this)))) return; + this->version = 0; + + unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + + for (const EncodingRecord& _ : encodingrec_iter) { - return 4 // header - + 8 * 3 // 3 EncodingRecord - + CmapSubtableFormat4::get_sub_table_size (this->format4_segments) - + CmapSubtableFormat12::get_sub_table_size (this->format12_groups); + unsigned format = (base+_.subtable).u.format; + if (!plan->glyphs_requested->is_empty ()) + { + hb_set_t unicodes_set; + hb_map_t cp_glyphid_map; + (base+_.subtable).collect_mapping (&unicodes_set, &cp_glyphid_map); + + auto table_iter = + + hb_zip (unicodes_set.iter(), unicodes_set.iter() | hb_map(cp_glyphid_map)) + | hb_filter (plan->_glyphset, hb_second) + | hb_filter ([plan] (const hb_pair_t& p) + { + return plan->unicodes->has (p.first) || + plan->glyphs_requested->has (p.second); + }) + | hb_map ([plan] (const hb_pair_t& p_org) + { + return hb_pair_t (p_org.first, plan->glyph_map->get(p_org.second)); + }) + ; + + if (format == 4) c->copy (_, table_iter, 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, table_iter, 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, table_iter, 14u, base, plan, &format14objidx); + } + /* when --gids option is not used, we iterate input unicodes instead of + * all codepoints in each subtable, which is more efficient */ + else + { + hb_set_t unicodes_set; + (base+_.subtable).collect_unicodes (&unicodes_set); + + if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + else if (format == 12) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 12u, base, plan, &format12objidx); + else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); + } } - hb_vector_t format4_segments; - hb_vector_t format12_groups; - }; + c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size); + } - bool _create_plan (const hb_subset_plan_t *plan, - subset_plan *cmap_plan) const + void closure_glyphs (const hb_set_t *unicodes, + hb_set_t *glyphset) const { - if (unlikely (!CmapSubtableFormat4::create_sub_table_plan (plan, &cmap_plan->format4_segments))) - return false; - - return CmapSubtableFormat12::create_sub_table_plan (plan, &cmap_plan->format12_groups); + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) + ; } - bool _subset (const hb_subset_plan_t *plan, - const subset_plan &cmap_subset_plan, - size_t dest_sz, - void *dest) const + bool subset (hb_subset_context_t *c) const { - hb_serialize_context_t c (dest, dest_sz); - - cmap *table = c.start_serialize (); - if (unlikely (!c.extend_min (*table))) - { - return false; - } + TRACE_SUBSET (this); - table->version.set (0); + cmap *cmap_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); - if (unlikely (!table->encodingRecord.serialize (&c, /* numTables */ 3))) - return false; - - // TODO(grieger): Convert the below to a for loop + auto encodingrec_iter = + + hb_iter (encodingRecord) + | hb_filter ([&] (const EncodingRecord& _) + { + if ((_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (this + _.subtable).u.format == 14) + return true; - // Format 4, Plat 0 Encoding Record - EncodingRecord &format4_plat0_rec = table->encodingRecord[0]; - format4_plat0_rec.platformID.set (0); // Unicode - format4_plat0_rec.encodingID.set (3); + return false; + }) + ; - // Format 4, Plat 3 Encoding Record - EncodingRecord &format4_plat3_rec = table->encodingRecord[1]; - format4_plat3_rec.platformID.set (3); // Windows - format4_plat3_rec.encodingID.set (1); // Unicode BMP + if (unlikely (!encodingrec_iter.len ())) return_trace (false); - // Format 12 Encoding Record - EncodingRecord &format12_rec = table->encodingRecord[2]; - format12_rec.platformID.set (3); // Windows - format12_rec.encodingID.set (10); // Unicode UCS-4 + const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; + bool has_format12 = false; - // Write out format 4 sub table + for (const EncodingRecord& _ : encodingrec_iter) { - CmapSubtable &subtable = format4_plat0_rec.subtable.serialize (&c, table); - format4_plat3_rec.subtable.set (format4_plat0_rec.subtable); - subtable.u.format.set (4); - - CmapSubtableFormat4 &format4 = subtable.u.format4; - if (unlikely (!format4.serialize (&c, plan, cmap_subset_plan.format4_segments))) - return false; + unsigned format = (this + _.subtable).u.format; + if (format == 12) has_format12 = true; + + const EncodingRecord *table = hb_addressof (_); + if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; + else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; + else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; + else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; } - // Write out format 12 sub table. - { - CmapSubtable &subtable = format12_rec.subtable.serialize (&c, table); - subtable.u.format.set (12); - - CmapSubtableFormat12 &format12 = subtable.u.format12; - if (unlikely (!format12.serialize (&c, cmap_subset_plan.format12_groups))) - return false; - } - - c.end_serialize (); - - return true; - } - - bool subset (hb_subset_plan_t *plan) const - { - subset_plan cmap_subset_plan; - - if (unlikely (!_create_plan (plan, &cmap_subset_plan))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cmap subsetting plan."); - return false; - } - - // We now know how big our blob needs to be - size_t dest_sz = cmap_subset_plan.final_size (); - void *dest = malloc (dest_sz); - if (unlikely (!dest)) { - DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for cmap subset output", (unsigned long) dest_sz); - return false; - } - - if (unlikely (!_subset (plan, cmap_subset_plan, dest_sz, dest))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to perform subsetting of cmap."); - free (dest); - return false; - } - - // all done, write the blob into dest - hb_blob_t *cmap_prime = hb_blob_create ((const char *) dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool result = plan->add_table (HB_OT_TAG_cmap, cmap_prime); - hb_blob_destroy (cmap_prime); - return result; + if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false); + if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); + + auto it = + + hb_iter (c->plan->unicodes) + | hb_map ([&] (hb_codepoint_t _) + { + hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID; + c->plan->new_gid_for_codepoint (_, &new_gid); + return hb_pair_t (_, new_gid); + }) + | hb_filter ([&] (const hb_pair_t _) + { return (_.second != HB_MAP_VALUE_INVALID); }) + ; + cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan); + return_trace (true); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const @@ -969,6 +1478,15 @@ struct cmap const CmapSubtable *subtable; + /* Symbol subtable. + * Prefer symbol if available. + * https://github.com/harfbuzz/harfbuzz/issues/1918 */ + if ((subtable = this->find_subtable (3, 0))) + { + if (symbol) *symbol = true; + return subtable; + } + /* 32-bit subtables. */ if ((subtable = this->find_subtable (3, 10))) return subtable; if ((subtable = this->find_subtable (0, 6))) return subtable; @@ -981,13 +1499,6 @@ struct cmap if ((subtable = this->find_subtable (0, 1))) return subtable; if ((subtable = this->find_subtable (0, 0))) return subtable; - /* Symbol subtable. */ - if ((subtable = this->find_subtable (3, 0))) - { - if (symbol) *symbol = true; - return subtable; - } - /* Meh. */ return &Null (CmapSubtable); } @@ -1008,9 +1519,9 @@ struct cmap this->get_glyph_data = subtable; if (unlikely (symbol)) - { this->get_glyph_funcZ = get_glyph_from_symbol; - } else { + else + { switch (subtable->u.format) { /* Accelerate format 4 and format 12. */ default: @@ -1020,20 +1531,20 @@ struct cmap this->get_glyph_funcZ = get_glyph_from; break; case 4: - { - this->format4_accel.init (&subtable->u.format4); - this->get_glyph_data = &this->format4_accel; - this->get_glyph_funcZ = this->format4_accel.get_glyph_func; - } + { + this->format4_accel.init (&subtable->u.format4); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = this->format4_accel.get_glyph_func; break; } + } } } void fini () { this->table.destroy (); } bool get_nominal_glyph (hb_codepoint_t unicode, - hb_codepoint_t *glyph) const + hb_codepoint_t *glyph) const { if (unlikely (!this->get_glyph_funcZ)) return false; return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); @@ -1076,19 +1587,16 @@ struct cmap return get_nominal_glyph (unicode, glyph); } - void collect_unicodes (hb_set_t *out) const - { - subtable->collect_unicodes (out); - } + void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const + { subtable->collect_unicodes (out, num_glyphs); } + void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, + unsigned num_glyphs = UINT_MAX) const + { subtable->collect_mapping (unicodes, mapping, num_glyphs); } void collect_variation_selectors (hb_set_t *out) const - { - subtable_uvs->collect_variation_selectors (out); - } + { subtable_uvs->collect_variation_selectors (out); } void collect_variation_unicodes (hb_codepoint_t variation_selector, hb_set_t *out) const - { - subtable_uvs->collect_variation_unicodes (variation_selector, out); - } + { subtable_uvs->collect_variation_unicodes (variation_selector, out); } protected: typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, @@ -1096,18 +1604,18 @@ struct cmap hb_codepoint_t *glyph); template - static bool get_glyph_from (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) + HB_INTERNAL static bool get_glyph_from (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) { const Type *typed_obj = (const Type *) obj; return typed_obj->get_glyph (codepoint, glyph); } template - static bool get_glyph_from_symbol (const void *obj, - hb_codepoint_t codepoint, - hb_codepoint_t *glyph) + HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, + hb_codepoint_t codepoint, + hb_codepoint_t *glyph) { const Type *typed_obj = (const Type *) obj; if (likely (typed_obj->get_glyph (codepoint, glyph))) @@ -1135,6 +1643,7 @@ struct cmap CmapSubtableFormat4::accelerator_t format4_accel; + public: hb_blob_ptr_t table; }; @@ -1144,8 +1653,8 @@ struct cmap unsigned int encoding_id) const { EncodingRecord key; - key.platformID.set (platform_id); - key.encodingID.set (encoding_id); + key.platformID = platform_id; + key.encodingID = encoding_id; const EncodingRecord &result = encodingRecord.bsearch (key); if (!result.subtable) @@ -1154,6 +1663,28 @@ struct cmap return &(this+result.subtable); } + const EncodingRecord *find_encodingrec (unsigned int platform_id, + unsigned int encoding_id) const + { + EncodingRecord key; + key.platformID = platform_id; + key.encodingID = encoding_id; + + return encodingRecord.as_array ().bsearch (key); + } + + bool find_subtable (unsigned format) const + { + auto it = + + hb_iter (encodingRecord) + | hb_map (&EncodingRecord::subtable) + | hb_map (hb_add (this)) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) + ; + + return it.len (); + } + public: bool sanitize (hb_sanitize_context_t *c) const @@ -1165,9 +1696,9 @@ struct cmap } protected: - HBUINT16 version; /* Table version number (0). */ + HBUINT16 version; /* Table version number (0). */ SortedArrayOf - encodingRecord; /* Encoding tables. */ + encodingRecord; /* Encoding tables. */ public: DEFINE_SIZE_ARRAY (4, encodingRecord); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh index 36ec2be984fb4..3e619bd40356c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cbdt-table.hh @@ -21,7 +21,7 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Seigo Nonaka + * Google Author(s): Seigo Nonaka, Calder Kitagawa */ #ifndef HB_OT_COLOR_CBDT_TABLE_HH @@ -43,6 +43,35 @@ namespace OT { +struct cblc_bitmap_size_subset_context_t +{ + const char *cbdt; + unsigned int cbdt_length; + hb_vector_t *cbdt_prime; + unsigned int size; /* INOUT + * Input: old size of IndexSubtable + * Output: new size of IndexSubtable + */ + unsigned int num_tables; /* INOUT + * Input: old number of subtables. + * Output: new number of subtables. + */ + hb_codepoint_t start_glyph; /* OUT */ + hb_codepoint_t end_glyph; /* OUT */ +}; + +static inline bool +_copy_data_to_cbdt (hb_vector_t *cbdt_prime, + const void *data, + unsigned length) +{ + unsigned int new_len = cbdt_prime->length + length; + if (unlikely (!cbdt_prime->alloc (new_len))) return false; + memcpy (cbdt_prime->arrayZ + cbdt_prime->length, data, length); + cbdt_prime->length = new_len; + return true; +} + struct SmallGlyphMetrics { bool sanitize (hb_sanitize_context_t *c) const @@ -51,12 +80,12 @@ struct SmallGlyphMetrics return_trace (c->check_struct (this)); } - void get_extents (hb_glyph_extents_t *extents) const + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) const { - extents->x_bearing = bearingX; - extents->y_bearing = bearingY; - extents->width = width; - extents->height = - (hb_position_t) height; + extents->x_bearing = font->em_scale_x (bearingX); + extents->y_bearing = font->em_scale_y (bearingY); + extents->width = font->em_scale_x (width); + extents->height = font->em_scale_y (-static_cast(height)); } HBUINT8 height; @@ -65,7 +94,7 @@ struct SmallGlyphMetrics HBINT8 bearingY; HBUINT8 advance; public: - DEFINE_SIZE_STATIC(5); + DEFINE_SIZE_STATIC (5); }; struct BigGlyphMetrics : SmallGlyphMetrics @@ -74,7 +103,7 @@ struct BigGlyphMetrics : SmallGlyphMetrics HBINT8 vertBearingY; HBUINT8 vertAdvance; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; struct SBitLineMetrics @@ -98,7 +127,7 @@ struct SBitLineMetrics HBINT8 padding1; HBINT8 padding2; public: - DEFINE_SIZE_STATIC(12); + DEFINE_SIZE_STATIC (12); }; @@ -118,7 +147,7 @@ struct IndexSubtableHeader HBUINT16 imageFormat; HBUINT32 imageDataOffset; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; template @@ -143,11 +172,23 @@ struct IndexSubtableFormat1Or3 return true; } + bool add_offset (hb_serialize_context_t *c, + unsigned int offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + Offset embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + IndexSubtableHeader header; - UnsizedArrayOf > + UnsizedArrayOf> offsetArrayZ; public: - DEFINE_SIZE_ARRAY(8, offsetArrayZ); + DEFINE_SIZE_ARRAY (8, offsetArrayZ); }; struct IndexSubtableFormat1 : IndexSubtableFormat1Or3 {}; @@ -159,35 +200,153 @@ struct IndexSubtable { TRACE_SANITIZE (this); if (!u.header.sanitize (c)) return_trace (false); - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 1: return_trace (u.format1.sanitize (c, glyph_count)); case 3: return_trace (u.format3.sanitize (c, glyph_count)); default:return_trace (true); } } + bool + finish_subtable (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_glyphs, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: { + if (!u.format3.add_offset (c, local_offset, size)) + return_trace (false); + if (!(num_glyphs & 0x01)) // Pad to 32-bit alignment if needed. + return_trace (u.format3.add_offset (c, 0, size)); + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: case 4: // No-op. + case 5: // Pad to 32-bit aligned. + default: return_trace (false); + } + } + + bool + fill_missing_glyphs (hb_serialize_context_t *c, + unsigned int cbdt_prime_len, + unsigned int num_missing, + unsigned int *size /* OUT (accumulated) */, + unsigned int *num_glyphs /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + + unsigned int local_offset = cbdt_prime_len - u.header.imageDataOffset; + switch (u.header.indexFormat) + { + case 1: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format1.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + case 3: { + for (unsigned int i = 0; i < num_missing; i++) + { + if (unlikely (!u.format3.add_offset (c, local_offset, size))) + return_trace (false); + *num_glyphs += 1; + } + return_trace (true); + } + // TODO: implement 2, 4, 5. + case 2: // Add empty space in cbdt_prime?. + case 4: case 5: // No-op as sparse is supported. + default: return_trace (false); + } + } + + bool + copy_glyph_at_idx (hb_serialize_context_t *c, unsigned int idx, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */, + IndexSubtable *subtable_prime /* INOUT */, + unsigned int *size /* OUT (accumulated) */) const + { + TRACE_SERIALIZE (this); + + unsigned int offset, length, format; + if (unlikely (!get_image_data (idx, &offset, &length, &format))) return_trace (false); + if (unlikely (offset > cbdt_length || cbdt_length - offset < length)) return_trace (false); + + auto *header_prime = subtable_prime->get_header (); + unsigned int new_local_offset = cbdt_prime->length - (unsigned int) header_prime->imageDataOffset; + if (unlikely (!_copy_data_to_cbdt (cbdt_prime, cbdt + offset, length))) return_trace (false); + + return_trace (subtable_prime->add_offset (c, new_local_offset, size)); + } + + bool + add_offset (hb_serialize_context_t *c, unsigned int local_offset, + unsigned int *size /* OUT (accumulated) */) + { + TRACE_SERIALIZE (this); + switch (u.header.indexFormat) + { + case 1: return_trace (u.format1.add_offset (c, local_offset, size)); + case 3: return_trace (u.format3.add_offset (c, local_offset, size)); + // TODO: Implement tables 2, 4, 5 + case 2: // Should be a no-op. + case 4: case 5: // Handle sparse cases. + default: return_trace (false); + } + } + bool get_extents (hb_glyph_extents_t *extents HB_UNUSED) const { - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 2: case 5: /* TODO */ case 1: case 3: case 4: /* Variable-metrics formats do not have metrics here. */ default:return (false); } } - bool get_image_data (unsigned int idx, - unsigned int *offset, - unsigned int *length, - unsigned int *format) const + bool + get_image_data (unsigned int idx, unsigned int *offset, + unsigned int *length, unsigned int *format) const { *format = u.header.imageFormat; - switch (u.header.indexFormat) { + switch (u.header.indexFormat) + { case 1: return u.format1.get_image_data (idx, offset, length); case 3: return u.format3.get_image_data (idx, offset, length); default: return false; } } + const IndexSubtableHeader* get_header () const { return &u.header; } + + void populate_header (unsigned index_format, + unsigned image_format, + unsigned int image_data_offset, + unsigned int *size) + { + u.header.indexFormat = index_format; + u.header.imageFormat = image_format; + u.header.imageDataOffset = image_data_offset; + switch (u.header.indexFormat) + { + case 1: *size += IndexSubtableFormat1::min_size; break; + case 3: *size += IndexSubtableFormat3::min_size; break; + } + } + protected: union { IndexSubtableHeader header; @@ -209,12 +368,135 @@ struct IndexSubtableRecord offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1)); } - bool get_extents (hb_glyph_extents_t *extents, - const void *base) const + const IndexSubtable* get_subtable (const void *base) const + { + return &(base+offsetToSubtable); + } + + bool add_new_subtable (hb_subset_context_t* c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + IndexSubtableRecord *record, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed (); + if (unlikely (!subtable)) return_trace (false); + if (unlikely (!c->serializer->extend_min (subtable))) return_trace (false); + + auto *old_subtable = get_subtable (base); + auto *old_header = old_subtable->get_header (); + + subtable->populate_header (old_header->indexFormat, + old_header->imageFormat, + bitmap_size_context->cbdt_prime->length, + &bitmap_size_context->size); + + unsigned int num_glyphs = 0; + bool early_exit = false; + for (unsigned int i = *start; i < lookup->length; i++) + { + hb_codepoint_t new_gid = (*lookup)[i].first; + const IndexSubtableRecord *next_record = (*lookup)[i].second; + const IndexSubtable *next_subtable = next_record->get_subtable (base); + auto *next_header = next_subtable->get_header (); + if (next_header != old_header) + { + *start = i; + early_exit = true; + break; + } + unsigned int num_missing = record->add_glyph_for_subset (new_gid); + if (unlikely (!subtable->fill_missing_glyphs (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_missing, + &bitmap_size_context->size, + &num_glyphs))) + return_trace (false); + + hb_codepoint_t old_gid = 0; + c->plan->old_gid_for_new_gid (new_gid, &old_gid); + if (old_gid < next_record->firstGlyphIndex) + return_trace (false); + + unsigned int old_idx = (unsigned int) old_gid - next_record->firstGlyphIndex; + if (unlikely (!next_subtable->copy_glyph_at_idx (c->serializer, + old_idx, + bitmap_size_context->cbdt, + bitmap_size_context->cbdt_length, + bitmap_size_context->cbdt_prime, + subtable, + &bitmap_size_context->size))) + return_trace (false); + num_glyphs += 1; + } + if (!early_exit) + *start = lookup->length; + if (unlikely (!subtable->finish_subtable (c->serializer, + bitmap_size_context->cbdt_prime->length, + num_glyphs, + &bitmap_size_context->size))) + return_trace (false); + return_trace (true); + } + + bool add_new_record (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context, + const hb_vector_t> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t* records /* INOUT */) const + { + TRACE_SERIALIZE (this); + auto snap = c->serializer->snapshot (); + unsigned int old_size = bitmap_size_context->size; + unsigned int old_cbdt_prime_length = bitmap_size_context->cbdt_prime->length; + + // Set to invalid state to indicate filling glyphs is not yet started. + if (unlikely (!records->resize (records->length + 1))) + return_trace (c->serializer->check_success (false)); + + (*records)[records->length - 1].firstGlyphIndex = 1; + (*records)[records->length - 1].lastGlyphIndex = 0; + bitmap_size_context->size += IndexSubtableRecord::min_size; + + c->serializer->push (); + + if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), lookup, base, start))) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + bitmap_size_context->cbdt_prime->shrink (old_cbdt_prime_length); + bitmap_size_context->size = old_size; + records->resize (records->length - 1); + return_trace (false); + } + + bitmap_size_context->num_tables += 1; + return_trace (true); + } + + unsigned int add_glyph_for_subset (hb_codepoint_t gid) { - return (base+offsetToSubtable).get_extents (extents); + if (firstGlyphIndex > lastGlyphIndex) + { + firstGlyphIndex = gid; + lastGlyphIndex = gid; + return 0; + } + // TODO maybe assert? this shouldn't occur. + if (lastGlyphIndex > gid) + return 0; + unsigned int num_missing = (unsigned int) (gid - lastGlyphIndex - 1); + lastGlyphIndex = gid; + return num_missing; } + bool get_extents (hb_glyph_extents_t *extents, const void *base) const + { return (base+offsetToSubtable).get_extents (extents); } + bool get_image_data (unsigned int gid, const void *base, unsigned int *offset, @@ -226,11 +508,11 @@ struct IndexSubtableRecord offset, length, format); } - GlyphID firstGlyphIndex; - GlyphID lastGlyphIndex; + HBGlyphID firstGlyphIndex; + HBGlyphID lastGlyphIndex; LOffsetTo offsetToSubtable; public: - DEFINE_SIZE_STATIC(8); + DEFINE_SIZE_STATIC (8); }; struct IndexSubtableArray @@ -243,6 +525,79 @@ struct IndexSubtableArray return_trace (indexSubtablesZ.sanitize (c, count, this)); } + void + build_lookup (hb_subset_context_t *c, cblc_bitmap_size_subset_context_t *bitmap_size_context, + hb_vector_t> *lookup /* OUT */) const + { + bool start_glyph_is_set = false; + for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_glyphs (); new_gid++) + { + hb_codepoint_t old_gid; + if (unlikely (!c->plan->old_gid_for_new_gid (new_gid, &old_gid))) continue; + + const IndexSubtableRecord* record = find_table (old_gid, bitmap_size_context->num_tables); + if (unlikely (!record)) continue; + + // Don't add gaps to the lookup. The best way to determine if a glyph is a + // gap is that it has no image data. + unsigned int offset, length, format; + if (unlikely (!record->get_image_data (old_gid, this, &offset, &length, &format))) continue; + + lookup->push (hb_pair_t (new_gid, record)); + + if (!start_glyph_is_set) + { + bitmap_size_context->start_glyph = new_gid; + start_glyph_is_set = true; + } + + bitmap_size_context->end_glyph = new_gid; + } + } + + bool + subset (hb_subset_context_t *c, + cblc_bitmap_size_subset_context_t *bitmap_size_context) const + { + TRACE_SUBSET (this); + + auto *dst = c->serializer->start_embed (); + if (unlikely (!dst)) return_trace (false); + + hb_vector_t> lookup; + build_lookup (c, bitmap_size_context, &lookup); + if (unlikely (lookup.in_error ())) + return c->serializer->check_success (false); + + bitmap_size_context->size = 0; + bitmap_size_context->num_tables = 0; + hb_vector_t records; + for (unsigned int start = 0; start < lookup.length;) + { + if (unlikely (!lookup[start].second->add_new_record (c, bitmap_size_context, &lookup, this, &start, &records))) + { + // Discard any leftover pushes to the serializer from successful records. + for (unsigned int i = 0; i < records.length; i++) + c->serializer->pop_discard (); + return_trace (false); + } + } + + /* Workaround to ensure offset ordering is from least to greatest when + * resolving links. */ + hb_vector_t objidxs; + for (unsigned int i = 0; i < records.length; i++) + objidxs.push (c->serializer->pop_pack ()); + for (unsigned int i = 0; i < records.length; i++) + { + IndexSubtableRecord* record = c->serializer->embed (records[i]); + if (unlikely (!record)) return_trace (false); + c->serializer->add_link (record->offsetToSubtable, objidxs[records.length - 1 - i]); + } + return_trace (true); + } + public: const IndexSubtableRecord* find_table (hb_codepoint_t glyph, unsigned int numTables) const { @@ -274,14 +629,48 @@ struct BitmapSizeTable vertical.sanitize (c)); } - const IndexSubtableRecord *find_table (hb_codepoint_t glyph, - const void *base, - const void **out_base) const + const IndexSubtableRecord * + find_table (hb_codepoint_t glyph, const void *base, const void **out_base) const { *out_base = &(base+indexSubtableArrayOffset); return (base+indexSubtableArrayOffset).find_table (glyph, numberOfIndexSubtables); } + bool + subset (hb_subset_context_t *c, const void *base, + const char *cbdt, unsigned int cbdt_length, + hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + auto *out_table = c->serializer->embed (this); + if (unlikely (!out_table)) return_trace (false); + + cblc_bitmap_size_subset_context_t bitmap_size_context; + bitmap_size_context.cbdt = cbdt; + bitmap_size_context.cbdt_length = cbdt_length; + bitmap_size_context.cbdt_prime = cbdt_prime; + bitmap_size_context.size = indexTablesSize; + bitmap_size_context.num_tables = numberOfIndexSubtables; + bitmap_size_context.start_glyph = 1; + bitmap_size_context.end_glyph = 0; + + if (!out_table->indexSubtableArrayOffset.serialize_subset (c, + indexSubtableArrayOffset, + base, + &bitmap_size_context)) + return_trace (false); + if (!bitmap_size_context.size || + !bitmap_size_context.num_tables || + bitmap_size_context.start_glyph > bitmap_size_context.end_glyph) + return_trace (false); + + out_table->indexTablesSize = bitmap_size_context.size; + out_table->numberOfIndexSubtables = bitmap_size_context.num_tables; + out_table->startGlyphIndex = bitmap_size_context.start_glyph; + out_table->endGlyphIndex = bitmap_size_context.end_glyph; + return_trace (true); + } + protected: LNNOffsetTo indexSubtableArrayOffset; @@ -290,14 +679,14 @@ struct BitmapSizeTable HBUINT32 colorRef; SBitLineMetrics horizontal; SBitLineMetrics vertical; - GlyphID startGlyphIndex; - GlyphID endGlyphIndex; + HBGlyphID startGlyphIndex; + HBGlyphID endGlyphIndex; HBUINT8 ppemX; HBUINT8 ppemY; HBUINT8 bitDepth; HBINT8 flags; public: - DEFINE_SIZE_STATIC(48); + DEFINE_SIZE_STATIC (48); }; @@ -310,7 +699,7 @@ struct GlyphBitmapDataFormat17 SmallGlyphMetrics glyphMetrics; LArrayOf data; public: - DEFINE_SIZE_ARRAY(9, data); + DEFINE_SIZE_ARRAY (9, data); }; struct GlyphBitmapDataFormat18 @@ -318,14 +707,14 @@ struct GlyphBitmapDataFormat18 BigGlyphMetrics glyphMetrics; LArrayOf data; public: - DEFINE_SIZE_ARRAY(12, data); + DEFINE_SIZE_ARRAY (12, data); }; struct GlyphBitmapDataFormat19 { LArrayOf data; public: - DEFINE_SIZE_ARRAY(4, data); + DEFINE_SIZE_ARRAY (4, data); }; struct CBLC @@ -342,22 +731,60 @@ struct CBLC sizeTables.sanitize (c, this)); } + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t* cbdt_prime) + { + hb_blob_t *cbdt_prime_blob = hb_blob_create (cbdt_prime->arrayZ, + cbdt_prime->length, + HB_MEMORY_MODE_WRITABLE, + cbdt_prime->arrayZ, + free); + cbdt_prime->init (); // Leak arrayZ to the blob. + bool ret = c->plan->add_table (HB_OT_TAG_CBDT, cbdt_prime_blob); + hb_blob_destroy (cbdt_prime_blob); + return ret; + } + + bool + subset_size_table (hb_subset_context_t *c, const BitmapSizeTable& table, + const char *cbdt /* IN */, unsigned int cbdt_length, + CBLC *cblc_prime /* INOUT */, hb_vector_t *cbdt_prime /* INOUT */) const + { + TRACE_SUBSET (this); + cblc_prime->sizeTables.len++; + + auto snap = c->serializer->snapshot (); + auto cbdt_prime_len = cbdt_prime->length; + + if (!table.subset (c, this, cbdt, cbdt_length, cbdt_prime)) + { + cblc_prime->sizeTables.len--; + c->serializer->revert (snap); + cbdt_prime->shrink (cbdt_prime_len); + return_trace (false); + } + return_trace (true); + } + + // Implemented in cc file as it depends on definition of CBDT. + HB_INTERNAL bool subset (hb_subset_context_t *c) const; + protected: const BitmapSizeTable &choose_strike (hb_font_t *font) const { unsigned count = sizeTables.len; if (unlikely (!count)) - return Null(BitmapSizeTable); + return Null (BitmapSizeTable); - unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem); + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); if (!requested_ppem) requested_ppem = 1<<30; /* Choose largest strike. */ unsigned int best_i = 0; - unsigned int best_ppem = MAX (sizeTables[0].ppemX, sizeTables[0].ppemY); + unsigned int best_ppem = hb_max (sizeTables[0].ppemX, sizeTables[0].ppemY); for (unsigned int i = 1; i < count; i++) { - unsigned int ppem = MAX (sizeTables[i].ppemX, sizeTables[i].ppemY); + unsigned int ppem = hb_max (sizeTables[i].ppemX, sizeTables[i].ppemY); if ((requested_ppem <= ppem && ppem < best_ppem) || (requested_ppem > best_ppem && ppem > best_ppem)) { @@ -373,7 +800,7 @@ struct CBLC FixedVersion<> version; LArrayOf sizeTables; public: - DEFINE_SIZE_ARRAY(8, sizeTables); + DEFINE_SIZE_ARRAY (8, sizeTables); }; struct CBDT @@ -384,8 +811,8 @@ struct CBDT { void init (hb_face_t *face) { - cblc = hb_sanitize_context_t().reference_table (face); - cbdt = hb_sanitize_context_t().reference_table (face); + cblc = hb_sanitize_context_t ().reference_table (face); + cbdt = hb_sanitize_context_t ().reference_table (face); upem = hb_face_get_upem (face); } @@ -396,8 +823,8 @@ struct CBDT this->cbdt.destroy (); } - bool get_extents (hb_font_t *font, hb_codepoint_t glyph, - hb_glyph_extents_t *extents) const + bool + get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -412,48 +839,42 @@ struct CBDT if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) return false; + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return false; + + switch (image_format) { - unsigned int cbdt_len = cbdt.get_length (); - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + case 17: { + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return false; - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return false; - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat17.glyphMetrics.get_extents (extents); - break; - } - case 18: { - if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) - return false; - const GlyphBitmapDataFormat18& glyphFormat18 = - StructAtOffset (this->cbdt, image_offset); - glyphFormat18.glyphMetrics.get_extents (extents); - break; - } - default: - // TODO: Support other image formats. - return false; - } + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + glyphFormat17.glyphMetrics.get_extents (font, extents); + break; + } + case 18: { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return false; + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + glyphFormat18.glyphMetrics.get_extents (font, extents); + break; + } + default: return false; /* TODO: Support other image formats. */ } /* Convert to font units. */ - double x_scale = upem / (double) strike.ppemX; - double y_scale = upem / (double) strike.ppemY; - extents->x_bearing = round (extents->x_bearing * x_scale); - extents->y_bearing = round (extents->y_bearing * y_scale); - extents->width = round (extents->width * x_scale); - extents->height = round (extents->height * y_scale); + float x_scale = upem / (float) strike.ppemX; + float y_scale = upem / (float) strike.ppemY; + extents->x_bearing = roundf (extents->x_bearing * x_scale); + extents->y_bearing = roundf (extents->y_bearing * y_scale); + extents->width = roundf (extents->width * x_scale); + extents->height = roundf (extents->height * y_scale); return true; } - hb_blob_t* reference_png (hb_font_t *font, - hb_codepoint_t glyph) const + hb_blob_t* + reference_png (hb_font_t *font, hb_codepoint_t glyph) const { const void *base; const BitmapSizeTable &strike = this->cblc->choose_strike (font); @@ -465,44 +886,41 @@ struct CBDT if (!subtable_record->get_image_data (glyph, base, &image_offset, &image_length, &image_format)) return hb_blob_get_empty (); + unsigned int cbdt_len = cbdt.get_length (); + if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + return hb_blob_get_empty (); + + switch (image_format) + { + case 17: { - unsigned int cbdt_len = cbdt.get_length (); - if (unlikely (image_offset > cbdt_len || cbdt_len - image_offset < image_length)) + if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) return hb_blob_get_empty (); - - switch (image_format) - { - case 17: { - if (unlikely (image_length < GlyphBitmapDataFormat17::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat17& glyphFormat17 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat17::min_size, - glyphFormat17.data.len); - } - case 18: { - if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat18& glyphFormat18 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat18::min_size, - glyphFormat18.data.len); - } - case 19: { - if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) - return hb_blob_get_empty (); - const GlyphBitmapDataFormat19& glyphFormat19 = - StructAtOffset (this->cbdt, image_offset); - return hb_blob_create_sub_blob (cbdt.get_blob (), - image_offset + GlyphBitmapDataFormat19::min_size, - glyphFormat19.data.len); - } - } + auto &glyphFormat17 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat17::min_size, + glyphFormat17.data.len); + } + case 18: + { + if (unlikely (image_length < GlyphBitmapDataFormat18::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat18 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat18::min_size, + glyphFormat18.data.len); + } + case 19: + { + if (unlikely (image_length < GlyphBitmapDataFormat19::min_size)) + return hb_blob_get_empty (); + auto &glyphFormat19 = StructAtOffset (this->cbdt, image_offset); + return hb_blob_create_sub_blob (cbdt.get_blob (), + image_offset + GlyphBitmapDataFormat19::min_size, + glyphFormat19.data.len); + } + default: return hb_blob_get_empty (); /* TODO: Support other image formats. */ } - - return hb_blob_get_empty (); } bool has_data () const { return cbdt.get_length (); } @@ -525,9 +943,41 @@ struct CBDT FixedVersion<> version; UnsizedArrayOf dataZ; public: - DEFINE_SIZE_ARRAY(4, dataZ); + DEFINE_SIZE_ARRAY (4, dataZ); }; +inline bool +CBLC::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + auto *cblc_prime = c->serializer->start_embed (); + + // Use a vector as a secondary buffer as the tables need to be built in parallel. + hb_vector_t cbdt_prime; + + if (unlikely (!cblc_prime)) return_trace (false); + if (unlikely (!c->serializer->extend_min (cblc_prime))) return_trace (false); + cblc_prime->version = version; + + hb_blob_t* cbdt_blob = hb_sanitize_context_t ().reference_table (c->plan->source); + unsigned int cbdt_length; + CBDT* cbdt = (CBDT *) hb_blob_get_data (cbdt_blob, &cbdt_length); + if (unlikely (cbdt_length < CBDT::min_size)) + { + hb_blob_destroy (cbdt_blob); + return_trace (false); + } + _copy_data_to_cbdt (&cbdt_prime, cbdt, CBDT::min_size); + + for (const BitmapSizeTable& table : + sizeTables.iter ()) + subset_size_table (c, table, (const char *) cbdt, cbdt_length, cblc_prime, &cbdt_prime); + + hb_blob_destroy (cbdt_blob); + + return_trace (CBLC::sink_cbdt (c, &cbdt_prime)); +} + struct CBDT_accelerator_t : CBDT::accelerator_t {}; } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh index 362b4a14de6c9..21821d458a540 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-colr-table.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -20,6 +21,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa */ #ifndef HB_OT_COLOR_COLR_TABLE_HH @@ -39,6 +42,8 @@ namespace OT { struct LayerRecord { + operator hb_ot_color_layer_t () const { return {glyphId, colorIdx}; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -46,7 +51,7 @@ struct LayerRecord } public: - GlyphID glyphId; /* Glyph ID of layer glyph */ + HBGlyphID glyphId; /* Glyph ID of layer glyph */ Index colorIdx; /* Index value to use with a * selected color palette. * An index value of 0xFFFF @@ -73,7 +78,7 @@ struct BaseGlyphRecord } public: - GlyphID glyphId; /* Glyph ID of reference glyph */ + HBGlyphID glyphId; /* Glyph ID of reference glyph */ HBUINT16 firstLayerIdx; /* Index (from beginning of * the Layer Records) to the * layer record. There will be @@ -98,22 +103,50 @@ struct COLR { const BaseGlyphRecord &record = (this+baseGlyphsZ).bsearch (numBaseGlyphs, glyph); - hb_array_t all_layers ((this+layersZ).arrayZ, numLayers); + hb_array_t all_layers = (this+layersZ).as_array (numLayers); hb_array_t glyph_layers = all_layers.sub_array (record.firstLayerIdx, record.numLayers); if (count) { - hb_array_t segment_layers = glyph_layers.sub_array (start_offset, *count); - *count = segment_layers.length; - for (unsigned int i = 0; i < segment_layers.length; i++) - { - layers[i].glyph = segment_layers.arrayZ[i].glyphId; - layers[i].color_index = segment_layers.arrayZ[i].colorIdx; - } + + glyph_layers.sub_array (start_offset, count) + | hb_sink (hb_array (layers, *count)) + ; } return glyph_layers.length; } + struct accelerator_t + { + accelerator_t () {} + ~accelerator_t () { fini (); } + + void init (hb_face_t *face) + { colr = hb_sanitize_context_t ().reference_table (face); } + + void fini () { this->colr.destroy (); } + + bool is_valid () { return colr.get_blob ()->length; } + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { colr->closure_glyphs (glyph, related_ids); } + + private: + hb_blob_ptr_t colr; + }; + + void closure_glyphs (hb_codepoint_t glyph, + hb_set_t *related_ids /* OUT */) const + { + const BaseGlyphRecord *record = get_base_glyph_record (glyph); + if (!record) return; + + auto glyph_layers = (this+layersZ).as_array (numLayers).sub_array (record->firstLayerIdx, + record->numLayers); + if (!glyph_layers.length) return; + related_ids->add_array (&glyph_layers[0].glyphId, glyph_layers.length, LayerRecord::min_size); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -122,12 +155,117 @@ struct COLR (this+layersZ).sanitize (c, numLayers))); } + template + bool serialize (hb_serialize_context_t *c, + unsigned version, + BaseIterator base_it, + LayerIterator layer_it) + { + TRACE_SERIALIZE (this); + if (unlikely (base_it.len () != layer_it.len ())) + return_trace (false); + + if (unlikely (!c->extend_min (this))) return_trace (false); + this->version = version; + numLayers = 0; + numBaseGlyphs = base_it.len (); + baseGlyphsZ = COLR::min_size; + layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size; + + for (const hb_item_type _ : + base_it.iter ()) + { + auto* record = c->embed (_); + if (unlikely (!record)) return_trace (false); + record->firstLayerIdx = numLayers; + numLayers += record->numLayers; + } + + for (const hb_item_type& _ : + layer_it.iter ()) + _.as_array ().copy (c); + + return_trace (true); + } + + const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const + { + if ((unsigned int) gid == 0) // Ignore notdef. + return nullptr; + const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) gid); + if ((record && (hb_codepoint_t) record->glyphId != gid)) + record = nullptr; + return record; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + + auto base_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map_retains_sorting ([&](hb_codepoint_t new_gid) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + if (unlikely (!old_record)) + return hb_pair_t (false, Null (BaseGlyphRecord)); + + BaseGlyphRecord new_record; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t (true, new_record); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + auto layer_it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + const BaseGlyphRecord* old_record = get_base_glyph_record (old_gid); + hb_vector_t out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t> (false, out_layers); + + auto layers = (this+layersZ).as_array (numLayers).sub_array (old_record->firstLayerIdx, + old_record->numLayers); + out_layers.resize (layers.length); + for (unsigned int i = 0; i < layers.length; i++) { + out_layers[i] = layers[i]; + hb_codepoint_t new_gid = 0; + if (unlikely (!c->plan->new_gid_for_old_gid (out_layers[i].glyphId, &new_gid))) + return hb_pair_t> (false, out_layers); + out_layers[i].glyphId = new_gid; + } + + return hb_pair_t> (true, out_layers); + }) + | hb_filter (hb_first) + | hb_map_retains_sorting (hb_second) + ; + + if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ())) + return_trace (false); + + COLR *colr_prime = c->serializer->start_embed (); + return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it)); + } + protected: HBUINT16 version; /* Table version number (starts at 0). */ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */ - LNNOffsetTo > + LNNOffsetTo> baseGlyphsZ; /* Offset to Base Glyph records. */ - LNNOffsetTo > + LNNOffsetTo> layersZ; /* Offset to Layer Records. */ HBUINT16 numLayers; /* Number of Layer Records. */ public: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh index f4ef697341780..f5f642d6bfac0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-cpal-table.hh @@ -87,15 +87,15 @@ struct CPALV1Tail } protected: - LNNOffsetTo > + LNNOffsetTo> paletteFlagsZ; /* Offset from the beginning of CPAL table to * the Palette Type Array. Set to 0 if no array * is provided. */ - LNNOffsetTo > + LNNOffsetTo> paletteLabelsZ; /* Offset from the beginning of CPAL table to * the palette labels array. Set to 0 if no * array is provided. */ - LNNOffsetTo > + LNNOffsetTo> colorLabelsZ; /* Offset from the beginning of CPAL table to * the color labels array. Set to 0 * if no array is provided. */ @@ -115,7 +115,7 @@ struct CPAL { return min_size + numPalettes * sizeof (colorRecordIndicesZ[0]); } unsigned int get_palette_count () const { return numPalettes; } - unsigned int get_color_count () const { return numColors; } + unsigned int get_color_count () const { return numColors; } hb_ot_color_palette_flags_t get_palette_flags (unsigned int palette_index) const { return v1 ().get_palette_flags (this, palette_index, numPalettes); } @@ -142,12 +142,9 @@ struct CPAL numColors); if (color_count) { - hb_array_t segment_colors = palette_colors.sub_array (start_offset, *color_count); - /* Always return numColors colors per palette even if it has out-of-bounds start index. */ - unsigned int count = MIN (MAX (numColors - start_offset, 0), *color_count); - *color_count = count; - for (unsigned int i = 0; i < count; i++) - colors[i] = segment_colors[i]; /* Bound-checked read. */ + + palette_colors.sub_array (start_offset, color_count) + | hb_sink (hb_array (colors, *color_count)) + ; } return numColors; } @@ -155,7 +152,7 @@ struct CPAL private: const CPALV1Tail& v1 () const { - if (version == 0) return Null(CPALV1Tail); + if (version == 0) return Null (CPALV1Tail); return StructAfter (*this); } @@ -176,7 +173,7 @@ struct CPAL HBUINT16 numPalettes; /* Number of palettes in the table. */ HBUINT16 numColorRecords; /* Total number of color records, combined for * all palettes. */ - LNNOffsetTo > + LNNOffsetTo> colorRecordsZ; /* Offset from the beginning of CPAL table to * the first ColorRecord. */ UnsizedArrayOf diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh index 5b89796d561b5..27b935edbbccb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-sbix-table.hh @@ -1,5 +1,6 @@ /* * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2020 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * @@ -20,12 +21,15 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Calder Kitagawa */ #ifndef HB_OT_COLOR_SBIX_TABLE_HH #define HB_OT_COLOR_SBIX_TABLE_HH #include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" /* * sbix -- Standard Bitmap Graphics @@ -40,6 +44,20 @@ namespace OT { struct SBIXGlyph { + SBIXGlyph* copy (hb_serialize_context_t *c, unsigned int data_length) const + { + TRACE_SERIALIZE (this); + SBIXGlyph* new_glyph = c->start_embed (); + if (unlikely (!new_glyph)) return_trace (nullptr); + if (unlikely (!c->extend_min (new_glyph))) return_trace (nullptr); + + new_glyph->xOffset = xOffset; + new_glyph->yOffset = yOffset; + new_glyph->graphicType = graphicType; + data.copy (c, data_length); + return_trace (new_glyph); + } + HBINT16 xOffset; /* The horizontal (x-axis) offset from the left * edge of the graphic to the glyph’s origin. * That is, the x-coordinate of the point on the @@ -62,6 +80,9 @@ struct SBIXGlyph struct SBIXStrike { + static unsigned int get_size (unsigned num_glyphs) + { return min_size + num_glyphs * HBUINT32::static_size; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -116,16 +137,59 @@ struct SBIXStrike return hb_blob_create_sub_blob (sbix_blob, glyph_offset, glyph_length); } + bool subset (hb_subset_context_t *c, unsigned int available_len) const + { + TRACE_SUBSET (this); + unsigned int num_output_glyphs = c->plan->num_output_glyphs (); + + auto* out = c->serializer->start_embed (); + if (unlikely (!out)) return_trace (false); + auto snap = c->serializer->snapshot (); + if (unlikely (!c->serializer->extend (*out, num_output_glyphs + 1))) return_trace (false); + out->ppem = ppem; + out->resolution = resolution; + HBUINT32 head; + head = get_size (num_output_glyphs + 1); + + bool has_glyphs = false; + for (unsigned new_gid = 0; new_gid < num_output_glyphs; new_gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid) || + unlikely (imageOffsetsZ[old_gid].is_null () || + imageOffsetsZ[old_gid + 1].is_null () || + imageOffsetsZ[old_gid + 1] <= imageOffsetsZ[old_gid] || + imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid] <= SBIXGlyph::min_size) || + (unsigned int) imageOffsetsZ[old_gid + 1] > available_len) + { + out->imageOffsetsZ[new_gid] = head; + continue; + } + has_glyphs = true; + unsigned int delta = imageOffsetsZ[old_gid + 1] - imageOffsetsZ[old_gid]; + unsigned int glyph_data_length = delta - SBIXGlyph::min_size; + if (!(this+imageOffsetsZ[old_gid]).copy (c->serializer, glyph_data_length)) + return_trace (false); + out->imageOffsetsZ[new_gid] = head; + head += delta; + } + if (has_glyphs) + out->imageOffsetsZ[num_output_glyphs] = head; + else + c->serializer->revert (snap); + return_trace (has_glyphs); + } + public: HBUINT16 ppem; /* The PPEM size for which this strike was designed. */ HBUINT16 resolution; /* The device pixel density (in PPI) for which this * strike was designed. (E.g., 96 PPI, 192 PPI.) */ protected: - UnsizedArrayOf > + UnsizedArrayOf> imageOffsetsZ; /* Offset from the beginning of the strike data header * to bitmap data for an individual glyph ID. */ public: - DEFINE_SIZE_STATIC (8); + DEFINE_SIZE_ARRAY (4, imageOffsetsZ); }; struct sbix @@ -140,7 +204,7 @@ struct sbix { void init (hb_face_t *face) { - table = hb_sanitize_context_t().reference_table (face); + table = hb_sanitize_context_t ().reference_table (face); num_glyphs = face->get_num_glyphs (); } void fini () { table.destroy (); } @@ -173,9 +237,9 @@ struct sbix { unsigned count = table->strikes.len; if (unlikely (!count)) - return Null(SBIXStrike); + return Null (SBIXStrike); - unsigned int requested_ppem = MAX (font->x_ppem, font->y_ppem); + unsigned int requested_ppem = hb_max (font->x_ppem, font->y_ppem); if (!requested_ppem) requested_ppem = 1<<30; /* Choose largest strike. */ /* TODO Add DPI sensitivity as well? */ @@ -235,18 +299,25 @@ struct sbix const PNGHeader &png = *blob->as(); extents->x_bearing = x_offset; - extents->y_bearing = y_offset; + extents->y_bearing = png.IHDR.height + y_offset; extents->width = png.IHDR.width; - extents->height = png.IHDR.height; + extents->height = -1 * png.IHDR.height; /* Convert to font units. */ if (strike_ppem) { - double scale = font->face->get_upem () / (double) strike_ppem; - extents->x_bearing = round (extents->x_bearing * scale); - extents->y_bearing = round (extents->y_bearing * scale); - extents->width = round (extents->width * scale); - extents->height = round (extents->height * scale); + float scale = font->face->get_upem () / (float) strike_ppem; + extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale); + extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale); + extents->width = font->em_scalef_x (extents->width * scale); + extents->height = font->em_scalef_y (extents->height * scale); + } + else + { + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); } hb_blob_destroy (blob); @@ -268,6 +339,63 @@ struct sbix strikes.sanitize (c, this))); } + bool + add_strike (hb_subset_context_t *c, unsigned i) const + { + if (strikes[i].is_null () || c->source_blob->length < (unsigned) strikes[i]) + return false; + + return (this+strikes[i]).subset (c, c->source_blob->length - (unsigned) strikes[i]); + } + + bool serialize_strike_offsets (hb_subset_context_t *c) const + { + TRACE_SERIALIZE (this); + + auto *out = c->serializer->start_embed> (); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t*> new_strikes; + hb_vector_t objidxs; + for (int i = strikes.len - 1; i >= 0; --i) + { + auto* o = out->serialize_append (c->serializer); + if (unlikely (!o)) return_trace (false); + *o = 0; + auto snap = c->serializer->snapshot (); + c->serializer->push (); + bool ret = add_strike (c, i); + if (!ret) + { + c->serializer->pop_discard (); + out->pop (); + c->serializer->revert (snap); + } + else + { + objidxs.push (c->serializer->pop_pack ()); + new_strikes.push (o); + } + } + for (unsigned int i = 0; i < new_strikes.length; ++i) + c->serializer->add_link (*new_strikes[i], objidxs[new_strikes.length - 1 - i]); + + return_trace (true); + } + + bool subset (hb_subset_context_t* c) const + { + TRACE_SUBSET (this); + + sbix *sbix_prime = c->serializer->start_embed (); + if (unlikely (!sbix_prime)) return_trace (false); + if (unlikely (!c->serializer->embed (this->version))) return_trace (false); + if (unlikely (!c->serializer->embed (this->flags))) return_trace (false); + + return_trace (serialize_strike_offsets (c)); + } + protected: HBUINT16 version; /* Table version number — set to 1 */ HBUINT16 flags; /* Bit 0: Set to 1. Bit 1: Draw outlines. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh index eb0ba22debc16..ccf9ed3365c2d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color-svg-table.hh @@ -62,7 +62,7 @@ struct SVGDocumentIndexEntry * this index entry. */ HBUINT16 endGlyphID; /* The last glyph ID in the range described by * this index entry. Must be >= startGlyphID. */ - LNNOffsetTo > + LNNOffsetTo> svgDoc; /* Offset from the beginning of the SVG Document Index * to an SVG document. Must be non-zero. */ HBUINT32 svgDocLength; /* Length of the SVG document. @@ -80,7 +80,7 @@ struct SVG struct accelerator_t { void init (hb_face_t *face) - { table = hb_sanitize_context_t().reference_table (face); } + { table = hb_sanitize_context_t ().reference_table (face); } void fini () { table.destroy (); } hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const @@ -107,7 +107,7 @@ struct SVG protected: HBUINT16 version; /* Table version (starting at 0). */ - LOffsetTo > + LOffsetTo> svgDocEntries; /* Offset (relative to the start of the SVG table) to the * SVG Documents Index. Must be non-zero. */ /* Array of SVG Document Index Entries. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc index 84aeb96126e8d..d37e134c08ba9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.cc @@ -25,20 +25,21 @@ * Google Author(s): Sascha Brawer, Behdad Esfahbod */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_COLOR + +#include "hb-ot.h" + #include "hb-ot-color-cbdt-table.hh" #include "hb-ot-color-colr-table.hh" #include "hb-ot-color-cpal-table.hh" #include "hb-ot-color-sbix-table.hh" #include "hb-ot-color-svg-table.hh" -#include "hb-ot-face.hh" -#include "hb-ot.h" #include #include -#include "hb-ot-layout.hh" - /** * SECTION:hb-ot-color @@ -47,6 +48,8 @@ * @include: hb-ot.h * * Functions for fetching color-font information from OpenType font faces. + * + * HarfBuzz supports `COLR`/`CPAL`, `sbix`, `CBDT`, and `SVG` color fonts. **/ @@ -57,9 +60,11 @@ /** * hb_ot_color_has_palettes: - * @face: a font face. + * @face: #hb_face_t to work upon + * + * Tests whether a face includes a `CPAL` color-palette table. * - * Returns: whether CPAL table is available. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -71,10 +76,11 @@ hb_ot_color_has_palettes (hb_face_t *face) /** * hb_ot_color_palette_get_count: - * @face: a font face. + * @face: #hb_face_t to work upon * - * Returns: the number of color palettes in @face, or zero if @face has - * no colors. + * Fetches the number of color palettes in a face. + * + * Return value: the number of palettes found * * Since: 2.1.0 */ @@ -86,13 +92,16 @@ hb_ot_color_palette_get_count (hb_face_t *face) /** * hb_ot_color_palette_get_name_id: - * @face: a font face. - * @palette_index: the index of the color palette whose name is being requested. + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the `name` table Name ID that provides display names for + * a `CPAL` color palette. * - * Retrieves the name id of a color palette. For example, a color font can - * have themed palettes like "Spring", "Summer", "Fall", and "Winter". + * Palette display names can be generic (e.g., "Default") or provide + * specific, themed names (e.g., "Spring", "Summer", "Fall", and "Winter"). * - * Returns: an identifier within @face's `name` table. + * Return value: the Named ID found for the palette. * If the requested palette has no name the result is #HB_OT_NAME_ID_INVALID. * * Since: 2.1.0 @@ -106,10 +115,16 @@ hb_ot_color_palette_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_color_get_name_id: - * @face: a font face. - * @color_index: palette entry index. + * @face: #hb_face_t to work upon + * @color_index: The index of the color * - * Returns: Name ID associated with a palette entry, e.g. eye color + * Fetches the `name` table Name ID that provides display names for + * the specificed color in a face's `CPAL` color palette. + * + * Display names can be generic (e.g., "Background") or specific + * (e.g., "Eye color"). + * + * Return value: the Name ID found for the color. * * Since: 2.1.0 */ @@ -122,10 +137,12 @@ hb_ot_color_palette_color_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_get_flags: - * @face: a font face - * @palette_index: the index of the color palette whose flags are being requested + * @face: #hb_face_t to work upon + * @palette_index: The index of the color palette + * + * Fetches the flags defined for a color palette. * - * Returns: the flags for the requested color palette. + * Return value: the #hb_ot_color_palette_flags_t of the requested color palette * * Since: 2.1.0 */ @@ -138,25 +155,22 @@ hb_ot_color_palette_get_flags (hb_face_t *face, /** * hb_ot_color_palette_get_colors: - * @face: a font face. - * @palette_index:the index of the color palette whose colors - * are being requested. - * @start_offset: the index of the first color being requested. - * @color_count: (inout) (optional): on input, how many colors - * can be maximally stored into the @colors array; - * on output, how many colors were actually stored. - * @colors: (array length=color_count) (out) (optional): - * an array of #hb_color_t records. After calling - * this function, @colors will be filled with - * the palette colors. If @colors is NULL, the function - * will just return the number of total colors - * without storing any actual colors; this can be used - * for allocating a buffer of suitable size before calling - * hb_ot_color_palette_get_colors() a second time. - * - * Retrieves the colors in a color palette. - * - * Returns: the total number of colors in the palette. + * @face: #hb_face_t to work upon + * @palette_index: the index of the color palette to query + * @start_offset: offset of the first color to retrieve + * @color_count: (inout) (optional): Input = the maximum number of colors to return; + * Output = the actual number of colors returned (may be zero) + * @colors: (out) (array length=color_count) (nullable): The array of #hb_color_t records found + * + * Fetches a list of the colors in a color palette. + * + * After calling this function, @colors will be filled with the palette + * colors. If @colors is NULL, the function will just return the number + * of total colors without storing any actual colors; this can be used + * for allocating a buffer of suitable size before calling + * hb_ot_color_palette_get_colors() a second time. + * + * Return value: the total number of colors in the palette * * Since: 2.1.0 */ @@ -177,9 +191,11 @@ hb_ot_color_palette_get_colors (hb_face_t *face, /** * hb_ot_color_has_layers: - * @face: a font face. + * @face: #hb_face_t to work upon + * + * Tests whether a face includes any `COLR` color layers. * - * Returns: whether COLR table is available. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -191,14 +207,17 @@ hb_ot_color_has_layers (hb_face_t *face) /** * hb_ot_color_glyph_get_layers: - * @face: a font face. - * @glyph: a layered color glyph id. - * @start_offset: starting offset of layers. - * @count: (inout) (optional): gets number of layers available to be written on buffer - * and returns number of written layers. - * @layers: (array length=count) (out) (optional): layers buffer to buffer. + * @face: #hb_face_t to work upon + * @glyph: The glyph index to query + * @start_offset: offset of the first layer to retrieve + * @layer_count: (inout) (optional): Input = the maximum number of layers to return; + * Output = the actual number of layers returned (may be zero) + * @layers: (out) (array length=layer_count) (nullable): The array of layers found + * + * Fetches a list of all color layers for the specified glyph index in the specified + * face. The list returned will begin at the offset provided. * - * Returns: Total number of layers a layered color glyph have. + * Return value: Total number of layers available for the glyph index queried * * Since: 2.1.0 */ @@ -206,10 +225,10 @@ unsigned int hb_ot_color_glyph_get_layers (hb_face_t *face, hb_codepoint_t glyph, unsigned int start_offset, - unsigned int *count, /* IN/OUT. May be NULL. */ + unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */) { - return face->table.COLR->get_glyph_layers (glyph, start_offset, count, layers); + return face->table.COLR->get_glyph_layers (glyph, start_offset, layer_count, layers); } @@ -219,11 +238,11 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, /** * hb_ot_color_has_svg: - * @face: a font face. + * @face: #hb_face_t to work upon. * - * Check whether @face has SVG glyph images. + * Tests whether a face includes any `SVG` glyph images. * - * Returns true if available, false otherwise. + * Return value: true if data found, false otherwise. * * Since: 2.1.0 */ @@ -235,12 +254,12 @@ hb_ot_color_has_svg (hb_face_t *face) /** * hb_ot_color_glyph_reference_svg: - * @face: a font face. - * @glyph: a svg glyph index. + * @face: #hb_face_t to work upon + * @glyph: a svg glyph index * - * Get SVG document for a glyph. The blob may be either plain text or gzip-encoded. + * Fetches the SVG document for a glyph. The blob may be either plain text or gzip-encoded. * - * Returns: (transfer full): respective svg blob of the glyph, if available. + * Return value: (transfer full): An #hb_blob_t containing the SVG document of the glyph, if available * * Since: 2.1.0 */ @@ -257,11 +276,11 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) /** * hb_ot_color_has_png: - * @face: a font face. + * @face: #hb_face_t to work upon * - * Check whether @face has PNG glyph images (either CBDT or sbix tables). + * Tests whether a face has PNG glyph images (either in `CBDT` or `sbix` tables). * - * Returns true if available, false otherwise. + * Return value: true if data found, false otherwise * * Since: 2.1.0 */ @@ -273,14 +292,14 @@ hb_ot_color_has_png (hb_face_t *face) /** * hb_ot_color_glyph_reference_png: - * @font: a font object, not face. upem should be set on - * that font object if one wants to get optimal png blob, otherwise - * return the biggest one - * @glyph: a glyph index. + * @font: #hb_font_t to work upon + * @glyph: a glyph index * - * Get PNG image for a glyph. + * Fetches the PNG image for a glyph. This function takes a font object, not a face object, + * as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font + * object. If UPEM is unset, the blob returned will be the largest PNG available. * - * Returns: (transfer full): respective PNG blob of the glyph, if available. + * Return value: (transfer full): An #hb_blob_t containing the PNG image for the glyph, if available * * Since: 2.1.0 */ @@ -297,3 +316,6 @@ hb_ot_color_glyph_reference_png (hb_font_t *font, hb_codepoint_t glyph) return blob; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h index 5736890fbe538..593447568dd4d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-color.h @@ -59,11 +59,11 @@ hb_ot_color_palette_color_get_name_id (hb_face_t *face, /** * hb_ot_color_palette_flags_t: - * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: default indicating that there is nothing special + * @HB_OT_COLOR_PALETTE_FLAG_DEFAULT: Default indicating that there is nothing special * to note about a color palette. - * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: flag indicating that the color + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND: Flag indicating that the color * palette is appropriate to use when displaying the font on a light background such as white. - * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: flag indicating that the color + * @HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND: Flag indicating that the color * palette is appropriate to use when displaying the font on a dark background such as black. * * Since: 2.1.0 @@ -110,7 +110,7 @@ HB_EXTERN unsigned int hb_ot_color_glyph_get_layers (hb_face_t *face, hb_codepoint_t glyph, unsigned int start_offset, - unsigned int *count, /* IN/OUT. May be NULL. */ + unsigned int *layer_count, /* IN/OUT. May be NULL. */ hb_ot_color_layer_t *layers /* OUT. May be NULL. */); /* diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h index 2a31b32067133..4fdb2b36ab9b2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-deprecated.h @@ -40,6 +40,10 @@ HB_BEGIN_DECLS #ifndef HB_DISABLE_DEPRECATED +/* https://github.com/harfbuzz/harfbuzz/issues/1734 */ +#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER + + /* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */ HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) hb_bool_t hb_ot_layout_table_choose_script (hb_face_t *face, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh new file mode 100644 index 0000000000000..367e143fdfb90 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face-table-list.hh @@ -0,0 +1,138 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2012,2013 Google, Inc. + * Copyright © 2019, Facebook Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_FACE_TABLE_LIST_HH +#define HB_OT_FACE_TABLE_LIST_HH +#endif /* HB_OT_FACE_TABLE_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_OT_ACCELERATOR +#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) +#define _HB_OT_ACCELERATOR_UNDEF +#endif + + +/* This lists font tables that the hb_face_t will contain and lazily + * load. Don't add a table unless it's used though. This is not + * exactly free. */ + +/* v--- Add new tables in the right place here. */ + + +/* OpenType fundamentals. */ +HB_OT_TABLE (OT, head) +#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT) +HB_OT_ACCELERATOR (OT, cmap) +#endif +HB_OT_TABLE (OT, hhea) +HB_OT_ACCELERATOR (OT, hmtx) +HB_OT_TABLE (OT, OS2) +#if !defined(HB_NO_OT_FONT_GLYPH_NAMES) || !defined(HB_NO_METRICS) || !defined(HB_NO_STYLE) +HB_OT_ACCELERATOR (OT, post) +#endif +#ifndef HB_NO_NAME +HB_OT_ACCELERATOR (OT, name) +#endif +#ifndef HB_NO_STYLE +HB_OT_TABLE (OT, STAT) +#endif +#ifndef HB_NO_META +HB_OT_ACCELERATOR (OT, meta) +#endif + +/* Vertical layout. */ +HB_OT_TABLE (OT, vhea) +HB_OT_ACCELERATOR (OT, vmtx) + +/* TrueType outlines. */ +HB_OT_ACCELERATOR (OT, glyf) + +/* CFF outlines. */ +#ifndef HB_NO_CFF +HB_OT_ACCELERATOR (OT, cff1) +HB_OT_ACCELERATOR (OT, cff2) +HB_OT_TABLE (OT, VORG) +#endif + +/* OpenType variations. */ +#ifndef HB_NO_VAR +HB_OT_TABLE (OT, fvar) +HB_OT_TABLE (OT, avar) +HB_OT_ACCELERATOR (OT, gvar) +HB_OT_TABLE (OT, MVAR) +#endif + +/* Legacy kern. */ +#ifndef HB_NO_OT_KERN +HB_OT_TABLE (OT, kern) +#endif + +/* OpenType shaping. */ +#ifndef HB_NO_OT_LAYOUT +HB_OT_ACCELERATOR (OT, GDEF) +HB_OT_ACCELERATOR (OT, GSUB) +HB_OT_ACCELERATOR (OT, GPOS) +//HB_OT_TABLE (OT, JSTF) +#endif + +/* OpenType baseline. */ +#ifndef HB_NO_BASE +HB_OT_TABLE (OT, BASE) +#endif + +/* AAT shaping. */ +#ifndef HB_NO_AAT +HB_OT_TABLE (AAT, morx) +HB_OT_TABLE (AAT, mort) +HB_OT_TABLE (AAT, kerx) +HB_OT_TABLE (AAT, ankr) +HB_OT_TABLE (AAT, trak) +HB_OT_TABLE (AAT, ltag) +HB_OT_TABLE (AAT, feat) +// HB_OT_TABLE (AAT, opbd) +#endif + +/* OpenType color fonts. */ +#ifndef HB_NO_COLOR +HB_OT_TABLE (OT, COLR) +HB_OT_TABLE (OT, CPAL) +HB_OT_ACCELERATOR (OT, CBDT) +HB_OT_ACCELERATOR (OT, sbix) +HB_OT_ACCELERATOR (OT, SVG) +#endif + +/* OpenType math. */ +#ifndef HB_NO_MATH +HB_OT_TABLE (OT, MATH) +#endif + + +#ifdef _HB_OT_ACCELERATOR_UNDEF +#undef HB_OT_ACCELERATOR +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc index 9b17526b7e404..5ef8df43ce7c0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.cc @@ -32,6 +32,7 @@ #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-kern-table.hh" +#include "hb-ot-meta-table.hh" #include "hb-ot-name-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-color-cbdt-table.hh" @@ -46,16 +47,12 @@ void hb_ot_face_t::init0 (hb_face_t *face) { this->face = face; #define HB_OT_TABLE(Namespace, Type) Type.init0 (); -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE } void hb_ot_face_t::fini () { #define HB_OT_TABLE(Namespace, Type) Type.fini (); -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh index 7f47ba6cb8edf..e24d380bca8cf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-face.hh @@ -38,54 +38,10 @@ * hb_ot_face_t */ -#define HB_OT_TABLES \ - /* OpenType fundamentals. */ \ - HB_OT_TABLE(OT, head) \ - HB_OT_ACCELERATOR(OT, cmap) \ - HB_OT_ACCELERATOR(OT, hmtx) \ - HB_OT_ACCELERATOR(OT, vmtx) \ - HB_OT_ACCELERATOR(OT, post) \ - HB_OT_TABLE(OT, kern) \ - HB_OT_ACCELERATOR(OT, glyf) \ - HB_OT_ACCELERATOR(OT, cff1) \ - HB_OT_ACCELERATOR(OT, cff2) \ - HB_OT_TABLE(OT, VORG) \ - HB_OT_ACCELERATOR(OT, name) \ - HB_OT_TABLE(OT, OS2) \ - HB_OT_TABLE(OT, STAT) \ - /* OpenType shaping. */ \ - HB_OT_ACCELERATOR(OT, GDEF) \ - HB_OT_ACCELERATOR(OT, GSUB) \ - HB_OT_ACCELERATOR(OT, GPOS) \ - HB_OT_TABLE(OT, BASE) \ - HB_OT_TABLE(OT, JSTF) \ - /* AAT shaping. */ \ - HB_OT_TABLE(AAT, mort) \ - HB_OT_TABLE(AAT, morx) \ - HB_OT_TABLE(AAT, kerx) \ - HB_OT_TABLE(AAT, ankr) \ - HB_OT_TABLE(AAT, trak) \ - HB_OT_TABLE(AAT, lcar) \ - HB_OT_TABLE(AAT, ltag) \ - HB_OT_TABLE(AAT, feat) \ - /* OpenType variations. */ \ - HB_OT_TABLE(OT, fvar) \ - HB_OT_TABLE(OT, avar) \ - HB_OT_TABLE(OT, MVAR) \ - /* OpenType math. */ \ - HB_OT_TABLE(OT, MATH) \ - /* OpenType color fonts. */ \ - HB_OT_TABLE(OT, COLR) \ - HB_OT_TABLE(OT, CPAL) \ - HB_OT_ACCELERATOR(OT, CBDT) \ - HB_OT_ACCELERATOR(OT, sbix) \ - HB_OT_ACCELERATOR(OT, SVG) \ - /* */ - /* Declare tables. */ #define HB_OT_TABLE(Namespace, Type) namespace Namespace { struct Type; } #define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type##_accelerator_t) -HB_OT_TABLES +#include "hb-ot-face-table-list.hh" #undef HB_OT_ACCELERATOR #undef HB_OT_TABLE @@ -100,9 +56,7 @@ struct hb_ot_face_t { ORDER_ZERO, #define HB_OT_TABLE(Namespace, Type) HB_OT_TABLE_ORDER (Namespace, Type), -#define HB_OT_ACCELERATOR(Namespace, Type) HB_OT_TABLE (Namespace, Type) - HB_OT_TABLES -#undef HB_OT_ACCELERATOR +#include "hb-ot-face-table-list.hh" #undef HB_OT_TABLE }; @@ -111,7 +65,7 @@ struct hb_ot_face_t hb_table_lazy_loader_t Type; #define HB_OT_ACCELERATOR(Namespace, Type) \ hb_face_lazy_loader_t Type; - HB_OT_TABLES +#include "hb-ot-face-table-list.hh" #undef HB_OT_ACCELERATOR #undef HB_OT_TABLE }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc index b290c49776269..f28de2af628f6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-font.cc @@ -26,6 +26,8 @@ #include "hb.hh" +#ifndef HB_NO_OT_FONT + #include "hb-ot.h" #include "hb-font.hh" @@ -37,7 +39,6 @@ #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" -#include "hb-ot-kern-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. @@ -52,7 +53,7 @@ * @short_description: OpenType font implementation * @include: hb-ot.h * - * Functions for using OpenType fonts with hb_shape(). Not that fonts returned + * Functions for using OpenType fonts with hb_shape(). Note that fonts returned * by hb_font_create() default to using these functions, so most clients would * never need to call these functions directly. **/ @@ -149,19 +150,21 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, *x = font->get_glyph_h_advance (glyph) / 2; +#ifndef HB_NO_OT_FONT_CFF const OT::VORG &VORG = *ot_face->VORG; if (VORG.has_data ()) { *y = font->em_scale_y (VORG.get_y_origin (glyph)); return true; } +#endif hb_glyph_extents_t extents = {0}; - if (ot_face->glyf->get_extents (glyph, &extents)) + if (ot_face->glyf->get_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t tsb = vmtx.get_side_bearing (glyph); - *y = font->em_scale_y (extents.y_bearing + tsb); + hb_position_t tsb = vmtx.get_side_bearing (font, glyph); + *y = extents.y_bearing + font->em_scale_y (tsb); return true; } @@ -180,23 +183,24 @@ hb_ot_get_glyph_extents (hb_font_t *font, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - bool ret = ot_face->sbix->get_extents (font, glyph, extents); - if (!ret) - ret = ot_face->glyf->get_extents (glyph, extents); - if (!ret) - ret = ot_face->cff1->get_extents (glyph, extents); - if (!ret) - ret = ot_face->cff2->get_extents (font, glyph, extents); - if (!ret) - ret = ot_face->CBDT->get_extents (font, glyph, extents); + +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->sbix->get_extents (font, glyph, extents)) return true; +#endif + if (ot_face->glyf->get_extents (font, glyph, extents)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_extents (font, glyph, extents)) return true; + if (ot_face->cff2->get_extents (font, glyph, extents)) return true; +#endif +#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR) + if (ot_face->CBDT->get_extents (font, glyph, extents)) return true; +#endif + // TODO Hook up side-bearings variations. - extents->x_bearing = font->em_scale_x (extents->x_bearing); - extents->y_bearing = font->em_scale_y (extents->y_bearing); - extents->width = font->em_scale_x (extents->width); - extents->height = font->em_scale_y (extents->height); - return ret; + return false; } +#ifndef HB_NO_OT_FONT_GLYPH_NAMES static hb_bool_t hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, void *font_data, @@ -205,9 +209,12 @@ hb_ot_get_glyph_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - return ot_face->post->get_glyph_name (glyph, name, size); + if (ot_face->post->get_glyph_name (glyph, name, size)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_name (glyph, name, size)) return true; +#endif + return false; } - static hb_bool_t hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, void *font_data, @@ -216,37 +223,34 @@ hb_ot_get_glyph_from_name (hb_font_t *font HB_UNUSED, void *user_data HB_UNUSED) { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - return ot_face->post->get_glyph_from_name (name, len, glyph); + if (ot_face->post->get_glyph_from_name (name, len, glyph)) return true; +#ifndef HB_NO_OT_FONT_CFF + if (ot_face->cff1->get_glyph_from_name (name, len, glyph)) return true; +#endif + return false; } +#endif static hb_bool_t hb_ot_get_font_h_extents (hb_font_t *font, - void *font_data, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; - metrics->ascender = font->em_scale_y (hmtx.ascender); - metrics->descender = font->em_scale_y (hmtx.descender); - metrics->line_gap = font->em_scale_y (hmtx.line_gap); - // TODO Hook up variations. - return hmtx.has_font_extents; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, &metrics->line_gap); } static hb_bool_t hb_ot_get_font_v_extents (hb_font_t *font, - void *font_data, + void *font_data HB_UNUSED, hb_font_extents_t *metrics, void *user_data HB_UNUSED) { - const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; - const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - metrics->ascender = font->em_scale_x (vmtx.ascender); - metrics->descender = font->em_scale_x (vmtx.descender); - metrics->line_gap = font->em_scale_x (vmtx.line_gap); - // TODO Hook up variations. - return vmtx.has_font_extents; + return _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_ASCENDER, &metrics->ascender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_DESCENDER, &metrics->descender) && + _hb_ot_metrics_get_position_common (font, HB_OT_METRICS_TAG_VERTICAL_LINE_GAP, &metrics->line_gap); } #if HB_USE_ATEXIT @@ -270,8 +274,10 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_tface->table, nullptr); } + +#ifndef HB_NO_VAR +int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_side_bearing_var (font, glyph, is_vertical); +} + +unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_advance_var (font, glyph, is_vertical); +} +#endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh index 252d0b4eb1a4c..cd95828e2f5a2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-glyf-table.hh @@ -1,5 +1,7 @@ /* * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -21,7 +23,8 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter + * Adobe Author(s): Michiharu Ariza */ #ifndef HB_OT_GLYF_TABLE_HH @@ -29,7 +32,9 @@ #include "hb-open-type.hh" #include "hb-ot-head-table.hh" -#include "hb-subset-glyf.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-draw.hh" namespace OT { @@ -54,11 +59,12 @@ struct loca } protected: - UnsizedArrayOf dataZ; /* Location data. */ + UnsizedArrayOf + dataZ; /* Location data. */ public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it MIN() instead. */ + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ }; @@ -76,29 +82,143 @@ struct glyf bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const { TRACE_SANITIZE (this); - /* We don't check for anything specific here. The users of the - * struct do all the hard work... */ + /* Runtime checks as eager sanitizing each glyph is costy */ return_trace (true); } - bool subset (hb_subset_plan_t *plan) const + template + static bool + _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets) + { + unsigned max_offset = + + padded_offsets + | hb_reduce (hb_add, 0) + ; + unsigned num_offsets = padded_offsets.len () + 1; + bool use_short_loca = max_offset < 0x1FFFF; + unsigned entry_size = use_short_loca ? 2 : 4; + char *loca_prime_data = (char *) calloc (entry_size, num_offsets); + + if (unlikely (!loca_prime_data)) return false; + + DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d " + "max_offset %d size %d", + entry_size, num_offsets, max_offset, entry_size * num_offsets); + + if (use_short_loca) + _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets)); + else + _write_loca (padded_offsets, 0, hb_array ((HBUINT32 *) loca_prime_data, num_offsets)); + + hb_blob_t *loca_blob = hb_blob_create (loca_prime_data, + entry_size * num_offsets, + HB_MEMORY_MODE_WRITABLE, + loca_prime_data, + free); + + bool result = plan->add_table (HB_OT_TAG_loca, loca_blob) + && _add_head_and_set_loca_version (plan, use_short_loca); + + hb_blob_destroy (loca_blob); + return result; + } + + template + static void + _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest) { - hb_blob_t *glyf_prime = nullptr; - hb_blob_t *loca_prime = nullptr; - - bool success = true; - bool use_short_loca = false; - if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) { - success = success && plan->add_table (HB_OT_TAG_glyf, glyf_prime); - success = success && plan->add_table (HB_OT_TAG_loca, loca_prime); - success = success && _add_head_and_set_loca_version (plan, use_short_loca); - } else { - success = false; + unsigned int offset = 0; + dest << 0; + + it + | hb_map ([=, &offset] (unsigned int padded_size) + { + offset += padded_size; + DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset); + return offset >> right_shift; + }) + | hb_sink (dest) + ; + } + + /* requires source of SubsetGlyph complains the identifier isn't declared */ + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const hb_subset_plan_t *plan) + { + TRACE_SERIALIZE (this); + unsigned init_len = c->length (); + for (const auto &_ : it) _.serialize (c, plan); + + /* As a special case when all glyph in the font are empty, add a zero byte + * to the table, so that OTS doesn’t reject it, and to make the table work + * on Windows as well. + * See https://github.com/khaledhosny/ots/issues/52 */ + if (init_len == c->length ()) + { + HBUINT8 empty_byte; + empty_byte = 0; + c->copy (empty_byte); } - hb_blob_destroy (loca_prime); - hb_blob_destroy (glyf_prime); + return_trace (true); + } - return success; + /* Byte region(s) per glyph to output + unpadded, hints removed if so requested + If we fail to process a glyph we produce an empty (0-length) glyph */ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + glyf *glyf_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false); + + hb_vector_t glyphs; + _populate_subset_glyphs (c->plan, &glyphs); + + glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan); + + auto padded_offsets = + + hb_iter (glyphs) + | hb_map (&SubsetGlyph::padded_size) + ; + + if (c->serializer->in_error ()) return_trace (false); + return_trace (c->serializer->check_success (_add_loca_and_head (c->plan, + padded_offsets))); + } + + template + void + _populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_vector_t *glyphs /* OUT */) const + { + OT::glyf::accelerator_t glyf; + glyf.init (plan->source); + + + hb_range (plan->num_output_glyphs ()) + | hb_map ([&] (hb_codepoint_t new_gid) + { + SubsetGlyph subset_glyph = {0}; + subset_glyph.new_gid = new_gid; + + /* should never fail: all old gids should be mapped */ + if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) + return subset_glyph; + + subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true); + if (plan->drop_hints) subset_glyph.drop_hints_bytes (); + else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes (); + + return subset_glyph; + }) + | hb_sink (glyphs) + ; + + glyf.fini (); } static bool @@ -112,218 +232,292 @@ struct glyf return false; head *head_prime = (head *) hb_blob_get_data_writable (head_prime_blob, nullptr); - head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); + head_prime->indexToLocFormat = use_short_loca ? 0 : 1; bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob); hb_blob_destroy (head_prime_blob); return success; } - struct GlyphHeader - { - HBINT16 numberOfContours; /* If the number of contours is - * greater than or equal to zero, - * this is a simple glyph; if negative, - * this is a composite glyph. */ - FWORD xMin; /* Minimum x for coordinate data. */ - FWORD yMin; /* Minimum y for coordinate data. */ - FWORD xMax; /* Maximum x for coordinate data. */ - FWORD yMax; /* Maximum y for coordinate data. */ - - DEFINE_SIZE_STATIC (10); - }; - - struct CompositeGlyphHeader + struct CompositeGlyphChain { - enum composite_glyph_flag_t { - ARG_1_AND_2_ARE_WORDS = 0x0001, - ARGS_ARE_XY_VALUES = 0x0002, - ROUND_XY_TO_GRID = 0x0004, - WE_HAVE_A_SCALE = 0x0008, - MORE_COMPONENTS = 0x0020, - WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, - WE_HAVE_A_TWO_BY_TWO = 0x0080, - WE_HAVE_INSTRUCTIONS = 0x0100, - USE_MY_METRICS = 0x0200, - OVERLAP_COMPOUND = 0x0400, - SCALED_COMPONENT_OFFSET = 0x0800, - UNSCALED_COMPONENT_OFFSET = 0x1000 + protected: + enum composite_glyph_flag_t + { + ARG_1_AND_2_ARE_WORDS = 0x0001, + ARGS_ARE_XY_VALUES = 0x0002, + ROUND_XY_TO_GRID = 0x0004, + WE_HAVE_A_SCALE = 0x0008, + MORE_COMPONENTS = 0x0020, + WE_HAVE_AN_X_AND_Y_SCALE = 0x0040, + WE_HAVE_A_TWO_BY_TWO = 0x0080, + WE_HAVE_INSTRUCTIONS = 0x0100, + USE_MY_METRICS = 0x0200, + OVERLAP_COMPOUND = 0x0400, + SCALED_COMPONENT_OFFSET = 0x0800, + UNSCALED_COMPONENT_OFFSET = 0x1000 }; - HBUINT16 flags; - GlyphID glyphIndex; - + public: unsigned int get_size () const { unsigned int size = min_size; - // arg1 and 2 are int16 + /* arg1 and 2 are int16 */ if (flags & ARG_1_AND_2_ARE_WORDS) size += 4; - // arg1 and 2 are int8 + /* arg1 and 2 are int8 */ else size += 2; - // One x 16 bit (scale) + /* One x 16 bit (scale) */ if (flags & WE_HAVE_A_SCALE) size += 2; - // Two x 16 bit (xscale, yscale) + /* Two x 16 bit (xscale, yscale) */ else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) size += 4; - // Four x 16 bit (xscale, scale01, scale10, yscale) + /* Four x 16 bit (xscale, scale01, scale10, yscale) */ else if (flags & WE_HAVE_A_TWO_BY_TWO) size += 8; return size; } - struct Iterator + void set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; } + hb_codepoint_t get_glyph_index () const { return glyphIndex; } + + void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; } + bool has_instructions () const { return flags & WE_HAVE_INSTRUCTIONS; } + + bool has_more () const { return flags & MORE_COMPONENTS; } + bool is_use_my_metrics () const { return flags & USE_MY_METRICS; } + bool is_anchored () const { return !(flags & ARGS_ARE_XY_VALUES); } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const { - const char *glyph_start; - const char *glyph_end; - const CompositeGlyphHeader *current; + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *) p)[0]; + point2 = ((const HBUINT16 *) p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } - bool move_to_next () + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) { - if (current->flags & CompositeGlyphHeader::MORE_COMPONENTS) + if (scaled_offsets ()) { - const CompositeGlyphHeader *possible = - &StructAfter (*current); - if (!in_range (possible)) - return false; - current = possible; - return true; + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); } - return false; } + } - bool in_range (const CompositeGlyphHeader *composite) const - { - return (const char *) composite >= glyph_start - && ((const char *) composite + CompositeGlyphHeader::min_size) <= glyph_end - && ((const char *) composite + composite->get_size ()) <= glyph_end; - } - }; + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } - static bool get_iterator (const char * glyph_data, - unsigned int length, - CompositeGlyphHeader::Iterator *iterator /* OUT */) + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const { - if (length < GlyphHeader::static_size) - return false; /* Empty glyph; zero extents. */ + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; - const GlyphHeader &glyph_header = StructAtOffset (glyph_data, 0); - if (glyph_header.numberOfContours < 0) + int tx, ty; + const HBINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) { - const CompositeGlyphHeader *possible = - &StructAfter (glyph_header); - - iterator->glyph_start = glyph_data; - iterator->glyph_end = (const char *) glyph_data + length; - if (!iterator->in_range (possible)) - return false; - iterator->current = possible; - return true; + tx = *(const HBINT16 *) p; + p += HBINT16::static_size; + ty = *(const HBINT16 *) p; + p += HBINT16::static_size; } + else + { + tx = *p++; + ty = *p++; + } + if (is_anchored ()) tx = ty = 0; - return false; + trans.init ((float) tx, (float) ty); + + { + const F2DOT14 *points = (const F2DOT14 *) p; + if (flags & WE_HAVE_A_SCALE) + { + matrix[0] = matrix[3] = points[0].to_float (); + return true; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + matrix[0] = points[0].to_float (); + matrix[3] = points[1].to_float (); + return true; + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + matrix[0] = points[0].to_float (); + matrix[1] = points[1].to_float (); + matrix[2] = points[2].to_float (); + matrix[3] = points[3].to_float (); + return true; + } + } + return tx || ty; } + protected: + HBUINT16 flags; + HBGlyphID glyphIndex; + public: DEFINE_SIZE_MIN (4); }; - struct accelerator_t + struct composite_iter_t : hb_iter_with_fallback_t { - void init (hb_face_t *face) + typedef const CompositeGlyphChain *__item_t__; + composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) : + glyph (glyph_), current (current_) + { if (!check_range (current)) current = nullptr; } + composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {} + + const CompositeGlyphChain &__item__ () const { return *current; } + bool __more__ () const { return current; } + void __next__ () { - memset (this, 0, sizeof (accelerator_t)); + if (!current->has_more ()) { current = nullptr; return; } - const OT::head &head = *face->table.head; - if (head.indexToLocFormat > 1 || head.glyphDataFormat != 0) - /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ - return; - short_offset = 0 == head.indexToLocFormat; - - loca_table = hb_sanitize_context_t ().reference_table (face); - glyf_table = hb_sanitize_context_t ().reference_table (face); - - num_glyphs = MAX (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + const CompositeGlyphChain *possible = &StructAfter (*current); + if (!check_range (possible)) { current = nullptr; return; } + current = possible; } + bool operator != (const composite_iter_t& o) const + { return glyph != o.glyph || current != o.current; } - void fini () + bool check_range (const CompositeGlyphChain *composite) const { - loca_table.destroy (); - glyf_table.destroy (); + return glyph.check_range (composite, CompositeGlyphChain::min_size) + && glyph.check_range (composite, composite->get_size ()); } - /* - * Returns true if the referenced glyph is a valid glyph and a composite glyph. - * If true is returned a pointer to the composite glyph will be written into - * composite. - */ - bool get_composite (hb_codepoint_t glyph, - CompositeGlyphHeader::Iterator *composite /* OUT */) const - { - if (unlikely (!num_glyphs)) - return false; + private: + hb_bytes_t glyph; + __item_t__ current; + }; - unsigned int start_offset, end_offset; - if (!get_offsets (glyph, &start_offset, &end_offset)) - return false; /* glyph not found */ + enum phantom_point_index_t + { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; - return CompositeGlyphHeader::get_iterator ((const char *) this->glyf_table + start_offset, - end_offset - start_offset, - composite); - } + struct accelerator_t; - enum simple_glyph_flag_t { - FLAG_ON_CURVE = 0x01, - FLAG_X_SHORT = 0x02, - FLAG_Y_SHORT = 0x04, - FLAG_REPEAT = 0x08, - FLAG_X_SAME = 0x10, - FLAG_Y_SAME = 0x20, + struct Glyph + { + enum simple_glyph_flag_t + { + FLAG_ON_CURVE = 0x01, + FLAG_X_SHORT = 0x02, + FLAG_Y_SHORT = 0x04, + FLAG_REPEAT = 0x08, + FLAG_X_SAME = 0x10, + FLAG_Y_SAME = 0x20, FLAG_RESERVED1 = 0x40, FLAG_RESERVED2 = 0x80 }; - /* based on FontTools _g_l_y_f.py::trim */ - bool remove_padding (unsigned int start_offset, - unsigned int *end_offset) const + private: + struct GlyphHeader { - if (*end_offset - start_offset < GlyphHeader::static_size) return true; + bool has_data () const { return numberOfContours; } - const char *glyph = ((const char *) glyf_table) + start_offset; - const char * const glyph_end = glyph + (*end_offset - start_offset); - const GlyphHeader &glyph_header = StructAtOffset (glyph, 0); - int16_t num_contours = (int16_t) glyph_header.numberOfContours; + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + /* Undocumented rasterizer behavior: shift glyph to the left by (lsb - xMin), i.e., xMin = lsb */ + /* extents->x_bearing = hb_min (glyph_header.xMin, glyph_header.xMax); */ + extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid)); + extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax)); + extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax)); + extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax)); - if (num_contours < 0) - /* Trimming for composites not implemented. - * If removing hints it falls out of that. */ return true; - else if (num_contours > 0) + } + + HBINT16 numberOfContours; + /* If the number of contours is + * greater than or equal to zero, + * this is a simple glyph; if negative, + * this is a composite glyph. */ + FWORD xMin; /* Minimum x for coordinate data. */ + FWORD yMin; /* Minimum y for coordinate data. */ + FWORD xMax; /* Maximum x for coordinate data. */ + FWORD yMax; /* Maximum y for coordinate data. */ + public: + DEFINE_SIZE_STATIC (10); + }; + + struct SimpleGlyph + { + const GlyphHeader &header; + hb_bytes_t bytes; + SimpleGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} + + unsigned int instruction_len_offset () const + { return GlyphHeader::static_size + 2 * header.numberOfContours; } + + unsigned int length (unsigned int instruction_len) const + { return instruction_len_offset () + 2 + instruction_len; } + + unsigned int instructions_length () const { + unsigned int instruction_length_offset = instruction_len_offset (); + if (unlikely (instruction_length_offset + 2 > bytes.length)) return 0; + + const HBUINT16 &instructionLength = StructAtOffset (&bytes, instruction_length_offset); + /* Out of bounds of the current glyph */ + if (unlikely (length (instructionLength) > bytes.length)) return 0; + return instructionLength; + } + + const Glyph trim_padding () const + { + /* based on FontTools _g_l_y_f.py::trim */ + const char *glyph = bytes.arrayZ; + const char *glyph_end = glyph + bytes.length; /* simple glyph w/contours, possibly trimmable */ - glyph += GlyphHeader::static_size + 2 * num_contours; + glyph += instruction_len_offset (); - if (unlikely (glyph + 2 >= glyph_end)) return false; - uint16_t nCoordinates = (uint16_t) StructAtOffset (glyph - 2, 0) + 1; - uint16_t nInstructions = (uint16_t) StructAtOffset (glyph, 0); + if (unlikely (glyph + 2 >= glyph_end)) return Glyph (); + unsigned int num_coordinates = StructAtOffset (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset (glyph, 0); - glyph += 2 + nInstructions; - if (unlikely (glyph + 2 >= glyph_end)) return false; + glyph += 2 + num_instructions; - unsigned int coordBytes = 0; - unsigned int coordsWithFlags = 0; + unsigned int coord_bytes = 0; + unsigned int coords_with_flags = 0; while (glyph < glyph_end) { - uint8_t flag = (uint8_t) *glyph; + uint8_t flag = *glyph; glyph++; unsigned int repeat = 1; if (flag & FLAG_REPEAT) { - if (glyph >= glyph_end) - { - DEBUG_MSG(SUBSET, nullptr, "Bad flag"); - return false; - } - repeat = ((uint8_t) *glyph) + 1; + if (unlikely (glyph >= glyph_end)) return Glyph (); + repeat = *glyph + 1; glyph++; } @@ -335,143 +529,728 @@ struct glyf if (flag & FLAG_Y_SHORT) yBytes = 1; else if ((flag & FLAG_Y_SAME) == 0) yBytes = 2; - coordBytes += (xBytes + yBytes) * repeat; - coordsWithFlags += repeat; - if (coordsWithFlags >= nCoordinates) - break; + coord_bytes += (xBytes + yBytes) * repeat; + coords_with_flags += repeat; + if (coords_with_flags >= num_coordinates) break; } - if (coordsWithFlags != nCoordinates) + if (unlikely (coords_with_flags != num_coordinates)) return Glyph (); + return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph))); + } + + /* zero instruction length */ + void drop_hints () + { + GlyphHeader &glyph_header = const_cast (header); + (HBUINT16 &) StructAtOffset (&glyph_header, instruction_len_offset ()) = 0; + } + + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + unsigned int instructions_len = instructions_length (); + unsigned int glyph_length = length (instructions_len); + dest_start = bytes.sub_array (0, glyph_length - instructions_len); + dest_end = bytes.sub_array (glyph_length, bytes.length - glyph_length); + } + + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, + const hb_bytes_t &bytes, + void (* setter) (contour_point_t &_, float v), + const simple_glyph_flag_t short_flag, + const simple_glyph_flag_t same_flag) + { + float v = 0; + for (unsigned i = 0; i < points_.length; i++) { - DEBUG_MSG(SUBSET, nullptr, "Expect %d coords to have flags, got flags for %d", nCoordinates, coordsWithFlags); - return false; + uint8_t flag = points_[i].flag; + if (flag & short_flag) + { + if (unlikely (!bytes.check_range (p))) return false; + if (flag & same_flag) + v += *p++; + else + v -= *p++; + } + else + { + if (!(flag & same_flag)) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false; + v += *(const HBINT16 *) p; + p += HBINT16::static_size; + } + } + setter (points_[i], v); } - glyph += coordBytes; + return true; + } + + bool get_contour_points (contour_point_vector_t &points_ /* OUT */, + bool phantom_only = false) const + { + const HBUINT16 *endPtsOfContours = &StructAfter (header); + int num_contours = header.numberOfContours; + if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false; + unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; + + points_.resize (num_points); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); + if (phantom_only) return true; - if (glyph < glyph_end) - *end_offset -= glyph_end - glyph; + for (int i = 0; i < num_contours; i++) + points_[endPtsOfContours[i]].is_end_point = true; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&endPtsOfContours[num_contours + 1], + endPtsOfContours[num_contours]); + + /* Read flags */ + for (unsigned int i = 0; i < num_points; i++) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t flag = *p++; + points_[i].flag = flag; + if (flag & FLAG_REPEAT) + { + if (unlikely (!bytes.check_range (p))) return false; + unsigned int repeat_count = *p++; + while ((repeat_count-- > 0) && (++i < num_points)) + points_[i].flag = flag; + } + } + + /* Read x & y coordinates */ + return read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.x = v; }, + FLAG_X_SHORT, FLAG_X_SAME) + && read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.y = v; }, + FLAG_Y_SHORT, FLAG_Y_SAME); } - return true; - } + }; - bool get_offsets (hb_codepoint_t glyph, - unsigned int *start_offset /* OUT */, - unsigned int *end_offset /* OUT */) const + struct CompositeGlyph { - if (unlikely (glyph >= num_glyphs)) - return false; + const GlyphHeader &header; + hb_bytes_t bytes; + CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) : + header (header_), bytes (bytes_) {} - if (short_offset) + composite_iter_t get_iterator () const + { return composite_iter_t (bytes, &StructAfter (header)); } + + unsigned int instructions_length (hb_bytes_t bytes) const { - const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; - *start_offset = 2 * offsets[glyph]; - *end_offset = 2 * offsets[glyph + 1]; + unsigned int start = bytes.length; + unsigned int end = bytes.length; + const CompositeGlyphChain *last = nullptr; + for (auto &item : get_iterator ()) + last = &item; + if (unlikely (!last)) return 0; + + if (last->has_instructions ()) + start = (char *) last - &bytes + last->get_size (); + if (unlikely (start > end)) return 0; + return end - start; } - else + + /* Trimming for composites not implemented. + * If removing hints it falls out of that. */ + const Glyph trim_padding () const { return Glyph (bytes); } + + void drop_hints () { - const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + for (const auto &_ : get_iterator ()) + const_cast (_).drop_instructions_flag (); + } + + /* Chop instructions off the end */ + void drop_hints_bytes (hb_bytes_t &dest_start) const + { dest_start = bytes.sub_array (0, bytes.length - instructions_length (bytes)); } + }; + + enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE }; + + public: + composite_iter_t get_composite_iterator () const + { + if (type != COMPOSITE) return composite_iter_t (); + return CompositeGlyph (*header, bytes).get_iterator (); + } - *start_offset = offsets[glyph]; - *end_offset = offsets[glyph + 1]; + const Glyph trim_padding () const + { + switch (type) { + case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding (); + case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding (); + default: return bytes; } + } - if (*start_offset > *end_offset || *end_offset > glyf_table.get_length ()) - return false; + void drop_hints () + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return; + default: return; + } + } - return true; + void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const + { + switch (type) { + case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return; + case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return; + default: return; + } } - bool get_instruction_offsets (unsigned int start_offset, - unsigned int end_offset, - unsigned int *instruction_start /* OUT */, - unsigned int *instruction_end /* OUT */) const + /* Note: Recursively calls itself. + * all_points includes phantom points + */ + bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator, + contour_point_vector_t &all_points /* OUT */, + bool phantom_only = false, + unsigned int depth = 0) const { - if (end_offset - start_offset < GlyphHeader::static_size) + if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false; + contour_point_vector_t points; + + switch (type) { + case COMPOSITE: { - *instruction_start = 0; - *instruction_end = 0; - return true; /* Empty glyph; no instructions. */ + /* pseudo component points for each component in composite glyph */ + unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ()); + if (unlikely (!points.resize (num_points))) return false; + for (unsigned i = 0; i < points.length; i++) + points[i].init (); + break; } - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); - int16_t num_contours = (int16_t) glyph_header.numberOfContours; - if (num_contours < 0) + case SIMPLE: + if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only))) + return false; + break; + } + + /* Init phantom points */ + if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false; + hb_array_t phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT); { - CompositeGlyphHeader::Iterator composite_it; - if (unlikely (!CompositeGlyphHeader::get_iterator ( - (const char*) this->glyf_table + start_offset, - end_offset - start_offset, &composite_it))) return false; - const CompositeGlyphHeader *last; - do { - last = composite_it.current; - } while (composite_it.move_to_next ()); - - if ((uint16_t) last->flags & CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS) - *instruction_start = ((char *) last - (char *) glyf_table->dataZ.arrayZ) + last->get_size (); - else - *instruction_start = end_offset; - *instruction_end = end_offset; - if (unlikely (*instruction_start > *instruction_end)) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init (); + int h_delta = (int) header->xMin - glyf_accelerator.hmtx->get_side_bearing (gid); + int v_orig = (int) header->yMax + glyf_accelerator.vmtx->get_side_bearing (gid); + unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid); + unsigned v_adv = glyf_accelerator.vmtx->get_advance (gid); + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_orig; + phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv; + } + +#ifndef HB_NO_VAR + if (unlikely (!glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ()))) + return false; +#endif + + switch (type) { + case SIMPLE: + all_points.extend (points.as_array ()); + break; + case COMPOSITE: + { + unsigned int comp_index = 0; + for (auto &item : get_composite_iterator ()) { - DEBUG_MSG(SUBSET, nullptr, "Invalid instruction offset, %d is outside [%d, %d]", *instruction_start, start_offset, end_offset); - return false; + contour_point_vector_t comp_points; + if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ()) + .get_points (font, glyf_accelerator, comp_points, + phantom_only, depth + 1) + || comp_points.length < PHANTOM_COUNT)) + return false; + + /* Copy phantom points from component if USE_MY_METRICS flag set */ + if (item.is_use_my_metrics ()) + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i]; + + /* Apply component transformation & translation */ + item.transform_points (comp_points); + + /* Apply translation from gvar */ + comp_points.translate (points[comp_index]); + + if (item.is_anchored ()) + { + unsigned int p1, p2; + item.get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + + all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT)); + + comp_index++; } + + all_points.extend (phantoms); + } break; + default: + all_points.extend (phantoms); } - else + + if (depth == 0) /* Apply at top level */ { - unsigned int instruction_length_offset = start_offset + GlyphHeader::static_size + 2 * num_contours; - if (unlikely (instruction_length_offset + 2 > end_offset)) + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (-phantoms[PHANTOM_LEFT].x, 0.f); + if (delta.x) all_points.translate (delta); + } + + return true; + } + + bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator, + hb_glyph_extents_t *extents) const + { + if (type == EMPTY) return true; /* Empty glyph; zero extents. */ + return header->get_extents (font, glyf_accelerator, gid, extents); + } + + hb_bytes_t get_bytes () const { return bytes; } + + Glyph (hb_bytes_t bytes_ = hb_bytes_t (), + hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_), + header (bytes.as ()) + { + int num_contours = header->numberOfContours; + if (unlikely (num_contours == 0)) type = EMPTY; + else if (num_contours > 0) type = SIMPLE; + else type = COMPOSITE; /* negative numbers */ + } + + protected: + hb_bytes_t bytes; + hb_codepoint_t gid; + const GlyphHeader *header; + unsigned type; + }; + + struct accelerator_t + { + void init (hb_face_t *face_) + { + short_offset = false; + num_glyphs = 0; + loca_table = nullptr; + glyf_table = nullptr; +#ifndef HB_NO_VAR + gvar = nullptr; +#endif + hmtx = nullptr; + vmtx = nullptr; + face = face_; + const OT::head &head = *face->table.head; + if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0) + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + return; + short_offset = 0 == head.indexToLocFormat; + + loca_table = hb_sanitize_context_t ().reference_table (face); + glyf_table = hb_sanitize_context_t ().reference_table (face); +#ifndef HB_NO_VAR + gvar = face->table.gvar; +#endif + hmtx = face->table.hmtx; + vmtx = face->table.vmtx; + + num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ()); + } + + void fini () + { + loca_table.destroy (); + glyf_table.destroy (); + } + + protected: + template + bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer) const + { + if (gid >= num_glyphs) return false; + + /* Making this alloc free is not that easy + https://github.com/harfbuzz/harfbuzz/issues/2095 + mostly because of gvar handling in VF fonts, + perhaps a separate path for non-VF fonts can be considered */ + contour_point_vector_t all_points; + + bool phantom_only = !consumer.is_consuming_contour_points (); + if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, phantom_only))) + return false; + + if (consumer.is_consuming_contour_points ()) + { + for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index) + consumer.consume_point (all_points[point_index]); + consumer.points_end (); + } + + /* Where to write phantoms, nullptr if not requested */ + contour_point_t *phantoms = consumer.get_phantoms_sink (); + if (phantoms) + for (unsigned i = 0; i < PHANTOM_COUNT; ++i) + phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i]; + + return true; + } + +#ifndef HB_NO_VAR + struct points_aggregator_t + { + hb_font_t *font; + hb_glyph_extents_t *extents; + contour_point_t *phantoms; + + struct contour_bounds_t + { + contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; } + + void add (const contour_point_t &p) { - DEBUG_MSG(SUBSET, nullptr, "Glyph size is too short, missing field instructionLength."); - return false; + min_x = hb_min (min_x, p.x); + min_y = hb_min (min_y, p.y); + max_x = hb_max (max_x, p.x); + max_y = hb_max (max_y, p.y); } - const HBUINT16 &instruction_length = StructAtOffset (glyf_table, instruction_length_offset); - unsigned int start = instruction_length_offset + 2; - unsigned int end = start + (uint16_t) instruction_length; - if (unlikely (end > end_offset)) // Out of bounds of the current glyph + bool empty () const { return (min_x >= max_x) || (min_y >= max_y); } + + void get_extents (hb_font_t *font, hb_glyph_extents_t *extents) { - DEBUG_MSG(SUBSET, nullptr, "The instructions array overruns the glyph's boundaries."); - return false; + if (unlikely (empty ())) + { + extents->width = 0; + extents->x_bearing = 0; + extents->height = 0; + extents->y_bearing = 0; + return; + } + extents->x_bearing = font->em_scalef_x (min_x); + extents->width = font->em_scalef_x (max_x - min_x); + extents->y_bearing = font->em_scalef_y (max_y); + extents->height = font->em_scalef_y (min_y - max_y); } - *instruction_start = start; - *instruction_end = end; + protected: + float min_x, min_y, max_x, max_y; + } bounds; + + points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_) + { + font = font_; + extents = extents_; + phantoms = phantoms_; + if (extents) bounds = contour_bounds_t (); } - return true; + + void consume_point (const contour_point_t &point) { bounds.add (point); } + void points_end () { bounds.get_extents (font, extents); } + + bool is_consuming_contour_points () { return extents; } + contour_point_t *get_phantoms_sink () { return phantoms; } + }; + + public: + unsigned + get_advance_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const + { + if (unlikely (gid >= num_glyphs)) return 0; + + bool success = false; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (likely (font->num_coords == gvar->get_axis_count ())) + success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms)); + + if (unlikely (!success)) + return is_vertical ? vmtx->get_advance (gid) : hmtx->get_advance (gid); + + float result = is_vertical + ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y + : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2); } - bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const { + if (unlikely (gid >= num_glyphs)) return 0; + + hb_glyph_extents_t extents; + + contour_point_t phantoms[PHANTOM_COUNT]; + if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms)))) + return is_vertical ? vmtx->get_side_bearing (gid) : hmtx->get_side_bearing (gid); + + return is_vertical + ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing + : floorf (phantoms[PHANTOM_LEFT].x); + } +#endif + + public: + bool get_extents (hb_font_t *font, hb_codepoint_t gid, hb_glyph_extents_t *extents) const + { + if (unlikely (gid >= num_glyphs)) return false; + +#ifndef HB_NO_VAR + if (font->num_coords && font->num_coords == gvar->get_axis_count ()) + return get_points (font, gid, points_aggregator_t (font, extents, nullptr)); +#endif + return glyph_for_gid (gid).get_extents (font, *this, extents); + } + + const Glyph + glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const + { + if (unlikely (gid >= num_glyphs)) return Glyph (); + unsigned int start_offset, end_offset; - if (!get_offsets (glyph, &start_offset, &end_offset)) - return false; - if (end_offset - start_offset < GlyphHeader::static_size) - return true; /* Empty glyph; zero extents. */ + if (short_offset) + { + const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ; + start_offset = 2 * offsets[gid]; + end_offset = 2 * offsets[gid + 1]; + } + else + { + const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ; + start_offset = offsets[gid]; + end_offset = offsets[gid + 1]; + } + + if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ())) + return Glyph (); - const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset, + end_offset - start_offset), gid); + return needs_padding_removal ? glyph.trim_padding () : glyph; + } - extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); - extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); - extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; - extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + void + add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain, + unsigned int depth = 0) const + { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return; + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return; - return true; + gids_to_retain->add (gid); + + for (auto &item : glyph_for_gid (gid).get_composite_iterator ()) + add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth); } +#ifdef HB_EXPERIMENTAL_API + struct path_builder_t + { + hb_font_t *font; + draw_helper_t *draw_helper; + + struct optional_point_t + { + optional_point_t () { has_data = false; } + optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; } + + bool has_data; + float x; + float y; + + optional_point_t lerp (optional_point_t p, float t) + { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); } + } first_oncurve, first_offcurve, last_offcurve; + + path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_) + { + font = font_; + draw_helper = &draw_helper_; + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + } + + /* based on https://github.com/RazrFalcon/ttf-parser/blob/4f32821/src/glyf.rs#L287 + See also: + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html + * https://stackoverflow.com/a/20772557 */ + void consume_point (const contour_point_t &point) + { + /* Skip empty contours */ + if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data)) + return; + + bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE; + optional_point_t p (point.x, point.y); + if (!first_oncurve.has_data) + { + if (is_on_curve) + { + first_oncurve = p; + draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + } + else + { + if (first_offcurve.has_data) + { + optional_point_t mid = first_offcurve.lerp (p, .5f); + first_oncurve = mid; + last_offcurve = p; + draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + } + else + first_offcurve = p; + } + } + else + { + if (last_offcurve.has_data) + { + if (is_on_curve) + { + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + last_offcurve = optional_point_t (); + } + else + { + optional_point_t mid = last_offcurve.lerp (p, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = p; + } + } + else + { + if (is_on_curve) + draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y)); + else + last_offcurve = p; + } + } + + if (point.is_end_point) + { + if (first_offcurve.has_data && last_offcurve.has_data) + { + optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f); + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (mid.x), font->em_scalef_y (mid.y)); + last_offcurve = optional_point_t (); + /* now check the rest */ + } + + if (first_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (last_offcurve.has_data && first_oncurve.has_data) + draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y), + font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + else if (first_oncurve.has_data) + draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y)); + + /* Getting ready for the next contour */ + first_oncurve = first_offcurve = last_offcurve = optional_point_t (); + draw_helper->end_path (); + } + } + void points_end () {} + + bool is_consuming_contour_points () { return true; } + contour_point_t *get_phantoms_sink () { return nullptr; } + }; + + bool + get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const + { return get_points (font, gid, path_builder_t (font, draw_helper)); } +#endif + +#ifndef HB_NO_VAR + const gvar_accelerator_t *gvar; +#endif + const hmtx_accelerator_t *hmtx; + const vmtx_accelerator_t *vmtx; + private: bool short_offset; unsigned int num_glyphs; hb_blob_ptr_t loca_table; hb_blob_ptr_t glyf_table; + hb_face_t *face; + }; + + struct SubsetGlyph + { + hb_codepoint_t new_gid; + hb_codepoint_t old_gid; + Glyph source_glyph; + hb_bytes_t dest_start; /* region of source_glyph to copy first */ + hb_bytes_t dest_end; /* region of source_glyph to copy second */ + + bool serialize (hb_serialize_context_t *c, + const hb_subset_plan_t *plan) const + { + TRACE_SERIALIZE (this); + + hb_bytes_t dest_glyph = dest_start.copy (c); + dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length); + unsigned int pad_length = padding (); + DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length); + + HBUINT8 pad; + pad = 0; + while (pad_length > 0) + { + c->embed (pad); + pad_length--; + } + + if (unlikely (!dest_glyph.length)) return_trace (true); + + /* update components gids */ + for (auto &_ : Glyph (dest_glyph).get_composite_iterator ()) + { + hb_codepoint_t new_gid; + if (plan->new_gid_for_old_gid (_.get_glyph_index (), &new_gid)) + const_cast (_).set_glyph_index (new_gid); + } + + if (plan->drop_hints) Glyph (dest_glyph).drop_hints (); + + return_trace (true); + } + + void drop_hints_bytes () + { source_glyph.drop_hints_bytes (dest_start, dest_end); } + + unsigned int length () const { return dest_start.length + dest_end.length; } + /* pad to 2 to ensure 2-byte loca will be ok */ + unsigned int padding () const { return length () % 2; } + unsigned int padded_size () const { return length () + padding (); } }; protected: - UnsizedArrayOf dataZ; /* Glyphs data. */ + UnsizedArrayOf + dataZ; /* Glyphs data. */ public: - DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always - * check the size externally, allow Null() object of it by - * defining it MIN() instead. */ + DEFINE_SIZE_MIN (0); /* In reality, this is UNBOUNDED() type; but since we always + * check the size externally, allow Null() object of it by + * defining it _MIN instead. */ }; struct glyf_accelerator_t : glyf::accelerator_t {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh index d27d098b69f98..201ffc50be7bc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hdmx-table.hh @@ -41,68 +41,31 @@ namespace OT { struct DeviceRecord { - struct SubsetView - { - const DeviceRecord *source_device_record; - unsigned int sizeDeviceRecord; - hb_subset_plan_t *subset_plan; - - void init (const DeviceRecord *source_device_record, - unsigned int sizeDeviceRecord, - hb_subset_plan_t *subset_plan) - { - this->source_device_record = source_device_record; - this->sizeDeviceRecord = sizeDeviceRecord; - this->subset_plan = subset_plan; - } - - unsigned int len () const - { return this->subset_plan->glyphs.length; } - - const HBUINT8* operator [] (unsigned int i) const - { - if (unlikely (i >= len ())) return nullptr; - hb_codepoint_t gid = this->subset_plan->glyphs [i]; - - if (gid >= sizeDeviceRecord - DeviceRecord::min_size) - return nullptr; - return &(this->source_device_record->widthsZ[gid]); - } - }; - - static unsigned int get_size (unsigned int count) + static unsigned int get_size (unsigned count) { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } - bool serialize (hb_serialize_context_t *c, const SubsetView &subset_view) + template + bool serialize (hb_serialize_context_t *c, unsigned pixelSize, Iterator it) { TRACE_SERIALIZE (this); - unsigned int size = get_size (subset_view.len ()); - if (unlikely (!c->allocate_size (size))) - { - DEBUG_MSG(SUBSET, nullptr, "Couldn't allocate enough space for DeviceRecord: %d.", - size); - return_trace (false); - } - - this->pixelSize.set (subset_view.source_device_record->pixelSize); - this->maxWidth.set (subset_view.source_device_record->maxWidth); - - for (unsigned int i = 0; i < subset_view.len (); i++) - { - const HBUINT8 *width = subset_view[i]; - if (!width) - { - DEBUG_MSG(SUBSET, nullptr, "HDMX width for new gid %d is missing.", i); - return_trace (false); - } - widthsZ[i].set (*width); - } + unsigned length = it.len (); + + if (unlikely (!c->extend (*this, length))) return_trace (false); + + this->pixelSize = pixelSize; + this->maxWidth = + + it + | hb_reduce (hb_max, 0u); + + + it + | hb_sink (widthsZ.as_array (length)); return_trace (true); } - bool sanitize (hb_sanitize_context_t *c, unsigned int sizeDeviceRecord) const + bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && @@ -132,62 +95,60 @@ struct hdmx return StructAtOffset (&this->firstDeviceRecord, i * sizeDeviceRecord); } - bool serialize (hb_serialize_context_t *c, const hdmx *source_hdmx, hb_subset_plan_t *plan) + template + bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min ((*this)))) return_trace (false); - this->version.set (source_hdmx->version); - this->numRecords.set (source_hdmx->numRecords); - this->sizeDeviceRecord.set (DeviceRecord::get_size (plan->glyphs.length)); + this->version = version; + this->numRecords = it.len (); + this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0); - for (unsigned int i = 0; i < source_hdmx->numRecords; i++) - { - DeviceRecord::SubsetView subset_view; - subset_view.init (&(*source_hdmx)[i], source_hdmx->sizeDeviceRecord, plan); + for (const hb_item_type& _ : +it) + c->start_embed ()->serialize (c, _.first, _.second); - if (!c->start_embed ()->serialize (c, subset_view)) - return_trace (false); - } - - return_trace (true); + return_trace (c->successful); } - static size_t get_subsetted_size (const hdmx *source_hdmx, hb_subset_plan_t *plan) + + bool subset (hb_subset_context_t *c) const { - return min_size + source_hdmx->numRecords * DeviceRecord::get_size (plan->glyphs.length); + TRACE_SUBSET (this); + + hdmx *hdmx_prime = c->serializer->start_embed (); + if (unlikely (!hdmx_prime)) return_trace (false); + + auto it = + + hb_range ((unsigned) numRecords) + | hb_map ([c, this] (unsigned _) + { + const DeviceRecord *device_record = + &StructAtOffset (&firstDeviceRecord, + _ * sizeDeviceRecord); + auto row = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map (c->plan->reverse_glyph_map) + | hb_map ([this, c, device_record] (hb_codepoint_t _) + { + if (c->plan->is_empty_glyph (_)) + return Null (HBUINT8); + return device_record->widthsZ.as_array (get_num_glyphs ()) [_]; + }) + ; + return hb_pair ((unsigned) device_record->pixelSize, +row); + }) + ; + + hdmx_prime->serialize (c->serializer, version, it); + return_trace (true); } - bool subset (hb_subset_plan_t *plan) const + unsigned get_num_glyphs () const { - size_t dest_size = get_subsetted_size (this, plan); - hdmx *dest = (hdmx *) malloc (dest_size); - if (unlikely (!dest)) - { - DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %lu for hdmx subset output.", (unsigned long) dest_size); - return false; - } - - hb_serialize_context_t c (dest, dest_size); - hdmx *hdmx_prime = c.start_serialize (); - if (!hdmx_prime || !hdmx_prime->serialize (&c, this, plan)) - { - free (dest); - DEBUG_MSG(SUBSET, nullptr, "Failed to serialize write new hdmx."); - return false; - } - c.end_serialize (); - - hb_blob_t *hdmx_prime_blob = hb_blob_create ((const char *) dest, - dest_size, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool result = plan->add_table (HB_OT_TAG_hdmx, hdmx_prime_blob); - hb_blob_destroy (hdmx_prime_blob); - - return result; + return sizeDeviceRecord - DeviceRecord::min_size; } bool sanitize (hb_sanitize_context_t *c) const @@ -200,10 +161,12 @@ struct hdmx } protected: - HBUINT16 version; /* Table version number (0) */ - HBUINT16 numRecords; /* Number of device records. */ - HBUINT32 sizeDeviceRecord; /* Size of a device record, 32-bit aligned. */ - DeviceRecord firstDeviceRecord; /* Array of device records. */ + HBUINT16 version; /* Table version number (0) */ + HBUINT16 numRecords; /* Number of device records. */ + HBUINT32 sizeDeviceRecord; + /* Size of a device record, 32-bit aligned. */ + DeviceRecord firstDeviceRecord; + /* Array of device records. */ public: DEFINE_SIZE_MIN (8); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh index d7448d2dfcee9..3f4af706bc888 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-head-table.hh @@ -54,6 +54,18 @@ struct head return 16 <= upem && upem <= 16384 ? upem : 1000; } + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace ((bool) c->embed (this)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace (serialize (c->serializer)); + } + enum mac_style_flag_t { BOLD = 1u<<0, ITALIC = 1u<<1, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh index 66879a085a008..37ef8744572d3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hhea-table.hh @@ -45,6 +45,8 @@ namespace OT { template struct _hea { + bool has_data () const { return version.major; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -52,35 +54,38 @@ struct _hea } public: - FixedVersion<>version; /* 0x00010000u for version 1.0. */ - FWORD ascender; /* Typographic ascent. */ - FWORD descender; /* Typographic descent. */ - FWORD lineGap; /* Typographic line gap. */ - UFWORD advanceMax; /* Maximum advance width/height value in - * metrics table. */ - FWORD minLeadingBearing; /* Minimum left/top sidebearing value in - * metrics table. */ - FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value; - * calculated as Min(aw - lsb - - * (xMax - xMin)) for horizontal. */ - FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), - * vertical: minLeadingBearing+(yMax-yMin). */ - HBINT16 caretSlopeRise; /* Used to calculate the slope of the - * cursor (rise/run); 1 for vertical caret, - * 0 for horizontal.*/ - HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ - HBINT16 caretOffset; /* The amount by which a slanted - * highlight on a glyph needs - * to be shifted to produce the - * best appearance. Set to 0 for - * non-slanted fonts. */ - HBINT16 reserved1; /* Set to 0. */ - HBINT16 reserved2; /* Set to 0. */ - HBINT16 reserved3; /* Set to 0. */ - HBINT16 reserved4; /* Set to 0. */ - HBINT16 metricDataFormat; /* 0 for current format. */ - HBUINT16 numberOfLongMetrics; /* Number of LongMetric entries in metric - * table. */ + FixedVersion<>version; /* 0x00010000u for version 1.0. */ + FWORD ascender; /* Typographic ascent. */ + FWORD descender; /* Typographic descent. */ + FWORD lineGap; /* Typographic line gap. */ + UFWORD advanceMax; /* Maximum advance width/height value in + * metrics table. */ + FWORD minLeadingBearing; + /* Minimum left/top sidebearing value in + * metrics table. */ + FWORD minTrailingBearing; + /* Minimum right/bottom sidebearing value; + * calculated as Min(aw - lsb - + * (xMax - xMin)) for horizontal. */ + FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)), + * vertical: minLeadingBearing+(yMax-yMin). */ + HBINT16 caretSlopeRise; /* Used to calculate the slope of the + * cursor (rise/run); 1 for vertical caret, + * 0 for horizontal.*/ + HBINT16 caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */ + HBINT16 caretOffset; /* The amount by which a slanted + * highlight on a glyph needs + * to be shifted to produce the + * best appearance. Set to 0 for + * non-slanted fonts. */ + HBINT16 reserved1; /* Set to 0. */ + HBINT16 reserved2; /* Set to 0. */ + HBINT16 reserved3; /* Set to 0. */ + HBINT16 reserved4; /* Set to 0. */ + HBINT16 metricDataFormat;/* 0 for current format. */ + HBUINT16 numberOfLongMetrics; + /* Number of LongMetric entries in metric + * table. */ public: DEFINE_SIZE_STATIC (36); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh index dfb0f78d6433a..0a2973d8eb85c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-hmtx-table.hh @@ -29,8 +29,8 @@ #include "hb-open-type.hh" #include "hb-ot-hhea-table.hh" -#include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-metrics.hh" /* * hmtx -- Horizontal Metrics @@ -42,6 +42,13 @@ #define HB_OT_TAG_vmtx HB_TAG('v','m','t','x') +HB_INTERNAL int +_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + +HB_INTERNAL unsigned +_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical); + + namespace OT { @@ -53,6 +60,7 @@ struct LongMetric DEFINE_SIZE_STATIC (4); }; + template struct hmtxvmtx { @@ -66,7 +74,7 @@ struct hmtxvmtx bool subset_update_header (hb_subset_plan_t *plan, - unsigned int num_hmetrics) const + unsigned int num_hmetrics) const { hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table (plan->source, H::tableTag); hb_blob_t *dest_blob = hb_blob_copy_writable_or_fail (src_blob); @@ -78,7 +86,7 @@ struct hmtxvmtx unsigned int length; H *table = (H *) hb_blob_get_data (dest_blob, &length); - table->numberOfLongMetrics.set (num_hmetrics); + table->numberOfLongMetrics = num_hmetrics; bool result = plan->add_table (H::tableTag, dest_blob); hb_blob_destroy (dest_blob); @@ -86,100 +94,66 @@ struct hmtxvmtx return result; } - bool subset (hb_subset_plan_t *plan) const + template + void serialize (hb_serialize_context_t *c, + Iterator it, + unsigned num_advances) { - typename T::accelerator_t _mtx; - _mtx.init (plan->source); - - /* All the trailing glyphs with the same advance can use one LongMetric - * and just keep LSB */ - hb_vector_t &gids = plan->glyphs; - unsigned int num_advances = gids.length; - unsigned int last_advance = _mtx.get_advance (gids[num_advances - 1]); - while (num_advances > 1 && - last_advance == _mtx.get_advance (gids[num_advances - 2])) - { - num_advances--; - } - - /* alloc the new table */ - size_t dest_sz = num_advances * 4 - + (gids.length - num_advances) * 2; - void *dest = (void *) malloc (dest_sz); - if (unlikely (!dest)) + unsigned idx = 0; + for (auto _ : it) { - return false; - } - DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances); - DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %u bytes", HB_UNTAG(T::tableTag), num_advances, gids.length - num_advances, (unsigned int) dest_sz); - - const char *source_table = hb_blob_get_data (_mtx.table.get_blob (), nullptr); - // Copy everything over - LongMetric * old_metrics = (LongMetric *) source_table; - FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances); - char * dest_pos = (char *) dest; - - bool failed = false; - for (unsigned int i = 0; i < gids.length; i++) - { - /* the last metric or the one for gids[i] */ - LongMetric *src_metric = old_metrics + MIN ((hb_codepoint_t) _mtx.num_advances - 1, gids[i]); - if (gids[i] < _mtx.num_advances) + if (idx < num_advances) { - /* src is a LongMetric */ - if (i < num_advances) - { - /* dest is a LongMetric, copy it */ - *((LongMetric *) dest_pos) = *src_metric; - } - else - { - /* dest just sb */ - *((FWORD *) dest_pos) = src_metric->sb; - } + LongMetric lm; + lm.advance = _.first; + lm.sb = _.second; + if (unlikely (!c->embed (&lm))) return; } else { - if (gids[i] >= _mtx.num_metrics) - { - DEBUG_MSG(SUBSET, nullptr, "gid %d is >= number of source metrics %d", - gids[i], _mtx.num_metrics); - failed = true; - break; - } - FWORD src_sb = *(lsbs + gids[i] - _mtx.num_advances); - if (i < num_advances) - { - /* dest needs a full LongMetric */ - LongMetric *metric = (LongMetric *)dest_pos; - metric->advance = src_metric->advance; - metric->sb = src_sb; - } - else - { - /* dest just needs an sb */ - *((FWORD *) dest_pos) = src_sb; - } + FWORD *sb = c->allocate_size (FWORD::static_size); + if (unlikely (!sb)) return; + *sb = _.second; } - dest_pos += (i < num_advances ? 4 : 2); + idx++; } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + T *table_prime = c->serializer->start_embed (); + if (unlikely (!table_prime)) return_trace (false); + + accelerator_t _mtx; + _mtx.init (c->plan->source); + unsigned num_advances = _mtx.num_advances_for_subset (c->plan); + + auto it = + + hb_range (c->plan->num_output_glyphs ()) + | hb_map ([c, &_mtx] (unsigned _) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (_, &old_gid)) + return hb_pair (0u, 0); + return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid)); + }) + ; + + table_prime->serialize (c->serializer, it, num_advances); + _mtx.fini (); + if (unlikely (c->serializer->ran_out_of_room || c->serializer->in_error ())) + return_trace (false); + // Amend header num hmetrics - if (failed || unlikely (!subset_update_header (plan, num_advances))) - { - free (dest); - return false; - } + if (unlikely (!subset_update_header (c->plan, num_advances))) + return_trace (false); - hb_blob_t *result = hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool success = plan->add_table (T::tableTag, result); - hb_blob_destroy (result); - return success; + return_trace (true); } struct accelerator_t @@ -187,34 +161,13 @@ struct hmtxvmtx friend struct hmtxvmtx; void init (hb_face_t *face, - unsigned int default_advance_ = 0) + unsigned int default_advance_ = 0) { default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face); - bool got_font_extents = false; - if (T::os2Tag != HB_TAG_NONE && face->table.OS2->is_typo_metrics ()) - { - ascender = abs (face->table.OS2->sTypoAscender); - descender = -abs (face->table.OS2->sTypoDescender); - line_gap = face->table.OS2->sTypoLineGap; - got_font_extents = (ascender | descender) != 0; - } + num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics; - hb_blob_t *_hea_blob = hb_sanitize_context_t().reference_table (face); - const H *_hea_table = _hea_blob->as (); - num_advances = _hea_table->numberOfLongMetrics; - if (!got_font_extents) - { - ascender = abs (_hea_table->ascender); - descender = -abs (_hea_table->descender); - line_gap = _hea_table->lineGap; - got_font_extents = (ascender | descender) != 0; - } - hb_blob_destroy (_hea_blob); - - has_font_extents = got_font_extents; - - table = hb_sanitize_context_t().reference_table (face, T::tableTag); + table = hb_sanitize_context_t ().reference_table (face, T::tableTag); /* Cap num_metrics() and num_advances() based on table length. */ unsigned int len = table.get_length (); @@ -231,7 +184,7 @@ struct hmtxvmtx table = hb_blob_get_empty (); } - var_table = hb_sanitize_context_t().reference_table (face, T::variationsTag); + var_table = hb_sanitize_context_t ().reference_table (face, T::variationsTag); } void fini () @@ -240,8 +193,7 @@ struct hmtxvmtx var_table.destroy (); } - /* TODO Add variations version. */ - unsigned int get_side_bearing (hb_codepoint_t glyph) const + int get_side_bearing (hb_codepoint_t glyph) const { if (glyph < num_advances) return table->longMetricZ[glyph].sb; @@ -253,6 +205,23 @@ struct hmtxvmtx return bearings[glyph - num_advances]; } + int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const + { + int side_bearing = get_side_bearing (glyph); + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return side_bearing; + + if (var_table.get_length ()) + return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + + return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return side_bearing; +#endif + } + unsigned int get_advance (hb_codepoint_t glyph) const { if (unlikely (glyph >= num_metrics)) @@ -266,25 +235,52 @@ struct hmtxvmtx return default_advance; } - return table->longMetricZ[MIN (glyph, (uint32_t) num_advances - 1)].advance; + return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance; } unsigned int get_advance (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int advance = get_advance (glyph); - if (likely (glyph < num_metrics)) + +#ifndef HB_NO_VAR + if (unlikely (glyph >= num_metrics) || !font->num_coords) + return advance; + + if (var_table.get_length ()) + return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?! + + return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx); +#else + return advance; +#endif + } + + unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const + { + unsigned int num_advances = plan->num_output_glyphs (); + unsigned int last_advance = _advance_for_new_gid (plan, + num_advances - 1); + while (num_advances > 1 && + last_advance == _advance_for_new_gid (plan, + num_advances - 2)) { - advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! + num_advances--; } - return advance; + + return num_advances; } - public: - bool has_font_extents; - int ascender; - int descender; - int line_gap; + private: + unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan, + hb_codepoint_t new_gid) const + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (new_gid, &old_gid)) + return 0; + + return get_advance (old_gid); + } protected: unsigned int num_metrics; @@ -297,27 +293,29 @@ struct hmtxvmtx }; protected: - UnsizedArrayOflongMetricZ;/* Paired advance width and leading - * bearing values for each glyph. The - * value numOfHMetrics comes from - * the 'hhea' table. If the font is - * monospaced, only one entry need - * be in the array, but that entry is - * required. The last entry applies to - * all subsequent glyphs. */ -/*UnsizedArrayOf leadingBearingX;*//* Here the advance is assumed - * to be the same as the advance - * for the last entry above. The - * number of entries in this array is - * derived from numGlyphs (from 'maxp' - * table) minus numberOfLongMetrics. - * This generally is used with a run - * of monospaced glyphs (e.g., Kanji - * fonts or Courier fonts). Only one - * run is allowed and it must be at - * the end. This allows a monospaced - * font to vary the side bearing - * values for each glyph. */ + UnsizedArrayOf + longMetricZ; /* Paired advance width and leading + * bearing values for each glyph. The + * value numOfHMetrics comes from + * the 'hhea' table. If the font is + * monospaced, only one entry need + * be in the array, but that entry is + * required. The last entry applies to + * all subsequent glyphs. */ +/*UnsizedArrayOf leadingBearingX;*/ + /* Here the advance is assumed + * to be the same as the advance + * for the last entry above. The + * number of entries in this array is + * derived from numGlyphs (from 'maxp' + * table) minus numberOfLongMetrics. + * This generally is used with a run + * of monospaced glyphs (e.g., Kanji + * fonts or Courier fonts). Only one + * run is allowed and it must be at + * the end. This allows a monospaced + * font to vary the side bearing + * values for each glyph. */ public: DEFINE_SIZE_ARRAY (0, longMetricZ); }; @@ -325,12 +323,12 @@ struct hmtxvmtx struct hmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_hmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_HVAR; - static constexpr hb_tag_t os2Tag = HB_OT_TAG_OS2; + static constexpr bool is_horizontal = true; }; struct vmtx : hmtxvmtx { static constexpr hb_tag_t tableTag = HB_OT_TAG_vmtx; static constexpr hb_tag_t variationsTag = HB_OT_TAG_VVAR; - static constexpr hb_tag_t os2Tag = HB_TAG_NONE; + static constexpr bool is_horizontal = false; }; struct hmtx_accelerator_t : hmtx::accelerator_t {}; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh index 9d870ecfc8a20..37ea1276ea6b3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-kern-table.hh @@ -47,9 +47,9 @@ struct KernSubTableFormat3 int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const { hb_array_t kernValue = kernValueZ.as_array (kernValueCount); - hb_array_t leftClass = StructAfter > (kernValue).as_array (glyphCount); - hb_array_t rightClass = StructAfter > (leftClass).as_array (glyphCount); - hb_array_t kernIndex = StructAfter > (rightClass).as_array (leftClassCount * rightClassCount); + hb_array_t leftClass = StructAfter> (kernValue).as_array (glyphCount); + hb_array_t rightClass = StructAfter> (leftClass).as_array (glyphCount); + hb_array_t kernIndex = StructAfter> (rightClass).as_array (leftClassCount * rightClassCount); unsigned int leftC = leftClass[left]; unsigned int rightC = rightClass[right]; @@ -86,21 +86,26 @@ struct KernSubTableFormat3 } protected: - KernSubTableHeader header; - HBUINT16 glyphCount; /* The number of glyphs in this font. */ - HBUINT8 kernValueCount; /* The number of kerning values. */ - HBUINT8 leftClassCount; /* The number of left-hand classes. */ - HBUINT8 rightClassCount;/* The number of right-hand classes. */ - HBUINT8 flags; /* Set to zero (reserved for future use). */ - UnsizedArrayOf kernValueZ; /* The kerning values. - * Length kernValueCount. */ + KernSubTableHeader + header; + HBUINT16 glyphCount; /* The number of glyphs in this font. */ + HBUINT8 kernValueCount; /* The number of kerning values. */ + HBUINT8 leftClassCount; /* The number of left-hand classes. */ + HBUINT8 rightClassCount;/* The number of right-hand classes. */ + HBUINT8 flags; /* Set to zero (reserved for future use). */ + UnsizedArrayOf + kernValueZ; /* The kerning values. + * Length kernValueCount. */ #if 0 - UnsizedArrayOfleftClass; /* The left-hand classes. - * Length glyphCount. */ - UnsizedArrayOfrightClass; /* The right-hand classes. - * Length glyphCount. */ - UnsizedArrayOfkernIndex; /* The indices into the kernValue array. - * Length leftClassCount * rightClassCount */ + UnsizedArrayOf + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOfkernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ #endif public: DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); @@ -121,16 +126,20 @@ struct KernSubTable } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int subtable_type = get_type (); TRACE_DISPATCH (this, subtable_type); switch (subtable_type) { case 0: return_trace (c->dispatch (u.format0)); - case 1: return_trace (u.header.apple ? c->dispatch (u.format1) : c->default_return_value ()); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (u.header.apple ? c->dispatch (u.format1, hb_forward (ds)...) : c->default_return_value ()); +#endif case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (u.header.apple ? c->dispatch (u.format3) : c->default_return_value ()); +#ifndef HB_NO_AAT_SHAPE + case 3: return_trace (u.header.apple ? c->dispatch (u.format3, hb_forward (ds)...) : c->default_return_value ()); +#endif default: return_trace (c->default_return_value ()); } } @@ -163,8 +172,8 @@ struct KernOTSubTableHeader static constexpr bool apple = false; typedef AAT::ObsoleteTypes Types; - unsigned int tuple_count () const { return 0; } - bool is_horizontal () const { return (coverage & Horizontal); } + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return (coverage & Horizontal); } enum Coverage { @@ -218,8 +227,8 @@ struct KernAATSubTableHeader static constexpr bool apple = true; typedef AAT::ObsoleteTypes Types; - unsigned int tuple_count () const { return 0; } - bool is_horizontal () const { return !(coverage & Vertical); } + unsigned tuple_count () const { return 0; } + bool is_horizontal () const { return !(coverage & Vertical); } enum Coverage { @@ -242,8 +251,8 @@ struct KernAATSubTableHeader HBUINT8 coverage; /* Coverage bits. */ HBUINT8 format; /* Subtable format. */ HBUINT16 tupleIndex; /* The tuple index (used for variations fonts). - * This value specifies which tuple this subtable covers. - * Note: We don't implement. */ + * This value specifies which tuple this subtable covers. + * Note: We don't implement. */ public: DEFINE_SIZE_STATIC (8); }; @@ -271,14 +280,16 @@ struct kern { static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - bool has_data () const { return u.version32; } - unsigned int get_type () const { return u.major; } + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } bool has_state_machine () const { switch (get_type ()) { case 0: return u.ot.has_state_machine (); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.has_state_machine (); +#endif default:return false; } } @@ -287,7 +298,9 @@ struct kern { switch (get_type ()) { case 0: return u.ot.has_cross_stream (); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.has_cross_stream (); +#endif default:return false; } } @@ -296,7 +309,9 @@ struct kern { switch (get_type ()) { case 0: return u.ot.get_h_kerning (left, right); +#ifndef HB_NO_AAT_SHAPE case 1: return u.aat.get_h_kerning (left, right); +#endif default:return 0; } } @@ -304,14 +319,16 @@ struct kern bool apply (AAT::hb_aat_apply_context_t *c) const { return dispatch (c); } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int subtable_type = get_type (); TRACE_DISPATCH (this, subtable_type); switch (subtable_type) { - case 0: return_trace (c->dispatch (u.ot)); - case 1: return_trace (c->dispatch (u.aat)); + case 0: return_trace (c->dispatch (u.ot, hb_forward (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, hb_forward (ds)...)); +#endif default: return_trace (c->default_return_value ()); } } @@ -328,7 +345,9 @@ struct kern HBUINT32 version32; HBUINT16 major; KernOT ot; +#ifndef HB_NO_AAT_SHAPE KernAAT aat; +#endif } u; public: DEFINE_SIZE_UNION (4, version32); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh index fe30e278795a4..f8cdb04691053 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-base-table.hh @@ -1,7 +1,7 @@ /* - * Copyright © 2016 Elie Roux + * Copyright © 2016 Elie Roux * Copyright © 2018 Google, Inc. - * Copyright © 2018 Ebrahim Byagowi + * Copyright © 2018-2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -32,9 +32,6 @@ #include "hb-open-type.hh" #include "hb-ot-layout-common.hh" -/* To be removed */ -typedef hb_tag_t hb_ot_layout_baseline_t; - namespace OT { /* @@ -76,7 +73,7 @@ struct BaseCoordFormat2 protected: HBUINT16 format; /* Format identifier--format = 2 */ FWORD coordinate; /* X or Y value, in design units */ - GlyphID referenceGlyph; /* Glyph ID of control glyph */ + HBGlyphID referenceGlyph; /* Glyph ID of control glyph */ HBUINT16 coordPoint; /* Index of contour point on the * reference glyph */ public: @@ -116,9 +113,11 @@ struct BaseCoordFormat3 struct BaseCoord { - hb_position_t get_coord (hb_font_t *font, + bool has_data () const { return u.format; } + + hb_position_t get_coord (hb_font_t *font, const VariationStore &var_store, - hb_direction_t direction) const + hb_direction_t direction) const { switch (u.format) { case 1: return u.format1.get_coord (); @@ -142,10 +141,10 @@ struct BaseCoord protected: union { - HBUINT16 format; - BaseCoordFormat1 format1; - BaseCoordFormat2 format2; - BaseCoordFormat3 format3; + HBUINT16 format; + BaseCoordFormat1 format1; + BaseCoordFormat2 format2; + BaseCoordFormat3 format3; } u; public: DEFINE_SIZE_UNION (2, format); @@ -153,14 +152,9 @@ struct BaseCoord struct FeatMinMaxRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const FeatMinMaxRecord &entry = * (const FeatMinMaxRecord *) entry_; - return key < (unsigned int) entry.tag ? -1 : - key > (unsigned int) entry.tag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + bool has_data () const { return tag; } void get_min_max (const BaseCoord **min, const BaseCoord **max) const { @@ -195,17 +189,12 @@ struct FeatMinMaxRecord struct MinMax { void get_min_max (hb_tag_t feature_tag, - const BaseCoord **min, - const BaseCoord **max) const + const BaseCoord **min, + const BaseCoord **max) const { - /* TODO Replace hb_bsearch() with .bsearch(). */ - const FeatMinMaxRecord *minMaxCoord = (const FeatMinMaxRecord *) - hb_bsearch (&feature_tag, featMinMaxRecords.arrayZ, - featMinMaxRecords.len, - FeatMinMaxRecord::static_size, - FeatMinMaxRecord::cmp); - if (minMaxCoord) - minMaxCoord->get_min_max (min, max); + const FeatMinMaxRecord &minMaxCoord = featMinMaxRecords.bsearch (feature_tag); + if (minMaxCoord.has_data ()) + minMaxCoord.get_min_max (min, max); else { if (likely (min)) *min = &(this+minCoord); @@ -271,17 +260,11 @@ struct BaseValues struct BaseLangSysRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const BaseLangSysRecord &entry = * (const BaseLangSysRecord *) entry_; - return key < (unsigned int) entry.baseLangSysTag ? -1 : - key > (unsigned int) entry.baseLangSysTag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return baseLangSysTag.cmp (key); } + + bool has_data () const { return baseLangSysTag; } - const MinMax &get_min_max () const - { return this+minMax; } + const MinMax &get_min_max () const { return this+minMax; } bool sanitize (hb_sanitize_context_t *c, const void *base) const { @@ -303,19 +286,14 @@ struct BaseScript { const MinMax &get_min_max (hb_tag_t language_tag) const { - /* TODO Replace hb_bsearch() with .bsearch(). */ - const BaseLangSysRecord* record = (const BaseLangSysRecord *) - hb_bsearch (&language_tag, baseLangSysRecords.arrayZ, - baseLangSysRecords.len, - BaseLangSysRecord::static_size, - BaseLangSysRecord::cmp); - return record ? record->get_min_max () : this+defaultMinMax; + const BaseLangSysRecord& record = baseLangSysRecords.bsearch (language_tag); + return record.has_data () ? record.get_min_max () : this+defaultMinMax; } const BaseCoord &get_base_coord (int baseline_tag_index) const { return (this+baseValues).get_base_coord (baseline_tag_index); } - bool is_empty () const { return !baseValues; } + bool has_data () const { return baseValues; } bool sanitize (hb_sanitize_context_t *c) const { @@ -345,14 +323,9 @@ struct BaseScript struct BaseScriptList; struct BaseScriptRecord { - static int cmp (const void *key_, const void *entry_) - { - hb_tag_t key = * (hb_tag_t *) key_; - const BaseScriptRecord &entry = * (const BaseScriptRecord *) entry_; - return key < (unsigned int) entry.baseScriptTag ? -1 : - key > (unsigned int) entry.baseScriptTag ? 1 : - 0; - } + int cmp (hb_tag_t key) const { return baseScriptTag.cmp (key); } + + bool has_data () const { return baseScriptTag; } const BaseScript &get_base_script (const BaseScriptList *list) const { return list+baseScript; } @@ -376,22 +349,11 @@ struct BaseScriptRecord struct BaseScriptList { - const BaseScriptRecord *find_record (hb_tag_t script) const - { - /* TODO Replace hb_bsearch() with .bsearch(). */ - return (const BaseScriptRecord *) hb_bsearch (&script, baseScriptRecords.arrayZ, - baseScriptRecords.len, - BaseScriptRecord::static_size, - BaseScriptRecord::cmp); - } - - /* TODO: Or client should handle fallback? */ const BaseScript &get_base_script (hb_tag_t script) const { - const BaseScriptRecord *record = find_record (script); - if (!record) record = find_record ((hb_script_t) HB_TAG ('D','F','L','T')); - - return record ? record->get_base_script (this) : Null (BaseScript); + const BaseScriptRecord *record = &baseScriptRecords.bsearch (script); + if (!record->has_data ()) record = &baseScriptRecords.bsearch (HB_TAG ('D','F','L','T')); + return record->has_data () ? record->get_base_script (this) : Null (BaseScript); } bool sanitize (hb_sanitize_context_t *c) const @@ -411,15 +373,20 @@ struct BaseScriptList struct Axis { - bool get_baseline (hb_ot_layout_baseline_t baseline, - hb_tag_t script_tag, - hb_tag_t language_tag, - const BaseCoord **coord) const + bool get_baseline (hb_tag_t baseline_tag, + hb_tag_t script_tag, + hb_tag_t language_tag, + const BaseCoord **coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (base_script.is_empty ()) return false; + if (!base_script.has_data ()) return false; - if (likely (coord)) *coord = &base_script.get_base_coord ((this+baseTagList).bsearch (baseline)); + if (likely (coord)) + { + unsigned int tag_index = 0; + (this+baseTagList).bfind (baseline_tag, &tag_index); + *coord = &base_script.get_base_coord (tag_index); + } return true; } @@ -431,7 +398,7 @@ struct Axis const BaseCoord **max_coord) const { const BaseScript &base_script = (this+baseScriptList).get_base_script (script_tag); - if (base_script.is_empty ()) return false; + if (!base_script.has_data ()) return false; base_script.get_min_max (language_tag).get_min_max (feature_tag, min_coord, max_coord); @@ -447,7 +414,7 @@ struct Axis } protected: - OffsetTo > + OffsetTo> baseTagList; /* Offset to BaseTagList table, from beginning * of Axis table (may be NULL) * Array of 4-byte baseline identification tags — must @@ -472,20 +439,21 @@ struct BASE const VariationStore &get_var_store () const { return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } - bool get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *base) const + bool get_baseline (hb_font_t *font, + hb_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *base) const { - const BaseCoord *base_coord; - if (!get_axis (direction).get_baseline (baseline, script_tag, language_tag, &base_coord)) + const BaseCoord *base_coord = nullptr; + if (unlikely (!get_axis (direction).get_baseline (baseline_tag, script_tag, language_tag, &base_coord) || + !base_coord || !base_coord->has_data ())) return false; - if (likely (base && base_coord)) *base = base_coord->get_coord (font, - get_var_store (), - direction); + if (likely (base)) + *base = base_coord->get_coord (font, get_var_store (), direction); + return true; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh index 7618057ed1c35..56773e68e3663 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-common.hh @@ -33,6 +33,7 @@ #include "hb-ot-layout.hh" #include "hb-open-type.hh" #include "hb-set.hh" +#include "hb-bimap.hh" #ifndef HB_MAX_NESTING_LEVEL @@ -59,6 +60,18 @@ #define HB_MAX_LANGSYS 2000 #endif +#ifndef HB_MAX_FEATURES +#define HB_MAX_FEATURES 750 +#endif + +#ifndef HB_MAX_FEATURE_INDICES +#define HB_MAX_FEATURE_INDICES 1500 +#endif + +#ifndef HB_MAX_LOOKUP_INDICES +#define HB_MAX_LOOKUP_INDICES 20000 +#endif + namespace OT { @@ -66,6 +79,213 @@ namespace OT { #define NOT_COVERED ((unsigned int) -1) +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +template +static inline void ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t &glyphs, + const hb_set_t &klasses, + hb_map_t *klass_map /*INOUT*/); + +struct hb_subset_layout_context_t : + hb_dispatch_context_t +{ + const char *get_name () { return "SUBSET_LAYOUT"; } + static return_t default_return_value () { return hb_empty_t (); } + + bool visitScript () + { + return script_count++ < HB_MAX_SCRIPTS; + } + + bool visitLangSys () + { + return langsys_count++ < HB_MAX_LANGSYS; + } + + bool visitFeatureIndex (int count) + { + feature_index_count += count; + return feature_index_count < HB_MAX_FEATURE_INDICES; + } + + bool visitLookupIndex() + { + lookup_index_count++; + return lookup_index_count < HB_MAX_LOOKUP_INDICES; + } + + hb_subset_context_t *subset_context; + const hb_tag_t table_tag; + const hb_map_t *lookup_index_map; + const hb_map_t *feature_index_map; + + hb_subset_layout_context_t (hb_subset_context_t *c_, + hb_tag_t tag_, + hb_map_t *lookup_map_, + hb_map_t *feature_map_) : + subset_context (c_), + table_tag (tag_), + lookup_index_map (lookup_map_), + feature_index_map (feature_map_), + script_count (0), + langsys_count (0), + feature_index_count (0), + lookup_index_count (0) + {} + + private: + unsigned script_count; + unsigned langsys_count; + unsigned feature_index_count; + unsigned lookup_index_count; +}; + +struct hb_collect_variation_indices_context_t : + hb_dispatch_context_t +{ + template + return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + hb_set_t *layout_variation_indices; + const hb_set_t *glyph_set; + const hb_map_t *gpos_lookups; + + hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + const hb_set_t *glyph_set_, + const hb_map_t *gpos_lookups_) : + layout_variation_indices (layout_variation_indices_), + glyph_set (glyph_set_), + gpos_lookups (gpos_lookups_) {} +}; + +template +struct subset_offset_array_t +{ + subset_offset_array_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_) : subset_context (subset_context_), + out (out_), base (base_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; +}; + + +template +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_, + Arg &&arg_) : subset_context (subset_context_), out (out_), + base (base_), arg (arg_) {} + + template + bool operator () (T&& offset) + { + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (subset_context, offset, base, arg); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset an array of offsets. Subsets the thing pointed to by each offset + * and discards the offset in the array if the subset operation results in an empty + * thing. + */ +struct +{ + template + subset_offset_array_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base) const + { return subset_offset_array_t (subset_context, out, base); } + + /* Variant with one extra argument passed to serialize_subset */ + template + subset_offset_array_arg_t + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base, Arg &&arg) const + { return subset_offset_array_arg_t (subset_context, out, base, arg); } +} +HB_FUNCOBJ (subset_offset_array); + +template +struct subset_record_array_t +{ + subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_) : subset_layout_context (c_), + out (out_), base (base_) {} + + template + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; +}; + +/* + * Helper to subset a RecordList/record array. Subsets each Record in the array and + * discards the record if the subset operation returns false. + */ +struct +{ + template + subset_record_array_t + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base) const + { return subset_record_array_t (c, out, base); } +} +HB_FUNCOBJ (subset_record_array); /* * @@ -88,6 +308,15 @@ struct Record { int cmp (hb_tag_t a) const { return tag.cmp (a); } + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -104,7 +333,7 @@ struct Record }; template -struct RecordArrayOf : SortedArrayOf > +struct RecordArrayOf : SortedArrayOf> { const OffsetTo& get_offset (unsigned int i) const { return (*this)[i].offset; } @@ -116,11 +345,12 @@ struct RecordArrayOf : SortedArrayOf > unsigned int *record_count /* IN/OUT */, hb_tag_t *record_tags /* OUT */) const { - if (record_count) { - const Record *arr = this->sub_array (start_offset, record_count); - unsigned int count = *record_count; - for (unsigned int i = 0; i < count; i++) - record_tags[i] = arr[i].tag; + if (record_count) + { + + this->sub_array (start_offset, record_count) + | hb_map (&Record::tag) + | hb_sink (hb_array (record_tags, *record_count)) + ; } return this->len; } @@ -136,14 +366,16 @@ struct RecordListOf : RecordArrayOf const Type& operator [] (unsigned int i) const { return this+this->get_offset (i); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const { TRACE_SUBSET (this); - struct RecordListOf *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - unsigned int count = this->len; - for (unsigned int i = 0; i < count; i++) - out->get_offset (i).serialize_subset (c, (*this)[i], out); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + this->iter () + | hb_apply (subset_record_array (l, out, this)) + ; return_trace (true); } @@ -154,11 +386,31 @@ struct RecordListOf : RecordArrayOf } }; +struct Feature; + +struct RecordListOfFeature : RecordListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + hb_zip (*this, hb_range (count)) + | hb_filter (l->feature_index_map, hb_second) + | hb_map (hb_first) + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } +}; struct RangeRecord { int cmp (hb_codepoint_t g) const - { return g < start ? -1 : g <= end ? 0 : +1; } + { return g < first ? -1 : g <= last ? 0 : +1; } bool sanitize (hb_sanitize_context_t *c) const { @@ -167,14 +419,14 @@ struct RangeRecord } bool intersects (const hb_set_t *glyphs) const - { return glyphs->intersects (start, end); } + { return glyphs->intersects (first, last); } template - bool add_coverage (set_t *glyphs) const - { return glyphs->add_range (start, end); } + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_range (first, last); } - GlyphID start; /* First GlyphID in the range */ - GlyphID end; /* Last GlyphID in the range */ + HBGlyphID first; /* First GlyphID in the range */ + HBGlyphID last; /* Last GlyphID in the range */ HBUINT16 value; /* Value */ public: DEFINE_SIZE_STATIC (6); @@ -184,15 +436,38 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord); struct IndexArray : ArrayOf { + bool intersects (const hb_map_t *indexes) const + { return hb_any (*this, indexes); } + + template + void serialize (hb_serialize_context_t *c, + hb_subset_layout_context_t *l, + Iterator it) + { + if (!it) return; + if (unlikely (!c->extend_min ((*this)))) return; + + for (const auto _ : it) + { + if (!l->visitLookupIndex()) break; + + Index i; + i = _; + c->copy (i); + this->len++; + } + } + unsigned int get_indexes (unsigned int start_offset, unsigned int *_count /* IN/OUT */, unsigned int *_indexes /* OUT */) const { - if (_count) { - const HBUINT16 *arr = this->sub_array (start_offset, _count); - unsigned int count = *_count; - for (unsigned int i = 0; i < count; i++) - _indexes[i] = arr[i]; + if (_count) + { + + this->sub_array (start_offset, _count) + | hb_sink (hb_array (_indexes, *_count)) + ; } return this->len; } @@ -204,11 +479,6 @@ struct IndexArray : ArrayOf }; -struct Script; -struct LangSys; -struct Feature; - - struct LangSys { unsigned int get_feature_count () const @@ -227,13 +497,49 @@ struct LangSys { if (reqFeatureIndex == 0xFFFFu) return Index::NOT_FOUND_INDEX; - return reqFeatureIndex;; + return reqFeatureIndex; } - bool subset (hb_subset_context_t *c) const + LangSys* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + bool operator == (const LangSys& o) const + { + if (featureIndex.len != o.featureIndex.len || + reqFeatureIndex != o.reqFeatureIndex) + return false; + + for (const auto _ : + hb_zip (featureIndex, o.featureIndex)) + if (_.first != _.second) return false; + + return true; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const { TRACE_SUBSET (this); - return_trace (c->serializer->embed (*this)); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 0xFFFFu; + + if (!l->visitFeatureIndex (featureIndex.len)) + return_trace (false); + + auto it = + + hb_iter (featureIndex) + | hb_filter (l->feature_index_map) + | hb_map (l->feature_index_map) + ; + + bool ret = bool (it); + out->featureIndex.serialize (c->serializer, l, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c, @@ -275,16 +581,46 @@ struct Script bool has_default_lang_sys () const { return defaultLangSys != 0; } const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag) const { TRACE_SUBSET (this); - struct Script *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - out->defaultLangSys.serialize_subset (c, this+defaultLangSys, out); - unsigned int count = langSys.len; - for (unsigned int i = 0; i < count; i++) - out->langSys.arrayZ[i].offset.serialize_subset (c, this+langSys[i].offset, out); - return_trace (true); + if (!l->visitScript ()) return_trace (false); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool defaultLang = false; + if (has_default_lang_sys ()) + { + c->serializer->push (); + const LangSys& ls = this+defaultLangSys; + bool ret = ls.subset (c, l); + if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) + { + c->serializer->pop_discard (); + out->defaultLangSys = 0; + } + else + { + c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); + defaultLang = true; + } + } + + + langSys.iter () + | hb_filter ([=] (const Record& record) {return l->visitLangSys (); }) + | hb_filter ([&] (const Record& record) + { + const LangSys& d = this+defaultLangSys; + const LangSys& l = this+record.offset; + return !(l == d); + }) + | hb_apply (subset_record_array (l, &(out->langSys), this)) + ; + + return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); } bool sanitize (hb_sanitize_context_t *c, @@ -381,6 +717,12 @@ struct FeatureParamsSize return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + HBUINT16 designSize; /* Represents the design size in 720/inch * units (decipoints). The design size entry * must be non-zero. When there is a design @@ -431,6 +773,12 @@ struct FeatureParamsStylisticSet return_trace (c->check_struct (this)); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + HBUINT16 version; /* (set to 0): This corresponds to a “minor” * version number. Additional data may be * added to the end of this Feature Parameters @@ -457,6 +805,27 @@ struct FeatureParamsStylisticSet /* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */ struct FeatureParamsCharacterVariants { + unsigned + get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const + { + if (char_count) + { + + characters.sub_array (start_offset, char_count) + | hb_sink (hb_array (chars, *char_count)) + ; + } + return characters.len; + } + + unsigned get_size () const + { return min_size + characters.len * HBUINT24::static_size; } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -500,6 +869,9 @@ struct FeatureParams { bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const { +#ifdef HB_NO_LAYOUT_FEATURE_PARAMS + return true; +#endif TRACE_SANITIZE (this); if (tag == HB_TAG ('s','i','z','e')) return_trace (u.size.sanitize (c)); @@ -510,26 +882,39 @@ struct FeatureParams return_trace (true); } + bool subset (hb_subset_context_t *c, const Tag* tag) const + { + TRACE_SUBSET (this); + if (!tag) return_trace (false); + if (*tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.subset (c)); + return_trace (false); + } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS const FeatureParamsSize& get_size_params (hb_tag_t tag) const { if (tag == HB_TAG ('s','i','z','e')) return u.size; return Null (FeatureParamsSize); } - const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const { if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ return u.stylisticSet; return Null (FeatureParamsStylisticSet); } - const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const { if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ return u.characterVariants; return Null (FeatureParamsCharacterVariants); } +#endif private: union { @@ -538,7 +923,7 @@ struct FeatureParams FeatureParamsCharacterVariants characterVariants; } u; public: - DEFINE_SIZE_STATIC (17); + DEFINE_SIZE_MIN (0); }; struct Feature @@ -557,13 +942,28 @@ struct Feature const FeatureParams &get_feature_params () const { return this+featureParams; } - bool subset (hb_subset_context_t *c) const + bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const + { return lookupIndex.intersects (lookup_indexes); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const { TRACE_SUBSET (this); - struct Feature *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - out->featureParams.set (0); /* TODO(subset) FeatureParams. */ - return_trace (true); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + bool subset_featureParams = out->featureParams.serialize_subset (c, featureParams, this, tag); + + auto it = + + hb_iter (lookupIndex) + | hb_filter (l->lookup_index_map) + | hb_map (l->lookup_index_map) + ; + + out->lookupIndex.serialize (c->serializer, l, it); + return_trace (bool (it) || subset_featureParams + || (tag && *tag == HB_TAG ('p', 'r', 'e', 'f'))); } bool sanitize (hb_sanitize_context_t *c, @@ -584,25 +984,25 @@ struct Feature * Adobe tools, only the 'size' feature had FeatureParams defined. */ - OffsetTo orig_offset = featureParams; + if (likely (featureParams.is_null ())) + return_trace (true); + + unsigned int orig_offset = featureParams; if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) return_trace (false); - if (likely (orig_offset.is_null ())) - return_trace (true); - if (featureParams == 0 && closure && closure->tag == HB_TAG ('s','i','z','e') && closure->list_base && closure->list_base < this) { - unsigned int new_offset_int = (unsigned int) orig_offset - + unsigned int new_offset_int = orig_offset - (((char *) this) - ((char *) closure->list_base)); OffsetTo new_offset; - /* Check that it did not overflow. */ - new_offset.set (new_offset_int); + /* Check that it would not overflow. */ + new_offset = new_offset_int; if (new_offset == new_offset_int && - c->try_set (&featureParams, new_offset) && + c->try_set (&featureParams, new_offset_int) && !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) return_trace (false); } @@ -648,16 +1048,19 @@ struct Lookup { unsigned int get_subtable_count () const { return subTable.len; } - template - const TSubTable& get_subtable (unsigned int i) const - { return this+CastR > (subTable)[i]; } - template const OffsetArrayOf& get_subtables () const - { return CastR > (subTable); } + { return reinterpret_cast &> (subTable); } template OffsetArrayOf& get_subtables () - { return CastR > (subTable); } + { return reinterpret_cast &> (subTable); } + + template + const TSubTable& get_subtable (unsigned int i) const + { return this+get_subtables ()[i]; } + template + TSubTable& get_subtable (unsigned int i) + { return this+get_subtables ()[i]; } unsigned int get_size () const { @@ -683,14 +1086,14 @@ struct Lookup return flag; } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { unsigned int lookup_type = get_type (); TRACE_DISPATCH (this, lookup_type); unsigned int count = get_subtable_count (); for (unsigned int i = 0; i < count; i++) { - typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type); + typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type, hb_forward (ds)...); if (c->stop_sublookup_iteration (r)) return_trace (r); } @@ -704,95 +1107,73 @@ struct Lookup { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - lookupType.set (lookup_type); - lookupFlag.set (lookup_props & 0xFFFFu); + lookupType = lookup_type; + lookupFlag = lookup_props & 0xFFFFu; if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); if (lookupFlag & LookupFlag::UseMarkFilteringSet) { if (unlikely (!c->extend (*this))) return_trace (false); HBUINT16 &markFilteringSet = StructAfter (subTable); - markFilteringSet.set (lookup_props >> 16); + markFilteringSet = lookup_props >> 16; } return_trace (true); } - /* Older compilers need this to NOT be locally defined in a function. */ - template - struct SubTableSubsetWrapper - { - SubTableSubsetWrapper (const TSubTable &subtable_, - unsigned int lookup_type_) : - subtable (subtable_), - lookup_type (lookup_type_) {} - - bool subset (hb_subset_context_t *c) const - { return subtable.dispatch (c, lookup_type); } - - private: - const TSubTable &subtable; - unsigned int lookup_type; - }; - template bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - struct Lookup *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); - - /* Subset the actual subtables. */ - /* TODO Drop empty ones, either by calling intersects() beforehand, - * or just dropping null offsets after. */ - const OffsetArrayOf& subtables = get_subtables (); - OffsetArrayOf& out_subtables = out->get_subtables (); - unsigned int count = subTable.len; - for (unsigned int i = 0; i < count; i++) - { - SubTableSubsetWrapper wrapper (this+subtables[i], get_type ()); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + out->lookupType = lookupType; + out->lookupFlag = lookupFlag; - out_subtables[i].serialize_subset (c, wrapper, out); - } + const hb_set_t *glyphset = c->plan->glyphset (); + unsigned int lookup_type = get_type (); + + hb_iter (get_subtables ()) + | hb_filter ([this, glyphset, lookup_type] (const OffsetTo &_) { return (this+_).intersects (glyphset, lookup_type); }) + | hb_apply (subset_offset_array (c, out->get_subtables (), this, lookup_type)) + ; return_trace (true); } - /* Older compilers need this to NOT be locally defined in a function. */ - template - struct SubTableSanitizeWrapper : TSubTable - { - bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) const - { return this->dispatch (c, lookup_type); } - }; - template bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + + unsigned subtables = get_subtable_count (); + if (unlikely (!c->visit_subtables (subtables))) return_trace (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) { const HBUINT16 &markFilteringSet = StructAfter (subTable); if (!markFilteringSet.sanitize (c)) return_trace (false); } - if (unlikely (!CastR > > (subTable) - .sanitize (c, this, get_type ()))) + if (unlikely (!get_subtables ().sanitize (c, this, get_type ()))) return_trace (false); - if (unlikely (get_type () == TSubTable::Extension)) + if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) { /* The spec says all subtables of an Extension lookup should * have the same type, which shall not be the Extension type * itself (but we already checked for that). - * This is specially important if one has a reverse type! */ + * This is specially important if one has a reverse type! + * + * We only do this if sanitizer edit_count is zero. Otherwise, + * some of the subtables might have become insane after they + * were sanity-checked by the edits of subsequent subtables. + * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 + */ unsigned int type = get_subtable (0).u.extension.get_type (); - unsigned int count = get_subtable_count (); - for (unsigned int i = 1; i < count; i++) + for (unsigned int i = 1; i < subtables; i++) if (get_subtable (i).u.extension.get_type () != type) return_trace (false); } return_trace (true); - return_trace (true); } private: @@ -800,7 +1181,7 @@ struct Lookup HBUINT16 lookupFlag; /* Lookup qualifiers */ ArrayOf subTable; /* Array of SubTables */ -/*HBUINT16 markFilteringSetX[VAR];*//* Index (base 0) into GDEF mark glyph sets +/*HBUINT16 markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets * structure. This field is only present if bit * UseMarkFilteringSet of lookup flags is set. */ public: @@ -809,6 +1190,32 @@ struct Lookup typedef OffsetListOf LookupList; +template +struct LookupOffsetList : OffsetListOf +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + unsigned count = this->len; + + hb_zip (*this, hb_range (count)) + | hb_filter (l->lookup_index_map, hb_second) + | hb_map (hb_first) + | hb_apply (subset_offset_array (c, *out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (OffsetListOf::sanitize (c, this)); + } +}; + /* * Coverage Table @@ -826,8 +1233,9 @@ struct CoverageFormat1 return i; } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); return_trace (glyphArray.serialize (c, glyphs)); @@ -852,20 +1260,20 @@ struct CoverageFormat1 { return glyphs->has (glyphArray[index]); } template - bool add_coverage (set_t *glyphs) const - { - return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); - } + bool collect_coverage (set_t *glyphs) const + { return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); } public: /* Older compilers need this to be public. */ - struct Iter { + struct iter_t + { void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; } void fini () {} - bool more () { return i < c->glyphArray.len; } + bool more () const { return i < c->glyphArray.len; } void next () { i++; } - hb_codepoint_t get_glyph () { return c->glyphArray[i]; } - unsigned int get_coverage () { return i; } + hb_codepoint_t get_glyph () const { return c->glyphArray[i]; } + bool operator != (const iter_t& o) const + { return i != o.i || c != o.c; } private: const struct CoverageFormat1 *c; @@ -875,7 +1283,7 @@ struct CoverageFormat1 protected: HBUINT16 coverageFormat; /* Format identifier--format = 1 */ - SortedArrayOf + SortedArrayOf glyphArray; /* Array of GlyphIDs--in numerical order */ public: DEFINE_SIZE_ARRAY (4, glyphArray); @@ -889,43 +1297,53 @@ struct CoverageFormat2 unsigned int get_coverage (hb_codepoint_t glyph_id) const { const RangeRecord &range = rangeRecord.bsearch (glyph_id); - return likely (range.start <= range.end) ? - (unsigned int) range.value + (glyph_id - range.start) : - NOT_COVERED; + return likely (range.first <= range.last) + ? (unsigned int) range.value + (glyph_id - range.first) + : NOT_COVERED; } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!glyphs)) { - rangeRecord.len.set (0); + rangeRecord.len = 0; return_trace (true); } - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i]) + /* TODO(iter) Write more efficiently? */ + + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) num_ranges++; - rangeRecord.len.set (num_ranges); - if (unlikely (!c->extend (rangeRecord))) return_trace (false); + last = g; + } + + if (unlikely (!rangeRecord.serialize (c, num_ranges))) return_trace (false); - unsigned int range = 0; - rangeRecord[range].start = glyphs[0]; - rangeRecord[range].value.set (0); - for (unsigned int i = 1; i < glyphs.length; i++) + unsigned count = 0; + unsigned range = (unsigned) -1; + last = (hb_codepoint_t) -2; + for (auto g: glyphs) { - if (glyphs[i - 1] + 1 != glyphs[i]) + if (last + 1 != g) { range++; - rangeRecord[range].start = glyphs[i]; - rangeRecord[range].value.set (i); + rangeRecord[range].first = g; + rangeRecord[range].value = count; } - rangeRecord[range].end = glyphs[i]; + rangeRecord[range].last = g; + last = g; + count++; } + return_trace (true); } @@ -951,7 +1369,7 @@ struct CoverageFormat2 for (i = 0; i < count; i++) { const RangeRecord &range = rangeRecord[i]; if (range.value <= index && - index < (unsigned int) range.value + (range.end - range.start) && + index < (unsigned int) range.value + (range.last - range.first) && range.intersects (glyphs)) return true; else if (index < range.value) @@ -961,57 +1379,61 @@ struct CoverageFormat2 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; return true; } public: /* Older compilers need this to be public. */ - struct Iter + struct iter_t { void init (const CoverageFormat2 &c_) { c = &c_; coverage = 0; i = 0; - j = c->rangeRecord.len ? c->rangeRecord[0].start : 0; - if (unlikely (c->rangeRecord[0].start > c->rangeRecord[0].end)) + j = c->rangeRecord.len ? c->rangeRecord[0].first : 0; + if (unlikely (c->rangeRecord[0].first > c->rangeRecord[0].last)) { /* Broken table. Skip. */ i = c->rangeRecord.len; } } void fini () {} - bool more () { return i < c->rangeRecord.len; } + bool more () const { return i < c->rangeRecord.len; } void next () { - if (j >= c->rangeRecord[i].end) + if (j >= c->rangeRecord[i].last) { i++; if (more ()) { - hb_codepoint_t old = j; - j = c->rangeRecord[i].start; - if (unlikely (j <= old)) + unsigned int old = coverage; + j = c->rangeRecord[i].first; + coverage = c->rangeRecord[i].value; + if (unlikely (coverage != old + 1)) { - /* Broken table. Skip. Important to avoid DoS. */ + /* Broken table. Skip. Important to avoid DoS. + * Also, our callers depend on coverage being + * consecutive and monotonically increasing, + * ie. iota(). */ i = c->rangeRecord.len; return; } - coverage = c->rangeRecord[i].value; } return; } coverage++; j++; } - hb_codepoint_t get_glyph () { return j; } - unsigned int get_coverage () { return coverage; } + hb_codepoint_t get_glyph () const { return j; } + bool operator != (const iter_t& o) const + { return i != o.i || j != o.j || c != o.c; } private: const struct CoverageFormat2 *c; @@ -1032,6 +1454,15 @@ struct CoverageFormat2 struct Coverage { + /* Has interface. */ + static constexpr unsigned SENTINEL = NOT_COVERED; + typedef unsigned int value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } unsigned int get_coverage (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -1041,17 +1472,24 @@ struct Coverage } } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + template + bool serialize (hb_serialize_context_t *c, Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i]) + unsigned count = 0; + unsigned num_ranges = 0; + hb_codepoint_t last = (hb_codepoint_t) -2; + for (auto g: glyphs) + { + if (last + 1 != g) num_ranges++; - u.format.set (glyphs.length * 2 < num_ranges * 3 ? 1 : 2); + last = g; + count++; + } + u.format = count <= num_ranges * 3 ? 1 : 2; switch (u.format) { @@ -1061,6 +1499,23 @@ struct Coverage } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + iter () + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + bool ret = bool (it); + Coverage_serialize (c->serializer, it); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1095,19 +1550,20 @@ struct Coverage /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.add_coverage (glyphs); - case 2: return u.format2.add_coverage (glyphs); + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); default:return false; } } - struct Iter + struct iter_t : hb_iter_with_fallback_t { - Iter (const Coverage &c_) + static constexpr bool is_sorted_iterator = true; + iter_t (const Coverage &c_ = Null (Coverage)) { memset (this, 0, sizeof (*this)); format = c_.u.format; @@ -1118,7 +1574,7 @@ struct Coverage default: return; } } - bool more () + bool __more__ () const { switch (format) { @@ -1127,7 +1583,7 @@ struct Coverage default:return false; } } - void next () + void __next__ () { switch (format) { @@ -1136,7 +1592,10 @@ struct Coverage default: break; } } - hb_codepoint_t get_glyph () + typedef hb_codepoint_t __item_t__; + __item_t__ __item__ () const { return get_glyph (); } + + hb_codepoint_t get_glyph () const { switch (format) { @@ -1145,23 +1604,25 @@ struct Coverage default:return 0; } } - unsigned int get_coverage () + bool operator != (const iter_t& o) const { + if (format != o.format) return true; switch (format) { - case 1: return u.format1.get_coverage (); - case 2: return u.format2.get_coverage (); - default:return -1; + case 1: return u.format1 != o.u.format1; + case 2: return u.format2 != o.u.format2; + default:return false; } } private: unsigned int format; union { - CoverageFormat2::Iter format2; /* Put this one first since it's larger; helps shut up compiler. */ - CoverageFormat1::Iter format1; + CoverageFormat2::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1::iter_t format1; } u; }; + iter_t iter () const { return iter_t (*this); } protected: union { @@ -1173,15 +1634,56 @@ struct Coverage DEFINE_SIZE_UNION (2, format); }; +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t &glyphs, + const hb_set_t &klasses, + hb_map_t *klass_map /*INOUT*/) +{ + if (!klass_map) + { + ClassDef_serialize (c, hb_zip (glyphs.iter (), + glyphs.iter () + | hb_map (gid_klass_map))); + return; + } + + /* any glyph not assigned a class value falls into Class zero (0), + * if any glyph assigned to class 0, remapping must start with 0->0*/ + if (glyphset.get_population () > gid_klass_map.get_population ()) + klass_map->set (0, 0); + + unsigned idx = klass_map->has (0) ? 1 : 0; + for (const unsigned k: klasses.iter ()) + { + if (klass_map->has (k)) continue; + klass_map->set (k, idx); + idx++; + } + + auto it = + + glyphs.iter () + | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t + { + unsigned new_klass = klass_map->get (gid_klass_map[gid]); + return hb_pair ((hb_codepoint_t)gid, new_klass); + }) + ; + + c->propagate_error (glyphs, klasses); + ClassDef_serialize (c, it); +} /* * Class Definition Table */ -static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses); - struct ClassDefFormat1 { friend struct ClassDef; @@ -1192,54 +1694,64 @@ struct ClassDefFormat1 return classValue[(unsigned int) (glyph_id - startGlyph)]; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!it)) { - startGlyph.set (0); - classValue.len.set (0); + startGlyph = 0; + classValue.len = 0; return_trace (true); } - hb_codepoint_t glyph_min = glyphs[0]; - hb_codepoint_t glyph_max = glyphs[glyphs.length - 1]; - - startGlyph.set (glyph_min); - classValue.len.set (glyph_max - glyph_min + 1); - if (unlikely (!c->extend (classValue))) return_trace (false); - - for (unsigned int i = 0; i < glyphs.length; i++) - classValue[glyphs[i] - glyph_min] = klasses[i]; + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); + unsigned glyph_count = glyph_max - glyph_min + 1; + startGlyph = glyph_min; + if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false); + for (const hb_pair_t gid_klass_pair : + it) + { + unsigned idx = gid_klass_pair.first - glyph_min; + classValue[idx] = gid_klass_pair.second; + } return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t glyphs; - hb_vector_t klasses; + + hb_sorted_vector_t glyphs; + hb_set_t orig_klasses; + hb_map_t gid_org_klass_map; hb_codepoint_t start = startGlyph; hb_codepoint_t end = start + classValue.len; - for (hb_codepoint_t g = start; g < end; g++) + for (const hb_codepoint_t gid : + hb_range (start, end) + | hb_filter (glyphset)) { - unsigned int value = classValue[g - start]; - if (!value) continue; - if (!glyphset.has (g)) continue; - glyphs.push()->set (glyph_map[g]); - klasses.push()->set (value); + unsigned klass = classValue[gid - start]; + if (!klass) continue; + + glyphs.push (glyph_map[gid]); + gid_org_klass_map.set (glyph_map[gid], klass); + orig_klasses.add (klass); } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); - return_trace (glyphs.length); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); + return_trace ((bool) glyphs); } bool sanitize (hb_sanitize_context_t *c) const @@ -1249,7 +1761,7 @@ struct ClassDefFormat1 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int start = 0; unsigned int count = classValue.len; @@ -1272,7 +1784,7 @@ struct ClassDefFormat1 } template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { unsigned int count = classValue.len; for (unsigned int i = 0; i < count; i++) @@ -1311,7 +1823,7 @@ struct ClassDefFormat1 protected: HBUINT16 classFormat; /* Format identifier--format = 1 */ - GlyphID startGlyph; /* First GlyphID of the classValueArray */ + HBGlyphID startGlyph; /* First GlyphID of the classValueArray */ ArrayOf classValue; /* Array of Class Values--one per GlyphID */ public: @@ -1328,69 +1840,90 @@ struct ClassDefFormat2 return rangeRecord.bsearch (glyph_id).value; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs.length)) + if (unlikely (!it)) { - rangeRecord.len.set (0); + rangeRecord.len = 0; return_trace (true); } - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) - num_ranges++; - rangeRecord.len.set (num_ranges); - if (unlikely (!c->extend (rangeRecord))) return_trace (false); + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = (*it).first; + unsigned prev_klass = (*it).second; + + RangeRecord range_rec; + range_rec.first = prev_gid; + range_rec.last = prev_gid; + range_rec.value = prev_klass; - unsigned int range = 0; - rangeRecord[range].start = glyphs[0]; - rangeRecord[range].value.set (klasses[0]); - for (unsigned int i = 1; i < glyphs.length; i++) + RangeRecord *record = c->copy (range_rec); + if (unlikely (!record)) return_trace (false); + + for (const auto gid_klass_pair : + (++it)) { - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) { - range++; - rangeRecord[range].start = glyphs[i]; - rangeRecord[range].value = klasses[i]; + if (unlikely (!record)) break; + record->last = prev_gid; + num_ranges++; + + range_rec.first = cur_gid; + range_rec.last = cur_gid; + range_rec.value = cur_klass; + + record = c->copy (range_rec); } - rangeRecord[range].end = glyphs[i]; + + prev_klass = cur_klass; + prev_gid = cur_gid; } + + if (likely (record)) record->last = prev_gid; + rangeRecord.len = num_ranges; return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t glyphs; - hb_vector_t klasses; - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) + hb_sorted_vector_t glyphs; + hb_set_t orig_klasses; + hb_map_t gid_org_klass_map; + + unsigned count = rangeRecord.len; + for (unsigned i = 0; i < count; i++) { - unsigned int value = rangeRecord[i].value; - if (!value) continue; - hb_codepoint_t start = rangeRecord[i].start; - hb_codepoint_t end = rangeRecord[i].end + 1; + unsigned klass = rangeRecord[i].value; + if (!klass) continue; + hb_codepoint_t start = rangeRecord[i].first; + hb_codepoint_t end = rangeRecord[i].last + 1; for (hb_codepoint_t g = start; g < end; g++) { if (!glyphset.has (g)) continue; - glyphs.push ()->set (glyph_map[g]); - klasses.push ()->set (value); + glyphs.push (glyph_map[g]); + gid_org_klass_map.set (glyph_map[g], klass); + orig_klasses.add (klass); } } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); - return_trace (glyphs.length); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); + return_trace ((bool) glyphs); } bool sanitize (hb_sanitize_context_t *c) const @@ -1400,24 +1933,24 @@ struct ClassDefFormat2 } template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) if (rangeRecord[i].value) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; return true; } template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { unsigned int count = rangeRecord.len; for (unsigned int i = 0; i < count; i++) { if (rangeRecord[i].value == klass) - if (unlikely (!rangeRecord[i].add_coverage (glyphs))) + if (unlikely (!rangeRecord[i].collect_coverage (glyphs))) return false; } return true; @@ -1443,9 +1976,9 @@ struct ClassDefFormat2 { if (!hb_set_next (glyphs, &g)) break; - if (g < rangeRecord[i].start) + if (g < rangeRecord[i].first) return true; - g = rangeRecord[i].end; + g = rangeRecord[i].last; } if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g)) return true; @@ -1468,6 +2001,15 @@ struct ClassDefFormat2 struct ClassDef { + /* Has interface. */ + static constexpr unsigned SENTINEL = 0; + typedef unsigned int value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Projection. */ + hb_codepoint_t operator () (hb_codepoint_t k) const { return get (k); } + + unsigned int get (hb_codepoint_t k) const { return get_class (k); } unsigned int get_class (hb_codepoint_t glyph_id) const { switch (u.format) { @@ -1477,44 +2019,58 @@ struct ClassDef } } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + template + bool serialize (hb_serialize_context_t *c, Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - unsigned int format = 2; - if (glyphs.length) + unsigned format = 2; + if (likely (it)) { - hb_codepoint_t glyph_min = glyphs[0]; - hb_codepoint_t glyph_max = glyphs[glyphs.length - 1]; + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); + + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = glyph_min; + unsigned prev_klass = (*it).second; - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < glyphs.length; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) + for (const auto gid_klass_pair : it) + { + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + if (cur_gid == glyph_min || !cur_klass) continue; + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) num_ranges++; - if (1 + (glyph_max - glyph_min + 1) < num_ranges * 3) + prev_gid = cur_gid; + prev_klass = cur_klass; + } + + if (1 + (glyph_max - glyph_min + 1) <= num_ranges * 3) format = 1; } - u.format.set (format); + u.format = format; switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, klasses)); - case 2: return_trace (u.format2.serialize (c, glyphs, klasses)); + case 1: return_trace (u.format1.serialize (c, it)); + case 2: return_trace (u.format2.serialize (c, it)); default:return_trace (false); } } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); switch (u.format) { - case 1: return_trace (u.format1.subset (c)); - case 2: return_trace (u.format2.subset (c)); + case 1: return_trace (u.format1.subset (c, klass_map)); + case 2: return_trace (u.format2.subset (c, klass_map)); default:return_trace (false); } } @@ -1533,11 +2089,11 @@ struct ClassDef /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_coverage (set_t *glyphs) const + bool collect_coverage (set_t *glyphs) const { switch (u.format) { - case 1: return u.format1.add_coverage (glyphs); - case 2: return u.format2.add_coverage (glyphs); + case 1: return u.format1.collect_coverage (glyphs); + case 2: return u.format2.collect_coverage (glyphs); default:return false; } } @@ -1545,11 +2101,11 @@ struct ClassDef /* Might return false if array looks unsorted. * Used for faster rejection of corrupt data. */ template - bool add_class (set_t *glyphs, unsigned int klass) const + bool collect_class (set_t *glyphs, unsigned int klass) const { switch (u.format) { - case 1: return u.format1.add_class (glyphs, klass); - case 2: return u.format2.add_class (glyphs, klass); + case 1: return u.format1.collect_class (glyphs, klass); + case 2: return u.format2.collect_class (glyphs, klass); default:return false; } } @@ -1581,10 +2137,10 @@ struct ClassDef DEFINE_SIZE_UNION (2, format); }; +template static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) -{ c->start_embed ()->serialize (c, glyphs, klasses); } + Iterator it) +{ c->start_embed ()->serialize (c, it); } /* @@ -1635,7 +2191,7 @@ struct VarRegionAxis struct VarRegionList { float evaluate (unsigned int region_index, - const int *coords, unsigned int coord_len) const + const int *coords, unsigned int coord_len) const { if (unlikely (region_index >= regionCount)) return 0.; @@ -1662,6 +2218,26 @@ struct VarRegionList axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount)); } + bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + VarRegionList *out = c->allocate_min (); + if (unlikely (!out)) return_trace (false); + axisCount = src->axisCount; + regionCount = region_map.get_population (); + if (unlikely (!c->allocate_size (get_size () - min_size))) return_trace (false); + unsigned int region_count = src->get_region_count (); + for (unsigned int r = 0; r < regionCount; r++) + { + unsigned int backward = region_map.backward (r); + if (backward >= region_count) return_trace (false); + memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); + } + + return_trace (true); + } + + unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } unsigned int get_region_count () const { return regionCount; } protected: @@ -1685,8 +2261,8 @@ struct VarData { return itemCount * get_row_size (); } float get_delta (unsigned int inner, - const int *coords, unsigned int coord_count, - const VarRegionList ®ions) const + const int *coords, unsigned int coord_count, + const VarRegionList ®ions) const { if (unlikely (inner >= itemCount)) return 0.; @@ -1694,7 +2270,7 @@ struct VarData unsigned int count = regionIndices.len; unsigned int scount = shortCount; - const HBUINT8 *bytes = &StructAfter (regionIndices); + const HBUINT8 *bytes = get_delta_bytes (); const HBUINT8 *row = bytes + inner * (scount + count); float delta = 0.; @@ -1716,16 +2292,16 @@ struct VarData return delta; } - void get_scalars (int *coords, unsigned int coord_count, + void get_scalars (const int *coords, unsigned int coord_count, const VarRegionList ®ions, float *scalars /*OUT */, unsigned int num_scalars) const { - assert (num_scalars == regionIndices.len); - for (unsigned int i = 0; i < num_scalars; i++) - { - scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); - } + unsigned count = hb_min (num_scalars, regionIndices.len); + for (unsigned int i = 0; i < count; i++) + scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); + for (unsigned int i = count; i < num_scalars; i++) + scalars[i] = 0.f; } bool sanitize (hb_sanitize_context_t *c) const @@ -1734,11 +2310,117 @@ struct VarData return_trace (c->check_struct (this) && regionIndices.sanitize (c) && shortCount <= regionIndices.len && - c->check_range (&StructAfter (regionIndices), + c->check_range (get_delta_bytes (), itemCount, get_row_size ())); } + bool serialize (hb_serialize_context_t *c, + const VarData *src, + const hb_inc_bimap_t &inner_map, + const hb_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + itemCount = inner_map.get_next_value (); + + /* Optimize short count */ + unsigned short ri_count = src->regionIndices.len; + enum delta_size_t { kZero=0, kByte, kShort }; + hb_vector_t delta_sz; + hb_vector_t ri_map; /* maps old index to new index */ + delta_sz.resize (ri_count); + ri_map.resize (ri_count); + unsigned int new_short_count = 0; + unsigned int r; + for (r = 0; r < ri_count; r++) + { + delta_sz[r] = kZero; + for (unsigned int i = 0; i < inner_map.get_next_value (); i++) + { + unsigned int old = inner_map.backward (i); + int16_t delta = src->get_item_delta (old, r); + if (delta < -128 || 127 < delta) + { + delta_sz[r] = kShort; + new_short_count++; + break; + } + else if (delta != 0) + delta_sz[r] = kByte; + } + } + unsigned int short_index = 0; + unsigned int byte_index = new_short_count; + unsigned int new_ri_count = 0; + for (r = 0; r < ri_count; r++) + if (delta_sz[r]) + { + ri_map[r] = (delta_sz[r] == kShort)? short_index++ : byte_index++; + new_ri_count++; + } + + shortCount = new_short_count; + regionIndices.len = new_ri_count; + + unsigned int size = regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount); + if (unlikely (!c->allocate_size (size))) + return_trace (false); + + for (r = 0; r < ri_count; r++) + if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]]; + + for (unsigned int i = 0; i < itemCount; i++) + { + unsigned int old = inner_map.backward (i); + for (unsigned int r = 0; r < ri_count; r++) + if (delta_sz[r]) set_item_delta (i, ri_map[r], src->get_item_delta (old, r)); + } + + return_trace (true); + } + + void collect_region_refs (hb_inc_bimap_t ®ion_map, const hb_inc_bimap_t &inner_map) const + { + for (unsigned int r = 0; r < regionIndices.len; r++) + { + unsigned int region = regionIndices[r]; + if (region_map.has (region)) continue; + for (unsigned int i = 0; i < inner_map.get_next_value (); i++) + if (get_item_delta (inner_map.backward (i), r) != 0) + { + region_map.add (region); + break; + } + } + } + + protected: + const HBUINT8 *get_delta_bytes () const + { return &StructAfter (regionIndices); } + + HBUINT8 *get_delta_bytes () + { return &StructAfter (regionIndices); } + + int16_t get_item_delta (unsigned int item, unsigned int region) const + { + if ( item >= itemCount || unlikely (region >= regionIndices.len)) return 0; + const HBINT8 *p = (const HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + return ((const HBINT16 *)p)[region]; + else + return (p + HBINT16::static_size * shortCount)[region - shortCount]; + } + + void set_item_delta (unsigned int item, unsigned int region, int16_t delta) + { + HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + ((HBINT16 *)p)[region] = delta; + else + (p + HBINT16::static_size * shortCount)[region - shortCount] = delta; + } + protected: HBUINT16 itemCount; HBUINT16 shortCount; @@ -1753,8 +2435,12 @@ struct VariationStore float get_delta (unsigned int outer, unsigned int inner, const int *coords, unsigned int coord_count) const { +#ifdef HB_NO_VAR + return 0.f; +#endif + if (unlikely (outer >= dataSets.len)) - return 0.; + return 0.f; return (this+dataSets[outer]).get_delta (inner, coords, coord_count, @@ -1771,6 +2457,10 @@ struct VariationStore bool sanitize (hb_sanitize_context_t *c) const { +#ifdef HB_NO_VAR + return true; +#endif + TRACE_SANITIZE (this); return_trace (c->check_struct (this) && format == 1 && @@ -1778,18 +2468,98 @@ struct VariationStore dataSets.sanitize (c, this)); } + bool serialize (hb_serialize_context_t *c, + const VariationStore *src, + const hb_array_t &inner_maps) + { + TRACE_SERIALIZE (this); + unsigned int set_count = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + if (inner_maps[i].get_population () > 0) set_count++; + + unsigned int size = min_size + HBUINT32::static_size * set_count; + if (unlikely (!c->allocate_size (size))) return_trace (false); + format = 1; + + hb_inc_bimap_t region_map; + for (unsigned int i = 0; i < inner_maps.length; i++) + (src+src->dataSets[i]).collect_region_refs (region_map, inner_maps[i]); + region_map.sort (); + + if (unlikely (!regions.serialize (c, this) + .serialize (c, &(src+src->regions), region_map))) return_trace (false); + + /* TODO: The following code could be simplified when + * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize () + */ + dataSets.len = set_count; + unsigned int set_index = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + if (unlikely (!dataSets[set_index++].serialize (c, this) + .serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) + return_trace (false); + } + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + VariationStore *varstore_prime = c->serializer->start_embed (); + if (unlikely (!varstore_prime)) return_trace (false); + + const hb_set_t *variation_indices = c->plan->layout_variation_indices; + if (variation_indices->is_empty ()) return_trace (false); + + hb_vector_t inner_maps; + inner_maps.resize ((unsigned) dataSets.len); + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].init (); + + for (unsigned idx : c->plan->layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + uint16_t minor = idx & 0xFFFF; + + if (major >= inner_maps.length) + { + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].fini (); + return_trace (false); + } + inner_maps[major].add (minor); + } + varstore_prime->serialize (c->serializer, this, inner_maps.as_array ()); + + for (unsigned i = 0; i < inner_maps.length; i++) + inner_maps[i].fini (); + return_trace (bool (varstore_prime->dataSets)); + } + unsigned int get_region_index_count (unsigned int ivs) const { return (this+dataSets[ivs]).get_region_index_count (); } void get_scalars (unsigned int ivs, - int *coords, unsigned int coord_count, + const int *coords, unsigned int coord_count, float *scalars /*OUT*/, unsigned int num_scalars) const { +#ifdef HB_NO_VAR + for (unsigned i = 0; i < num_scalars; i++) + scalars[i] = 0.f; + return; +#endif + (this+dataSets[ivs]).get_scalars (coords, coord_count, this+regions, &scalars[0], num_scalars); } + unsigned int get_sub_table_count () const { return dataSets.len; } + protected: HBUINT16 format; LOffsetTo regions; @@ -1806,6 +2576,14 @@ struct ConditionFormat1 { friend struct Condition; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } + private: bool evaluate (const int *coords, unsigned int coord_len) const { @@ -1838,6 +2616,17 @@ struct Condition } } + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1868,6 +2657,18 @@ struct ConditionSet return true; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + + conditions.iter () + | hb_apply (subset_offset_array (c, out->conditions, this)) + ; + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1884,6 +2685,30 @@ struct FeatureTableSubstitutionRecord { friend struct FeatureTableSubstitution; + void collect_lookups (const void *base, hb_set_t *lookup_indexes /* OUT */) const + { + return (base+feature).add_lookup_indexes_to (lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + if ((base+feature).intersects_lookup_indexes (lookup_indexes)) + feature_indexes->add (featureIndex); + } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->featureIndex = c->feature_index_map->get (featureIndex); + bool ret = out->feature.serialize_subset (c->subset_context, feature, base, c); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -1911,6 +2736,39 @@ struct FeatureTableSubstitution return nullptr; } + void collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + + hb_iter (substitutions) + | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) + | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) + { r.collect_lookups (this, lookup_indexes); }) + ; + } + + void closure_features (const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + for (const FeatureTableSubstitutionRecord& record : substitutions) + record.closure_features (this, lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + + substitutions.iter () + | hb_apply (subset_record_array (l, &(out->substitutions), this)) + ; + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1931,6 +2789,32 @@ struct FeatureVariationRecord { friend struct FeatureVariations; + void collect_lookups (const void *base, + const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + (base+substitutions).closure_features (lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->conditions.serialize_subset (c->subset_context, conditions, base); + out->substitutions.serialize_subset (c->subset_context, substitutions, base, c); + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); @@ -1952,7 +2836,7 @@ struct FeatureVariations static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu; bool find_index (const int *coords, unsigned int coord_len, - unsigned int *index) const + unsigned int *index) const { unsigned int count = varRecords.len; for (unsigned int i = 0; i < count; i++) @@ -1975,10 +2859,40 @@ struct FeatureVariations return (this+record.substitutions).find_substitute (feature_index); } - bool subset (hb_subset_context_t *c) const + FeatureVariations* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + void collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + for (const FeatureVariationRecord& r : varRecords) + r.collect_lookups (this, feature_indexes, lookup_indexes); + } + + void closure_features (const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + for (const FeatureVariationRecord& record : varRecords) + record.closure_features (this, lookup_indexes, feature_indexes); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const { TRACE_SUBSET (this); - return_trace (c->serializer->embed (*this)); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + + varRecords.iter () + | hb_apply (subset_record_array (l, &(out->varRecords), this)) + ; + return_trace (bool (out->varRecords)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2014,6 +2928,8 @@ struct HintingDevice hb_position_t get_y_delta (hb_font_t *font) const { return get_delta (font->y_ppem, font->y_scale); } + public: + unsigned int get_size () const { unsigned int f = deltaFormat; @@ -2027,6 +2943,12 @@ struct HintingDevice return_trace (c->check_struct (this) && c->check_range (this, this->get_size ())); } + HintingDevice* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + private: int get_delta (unsigned int ppem, int scale) const @@ -2088,6 +3010,32 @@ struct VariationDevice hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const { return font->em_scalef_y (get_delta (font, store)); } + VariationDevice* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto snap = c->snapshot (); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + if (!layout_variation_idx_map || layout_variation_idx_map->is_empty ()) return_trace (out); + + unsigned org_idx = (outerIndex << 16) + innerIndex; + if (!layout_variation_idx_map->has (org_idx)) + { + c->revert (snap); + return_trace (nullptr); + } + unsigned new_idx = layout_variation_idx_map->get (org_idx); + out->outerIndex = new_idx >> 16; + out->innerIndex = new_idx & 0xFFFF; + return_trace (out); + } + + void record_variation_index (hb_set_t *layout_variation_indices) const + { + unsigned var_idx = (outerIndex << 16) + innerIndex; + layout_variation_indices->add (var_idx); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -2126,10 +3074,14 @@ struct Device { switch (u.b.format) { +#ifndef HB_NO_HINTING case 1: case 2: case 3: return u.hinting.get_x_delta (font); +#endif +#ifndef HB_NO_VAR case 0x8000: return u.variation.get_x_delta (font, store); +#endif default: return 0; } @@ -2139,9 +3091,13 @@ struct Device switch (u.b.format) { case 1: case 2: case 3: +#ifndef HB_NO_HINTING return u.hinting.get_y_delta (font); +#endif +#ifndef HB_NO_VAR case 0x8000: return u.variation.get_y_delta (font, store); +#endif default: return 0; } @@ -2152,20 +3108,64 @@ struct Device TRACE_SANITIZE (this); if (!u.b.format.sanitize (c)) return_trace (false); switch (u.b.format) { +#ifndef HB_NO_HINTING case 1: case 2: case 3: return_trace (u.hinting.sanitize (c)); +#endif +#ifndef HB_NO_VAR case 0x8000: return_trace (u.variation.sanitize (c)); +#endif default: return_trace (true); } } + Device* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map=nullptr) const + { + TRACE_SERIALIZE (this); + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return_trace (reinterpret_cast (u.hinting.copy (c))); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return_trace (reinterpret_cast (u.variation.copy (c, layout_variation_idx_map))); +#endif + default: + return_trace (nullptr); + } + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return; +#endif +#ifndef HB_NO_VAR + case 0x8000: + u.variation.record_variation_index (layout_variation_indices); + return; +#endif + default: + return; + } + } + protected: union { DeviceHeader b; HintingDevice hinting; +#ifndef HB_NO_VAR VariationDevice variation; +#endif } u; public: DEFINE_SIZE_UNION (6, b); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh index 533c95a41e6c2..201a6c980fcaa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gdef-table.hh @@ -41,8 +41,18 @@ namespace OT { * Attachment List Table */ -typedef ArrayOf AttachPoint; /* Array of contour point indices--in - * increasing numerical order */ +/* Array of contour point indices--in increasing numerical order */ +struct AttachPoint : ArrayOf +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->serialize (c->serializer, + iter ())); + } +}; struct AttachList { @@ -63,15 +73,36 @@ struct AttachList if (point_count) { - hb_array_t array = points.sub_array (start_offset, point_count); - unsigned int count = array.length; - for (unsigned int i = 0; i < count; i++) - point_array[i] = array[i]; + + points.sub_array (start_offset, point_count) + | hb_sink (hb_array (point_array, *point_count)) + ; } return points.len; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, attachPoint) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->attachPoint, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -96,6 +127,13 @@ struct AttachList struct CaretValueFormat1 { friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } private: hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction) const @@ -119,6 +157,13 @@ struct CaretValueFormat1 struct CaretValueFormat2 { friend struct CaretValue; + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + return_trace (true); + } private: hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_codepoint_t glyph_id) const @@ -153,6 +198,19 @@ struct CaretValueFormat3 font->em_scale_y (coordinate) + (this+deviceTable).get_y_delta (font, var_store); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out), + hb_serialize_context_t::Head, c->plan->layout_variation_idx_map)); + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { (this+deviceTable).collect_variation_indices (layout_variation_indices); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -173,9 +231,9 @@ struct CaretValueFormat3 struct CaretValue { hb_position_t get_caret_value (hb_font_t *font, - hb_direction_t direction, - hb_codepoint_t glyph_id, - const VariationStore &var_store) const + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store) const { switch (u.format) { case 1: return u.format1.get_caret_value (font, direction); @@ -185,6 +243,32 @@ struct CaretValue } } + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + TRACE_DISPATCH (this, u.format); + if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); + switch (u.format) { + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + void collect_variation_indices (hb_set_t *layout_variation_indices) const + { + switch (u.format) { + case 1: + case 2: + return; + case 3: + u.format3.collect_variation_indices (layout_variation_indices); + return; + default: return; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -210,25 +294,45 @@ struct CaretValue struct LigGlyph { - unsigned int get_lig_carets (hb_font_t *font, - hb_direction_t direction, - hb_codepoint_t glyph_id, - const VariationStore &var_store, - unsigned int start_offset, - unsigned int *caret_count /* IN/OUT */, - hb_position_t *caret_array /* OUT */) const + unsigned get_lig_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph_id, + const VariationStore &var_store, + unsigned start_offset, + unsigned *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) const { if (caret_count) { - hb_array_t > array = carets.sub_array (start_offset, caret_count); - unsigned int count = array.length; - for (unsigned int i = 0; i < count; i++) - caret_array[i] = (this+array[i]).get_caret_value (font, direction, glyph_id, var_store); + + carets.sub_array (start_offset, caret_count) + | hb_map (hb_add (this)) + | hb_map ([&] (const CaretValue &value) { return value.get_caret_value (font, direction, glyph_id, var_store); }) + | hb_sink (hb_array (caret_array, *caret_count)) + ; } return carets.len; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (carets) + | hb_apply (subset_offset_array (c, out->carets, this)) + ; + + return_trace (bool (out->carets)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (const OffsetTo& offset : carets.iter ()) + (this+offset).collect_variation_indices (c->layout_variation_indices); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -265,6 +369,38 @@ struct LigCaretList return lig_glyph.get_lig_carets (font, direction, glyph_id, var_store, start_offset, caret_count, caret_array); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligGlyph) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligGlyph, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, ligGlyph) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigGlyph& _) { _.collect_variation_indices (c); }) + ; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -288,6 +424,34 @@ struct MarkGlyphSetsFormat1 bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { return (this+coverage[set_index]).get_coverage (glyph_id) != NOT_COVERED; } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + bool ret = true; + for (const LOffsetTo& offset : coverage.iter ()) + { + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //not using o->serialize_subset (c, offset, this, out) here because + //OTS doesn't allow null offset. + //See issue: https://github.com/khaledhosny/ots/issues/172 + c->serializer->push (); + c->dispatch (this+offset); + c->serializer->add_link (*o, c->serializer->pop_pack ()); + } + + return_trace (ret && out->coverage.len); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -296,7 +460,7 @@ struct MarkGlyphSetsFormat1 protected: HBUINT16 format; /* Format identifier--format = 1 */ - ArrayOf > + ArrayOf> coverage; /* Array of long offsets to mark set * coverage tables */ public: @@ -313,6 +477,15 @@ struct MarkGlyphSets } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c)); + default:return_trace (false); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -356,7 +529,7 @@ struct GDEF unsigned int get_glyph_class (hb_codepoint_t glyph) const { return (this+glyphClassDef).get_class (glyph); } void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const - { (this+glyphClassDef).add_class (glyphs, klass); } + { (this+glyphClassDef).collect_class (glyphs, klass); } bool has_mark_attachment_types () const { return markAttachClassDef != 0; } unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const @@ -386,7 +559,7 @@ struct GDEF bool has_var_store () const { return version.to_int () >= 0x00010003u && varStore != 0; } const VariationStore &get_var_store () const - { return version.to_int () >= 0x00010003u ? this+varStore : Null(VariationStore); } + { return version.to_int () >= 0x00010003u ? this+varStore : Null (VariationStore); } /* glyph_props is a 16-bit integer where the lower 8-bit have bits representing * glyph class and other bits, and high 8-bit the mark attachment type (if any). @@ -409,15 +582,15 @@ struct GDEF } } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; struct accelerator_t { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); - if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face))) + this->table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) { hb_blob_destroy (this->table.get_blob ()); this->table = hb_blob_get_empty (); @@ -436,24 +609,66 @@ struct GDEF (version.to_int () >= 0x00010003u ? varStore.static_size : 0); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { (this+ligCaretList).collect_variation_indices (c); } + + void remap_layout_variation_indices (const hb_set_t *layout_variation_indices, + hb_map_t *layout_variation_idx_map /* OUT */) const + { + if (version.to_int () < 0x00010003u || !varStore) return; + if (layout_variation_indices->is_empty ()) return; + + unsigned new_major = 0, new_minor = 0; + unsigned last_major = (layout_variation_indices->get_min ()) >> 16; + for (unsigned idx : layout_variation_indices->iter ()) + { + uint16_t major = idx >> 16; + if (major >= (this+varStore).get_sub_table_count ()) break; + if (major != last_major) + { + new_minor = 0; + ++new_major; + } + + unsigned new_idx = (new_major << 16) + new_minor; + layout_variation_idx_map->set (idx, new_idx); + ++new_minor; + last_major = major; + } + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - struct GDEF *out = c->serializer->embed (*this); + auto *out = c->serializer->embed (*this); if (unlikely (!out)) return_trace (false); - out->glyphClassDef.serialize_subset (c, this+glyphClassDef, out); - out->attachList.set (0);//TODO(subset) serialize_subset (c, this+attachList, out); - out->ligCaretList.set (0);//TODO(subset) serialize_subset (c, this+ligCaretList, out); - out->markAttachClassDef.serialize_subset (c, this+markAttachClassDef, out); + bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this); + bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this); + bool subset_markglyphsetsdef = true; if (version.to_int () >= 0x00010002u) - out->markGlyphSetsDef.set (0);// TODO(subset) serialize_subset (c, this+markGlyphSetsDef, out); + { + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + if (!subset_markglyphsetsdef && + version.to_int () == 0x00010002u) + out->version.minor = 0; + } + bool subset_varstore = true; if (version.to_int () >= 0x00010003u) - out->varStore.set (0);// TODO(subset) serialize_subset (c, this+varStore, out); + { + subset_varstore = out->varStore.serialize_subset (c, varStore, this); + if (!subset_varstore && version.to_int () == 0x00010003u) + out->version.minor = 2; + } - return_trace (true); + return_trace (subset_glyphclassdef || subset_attachlist || + subset_ligcaretlist || subset_markattachclassdef || + (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || + (out->version.to_int () >= 0x00010003u && subset_varstore)); } bool sanitize (hb_sanitize_context_t *c) const diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh index 2a51650058110..eddae150e5f6b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gpos-table.hh @@ -34,6 +34,11 @@ namespace OT { +struct MarkArray; +static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */); /* buffer **position** var allocations */ #define attach_chain() var.i16[0] /* glyph to which this attaches to, relative to current glyphs; negative for going back, positive for forward. */ @@ -74,14 +79,14 @@ struct ValueFormat : HBUINT16 /* All fields are options. Only those available advance the value pointer. */ #if 0 - HBINT16 xPlacement; /* Horizontal adjustment for + HBINT16 xPlacement; /* Horizontal adjustment for * placement--in design units */ - HBINT16 yPlacement; /* Vertical adjustment for + HBINT16 yPlacement; /* Vertical adjustment for * placement--in design units */ - HBINT16 xAdvance; /* Horizontal adjustment for + HBINT16 xAdvance; /* Horizontal adjustment for * advance--in design units (only used * for horizontal writing) */ - HBINT16 yAdvance; /* Vertical adjustment for advance--in + HBINT16 yAdvance; /* Vertical adjustment for advance--in * design units (only used for vertical * writing) */ OffsetTo xPlaDevice; /* Offset to Device table for @@ -101,10 +106,10 @@ struct ValueFormat : HBUINT16 unsigned int get_len () const { return hb_popcount ((unsigned int) *this); } unsigned int get_size () const { return get_len () * Value::static_size; } - bool apply_value (hb_ot_apply_context_t *c, - const void *base, - const Value *values, - hb_glyph_position_t &glyph_pos) const + bool apply_value (hb_ot_apply_context_t *c, + const void *base, + const Value *values, + hb_glyph_position_t &glyph_pos) const { bool ret = false; unsigned int format = *this; @@ -155,6 +160,60 @@ struct ValueFormat : HBUINT16 return ret; } + void serialize_copy (hb_serialize_context_t *c, const void *base, + const Value *values, const hb_map_t *layout_variation_idx_map) const + { + unsigned int format = *this; + if (!format) return; + + if (format & xPlacement) c->copy (*values++); + if (format & yPlacement) c->copy (*values++); + if (format & xAdvance) c->copy (*values++); + if (format & yAdvance) c->copy (*values++); + + if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *base, + const hb_array_t& values) const + { + unsigned format = *this; + unsigned i = 0; + if (format & xPlacement) i++; + if (format & yPlacement) i++; + if (format & xAdvance) i++; + if (format & yAdvance) i++; + if (format & xPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yPlaDevice) + { + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::xAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + + if (format & ValueFormat::yAdvDevice) + { + + (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices); + i++; + } + } + private: bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const { @@ -173,18 +232,42 @@ struct ValueFormat : HBUINT16 return true; } - static OffsetTo& get_device (Value* value) - { return *CastP > (value); } - static const OffsetTo& get_device (const Value* value, bool *worked=nullptr) + static inline OffsetTo& get_device (Value* value) + { + return *static_cast *> (value); + } + static inline const OffsetTo& get_device (const Value* value, bool *worked=nullptr) { if (worked) *worked |= bool (*value); - return *CastP > (value); + return *static_cast *> (value); + } + + bool copy_device (hb_serialize_context_t *c, const void *base, + const Value *src_value, const hb_map_t *layout_variation_idx_map) const + { + Value *dst_value = c->copy (*src_value); + + if (!dst_value) return false; + if (*dst_value == 0) return true; + + *dst_value = 0; + c->push (); + if ((base + get_device (src_value)).copy (c, layout_variation_idx_map)) + { + c->add_link (*dst_value, c->pop_pack ()); + return true; + } + else + { + c->pop_discard (); + return false; + } } - static const HBINT16& get_short (const Value* value, bool *worked=nullptr) + static inline const HBINT16& get_short (const Value* value, bool *worked=nullptr) { if (worked) *worked |= bool (*value); - return *CastP (value); + return *reinterpret_cast (value); } public: @@ -236,6 +319,13 @@ struct ValueFormat : HBUINT16 } }; +template +static void SinglePos_serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map); + struct AnchorFormat1 { @@ -253,6 +343,12 @@ struct AnchorFormat1 return_trace (c->check_struct (this)); } + AnchorFormat1* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + protected: HBUINT16 format; /* Format identifier--format = 1 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -267,6 +363,13 @@ struct AnchorFormat2 float *x, float *y) const { hb_font_t *font = c->font; + +#ifdef HB_NO_HINTING + *x = font->em_fscale_x (xCoordinate); + *y = font->em_fscale_y (yCoordinate); + return; +#endif + unsigned int x_ppem = font->x_ppem; unsigned int y_ppem = font->y_ppem; hb_position_t cx = 0, cy = 0; @@ -284,6 +387,12 @@ struct AnchorFormat2 return_trace (c->check_struct (this)); } + AnchorFormat2* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + protected: HBUINT16 format; /* Format identifier--format = 2 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -314,6 +423,26 @@ struct AnchorFormat3 return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this)); } + AnchorFormat3* copy (hb_serialize_context_t *c, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + if (!layout_variation_idx_map) return_trace (nullptr); + + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices); + (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices); + } + protected: HBUINT16 format; /* Format identifier--format = 3 */ FWORD xCoordinate; /* Horizontal value--in design units */ @@ -356,6 +485,29 @@ struct Anchor } } + Anchor* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 1: return_trace (reinterpret_cast (u.format1.copy (c))); + case 2: return_trace (reinterpret_cast (u.format2.copy (c))); + case 3: return_trace (reinterpret_cast (u.format3.copy (c, layout_variation_idx_map))); + default:return_trace (nullptr); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.format) { + case 1: case 2: + return; + case 3: + u.format3.collect_variation_indices (c); + return; + default: return; + } + } + protected: union { HBUINT16 format; /* Format identifier */ @@ -374,11 +526,46 @@ struct AnchorMatrix unsigned int cols, bool *found) const { *found = false; - if (unlikely (row >= rows || col >= cols)) return Null(Anchor); + if (unlikely (row >= rows || col >= cols)) return Null (Anchor); *found = !matrixZ[row * cols + col].is_null (); return this+matrixZ[row * cols + col]; } + template + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + Iterator index_iter) const + { + for (unsigned i : index_iter) + (this+matrixZ[i]).collect_variation_indices (c); + } + + template + bool serialize (hb_serialize_context_t *c, + unsigned num_rows, + AnchorMatrix const *offset_matrix, + const hb_map_t *layout_variation_idx_map, + Iterator index_iter) + { + TRACE_SERIALIZE (this); + if (!index_iter) return_trace (false); + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->rows = num_rows; + for (const unsigned i : index_iter) + { + auto *offset = c->embed (offset_matrix->matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_copy (c, offset_matrix->matrixZ[i], + offset_matrix, c->to_bias (this), + hb_serialize_context_t::Head, + layout_variation_idx_map); + } + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const { TRACE_SANITIZE (this); @@ -392,8 +579,7 @@ struct AnchorMatrix } HBUINT16 rows; /* Number of rows */ - protected: - UnsizedArrayOf > + UnsizedArrayOf> matrixZ; /* Matrix of offsets to Anchor tables-- * from beginning of AnchorMatrix table */ public: @@ -405,12 +591,34 @@ struct MarkRecord { friend struct MarkArray; + unsigned get_class () const { return (unsigned) klass; } bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && markAnchor.sanitize (c, base)); } + MarkRecord *copy (hb_serialize_context_t *c, + const void *src_base, + unsigned dst_bias, + const hb_map_t *klass_mapping, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->klass = klass_mapping->get (klass); + out->markAnchor.serialize_copy (c, markAnchor, src_base, dst_bias, hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+markAnchor).collect_variation_indices (c); + } + protected: HBUINT16 klass; /* Class defined for this mark */ OffsetTo @@ -446,8 +654,8 @@ struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage ord glyph_anchor.get_anchor (c, buffer->info[glyph_pos].codepoint, &base_x, &base_y); hb_glyph_position_t &o = buffer->cur_pos(); - o.x_offset = round (base_x - mark_x); - o.y_offset = round (base_y - mark_y); + o.x_offset = roundf (base_x - mark_x); + o.y_offset = roundf (base_y - mark_y); o.attach_type() = ATTACH_TYPE_MARK; o.attach_chain() = (int) glyph_pos - (int) buffer->idx; buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT; @@ -456,6 +664,21 @@ struct MarkArray : ArrayOf /* Array of MarkRecords--in Coverage ord return_trace (true); } + template + bool serialize (hb_serialize_context_t *c, + const hb_map_t *klass_mapping, + const hb_map_t *layout_variation_idx_map, + const void *base, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + if (unlikely (!c->check_assign (len, it.len ()))) return_trace (false); + c->copy_all (it, base, c->to_bias (this), klass_mapping, layout_variation_idx_map); + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -471,8 +694,22 @@ struct SinglePosFormat1 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_iter (this+coverage) + | hb_filter (c->glyph_set) + ; + + if (!it) return; + valueFormat.collect_variation_indices (c, this, values.as_array (valueFormat.get_len ())); + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -489,11 +726,48 @@ struct SinglePosFormat1 return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + auto out = c->extend_min (*this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, valFormat))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ()))) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -523,8 +797,29 @@ struct SinglePosFormat2 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if (!valueFormat.has_device ()) return; + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (c->glyph_set, hb_first) + ; + + if (!it) return; + + unsigned sub_length = valueFormat.get_len (); + const hb_array_t values_array = values.as_array (valueCount * sub_length); + + for (unsigned i : + it + | hb_map (hb_second)) + valueFormat.collect_variation_indices (c, this, values_array.sub_array (i * sub_length, sub_length)); + + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -545,11 +840,56 @@ struct SinglePosFormat2 return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + auto out = c->extend_min (*this); + if (unlikely (!out)) return; + if (unlikely (!c->check_assign (valueFormat, valFormat))) return; + if (unlikely (!c->check_assign (valueCount, it.len ()))) return; + + + it + | hb_map (hb_second) + | hb_apply ([&] (hb_array_t _) + { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); }) + ; + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned sub_length = valueFormat.get_len (); + auto values_array = values.as_array (valueCount * sub_length); + + auto it = + + hb_zip (this+coverage, hb_range ((unsigned) valueCount)) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (const hb_pair_t& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -576,14 +916,52 @@ struct SinglePosFormat2 struct SinglePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t first_val_iter = hb_second (*glyph_val_iter_pairs); + + for (const auto iter : glyph_val_iter_pairs) + for (const auto _ : hb_zip (iter.second, first_val_iter)) + if (_.first != _.second) + return 2; + + return 1; + } + + + template + void serialize (hb_serialize_context_t *c, + const void *src, + Iterator glyph_val_iter_pairs, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + + if (glyph_val_iter_pairs) format = get_format (glyph_val_iter_pairs); + + u.format = format; + switch (u.format) { + case 1: u.format1.serialize (c, src, glyph_val_iter_pairs, valFormat, layout_variation_idx_map); + return; + case 2: u.format2.serialize (c, src, glyph_val_iter_pairs, valFormat, layout_variation_idx_map); + return; + default:return; + } + } + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -596,13 +974,64 @@ struct SinglePos } u; }; +template +static void +SinglePos_serialize (hb_serialize_context_t *c, + const void *src, + Iterator it, + ValueFormat valFormat, + const hb_map_t *layout_variation_idx_map) +{ c->start_embed ()->serialize (c, src, it, valFormat, layout_variation_idx_map); } + struct PairValueRecord { friend struct PairSet; + int cmp (hb_codepoint_t k) const + { return secondGlyph.cmp (k); } + + struct serialize_closure_t + { + const void *base; + const ValueFormat *valueFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_map_t *layout_variation_idx_map; + }; + + bool serialize (hb_serialize_context_t *c, + serialize_closure_t *closure) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (*this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + out->secondGlyph = (*closure->glyph_map)[secondGlyph]; + + closure->valueFormats[0].serialize_copy (c, closure->base, &values[0], closure->layout_variation_idx_map); + closure->valueFormats[1].serialize_copy (c, closure->base, &values[closure->len1], closure->layout_variation_idx_map); + + return_trace (true); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats, + const void *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t values_array = values.as_array (record1_len + record2_len); + + if (valueFormats[0].has_device ()) + valueFormats[0].collect_variation_indices (c, base, values_array.sub_array (0, record1_len)); + + if (valueFormats[1].has_device ()) + valueFormats[1].collect_variation_indices (c, base, values_array.sub_array (record1_len, record2_len)); + } + protected: - GlyphID secondGlyph; /* GlyphID of second glyph in the + HBGlyphID secondGlyph; /* GlyphID of second glyph in the * pair--first glyph is listed in the * Coverage table */ ValueRecord values; /* Positioning data for the first glyph @@ -616,7 +1045,7 @@ struct PairSet friend struct PairPosFormat1; bool intersects (const hb_set_t *glyphs, - const ValueFormat *valueFormats) const + const ValueFormat *valueFormats) const { unsigned int len1 = valueFormats[0].get_len (); unsigned int len2 = valueFormats[1].get_len (); @@ -634,7 +1063,7 @@ struct PairSet } void collect_glyphs (hb_collect_glyphs_context_t *c, - const ValueFormat *valueFormats) const + const ValueFormat *valueFormats) const { unsigned int len1 = valueFormats[0].get_len (); unsigned int len2 = valueFormats[1].get_len (); @@ -644,9 +1073,27 @@ struct PairSet c->input->add_array (&record->secondGlyph, len, record_size); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueFormat *valueFormats) const + { + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size * (1 + len1 + len2); + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len; + for (unsigned i = 0; i < count; i++) + { + if (c->glyph_set->has (record->secondGlyph)) + { record->collect_variation_indices (c, valueFormats, this); } + + record = &StructAtOffset (record, record_size); + } + } + bool apply (hb_ot_apply_context_t *c, - const ValueFormat *valueFormats, - unsigned int pos) const + const ValueFormat *valueFormats, + unsigned int pos) const { TRACE_APPLY (this); hb_buffer_t *buffer = c->buffer; @@ -654,41 +1101,66 @@ struct PairSet unsigned int len2 = valueFormats[1].get_len (); unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2); - unsigned int count = len; + const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint, + &firstPairValueRecord, + len, + record_size); + if (record) + { + /* Note the intentional use of "|" instead of short-circuit "||". */ + if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) | + valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos])) + buffer->unsafe_to_break (buffer->idx, pos + 1); + if (len2) + pos++; + buffer->idx = pos; + return_trace (true); + } + return_trace (false); + } - /* Hand-coded bsearch. */ - if (unlikely (!count)) - return_trace (false); - hb_codepoint_t x = buffer->info[pos].codepoint; - int min = 0, max = (int) count - 1; - while (min <= max) + bool subset (hb_subset_context_t *c, + const ValueFormat valueFormats[2]) const + { + TRACE_SUBSET (this); + auto snap = c->serializer->snapshot (); + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->len = 0; + + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + unsigned len1 = valueFormats[0].get_len (); + unsigned len2 = valueFormats[1].get_len (); + unsigned record_size = HBUINT16::static_size + Value::static_size * (len1 + len2); + + PairValueRecord::serialize_closure_t closure = { - int mid = ((unsigned int) min + (unsigned int) max) / 2; - const PairValueRecord *record = &StructAtOffset (&firstPairValueRecord, record_size * mid); - hb_codepoint_t mid_x = record->secondGlyph; - if (x < mid_x) - max = mid - 1; - else if (x > mid_x) - min = mid + 1; - else - { - /* Note the intentional use of "|" instead of short-circuit "||". */ - if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) | - valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos])) - buffer->unsafe_to_break (buffer->idx, pos + 1); - if (len2) - pos++; - buffer->idx = pos; - return_trace (true); - } + this, + valueFormats, + len1, + &glyph_map, + c->plan->layout_variation_idx_map + }; + + const PairValueRecord *record = &firstPairValueRecord; + unsigned count = len, num = 0; + for (unsigned i = 0; i < count; i++) + { + if (glyphset.has (record->secondGlyph) + && record->serialize (c->serializer, &closure)) num++; + record = &StructAtOffset (record, record_size); } - return_trace (false); + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); } struct sanitize_closure_t { - const void *base; const ValueFormat *valueFormats; unsigned int len1; /* valueFormats[0].get_len() */ unsigned int stride; /* 1 + len1 + len2 */ @@ -705,8 +1177,8 @@ struct PairSet unsigned int count = len; const PairValueRecord *record = &firstPairValueRecord; - return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride) && - closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride)); + return_trace (closure->valueFormats[0].sanitize_values_stride_unsafe (c, this, &record->values[0], count, closure->stride) && + closure->valueFormats[1].sanitize_values_stride_unsafe (c, this, &record->values[closure->len1], count, closure->stride)); } protected: @@ -722,21 +1194,37 @@ struct PairPosFormat1 { bool intersects (const hb_set_t *glyphs) const { - unsigned int count = pairSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+pairSet[iter.get_coverage ()]).intersects (glyphs, valueFormat)) - return true; - } - return false; + return + + hb_zip (this+coverage, pairSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([glyphs, this] (const OffsetTo &_) + { return (this+_).intersects (glyphs, valueFormat); }) + | hb_any + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat[0].has_device ()) && (!valueFormat[1].has_device ())) return; + + auto it = + + hb_zip (this+coverage, pairSet) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + if (!it) return; + + it + | hb_map (hb_add (this)) + | hb_apply ([&] (const PairSet& _) { _.collect_variation_indices (c, valueFormat); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; unsigned int count = pairSet.len; for (unsigned int i = 0; i < count; i++) (this+pairSet[i]).collect_glyphs (c, valueFormat); @@ -761,8 +1249,43 @@ struct PairPosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat[0] = valueFormat[0]; + out->valueFormat[1] = valueFormat[1]; + + hb_sorted_vector_t new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const OffsetTo& _) + { + auto *o = out->pairSet.serialize_append (c->serializer); + if (unlikely (!o)) return false; + auto snap = c->serializer->snapshot (); + bool ret = o->serialize_subset (c, _, this, valueFormat); + if (!ret) + { + out->pairSet.pop (); + c->serializer->revert (snap); + } + return ret; + }, + hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -775,7 +1298,6 @@ struct PairPosFormat1 unsigned int len2 = valueFormat[1].get_len (); PairSet::sanitize_closure_t closure = { - this, valueFormat, len1, 1 + len1 + len2 @@ -810,10 +1332,43 @@ struct PairPosFormat2 (this+classDef2).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + if ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return; + + hb_set_t class1_set, class2_set; + for (const unsigned cp : c->glyph_set->iter ()) + { + unsigned klass1 = (this+classDef1).get (cp); + unsigned klass2 = (this+classDef2).get (cp); + class1_set.add (klass1); + class2_set.add (klass2); + } + + if (class1_set.is_empty () || class2_set.is_empty ()) return; + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + const hb_array_t values_array = values.as_array ((unsigned)class1Count * (unsigned) class2Count * (len1 + len2)); + for (const unsigned class1_idx : class1_set.iter ()) + { + for (const unsigned class2_idx : class2_set.iter ()) + { + unsigned start_offset = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + if (valueFormat1.has_device ()) + valueFormat1.collect_variation_indices (c, this, values_array.sub_array (start_offset, len1)); + + if (valueFormat2.has_device ()) + valueFormat2.collect_variation_indices (c, this, values_array.sub_array (start_offset+len1, len2)); + } + } + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - if (unlikely (!(this+classDef2).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+classDef2).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -853,8 +1408,50 @@ struct PairPosFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->valueFormat1 = valueFormat1; + out->valueFormat2 = valueFormat2; + + hb_map_t klass1_map; + out->classDef1.serialize_subset (c, classDef1, this, &klass1_map); + out->class1Count = klass1_map.get_population (); + + hb_map_t klass2_map; + out->classDef2.serialize_subset (c, classDef2, this, &klass2_map); + out->class2Count = klass2_map.get_population (); + + unsigned len1 = valueFormat1.get_len (); + unsigned len2 = valueFormat2.get_len (); + + + hb_range ((unsigned) class1Count) + | hb_filter (klass1_map) + | hb_apply ([&] (const unsigned class1_idx) + { + + hb_range ((unsigned) class2Count) + | hb_filter (klass2_map) + | hb_apply ([&] (const unsigned class2_idx) + { + unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2); + valueFormat1.serialize_copy (c->serializer, this, &values[idx], c->plan->layout_variation_idx_map); + valueFormat2.serialize_copy (c->serializer, this, &values[idx + len1], c->plan->layout_variation_idx_map); + }) + ; + }) + ; + + const hb_set_t &glyphset = *c->plan->_glyphset_gsub; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + out->coverage.serialize (c->serializer, out).serialize (c->serializer, it); + return_trace (out->class1Count && out->class2Count && bool (it)); } bool sanitize (hb_sanitize_context_t *c) const @@ -909,14 +1506,14 @@ struct PairPosFormat2 struct PairPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -940,6 +1537,27 @@ struct EntryExitRecord return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base)); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const void *src_base) const + { + (src_base+entryAnchor).collect_variation_indices (c); + (src_base+exitAnchor).collect_variation_indices (c); + } + + EntryExitRecord* copy (hb_serialize_context_t *c, + const void *src_base, + const void *dst_base, + const hb_map_t *layout_variation_idx_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + out->entryAnchor.serialize_copy (c, entryAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map); + out->exitAnchor.serialize_copy (c, exitAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map); + return_trace (out); + } + protected: OffsetTo entryAnchor; /* Offset to EntryAnchor table--from @@ -961,8 +1579,19 @@ struct CursivePosFormat1 bool intersects (const hb_set_t *glyphs) const { return (this+coverage).intersects (glyphs); } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const EntryExitRecord& record) { record.collect_variation_indices (c, this); }) + ; + } + void collect_glyphs (hb_collect_glyphs_context_t *c) const - { if (unlikely (!(this+coverage).add_coverage (c->input))) return; } + { if (unlikely (!(this+coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+coverage; } @@ -995,32 +1624,32 @@ struct CursivePosFormat1 /* Main-direction adjustment */ switch (c->direction) { case HB_DIRECTION_LTR: - pos[i].x_advance = round (exit_x) + pos[i].x_offset; + pos[i].x_advance = roundf (exit_x) + pos[i].x_offset; - d = round (entry_x) + pos[j].x_offset; + d = roundf (entry_x) + pos[j].x_offset; pos[j].x_advance -= d; pos[j].x_offset -= d; break; case HB_DIRECTION_RTL: - d = round (exit_x) + pos[i].x_offset; + d = roundf (exit_x) + pos[i].x_offset; pos[i].x_advance -= d; pos[i].x_offset -= d; - pos[j].x_advance = round (entry_x) + pos[j].x_offset; + pos[j].x_advance = roundf (entry_x) + pos[j].x_offset; break; case HB_DIRECTION_TTB: - pos[i].y_advance = round (exit_y) + pos[i].y_offset; + pos[i].y_advance = roundf (exit_y) + pos[i].y_offset; - d = round (entry_y) + pos[j].y_offset; + d = roundf (entry_y) + pos[j].y_offset; pos[j].y_advance -= d; pos[j].y_offset -= d; break; case HB_DIRECTION_BTT: - d = round (exit_y) + pos[i].y_offset; + d = roundf (exit_y) + pos[i].y_offset; pos[i].y_advance -= d; pos[i].y_offset -= d; - pos[j].y_advance = round (entry_y); + pos[j].y_advance = roundf (entry_y); break; case HB_DIRECTION_INVALID: default: @@ -1063,15 +1692,58 @@ struct CursivePosFormat1 else pos[child].x_offset = x_offset; + /* If parent was attached to child, break them free. + * https://github.com/harfbuzz/harfbuzz/issues/2469 + */ + if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain())) + pos[parent].attach_chain() = 0; + buffer->idx++; return_trace (true); } + template + void serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_base, + const hb_map_t *layout_variation_idx_map) + { + if (unlikely (!c->extend_min ((*this)))) return; + this->format = 1; + this->entryExitRecord.len = it.len (); + + for (const EntryExitRecord& entry_record : + it + | hb_map (hb_second)) + c->copy (entry_record, src_base, this, layout_variation_idx_map); + + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; + + coverage.serialize (c, this).serialize (c, glyphs); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out)) return_trace (false); + + auto it = + + hb_zip (this+coverage, entryExitRecord) + | hb_filter (glyphset, hb_first) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_pair_t + { return hb_pair (glyph_map[p.first], p.second);}) + ; + + bool ret = bool (it); + out->serialize (c->serializer, it, this, c->plan->layout_variation_idx_map); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -1094,13 +1766,13 @@ struct CursivePosFormat1 struct CursivePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1118,16 +1790,73 @@ typedef AnchorMatrix BaseArray; /* base-major-- * mark-minor-- * ordered by class--zero-based. */ +static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage, + const MarkArray &mark_array, + const hb_set_t &glyphset, + hb_map_t* klass_mapping /* INOUT */) +{ + hb_set_t orig_classes; + + + hb_zip (mark_coverage, mark_array) + | hb_filter (glyphset, hb_first) + | hb_map (hb_second) + | hb_map (&MarkRecord::get_class) + | hb_sink (orig_classes) + ; + + unsigned idx = 0; + for (auto klass : orig_classes.iter ()) + { + if (klass_mapping->has (klass)) continue; + klass_mapping->set (klass, idx); + idx++; + } +} + struct MarkBasePosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+markCoverage).intersects (glyphs) && - (this+baseCoverage).intersects (glyphs); } + { + return (this+markCoverage).intersects (glyphs) && + (this+baseCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t base_indexes; + for (const unsigned row : base_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + (this+baseArray).collect_variation_indices (c, base_indexes.iter ()); + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+markCoverage).add_coverage (c->input))) return; - if (unlikely (!(this+baseCoverage).add_coverage (c->input))) return; + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+baseCoverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+markCoverage; } @@ -1175,8 +1904,70 @@ struct MarkBasePosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark_iter = + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->markCoverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->markArray.serialize (c->serializer, out) + .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+markArray), + mark_iter + | hb_map (hb_second)); + + unsigned basecount = (this+baseArray).rows; + auto base_iter = + + hb_zip (this+baseCoverage, hb_range (basecount)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + base_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->baseCoverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t base_indexes; + for (const unsigned row : + base_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (base_indexes) + ; + } + out->baseArray.serialize (c->serializer, out) + .serialize (c->serializer, base_iter.len (), &(this+baseArray), c->plan->layout_variation_idx_map, base_indexes.iter ()); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1210,13 +2001,13 @@ struct MarkBasePosFormat1 struct MarkBasePos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1242,13 +2033,53 @@ typedef OffsetListOf LigatureArray; struct MarkLigPosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+markCoverage).intersects (glyphs) && - (this+ligatureCoverage).intersects (glyphs); } + { + return (this+markCoverage).intersects (glyphs) && + (this+ligatureCoverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+markCoverage, this+markArray) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+markArray)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+markCoverage, this+markArray, *c->glyph_set, &klass_mapping); + + unsigned ligcount = (this+ligatureArray).len; + auto lig_iter = + + hb_zip (this+ligatureCoverage, hb_range (ligcount)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + const LigatureArray& lig_array = this+ligatureArray; + for (const unsigned i : lig_iter) + { + hb_sorted_vector_t lig_indexes; + unsigned row_count = lig_array[i].rows; + for (unsigned row : + hb_range (row_count)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (lig_indexes) + ; + } + + lig_array[i].collect_variation_indices (c, lig_indexes.iter ()); + } + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+markCoverage).add_coverage (c->input))) return; - if (unlikely (!(this+ligatureCoverage).add_coverage (c->input))) return; + if (unlikely (!(this+markCoverage).collect_coverage (c->input))) return; + if (unlikely (!(this+ligatureCoverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+markCoverage; } @@ -1289,7 +2120,7 @@ struct MarkLigPosFormat1 unsigned int mark_id = _hb_glyph_info_get_lig_id (&buffer->cur()); unsigned int mark_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); if (lig_id && lig_id == mark_id && mark_comp > 0) - comp_index = MIN (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; + comp_index = hb_min (comp_count, _hb_glyph_info_get_lig_comp (&buffer->cur())) - 1; else comp_index = comp_count - 1; @@ -1335,13 +2166,13 @@ struct MarkLigPosFormat1 struct MarkLigPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1362,13 +2193,47 @@ typedef AnchorMatrix Mark2Array; /* mark2-major-- struct MarkMarkPosFormat1 { bool intersects (const hb_set_t *glyphs) const - { return (this+mark1Coverage).intersects (glyphs) && - (this+mark2Coverage).intersects (glyphs); } + { + return (this+mark1Coverage).intersects (glyphs) && + (this+mark2Coverage).intersects (glyphs); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const {} + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const MarkRecord& record) { record.collect_variation_indices (c, &(this+mark1Array)); }) + ; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, *c->glyph_set, &klass_mapping); + + unsigned mark2_count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2_count)) + | hb_filter (c->glyph_set, hb_first) + | hb_map (hb_second) + ; + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : mark2_iter) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + (this+mark2Array).collect_variation_indices (c, mark2_indexes.iter ()); + } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+mark1Coverage).add_coverage (c->input))) return; - if (unlikely (!(this+mark2Coverage).add_coverage (c->input))) return; + if (unlikely (!(this+mark1Coverage).collect_coverage (c->input))) return; + if (unlikely (!(this+mark2Coverage).collect_coverage (c->input))) return; } const Coverage &get_coverage () const { return this+mark1Coverage; } @@ -1395,12 +2260,15 @@ struct MarkMarkPosFormat1 unsigned int comp1 = _hb_glyph_info_get_lig_comp (&buffer->cur()); unsigned int comp2 = _hb_glyph_info_get_lig_comp (&buffer->info[j]); - if (likely (id1 == id2)) { + if (likely (id1 == id2)) + { if (id1 == 0) /* Marks belonging to the same base. */ goto good; else if (comp1 == comp2) /* Marks belonging to the same ligature component. */ goto good; - } else { + } + else + { /* If ligature ids don't match, it may be the case that one of the marks * itself is a ligature. In which case match. */ if ((id1 > 0 && !comp1) || (id2 > 0 && !comp2)) @@ -1420,8 +2288,70 @@ struct MarkMarkPosFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_map_t klass_mapping; + Markclass_closure_and_remap_indexes (this+mark1Coverage, this+mark1Array, glyphset, &klass_mapping); + + if (!klass_mapping.get_population ()) return_trace (false); + out->classCount = klass_mapping.get_population (); + + auto mark1_iter = + + hb_zip (this+mark1Coverage, this+mark1Array) + | hb_filter (glyphset, hb_first) + ; + + hb_sorted_vector_t new_coverage; + + mark1_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark1Coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + out->mark1Array.serialize (c->serializer, out) + .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+mark1Array), + mark1_iter + | hb_map (hb_second)); + + unsigned mark2count = (this+mark2Array).rows; + auto mark2_iter = + + hb_zip (this+mark2Coverage, hb_range (mark2count)) + | hb_filter (glyphset, hb_first) + ; + + new_coverage.reset (); + + mark2_iter + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + if (!out->mark2Coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ())) + return_trace (false); + + hb_sorted_vector_t mark2_indexes; + for (const unsigned row : + mark2_iter + | hb_map (hb_second)) + { + + hb_range ((unsigned) classCount) + | hb_filter (klass_mapping) + | hb_map ([&] (const unsigned col) { return row * (unsigned) classCount + col; }) + | hb_sink (mark2_indexes) + ; + } + out->mark2Array.serialize (c->serializer, out) + .serialize (c->serializer, mark2_iter.len (), &(this+mark2Array), c->plan->layout_variation_idx_map, mark2_indexes.iter ()); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1457,13 +2387,13 @@ struct MarkMarkPosFormat1 struct MarkMarkPos { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1509,24 +2439,30 @@ struct PosLookupSubTable Extension = 9 }; - template - typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const { TRACE_DISPATCH (this, lookup_type); switch (lookup_type) { - case Single: return_trace (u.single.dispatch (c)); - case Pair: return_trace (u.pair.dispatch (c)); - case Cursive: return_trace (u.cursive.dispatch (c)); - case MarkBase: return_trace (u.markBase.dispatch (c)); - case MarkLig: return_trace (u.markLig.dispatch (c)); - case MarkMark: return_trace (u.markMark.dispatch (c)); - case Context: return_trace (u.context.dispatch (c)); - case ChainContext: return_trace (u.chainContext.dispatch (c)); - case Extension: return_trace (u.extension.dispatch (c)); + case Single: return_trace (u.single.dispatch (c, hb_forward (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, hb_forward (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, hb_forward (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, hb_forward (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, hb_forward (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, hb_forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, hb_forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, hb_forward (ds)...)); default: return_trace (c->default_return_value ()); } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SinglePos single; @@ -1571,21 +2507,40 @@ struct PosLookup : Lookup hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { return dispatch (c); } + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + c->set_recurse_func (dispatch_closure_lookups_recurse_func); + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + template - void add_coverage (set_t *glyphs) const + void collect_coverage (set_t *glyphs) const { - hb_add_coverage_context_t c (glyphs); + hb_collect_coverage_context_t c (glyphs); dispatch (&c); } - static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); + static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); template static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - template - typename context_t::return_t dispatch (context_t *c) const - { return Lookup::dispatch (c); } + HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, hb_forward (ds)...); } bool subset (hb_subset_context_t *c) const { return Lookup::subset (c); } @@ -1604,21 +2559,39 @@ struct GPOS : GSUBGPOS static constexpr hb_tag_t tableTag = HB_OT_TAG_GPOS; const PosLookup& get_lookup (unsigned int i) const - { return CastR (GSUBGPOS::get_lookup (i)); } + { return static_cast (GSUBGPOS::get_lookup (i)); } static inline void position_start (hb_font_t *font, hb_buffer_t *buffer); static inline void position_finish_advances (hb_font_t *font, hb_buffer_t *buffer); static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer); bool subset (hb_subset_context_t *c) const - { return GSUBGPOS::subset (c); } + { + hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_features); + return GSUBGPOS::subset (&l); + } bool sanitize (hb_sanitize_context_t *c) const { return GSUBGPOS::sanitize (c); } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + for (unsigned i = 0; i < GSUBGPOS::get_lookup_count (); i++) + { + if (!c->gpos_lookups->has (i)) continue; + const PosLookup &l = get_lookup (i); + l.dispatch (c); + } + } + + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + typedef GSUBGPOS::accelerator_t accelerator_t; }; @@ -1732,14 +2705,21 @@ struct GPOS_accelerator_t : GPOS::accelerator_t {}; /* Out-of-class implementation for methods recursing */ +#ifndef HB_NO_OT_LAYOUT template -/*static*/ inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +/*static*/ typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) { const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index); return l.dispatch (c); } -/*static*/ inline bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline hb_closure_lookups_context_t::return_t PosLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +/*static*/ bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) { const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; @@ -1751,6 +2731,7 @@ template c->set_lookup_props (saved_lookup_props); return ret; } +#endif } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh index 288c07b552e41..de49c4e208452 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsub-table.hh @@ -34,10 +34,12 @@ namespace OT { +typedef hb_pair_t hb_codepoint_pair_t; + +template +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); -static inline void SingleSubst_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes); struct SingleSubstFormat1 { @@ -46,35 +48,30 @@ struct SingleSubstFormat1 void closure (hb_closure_context_t *c) const { - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - /* TODO Switch to range-based API to work around malicious fonts. - * https://github.com/harfbuzz/harfbuzz/issues/363 */ - hb_codepoint_t glyph_id = iter.get_glyph (); - if (c->glyphs->has (glyph_id)) - c->out->add ((glyph_id + deltaGlyphID) & 0xFFFFu); - } + unsigned d = deltaGlyphID; + + hb_iter (this+coverage) + | hb_filter (*c->glyphs) + | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - /* TODO Switch to range-based API to work around malicious fonts. - * https://github.com/harfbuzz/harfbuzz/issues/363 */ - hb_codepoint_t glyph_id = iter.get_glyph (); - c->output->add ((glyph_id + deltaGlyphID) & 0xFFFFu); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + unsigned d = deltaGlyphID; + + hb_iter (this+coverage) + | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; }) + | hb_sink (c->output) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -91,34 +88,41 @@ struct SingleSubstFormat1 return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - int delta) + Iterator glyphs, + unsigned delta) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); - deltaGlyphID.set (delta); /* TODO(serialize) overflow? */ + c->check_assign (deltaGlyphID, delta); return_trace (true); } bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t from; - hb_vector_t to; + hb_codepoint_t delta = deltaGlyphID; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (!glyphset.has (iter.get_glyph ())) continue; - from.push ()->set (glyph_map[iter.get_glyph ()]); - to.push ()->set (glyph_map[(iter.get_glyph () + delta) & 0xFFFF]); - } - c->serializer->propagate_error (from, to); - SingleSubst_serialize (c->serializer, from, to); - return_trace (from.length); + + auto it = + + hb_iter (this+coverage) + | hb_filter (glyphset) + | hb_map_retains_sorting ([&] (hb_codepoint_t g) { + return hb_codepoint_pair_t (g, + (g + delta) & 0xFFFF); }) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_codepoint_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -132,8 +136,8 @@ struct SingleSubstFormat1 OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - HBINT16 deltaGlyphID; /* Add to original GlyphID to get - * substitute GlyphID */ + HBUINT16 deltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID, modulo 0x10000 */ public: DEFINE_SIZE_STATIC (6); }; @@ -145,35 +149,28 @@ struct SingleSubstFormat2 void closure (hb_closure_context_t *c) const { - unsigned int count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - c->out->add (substitute[iter.get_coverage ()]); - } + + hb_zip (this+coverage, substitute) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - c->output->add (substitute[iter.get_coverage ()]); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, substitute) + | hb_map (hb_second) + | hb_sink (c->output) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -188,11 +185,21 @@ struct SingleSubstFormat2 return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) + Iterator it) { TRACE_SERIALIZE (this); + auto substitutes = + + it + | hb_map (hb_second) + ; + auto glyphs = + + it + | hb_map_retains_sorting (hb_first) + ; if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!substitute.serialize (c, substitutes))) return_trace (false); if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); @@ -202,19 +209,20 @@ struct SingleSubstFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - const hb_set_t &glyphset = *c->plan->glyphset; + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t from; - hb_vector_t to; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (!glyphset.has (iter.get_glyph ())) continue; - from.push ()->set (glyph_map[iter.get_glyph ()]); - to.push ()->set (glyph_map[substitute[iter.get_coverage ()]]); - } - c->serializer->propagate_error (from, to); - SingleSubst_serialize (c->serializer, from, to); - return_trace (from.length); + + auto it = + + hb_zip (this+coverage, substitute) + | hb_filter (glyphset, hb_first) + | hb_filter (glyphset, hb_second) + | hb_map_retains_sorting ([&] (hb_pair_t p) -> hb_codepoint_pair_t + { return hb_pair (glyph_map[p.first], glyph_map[p.second]); }) + ; + + bool ret = bool (it); + SingleSubst_serialize (c->serializer, it); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -228,7 +236,7 @@ struct SingleSubstFormat2 OffsetTo coverage; /* Offset to Coverage table--from * beginning of Substitution table */ - ArrayOf + ArrayOf substitute; /* Array of substitute * GlyphIDs--ordered by Coverage Index */ public: @@ -237,41 +245,44 @@ struct SingleSubstFormat2 struct SingleSubst { + + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) + Iterator glyphs) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); - unsigned int format = 2; - int delta = 0; - if (glyphs.length) + unsigned format = 2; + unsigned delta = 0; + if (glyphs) { format = 1; - /* TODO(serialize) check for wrap-around */ - delta = substitutes[0] - glyphs[0]; - for (unsigned int i = 1; i < glyphs.length; i++) - if (delta != (int) (substitutes[i] - glyphs[i])) { - format = 2; - break; - } + auto get_delta = [=] (hb_codepoint_pair_t _) + { return (unsigned) (_.second - _.first) & 0xFFFF; }; + delta = get_delta (*glyphs); + if (!hb_all (++(+glyphs), delta, get_delta)) format = 2; } - u.format.set (format); + u.format = format; switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, delta)); - case 2: return_trace (u.format2.serialize (c, glyphs, substitutes)); + case 1: return_trace (u.format1.serialize (c, + + glyphs + | hb_map_retains_sorting (hb_first), + delta)); + case 2: return_trace (u.format2.serialize (c, glyphs)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -284,20 +295,19 @@ struct SingleSubst } u; }; -static inline void +template +static void SingleSubst_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t substitutes) -{ c->start_embed ()->serialize (c, glyphs, substitutes); } + Iterator it) +{ c->start_embed ()->serialize (c, it); } struct Sequence { + bool intersects (const hb_set_t *glyphs) const + { return hb_all (substitute, glyphs); } + void closure (hb_closure_context_t *c) const - { - unsigned int count = substitute.len; - for (unsigned int i = 0; i < count; i++) - c->out->add (substitute[i]); - } + { c->output->add_array (substitute.arrayZ, substitute.len); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { c->output->add_array (substitute.arrayZ, substitute.len); } @@ -334,11 +344,30 @@ struct Sequence return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + Iterator subst) { TRACE_SERIALIZE (this); - return_trace (substitute.serialize (c, glyphs)); + return_trace (substitute.serialize (c, subst)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset)) return_trace (false); + + auto it = + + hb_iter (substitute) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it)); } bool sanitize (hb_sanitize_context_t *c) const @@ -348,7 +377,7 @@ struct Sequence } protected: - ArrayOf + ArrayOf substitute; /* String of GlyphIDs to substitute */ public: DEFINE_SIZE_ARRAY (2, substitute); @@ -361,31 +390,30 @@ struct MultipleSubstFormat1 void closure (hb_closure_context_t *c) const { - unsigned int count = sequence.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+sequence[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, sequence) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = sequence.len; - for (unsigned int i = 0; i < count; i++) - (this+sequence[i]).collect_glyphs (c); + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, sequence) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -398,9 +426,9 @@ struct MultipleSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -419,8 +447,24 @@ struct MultipleSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, sequence) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->sequence, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -444,27 +488,27 @@ struct MultipleSubstFormat1 struct MultipleSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, glyphs, substitute_len_list, substitute_glyphs_list)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -478,12 +522,11 @@ struct MultipleSubst struct AlternateSet { + bool intersects (const hb_set_t *glyphs) const + { return hb_any (alternates, glyphs); } + void closure (hb_closure_context_t *c) const - { - unsigned int count = alternates.len; - for (unsigned int i = 0; i < count; i++) - c->out->add (alternates[i]); - } + { c->output->add_array (alternates.arrayZ, alternates.len); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { c->output->add_array (alternates.arrayZ, alternates.len); } @@ -502,7 +545,7 @@ struct AlternateSet unsigned int shift = hb_ctz (lookup_mask); unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift); - /* If alt_index is MAX, randomize feature if it is the rand feature. */ + /* If alt_index is MAX_VALUE, randomize feature if it is the rand feature. */ if (alt_index == HB_OT_MAP_MAX_VALUE && c->random) alt_index = c->random_number () % count + 1; @@ -513,11 +556,44 @@ struct AlternateSet return_trace (true); } + unsigned + get_alternates (unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { + if (alternates.len && alternate_count) + { + + alternates.sub_array (start_offset, alternate_count) + | hb_sink (hb_array (alternate_glyphs, *alternate_count)) + ; + } + return alternates.len; + } + + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs) + Iterator alts) { TRACE_SERIALIZE (this); - return_trace (alternates.serialize (c, glyphs)); + return_trace (alternates.serialize (c, alts)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (alternates) + | hb_filter (glyphset) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, it) && + out->alternates); } bool sanitize (hb_sanitize_context_t *c) const @@ -527,7 +603,7 @@ struct AlternateSet } protected: - ArrayOf + ArrayOf alternates; /* Array of alternate GlyphIDs--in * arbitrary order */ public: @@ -541,35 +617,38 @@ struct AlternateSubstFormat1 void closure (hb_closure_context_t *c) const { - unsigned int count = alternateSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+alternateSet[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, alternateSet) + | hb_filter (c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = alternateSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - (this+alternateSet[iter.get_coverage ()]).collect_glyphs (c); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + hb_zip (this+coverage, alternateSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } + + unsigned + get_glyph_alternates (hb_codepoint_t gid, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const + { return (this+alternateSet[(this+coverage).get_coverage (gid)]) + .get_alternates (start_offset, alternate_count, alternate_glyphs); } bool apply (hb_ot_apply_context_t *c) const { @@ -582,9 +661,9 @@ struct AlternateSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -603,8 +682,24 @@ struct AlternateSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, alternateSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->alternateSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -628,27 +723,27 @@ struct AlternateSubstFormat1 struct AlternateSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); default:return_trace (false); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -664,40 +759,30 @@ struct AlternateSubst struct Ligature { bool intersects (const hb_set_t *glyphs) const - { - unsigned int count = component.lenP1; - for (unsigned int i = 1; i < count; i++) - if (!glyphs->has (component[i])) - return false; - return true; - } + { return hb_all (component, glyphs); } void closure (hb_closure_context_t *c) const { - unsigned int count = component.lenP1; - for (unsigned int i = 1; i < count; i++) - if (!c->glyphs->has (component[i])) - return; - c->out->add (ligGlyph); + if (!intersects (c->glyphs)) return; + c->output->add (ligGlyph); } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - c->input->add_array (component.arrayZ, component.lenP1 ? component.lenP1 - 1 : 0); + c->input->add_array (component.arrayZ, component.get_length ()); c->output->add (ligGlyph); } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); if (c->len != component.lenP1) - return_trace (false); + return false; for (unsigned int i = 1; i < c->len; i++) if (likely (c->glyphs[i] != component[i])) - return_trace (false); + return false; - return_trace (true); + return true; } bool apply (hb_ot_apply_context_t *c) const @@ -739,9 +824,11 @@ struct Ligature return_trace (true); } + template bool serialize (hb_serialize_context_t *c, - GlyphID ligature, - hb_array_t components /* Starting from second */) + hb_codepoint_t ligature, + Iterator components /* Starting from second */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -750,6 +837,25 @@ struct Ligature return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false); + + auto it = + + hb_iter (component) + | hb_map (glyph_map) + ; + + auto *out = c->serializer->start_embed (*this); + return_trace (out->serialize (c->serializer, + glyph_map[ligGlyph], + it)); + } + public: bool sanitize (hb_sanitize_context_t *c) const { @@ -758,8 +864,8 @@ struct Ligature } protected: - GlyphID ligGlyph; /* GlyphID of ligature to substitute */ - HeadlessArrayOf + HBGlyphID ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArrayOf component; /* Array of component GlyphIDs--start * with the second component--ordered * in writing direction */ @@ -771,38 +877,38 @@ struct LigatureSet { bool intersects (const hb_set_t *glyphs) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - if ((this+ligature[i]).intersects (glyphs)) - return true; - return false; + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - (this+ligature[i]).closure (c); + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.closure (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c) const { - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - (this+ligature[i]).collect_glyphs (c); + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - unsigned int num_ligs = ligature.len; - for (unsigned int i = 0; i < num_ligs; i++) - { - const Ligature &lig = this+ligature[i]; - if (lig.would_apply (c)) - return_trace (true); - } - return_trace (false); + return + + hb_iter (ligature) + | hb_map (hb_add (this)) + | hb_map ([c] (const Ligature &_) { return _.would_apply (c); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c) const @@ -819,16 +925,16 @@ struct LigatureSet } bool serialize (hb_serialize_context_t *c, - hb_array_t ligatures, + hb_array_t ligatures, hb_array_t component_count_list, - hb_array_t &component_list /* Starting from second for each ligature */) + hb_array_t &component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!ligature.serialize (c, ligatures.length))) return_trace (false); for (unsigned int i = 0; i < ligatures.length; i++) { - unsigned int component_count = MAX (component_count_list[i] - 1, 0); + unsigned int component_count = (unsigned) hb_max ((int) component_count_list[i] - 1, 0); if (unlikely (!ligature[i].serialize (c, this) .serialize (c, ligatures[i], @@ -839,6 +945,19 @@ struct LigatureSet return_trace (true); } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_iter (ligature) + | hb_filter (subset_offset_array (c, out->ligature, this)) + | hb_drain + ; + return_trace (bool (out->ligature)); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -857,59 +976,55 @@ struct LigatureSubstFormat1 { bool intersects (const hb_set_t *glyphs) const { - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ligatureSet[iter.get_coverage ()]).intersects (glyphs)) - return true; - } - return false; + return + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map ([this, glyphs] (const OffsetTo &_) + { return (this+_).intersects (glyphs); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ligatureSet[iter.get_coverage ()]).closure (c); - } + + hb_zip (this+coverage, ligatureSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.closure (c); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; - unsigned int count = ligatureSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - (this+ligatureSet[iter.get_coverage ()]).collect_glyphs (c); - } + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; + + + hb_zip (this+coverage, ligatureSet) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); }) + ; } const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); unsigned int index = (this+coverage).get_coverage (c->glyphs[0]); - if (likely (index == NOT_COVERED)) return_trace (false); + if (likely (index == NOT_COVERED)) return false; const LigatureSet &lig_set = this+ligatureSet[index]; - return_trace (lig_set.would_apply (c)); + return lig_set.would_apply (c); } bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); - unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); const LigatureSet &lig_set = this+ligatureSet[index]; @@ -917,11 +1032,11 @@ struct LigatureSubstFormat1 } bool serialize (hb_serialize_context_t *c, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -943,8 +1058,24 @@ struct LigatureSubstFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ligatureSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ligatureSet, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -968,16 +1099,16 @@ struct LigatureSubstFormat1 struct LigatureSubst { bool serialize (hb_serialize_context_t *c, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (u.format))) return_trace (false); unsigned int format = 1; - u.format.set (format); + u.format = format; switch (u.format) { case 1: return_trace (u.format1.serialize (c, first_glyphs, @@ -989,13 +1120,13 @@ struct LigatureSubst } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1015,7 +1146,6 @@ struct ChainContextSubst : ChainContext {}; struct ExtensionSubst : Extension { typedef struct SubstLookupSubTable SubTable; - bool is_reverse () const; }; @@ -1027,7 +1157,7 @@ struct ReverseChainSingleSubstFormat1 if (!(this+coverage).intersects (glyphs)) return false; - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); unsigned int count; @@ -1046,47 +1176,36 @@ struct ReverseChainSingleSubstFormat1 void closure (hb_closure_context_t *c) const { - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + if (!intersects (c->glyphs)) return; - unsigned int count; - - count = backtrack.len; - for (unsigned int i = 0; i < count; i++) - if (!(this+backtrack[i]).intersects (c->glyphs)) - return; - - count = lookahead.len; - for (unsigned int i = 0; i < count; i++) - if (!(this+lookahead[i]).intersects (c->glyphs)) - return; + const OffsetArrayOf &lookahead = StructAfter> (backtrack); + const ArrayOf &substitute = StructAfter> (lookahead); - const ArrayOf &substitute = StructAfter > (lookahead); - count = substitute.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - c->out->add (substitute[iter.get_coverage ()]); - } + + hb_zip (this+coverage, substitute) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_sink (c->output) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - if (unlikely (!(this+coverage).add_coverage (c->input))) return; + if (unlikely (!(this+coverage).collect_coverage (c->input))) return; unsigned int count; count = backtrack.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!(this+backtrack[i]).add_coverage (c->before))) return; + if (unlikely (!(this+backtrack[i]).collect_coverage (c->before))) return; - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); count = lookahead.len; for (unsigned int i = 0; i < count; i++) - if (unlikely (!(this+lookahead[i]).add_coverage (c->after))) return; + if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return; - const ArrayOf &substitute = StructAfter > (lookahead); + const ArrayOf &substitute = StructAfter> (lookahead); count = substitute.len; c->output->add_array (substitute.arrayZ, substitute.len); } @@ -1094,10 +1213,7 @@ struct ReverseChainSingleSubstFormat1 const Coverage &get_coverage () const { return this+coverage; } bool would_apply (hb_would_apply_context_t *c) const - { - TRACE_WOULD_APPLY (this); - return_trace (c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED); - } + { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; } bool apply (hb_ot_apply_context_t *c) const { @@ -1105,13 +1221,15 @@ struct ReverseChainSingleSubstFormat1 if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) return_trace (false); /* No chaining to this type */ - unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (backtrack); - const ArrayOf &substitute = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); + const ArrayOf &substitute = StructAfter> (lookahead); + + if (unlikely (index >= substitute.len)) return_trace (false); - unsigned int start_index = 0, end_index = 0; + unsigned int start_index = 0, end_index = 0; if (match_backtrack (c, backtrack.len, (HBUINT16 *) backtrack.arrayZ, match_coverage, this, @@ -1144,10 +1262,10 @@ struct ReverseChainSingleSubstFormat1 TRACE_SANITIZE (this); if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this))) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (backtrack); if (!lookahead.sanitize (c, this)) return_trace (false); - const ArrayOf &substitute = StructAfter > (lookahead); + const ArrayOf &substitute = StructAfter> (lookahead); return_trace (substitute.sanitize (c)); } @@ -1164,7 +1282,7 @@ struct ReverseChainSingleSubstFormat1 lookaheadX; /* Array of coverage tables * in lookahead sequence, in glyph * sequence order */ - ArrayOf + ArrayOf substituteX; /* Array of substitute * GlyphIDs--ordered by Coverage Index */ public: @@ -1173,13 +1291,13 @@ struct ReverseChainSingleSubstFormat1 struct ReverseChainSingleSubst { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1213,23 +1331,29 @@ struct SubstLookupSubTable ReverseChainSingle = 8 }; - template - typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const + template + typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type, Ts&&... ds) const { TRACE_DISPATCH (this, lookup_type); switch (lookup_type) { - case Single: return_trace (u.single.dispatch (c)); - case Multiple: return_trace (u.multiple.dispatch (c)); - case Alternate: return_trace (u.alternate.dispatch (c)); - case Ligature: return_trace (u.ligature.dispatch (c)); - case Context: return_trace (u.context.dispatch (c)); - case ChainContext: return_trace (u.chainContext.dispatch (c)); - case Extension: return_trace (u.extension.dispatch (c)); - case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c)); + case Single: return_trace (u.single.dispatch (c, hb_forward (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, hb_forward (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, hb_forward (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, hb_forward (ds)...)); + case Context: return_trace (u.context.dispatch (c, hb_forward (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, hb_forward (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, hb_forward (ds)...)); default: return_trace (c->default_return_value ()); } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SingleSubst single; @@ -1253,14 +1377,14 @@ struct SubstLookup : Lookup const SubTable& get_subtable (unsigned int i) const { return Lookup::get_subtable (i); } - static bool lookup_type_is_reverse (unsigned int lookup_type) + static inline bool lookup_type_is_reverse (unsigned int lookup_type) { return lookup_type == SubTable::ReverseChainSingle; } bool is_reverse () const { unsigned int type = get_type (); if (unlikely (type == SubTable::Extension)) - return CastR (get_subtable(0)).is_reverse (); + return reinterpret_cast (get_subtable (0)).is_reverse (); return lookup_type_is_reverse (type); } @@ -1290,6 +1414,24 @@ struct SubstLookup : Lookup return ret; } + hb_closure_lookups_context_t::return_t closure_lookups (hb_closure_lookups_context_t *c, unsigned this_index) const + { + if (c->is_lookup_visited (this_index)) + return hb_closure_lookups_context_t::default_return_value (); + + c->set_lookup_visited (this_index); + if (!intersects (c->glyphs)) + { + c->set_lookup_inactive (this_index); + return hb_closure_lookups_context_t::default_return_value (); + } + + c->set_recurse_func (dispatch_closure_lookups_recurse_func); + + hb_closure_lookups_context_t::return_t ret = dispatch (c); + return ret; + } + hb_collect_glyphs_context_t::return_t collect_glyphs (hb_collect_glyphs_context_t *c) const { c->set_recurse_func (dispatch_recurse_func); @@ -1297,90 +1439,93 @@ struct SubstLookup : Lookup } template - void add_coverage (set_t *glyphs) const + void collect_coverage (set_t *glyphs) const { - hb_add_coverage_context_t c (glyphs); + hb_collect_coverage_context_t c (glyphs); dispatch (&c); } bool would_apply (hb_would_apply_context_t *c, const hb_ot_layout_lookup_accelerator_t *accel) const { - TRACE_WOULD_APPLY (this); - if (unlikely (!c->len)) return_trace (false); - if (!accel->may_have (c->glyphs[0])) return_trace (false); - return_trace (dispatch (c)); + if (unlikely (!c->len)) return false; + if (!accel->may_have (c->glyphs[0])) return false; + return dispatch (c); } - static bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); + static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index); SubTable& serialize_subtable (hb_serialize_context_t *c, - unsigned int i) + unsigned int i) { return get_subtables ()[i].serialize (c, this); } bool serialize_single (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, - hb_array_t substitutes) + hb_sorted_array_t glyphs, + hb_array_t substitutes) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes)); + return_trace (serialize_subtable (c, 0).u.single. + serialize (c, hb_zip (glyphs, substitutes))); } bool serialize_multiple (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t substitute_len_list, - hb_array_t substitute_glyphs_list) + hb_array_t substitute_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.multiple.serialize (c, - glyphs, - substitute_len_list, - substitute_glyphs_list)); + return_trace (serialize_subtable (c, 0).u.multiple. + serialize (c, + glyphs, + substitute_len_list, + substitute_glyphs_list)); } bool serialize_alternate (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t glyphs, + hb_sorted_array_t glyphs, hb_array_t alternate_len_list, - hb_array_t alternate_glyphs_list) + hb_array_t alternate_glyphs_list) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.alternate.serialize (c, - glyphs, - alternate_len_list, - alternate_glyphs_list)); + return_trace (serialize_subtable (c, 0).u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)); } bool serialize_ligature (hb_serialize_context_t *c, uint32_t lookup_props, - hb_array_t first_glyphs, + hb_sorted_array_t first_glyphs, hb_array_t ligature_per_first_glyph_count_list, - hb_array_t ligatures_list, + hb_array_t ligatures_list, hb_array_t component_count_list, - hb_array_t component_list /* Starting from second for each ligature */) + hb_array_t component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false); - return_trace (serialize_subtable (c, 0).u.ligature.serialize (c, - first_glyphs, - ligature_per_first_glyph_count_list, - ligatures_list, - component_count_list, - component_list)); + return_trace (serialize_subtable (c, 0).u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)); } template - static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); - static hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index) + static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index) { if (!c->should_visit_lookup (lookup_index)) - return HB_VOID; + return hb_empty_t (); hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_index); @@ -1392,9 +1537,11 @@ struct SubstLookup : Lookup return ret; } - template - typename context_t::return_t dispatch (context_t *c) const - { return Lookup::dispatch (c); } + HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned lookup_index); + + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch (c, hb_forward (ds)...); } bool subset (hb_subset_context_t *c) const { return Lookup::subset (c); } @@ -1413,17 +1560,25 @@ struct GSUB : GSUBGPOS static constexpr hb_tag_t tableTag = HB_OT_TAG_GSUB; const SubstLookup& get_lookup (unsigned int i) const - { return CastR (GSUBGPOS::get_lookup (i)); } + { return static_cast (GSUBGPOS::get_lookup (i)); } bool subset (hb_subset_context_t *c) const - { return GSUBGPOS::subset (c); } + { + hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_features); + return GSUBGPOS::subset (&l); + } bool sanitize (hb_sanitize_context_t *c) const { return GSUBGPOS::sanitize (c); } - HB_INTERNAL bool is_blacklisted (hb_blob_t *blob, + HB_INTERNAL bool is_blocklisted (hb_blob_t *blob, hb_face_t *face) const; + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { GSUBGPOS::closure_lookups (face, glyphs, lookup_indexes); } + typedef GSUBGPOS::accelerator_t accelerator_t; }; @@ -1433,22 +1588,25 @@ struct GSUB_accelerator_t : GSUB::accelerator_t {}; /* Out-of-class implementation for methods recursing */ +#ifndef HB_NO_OT_LAYOUT /*static*/ inline bool ExtensionSubst::is_reverse () const { - unsigned int type = get_type (); - if (unlikely (type == SubTable::Extension)) - return CastR (get_subtable()).is_reverse (); - return SubstLookup::lookup_type_is_reverse (type); + return SubstLookup::lookup_type_is_reverse (get_type ()); } - template -/*static*/ inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +/*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) { const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); return l.dispatch (c); } -/*static*/ inline bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) +/*static*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +/*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index) { const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; @@ -1460,6 +1618,8 @@ template c->set_lookup_props (saved_lookup_props); return ret; } +#endif + } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh index 11d757a451210..1e7d76dd47bc2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-gsubgpos.hh @@ -42,30 +42,26 @@ namespace OT { struct hb_intersects_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "INTERSECTS"; } template return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); } static return_t default_return_value () { return false; } bool stop_sublookup_iteration (return_t r) const { return r; } const hb_set_t *glyphs; - unsigned int debug_depth; hb_intersects_context_t (const hb_set_t *glyphs_) : - glyphs (glyphs_), - debug_depth (0) {} + glyphs (glyphs_) {} }; struct hb_closure_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "CLOSURE"; } typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index); template - return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; } - static return_t default_return_value () { return HB_VOID; } + return_t dispatch (const T &obj) { obj.closure (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } void recurse (unsigned int lookup_index) { if (unlikely (nesting_level_left == 0 || !recurse_func)) @@ -76,26 +72,35 @@ struct hb_closure_context_t : nesting_level_left++; } + bool lookup_limit_exceeded () + { return lookup_count > HB_MAX_LOOKUP_INDICES; } + bool should_visit_lookup (unsigned int lookup_index) { + if (lookup_count++ > HB_MAX_LOOKUP_INDICES) + return false; + if (is_lookup_done (lookup_index)) return false; + done_lookups->set (lookup_index, glyphs->get_population ()); return true; } bool is_lookup_done (unsigned int lookup_index) { + if (done_lookups->in_error ()) + return true; + /* Have we visited this lookup with the current set of glyphs? */ return done_lookups->get (lookup_index) == glyphs->get_population (); } hb_face_t *face; hb_set_t *glyphs; - hb_set_t out[1]; + hb_set_t output[1]; recurse_func_t recurse_func; unsigned int nesting_level_left; - unsigned int debug_depth; hb_closure_context_t (hb_face_t *face_, hb_set_t *glyphs_, @@ -105,8 +110,9 @@ struct hb_closure_context_t : glyphs (glyphs_), recurse_func (nullptr), nesting_level_left (nesting_level_left_), - debug_depth (0), - done_lookups (done_lookups_) {} + done_lookups (done_lookups_), + lookup_count (0) + {} ~hb_closure_context_t () { flush (); } @@ -114,19 +120,87 @@ struct hb_closure_context_t : void flush () { - hb_set_union (glyphs, out); - hb_set_clear (out); + hb_set_del_range (output, face->get_num_glyphs (), hb_set_get_max (output)); /* Remove invalid glyphs. */ + hb_set_union (glyphs, output); + hb_set_clear (output); } private: hb_map_t *done_lookups; + unsigned int lookup_count; }; +struct hb_closure_lookups_context_t : + hb_dispatch_context_t +{ + typedef return_t (*recurse_func_t) (hb_closure_lookups_context_t *c, unsigned lookup_index); + template + return_t dispatch (const T &obj) { obj.closure_lookups (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + void recurse (unsigned lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return; + + /* Return if new lookup was recursed to before. */ + if (is_lookup_visited (lookup_index)) + return; + + set_lookup_visited (lookup_index); + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + } + + void set_lookup_visited (unsigned lookup_index) + { visited_lookups->add (lookup_index); } + + void set_lookup_inactive (unsigned lookup_index) + { inactive_lookups->add (lookup_index); } + + bool lookup_limit_exceeded () + { return lookup_count > HB_MAX_LOOKUP_INDICES; } + + bool is_lookup_visited (unsigned lookup_index) + { + if (lookup_count++ > HB_MAX_LOOKUP_INDICES) + return true; + + if (visited_lookups->in_error ()) + return true; + + return visited_lookups->has (lookup_index); + } + + hb_face_t *face; + const hb_set_t *glyphs; + recurse_func_t recurse_func; + unsigned int nesting_level_left; + + hb_closure_lookups_context_t (hb_face_t *face_, + const hb_set_t *glyphs_, + hb_set_t *visited_lookups_, + hb_set_t *inactive_lookups_, + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + glyphs (glyphs_), + recurse_func (nullptr), + nesting_level_left (nesting_level_left_), + visited_lookups (visited_lookups_), + inactive_lookups (inactive_lookups_), + lookup_count (0) {} + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } + + private: + hb_set_t *visited_lookups; + hb_set_t *inactive_lookups; + unsigned int lookup_count; +}; struct hb_would_apply_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "WOULD_APPLY"; } template return_t dispatch (const T &obj) { return obj.would_apply (this); } static return_t default_return_value () { return false; } @@ -136,7 +210,6 @@ struct hb_would_apply_context_t : const hb_codepoint_t *glyphs; unsigned int len; bool zero_context; - unsigned int debug_depth; hb_would_apply_context_t (hb_face_t *face_, const hb_codepoint_t *glyphs_, @@ -145,19 +218,16 @@ struct hb_would_apply_context_t : face (face_), glyphs (glyphs_), len (len_), - zero_context (zero_context_), - debug_depth (0) {} + zero_context (zero_context_) {} }; - struct hb_collect_glyphs_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { - const char *get_name () { return "COLLECT_GLYPHS"; } typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index); template - return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; } - static return_t default_return_value () { return HB_VOID; } + return_t dispatch (const T &obj) { obj.collect_glyphs (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } void recurse (unsigned int lookup_index) { if (unlikely (nesting_level_left == 0 || !recurse_func)) @@ -204,7 +274,6 @@ struct hb_collect_glyphs_context_t : recurse_func_t recurse_func; hb_set_t *recursed_lookups; unsigned int nesting_level_left; - unsigned int debug_depth; hb_collect_glyphs_context_t (hb_face_t *face_, hb_set_t *glyphs_before, /* OUT. May be NULL */ @@ -219,8 +288,7 @@ struct hb_collect_glyphs_context_t : output (glyphs_output ? glyphs_output : hb_set_get_empty ()), recurse_func (nullptr), recursed_lookups (hb_set_create ()), - nesting_level_left (nesting_level_left_), - debug_depth (0) {} + nesting_level_left (nesting_level_left_) {} ~hb_collect_glyphs_context_t () { hb_set_destroy (recursed_lookups); } void set_recurse_func (recurse_func_t func) { recurse_func = func; } @@ -229,26 +297,23 @@ struct hb_collect_glyphs_context_t : template -struct hb_add_coverage_context_t : - hb_dispatch_context_t, const Coverage &, HB_DEBUG_GET_COVERAGE> +struct hb_collect_coverage_context_t : + hb_dispatch_context_t, const Coverage &> { - const char *get_name () { return "GET_COVERAGE"; } - typedef const Coverage &return_t; + typedef const Coverage &return_t; // Stoopid that we have to dupe this here. template return_t dispatch (const T &obj) { return obj.get_coverage (); } - static return_t default_return_value () { return Null(Coverage); } + static return_t default_return_value () { return Null (Coverage); } bool stop_sublookup_iteration (return_t r) const { - r.add_coverage (set); + r.collect_coverage (set); return false; } - hb_add_coverage_context_t (set_t *set_) : - set (set_), - debug_depth (0) {} + hb_collect_coverage_context_t (set_t *set_) : + set (set_) {} set_t *set; - unsigned int debug_depth; }; @@ -276,7 +341,7 @@ struct hb_ot_apply_context_t : void set_mask (hb_mask_t mask_) { mask = mask_; } void set_syllable (uint8_t syllable_) { syllable = syllable_; } void set_match_func (match_func_t match_func_, - const void *match_data_) + const void *match_data_) { match_func = match_func_; match_data = match_data_; } enum may_match_t { @@ -286,7 +351,7 @@ struct hb_ot_apply_context_t : }; may_match_t may_match (const hb_glyph_info_t &info, - const HBUINT16 *glyph_data) const + const HBUINT16 *glyph_data) const { if (!(info.mask & mask) || (syllable && syllable != info.syllable ())) @@ -355,7 +420,7 @@ struct hb_ot_apply_context_t : } void reset (unsigned int start_index_, - unsigned int num_items_) + unsigned int num_items_) { idx = start_index_; num_items = num_items_; @@ -363,7 +428,11 @@ struct hb_ot_apply_context_t : matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); } - void reject () { num_items++; match_glyph_data--; } + void reject () + { + num_items++; + if (match_glyph_data) match_glyph_data--; + } matcher_t::may_skip_t may_skip (const hb_glyph_info_t &info) const @@ -387,7 +456,7 @@ struct hb_ot_apply_context_t : skip == matcher_t::SKIP_NO)) { num_items--; - match_glyph_data++; + if (match_glyph_data) match_glyph_data++; return true; } @@ -414,7 +483,7 @@ struct hb_ot_apply_context_t : skip == matcher_t::SKIP_NO)) { num_items--; - match_glyph_data++; + if (match_glyph_data) match_glyph_data++; return true; } @@ -467,7 +536,6 @@ struct hb_ot_apply_context_t : unsigned int lookup_index; unsigned int lookup_props; unsigned int nesting_level_left; - unsigned int debug_depth; bool has_glyph_classes; bool auto_zwnj; @@ -478,12 +546,18 @@ struct hb_ot_apply_context_t : hb_ot_apply_context_t (unsigned int table_index_, - hb_font_t *font_, - hb_buffer_t *buffer_) : + hb_font_t *font_, + hb_buffer_t *buffer_) : iter_input (), iter_context (), font (font_), face (font->face), buffer (buffer_), recurse_func (nullptr), - gdef (*face->table.GDEF->table), + gdef ( +#ifndef HB_NO_OT_LAYOUT + *face->table.GDEF->table +#else + Null (GDEF) +#endif + ), var_store (gdef.get_var_store ()), direction (buffer_->props.direction), lookup_mask (1), @@ -491,7 +565,6 @@ struct hb_ot_apply_context_t : lookup_index ((unsigned int) -1), lookup_props (0), nesting_level_left (HB_MAX_NESTING_LEVEL), - debug_depth (0), has_glyph_classes (gdef.has_glyph_classes ()), auto_zwnj (true), auto_zwj (true), @@ -595,13 +668,13 @@ struct hb_ot_apply_context_t : buffer->cur().codepoint = glyph_index; } void replace_glyph_with_ligature (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) const { _set_glyph_props (glyph_index, class_guess, true); buffer->replace_glyph (glyph_index); } void output_glyph_for_component (hb_codepoint_t glyph_index, - unsigned int class_guess) const + unsigned int class_guess) const { _set_glyph_props (glyph_index, class_guess, false, true); buffer->output_glyph (glyph_index); @@ -610,10 +683,10 @@ struct hb_ot_apply_context_t : struct hb_get_subtables_context_t : - hb_dispatch_context_t + hb_dispatch_context_t { template - static bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c) + static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c) { const Type *typed_obj = (const Type *) obj; return typed_obj->apply (c); @@ -629,7 +702,7 @@ struct hb_get_subtables_context_t : obj = &obj_; apply_func = apply_func_; digest.init (); - obj_.get_coverage ().add_coverage (&digest); + obj_.get_coverage ().collect_coverage (&digest); } bool apply (OT::hb_ot_apply_context_t *c) const @@ -646,22 +719,19 @@ struct hb_get_subtables_context_t : typedef hb_vector_t array_t; /* Dispatch interface. */ - const char *get_name () { return "GET_SUBTABLES"; } template return_t dispatch (const T &obj) { hb_applicable_t *entry = array.push(); entry->init (obj, apply_to); - return HB_VOID; + return hb_empty_t (); } - static return_t default_return_value () { return HB_VOID; } + static return_t default_return_value () { return hb_empty_t (); } hb_get_subtables_context_t (array_t &array_) : - array (array_), - debug_depth (0) {} + array (array_) {} array_t &array; - unsigned int debug_depth; }; @@ -700,15 +770,14 @@ static inline bool intersects_coverage (const hb_set_t *glyphs, const HBUINT16 & return (data+coverage).intersects (glyphs); } -static inline bool intersects_array (const hb_set_t *glyphs, - unsigned int count, - const HBUINT16 values[], - intersects_func_t intersects_func, - const void *intersects_data) +static inline bool array_is_subset_of (const hb_set_t *glyphs, + unsigned int count, + const HBUINT16 values[], + intersects_func_t intersects_func, + const void *intersects_data) { - for (unsigned int i = 0; i < count; i++) - if (likely (!intersects_func (glyphs, values[i], intersects_data))) - return false; + for (const HBUINT16 &_ : + hb_iter (values, count)) + if (!intersects_func (glyphs, _, intersects_data)) return false; return true; } @@ -720,12 +789,12 @@ static inline void collect_glyph (hb_set_t *glyphs, const HBUINT16 &value, const static inline void collect_class (hb_set_t *glyphs, const HBUINT16 &value, const void *data) { const ClassDef &class_def = *reinterpret_cast(data); - class_def.add_class (glyphs, value); + class_def.collect_class (glyphs, value); } static inline void collect_coverage (hb_set_t *glyphs, const HBUINT16 &value, const void *data) { const OffsetTo &coverage = (const OffsetTo&)value; - (data+coverage).add_coverage (glyphs); + (data+coverage).collect_coverage (glyphs); } static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, hb_set_t *glyphs, @@ -734,8 +803,10 @@ static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, collect_glyphs_func_t collect_func, const void *collect_data) { - for (unsigned int i = 0; i < count; i++) - collect_func (glyphs, values[i], collect_data); + return + + hb_iter (values, count) + | hb_apply ([&] (const HBUINT16 &_) { collect_func (glyphs, _, collect_data); }) + ; } @@ -846,7 +917,7 @@ static inline bool match_input (hb_ot_apply_context_t *c, if (ligbase == LIGBASE_NOT_CHECKED) { bool found = false; - const hb_glyph_info_t *out = buffer->out_info; + const auto *out = buffer->out_info; unsigned int j = buffer->out_len; while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id) { @@ -970,7 +1041,7 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, if (this_comp == 0) this_comp = last_num_components; unsigned int new_lig_comp = components_so_far - last_num_components + - MIN (this_comp, last_num_components); + hb_min (this_comp, last_num_components); _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp); } buffer->next_glyph (); @@ -984,18 +1055,19 @@ static inline bool ligate_input (hb_ot_apply_context_t *c, buffer->idx++; } - if (!is_mark_ligature && last_lig_id) { + if (!is_mark_ligature && last_lig_id) + { /* Re-adjust components for any marks following. */ - for (unsigned int i = buffer->idx; i < buffer->len; i++) { - if (last_lig_id == _hb_glyph_info_get_lig_id (&buffer->info[i])) { - unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); - if (!this_comp) - break; - unsigned int new_lig_comp = components_so_far - last_num_components + - MIN (this_comp, last_num_components); - _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); - } else - break; + for (unsigned i = buffer->idx; i < buffer->len; ++i) + { + if (last_lig_id != _hb_glyph_info_get_lig_id (&buffer->info[i])) break; + + unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); + if (!this_comp) break; + + unsigned new_lig_comp = components_so_far - last_num_components + + hb_min (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); } } return_trace (true); @@ -1050,6 +1122,17 @@ static inline bool match_lookahead (hb_ot_apply_context_t *c, struct LookupRecord { + LookupRecord* copy (hb_serialize_context_t *c, + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (*this); + if (unlikely (!out)) return_trace (nullptr); + + out->lookupListIndex = hb_map_get (lookup_map, lookupListIndex); + return_trace (out); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1170,7 +1253,7 @@ static inline bool apply_lookup (hb_ot_apply_context_t *c, else { /* NOTE: delta is negative. */ - delta = MAX (delta, (int) next - (int) count); + delta = hb_max (delta, (int) next - (int) count); next -= delta; } @@ -1221,9 +1304,9 @@ static inline bool context_intersects (const hb_set_t *glyphs, const HBUINT16 input[], /* Array of input values--start with second glyph */ ContextClosureLookupContext &lookup_context) { - return intersects_array (glyphs, - inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data); + return array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data); } static inline void context_closure_lookup (hb_closure_context_t *c, @@ -1296,7 +1379,9 @@ struct Rule void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - const UnsizedArrayOf &lookupRecord = StructAfter > + if (unlikely (c->lookup_limit_exceeded ())) return; + + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); context_closure_lookup (c, inputCount, inputZ.arrayZ, @@ -1304,10 +1389,19 @@ struct Rule lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const UnsizedArrayOf &lookupRecord = StructAfter> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + recurse_lookups (c, lookupCount, lookupRecord.arrayZ); + } + void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const { - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); context_collect_glyphs_lookup (c, inputCount, inputZ.arrayZ, @@ -1318,21 +1412,64 @@ struct Rule bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); - return_trace (context_would_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context)); + return context_would_apply_lookup (c, + inputCount, inputZ.arrayZ, + lookupCount, lookupRecord.arrayZ, + lookup_context); } bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - const UnsizedArrayOf &lookupRecord = StructAfter > + const UnsizedArrayOf &lookupRecord = StructAfter> (inputZ.as_array (inputCount ? inputCount - 1 : 0)); return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context)); } + bool serialize (hb_serialize_context_t *c, + const hb_map_t *input_mapping, /* old->new glyphid or class mapping */ + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + out->inputCount = inputCount; + out->lookupCount = lookupCount; + + const hb_array_t input = inputZ.as_array (inputCount - 1); + for (const auto org : input) + { + HBUINT16 d; + d = input_mapping->get (org); + c->copy (d); + } + + const UnsizedArrayOf &lookupRecord = StructAfter> + (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + c->copy (lookupRecord[i], lookup_map); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) const + { + TRACE_SUBSET (this); + + const hb_array_t input = inputZ.as_array ((inputCount ? inputCount - 1 : 0)); + if (!input.length) return_trace (false); + + const hb_map_t *mapping = klass_map == nullptr ? c->plan->glyph_map : klass_map; + if (!hb_all (input, mapping)) return_trace (false); + return_trace (serialize (c->serializer, mapping, lookup_map)); + } + public: bool sanitize (hb_sanitize_context_t *c) const { @@ -1364,53 +1501,99 @@ struct RuleSet bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).intersects (glyphs, lookup_context)) - return true; - return false; + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).closure (c, lookup_context); + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure_lookups (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).collect_glyphs (c, lookup_context); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - { - if ((this+rule[i]).would_apply (c, lookup_context)) - return_trace (true); - } - return_trace (false); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c, ContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) const + { + TRACE_SUBSET (this); + + auto snap = c->serializer->snapshot (); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const OffsetTo& _ : rule) { - if ((this+rule[i]).apply (c, lookup_context)) - return_trace (true); + if (!_) continue; + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + auto o_snap = c->serializer->snapshot (); + if (!o->serialize_subset (c, _, this, lookup_map, klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } } - return_trace (false); + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); + + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -1437,16 +1620,14 @@ struct ContextFormat1 nullptr }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context)) - return true; - } - return false; + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const RuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -1456,40 +1637,47 @@ struct ContextFormat1 nullptr }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context); - } + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); }) + ; } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); struct ContextCollectGlyphsLookupContext lookup_context = { {collect_glyph}, nullptr }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; struct ContextApplyLookupContext lookup_context = { {match_glyph}, nullptr }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -1512,8 +1700,26 @@ struct ContextFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1549,13 +1755,15 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (class_def.intersects_class (glyphs, i) && - (this+ruleSet[i]).intersects (glyphs, lookup_context)) - return true; - - return false; + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t p) + { return class_def.intersects_class (glyphs, p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -1570,17 +1778,30 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (class_def.intersects_class (c->glyphs, i)) { - const RuleSet &rule_set = this+ruleSet[i]; - rule_set.closure (c, lookup_context); - } + return + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return class_def.intersects_class (c->glyphs, _); }, + hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); const ClassDef &class_def = this+classDef; struct ContextCollectGlyphsLookupContext lookup_context = { @@ -1588,15 +1809,14 @@ struct ContextFormat2 &class_def }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ClassDef &class_def = this+classDef; unsigned int index = class_def.get_class (c->glyphs[0]); const RuleSet &rule_set = this+ruleSet[index]; @@ -1604,7 +1824,7 @@ struct ContextFormat2 {match_class}, &class_def }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -1628,8 +1848,45 @@ struct ContextFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + if (unlikely (!out->coverage.serialize_subset (c, coverage, this))) + return_trace (false); + + hb_map_t klass_map; + out->classDef.serialize_subset (c, classDef, this, &klass_map); + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + bool ret = true; + unsigned non_zero_index = 0, index = 0; + for (const hb_pair_t&> _ : + hb_enumerate (ruleSet) + | hb_filter (klass_map, hb_first)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + if (o->serialize_subset (c, _.second, this, lookup_map, &klass_map)) + non_zero_index = index; + + index++; + } + + if (!ret) return_trace (ret); + + //prune empty trailing ruleSets + --index; + while (index > non_zero_index) + { + out->ruleSet.pop (); + index--; + } + + return_trace (bool (out->ruleSet)); } bool sanitize (hb_sanitize_context_t *c) const @@ -1686,9 +1943,17 @@ struct ContextFormat3 lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); + recurse_lookups (c, lookupCount, lookupRecord); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverageZ[0]).add_coverage (c->input); + (this+coverageZ[0]).collect_coverage (c->input); const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); struct ContextCollectGlyphsLookupContext lookup_context = { @@ -1704,14 +1969,15 @@ struct ContextFormat3 bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); struct ContextApplyLookupContext lookup_context = { {match_coverage}, this }; - return_trace (context_would_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context)); + return context_would_apply_lookup (c, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookupCount, lookupRecord, + lookup_context); } const Coverage &get_coverage () const { return this+coverageZ[0]; } @@ -1733,8 +1999,28 @@ struct ContextFormat3 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->format = format; + out->glyphCount = glyphCount; + out->lookupCount = lookupCount; + + auto coverages = coverageZ.as_array (glyphCount); + + for (const OffsetTo& offset : coverages) + { + auto *o = c->serializer->allocate_size> (OffsetTo::static_size); + if (unlikely (!o)) return_trace (false); + if (!o->serialize_subset (c, offset, this)) return_trace (false); + } + + const LookupRecord *lookupRecord = &StructAfter (coverageZ.as_array (glyphCount)); + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + c->serializer->copy (lookupRecord[i], lookup_map); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -1755,7 +2041,7 @@ struct ContextFormat3 HBUINT16 glyphCount; /* Number of glyphs in the input glyph * sequence */ HBUINT16 lookupCount; /* Number of LookupRecords */ - UnsizedArrayOf > + UnsizedArrayOf> coverageZ; /* Array of offsets to Coverage * table in glyph sequence order */ /*UnsizedArrayOf @@ -1767,15 +2053,15 @@ struct ContextFormat3 struct Context { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (c->dispatch (u.format3)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -1819,15 +2105,15 @@ static inline bool chain_context_intersects (const hb_set_t *glyphs, const HBUINT16 lookahead[], ChainContextClosureLookupContext &lookup_context) { - return intersects_array (glyphs, - backtrackCount, backtrack, - lookup_context.funcs.intersects, lookup_context.intersects_data[0]) - && intersects_array (glyphs, - inputCount ? inputCount - 1 : 0, input, - lookup_context.funcs.intersects, lookup_context.intersects_data[1]) - && intersects_array (glyphs, - lookaheadCount, lookahead, - lookup_context.funcs.intersects, lookup_context.intersects_data[2]); + return array_is_subset_of (glyphs, + backtrackCount, backtrack, + lookup_context.funcs.intersects, lookup_context.intersects_data[0]) + && array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, lookup_context.intersects_data[1]) + && array_is_subset_of (glyphs, + lookaheadCount, lookahead, + lookup_context.funcs.intersects, lookup_context.intersects_data[2]); } static inline void chain_context_closure_lookup (hb_closure_context_t *c, @@ -1927,8 +2213,8 @@ struct ChainRule { bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); return chain_context_intersects (glyphs, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1939,9 +2225,11 @@ struct ChainRule void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + if (unlikely (c->lookup_limit_exceeded ())) return; + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); chain_context_closure_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1950,12 +2238,22 @@ struct ChainRule lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const { - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); chain_context_collect_glyphs_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1967,23 +2265,22 @@ struct ChainRule bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); - return_trace (chain_context_would_apply_lookup (c, - backtrack.len, backtrack.arrayZ, - input.lenP1, input.arrayZ, - lookahead.len, lookahead.arrayZ, lookup.len, - lookup.arrayZ, lookup_context)); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + return chain_context_would_apply_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, lookup.len, + lookup.arrayZ, lookup_context); } bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - const HeadlessArrayOf &input = StructAfter > (backtrack); - const ArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (chain_context_apply_lookup (c, backtrack.len, backtrack.arrayZ, input.lenP1, input.arrayZ, @@ -1991,15 +2288,99 @@ struct ChainRule lookup.arrayZ, lookup_context)); } + template + void serialize_array (hb_serialize_context_t *c, + HBUINT16 len, + Iterator it) const + { + c->copy (len); + for (const auto g : it) + { + HBUINT16 gid; + gid = g; + c->copy (gid); + } + } + + ChainRule* copy (hb_serialize_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + const hb_map_t *mapping = backtrack_map; + serialize_array (c, backtrack.len, + backtrack.iter () + | hb_map (mapping)); + + const HeadlessArrayOf &input = StructAfter> (backtrack); + if (input_map) mapping = input_map; + serialize_array (c, input.lenP1, + input.iter () + | hb_map (mapping)); + + const ArrayOf &lookahead = StructAfter> (input); + if (lookahead_map) mapping = lookahead_map; + serialize_array (c, lookahead.len, + lookahead.iter () + | hb_map (mapping)); + + const ArrayOf &lookupRecord = StructAfter> (lookahead); + HBUINT16 lookupCount; + lookupCount = lookupRecord.len; + if (!c->copy (lookupCount)) return_trace (nullptr); + + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + if (!c->copy (lookupRecord[i], lookup_map)) return_trace (nullptr); + + return_trace (out); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map = nullptr, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SUBSET (this); + + const HeadlessArrayOf &input = StructAfter> (backtrack); + const ArrayOf &lookahead = StructAfter> (input); + + if (!backtrack_map) + { + const hb_set_t &glyphset = *c->plan->glyphset (); + if (!hb_all (backtrack, glyphset) || + !hb_all (input, glyphset) || + !hb_all (lookahead, glyphset)) + return_trace (false); + + copy (c->serializer, lookup_map, c->plan->glyph_map); + } + else + { + if (!hb_all (backtrack, backtrack_map) || + !hb_all (input, input_map) || + !hb_all (lookahead, lookahead_map)) + return_trace (false); + + copy (c->serializer, lookup_map, backtrack_map, input_map, lookahead_map); + } + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!backtrack.sanitize (c)) return_trace (false); - const HeadlessArrayOf &input = StructAfter > (backtrack); + const HeadlessArrayOf &input = StructAfter> (backtrack); if (!input.sanitize (c)) return_trace (false); - const ArrayOf &lookahead = StructAfter > (input); + const ArrayOf &lookahead = StructAfter> (input); if (!lookahead.sanitize (c)) return_trace (false); - const ArrayOf &lookup = StructAfter > (lookahead); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (lookup.sanitize (c)); } @@ -2025,46 +2406,100 @@ struct ChainRuleSet { bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).intersects (glyphs, lookup_context)) - return true; - return false; + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c, ChainContextClosureLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).closure (c, lookup_context); + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c); }) + ; } void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const { - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - (this+rule[i]).collect_glyphs (c, lookup_context); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { - TRACE_WOULD_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).would_apply (c, lookup_context)) - return_trace (true); - - return_trace (false); + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; } bool apply (hb_ot_apply_context_t *c, ChainContextApplyLookupContext &lookup_context) const { TRACE_APPLY (this); - unsigned int num_rules = rule.len; - for (unsigned int i = 0; i < num_rules; i++) - if ((this+rule[i]).apply (c, lookup_context)) - return_trace (true); + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_klass_map = nullptr, + const hb_map_t *input_klass_map = nullptr, + const hb_map_t *lookahead_klass_map = nullptr) const + { + TRACE_SUBSET (this); + + auto snap = c->serializer->snapshot (); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const OffsetTo& _ : rule) + { + if (!_) continue; + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + auto o_snap = c->serializer->snapshot (); + if (!o->serialize_subset (c, _, this, + lookup_map, + backtrack_klass_map, + input_klass_map, + lookahead_klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } + } + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); - return_trace (false); + return_trace (ret); } bool sanitize (hb_sanitize_context_t *c) const @@ -2090,16 +2525,14 @@ struct ChainContextFormat1 {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (glyphs->has (iter.get_glyph ()) && - (this+ruleSet[iter.get_coverage ()]).intersects (glyphs, lookup_context)) - return true; - } - return false; + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const @@ -2109,40 +2542,47 @@ struct ChainContextFormat1 {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (Coverage::Iter iter (this+coverage); iter.more (); iter.next ()) - { - if (unlikely (iter.get_coverage () >= count)) - break; /* Work around malicious fonts. https://github.com/harfbuzz/harfbuzz/issues/363 */ - if (c->glyphs->has (iter.get_glyph ())) - (this+ruleSet[iter.get_coverage ()]).closure (c, lookup_context); - } + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); }) + ; } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); struct ChainContextCollectGlyphsLookupContext lookup_context = { {collect_glyph}, {nullptr, nullptr, nullptr} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; struct ChainContextApplyLookupContext lookup_context = { {match_glyph}, {nullptr, nullptr, nullptr} }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -2164,8 +2604,26 @@ struct ChainContextFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2204,13 +2662,15 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (input_class_def.intersects_class (glyphs, i) && - (this+ruleSet[i]).intersects (glyphs, lookup_context)) - return true; - - return false; + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t p) + { return input_class_def.intersects_class (glyphs, p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; } void closure (hb_closure_context_t *c) const { @@ -2228,17 +2688,30 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - if (input_class_def.intersects_class (c->glyphs, i)) { - const ChainRuleSet &rule_set = this+ruleSet[i]; - rule_set.closure (c, lookup_context); - } + return + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return input_class_def.intersects_class (c->glyphs, _); }, + hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); }) + ; } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - (this+coverage).add_coverage (c->input); + (this+coverage).collect_coverage (c->input); const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; @@ -2251,15 +2724,14 @@ struct ChainContextFormat2 &lookahead_class_def} }; - unsigned int count = ruleSet.len; - for (unsigned int i = 0; i < count; i++) - (this+ruleSet[i]).collect_glyphs (c, lookup_context); + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; } bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &input_class_def = this+inputClassDef; const ClassDef &lookahead_class_def = this+lookaheadClassDef; @@ -2272,7 +2744,7 @@ struct ChainContextFormat2 &input_class_def, &lookahead_class_def} }; - return_trace (rule_set.would_apply (c, lookup_context)); + return rule_set.would_apply (c, lookup_context); } const Coverage &get_coverage () const { return this+coverage; } @@ -2301,8 +2773,61 @@ struct ChainContextFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->coverage.serialize_subset (c, coverage, this); + + hb_map_t backtrack_klass_map; + out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, &backtrack_klass_map); + if (unlikely (!c->serializer->check_success (!backtrack_klass_map.in_error ()))) + return_trace (false); + + // subset inputClassDef based on glyphs survived in Coverage subsetting + hb_map_t input_klass_map; + out->inputClassDef.serialize_subset (c, inputClassDef, this, &input_klass_map); + if (unlikely (!c->serializer->check_success (!input_klass_map.in_error ()))) + return_trace (false); + + hb_map_t lookahead_klass_map; + out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, &lookahead_klass_map); + if (unlikely (!c->serializer->check_success (!lookahead_klass_map.in_error ()))) + return_trace (false); + + unsigned non_zero_index = 0, index = 0; + bool ret = true; + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (const OffsetTo& _ : + hb_enumerate (ruleSet) + | hb_filter (input_klass_map, hb_first) + | hb_map (hb_second)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + if (o->serialize_subset (c, _, this, + lookup_map, + &backtrack_klass_map, + &input_klass_map, + &lookahead_klass_map)) + non_zero_index = index; + + index++; + } + + if (!ret) return_trace (ret); + + //prune empty trailing ruleSets + --index; + while (index > non_zero_index) + { + out->ruleSet.pop (); + index--; + } + + return_trace (bool (out->ruleSet)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2343,12 +2868,12 @@ struct ChainContextFormat3 { bool intersects (const hb_set_t *glyphs) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!(this+input[0]).intersects (glyphs)) return false; - const OffsetArrayOf &lookahead = StructAfter > (input); + const OffsetArrayOf &lookahead = StructAfter> (input); struct ChainContextClosureLookupContext lookup_context = { {intersects_coverage}, {this, this, this} @@ -2362,13 +2887,13 @@ struct ChainContextFormat3 void closure (hb_closure_context_t *c) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!(this+input[0]).intersects (c->glyphs)) return; - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextClosureLookupContext lookup_context = { {intersects_coverage}, {this, this, this} @@ -2381,14 +2906,24 @@ struct ChainContextFormat3 lookup_context); } + void closure_lookups (hb_closure_lookups_context_t *c) const + { + const OffsetArrayOf &input = StructAfter> (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + void collect_glyphs (hb_collect_glyphs_context_t *c) const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); - (this+input[0]).add_coverage (c->input); + (this+input[0]).collect_coverage (c->input); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextCollectGlyphsLookupContext lookup_context = { {collect_coverage}, {this, this, this} @@ -2403,38 +2938,36 @@ struct ChainContextFormat3 bool would_apply (hb_would_apply_context_t *c) const { - TRACE_WOULD_APPLY (this); - - const OffsetArrayOf &input = StructAfter > (backtrack); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &input = StructAfter> (backtrack); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextApplyLookupContext lookup_context = { {match_coverage}, {this, this, this} }; - return_trace (chain_context_would_apply_lookup (c, - backtrack.len, (const HBUINT16 *) backtrack.arrayZ, - input.len, (const HBUINT16 *) input.arrayZ + 1, - lookahead.len, (const HBUINT16 *) lookahead.arrayZ, - lookup.len, lookup.arrayZ, lookup_context)); + return chain_context_would_apply_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, lookup_context); } const Coverage &get_coverage () const { - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); return this+input[0]; } bool apply (hb_ot_apply_context_t *c) const { TRACE_APPLY (this); - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); if (likely (index == NOT_COVERED)) return_trace (false); - const OffsetArrayOf &lookahead = StructAfter > (input); - const ArrayOf &lookup = StructAfter > (lookahead); + const OffsetArrayOf &lookahead = StructAfter> (input); + const ArrayOf &lookup = StructAfter> (lookahead); struct ChainContextApplyLookupContext lookup_context = { {match_coverage}, {this, this, this} @@ -2446,23 +2979,63 @@ struct ChainContextFormat3 lookup.len, lookup.arrayZ, lookup_context)); } + template + bool serialize_coverage_offsets (hb_subset_context_t *c, Iterator it, const void* base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) return_trace (false); + + + it + | hb_apply (subset_offset_array (c, *out, base)) + ; + + return_trace (out->len); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + + if (!serialize_coverage_offsets (c, backtrack.iter (), this)) + return_trace (false); + + const OffsetArrayOf &input = StructAfter> (backtrack); + if (!serialize_coverage_offsets (c, input.iter (), this)) + return_trace (false); + + const OffsetArrayOf &lookahead = StructAfter> (input); + if (!serialize_coverage_offsets (c, lookahead.iter (), this)) + return_trace (false); + + const ArrayOf &lookupRecord = StructAfter> (lookahead); + HBUINT16 lookupCount; + lookupCount = lookupRecord.len; + if (!c->serializer->copy (lookupCount)) return_trace (false); + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups; + for (unsigned i = 0; i < (unsigned) lookupCount; i++) + if (!c->serializer->copy (lookupRecord[i], lookup_map)) return_trace (false); + + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); if (!backtrack.sanitize (c, this)) return_trace (false); - const OffsetArrayOf &input = StructAfter > (backtrack); + const OffsetArrayOf &input = StructAfter> (backtrack); if (!input.sanitize (c, this)) return_trace (false); if (!input.len) return_trace (false); /* To be consistent with Context. */ - const OffsetArrayOf &lookahead = StructAfter > (input); + const OffsetArrayOf &lookahead = StructAfter> (input); if (!lookahead.sanitize (c, this)) return_trace (false); - const ArrayOf &lookup = StructAfter > (lookahead); + const ArrayOf &lookup = StructAfter> (lookahead); return_trace (lookup.sanitize (c)); } @@ -2489,15 +3062,15 @@ struct ChainContextFormat3 struct ChainContext { - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (c->dispatch (u.format1)); - case 2: return_trace (c->dispatch (u.format2)); - case 3: return_trace (c->dispatch (u.format3)); + case 1: return_trace (c->dispatch (u.format1, hb_forward (ds)...)); + case 2: return_trace (c->dispatch (u.format2, hb_forward (ds)...)); + case 3: return_trace (c->dispatch (u.format3, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -2519,26 +3092,24 @@ struct ExtensionFormat1 template const X& get_subtable () const - { - unsigned int offset = extensionOffset; - if (unlikely (!offset)) return Null(typename T::SubTable); - return StructAtOffset (this, offset); - } + { return this + reinterpret_cast &> (extensionOffset); } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, format); if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ()); - return_trace (get_subtable ().dispatch (c, get_type ())); + return_trace (get_subtable ().dispatch (c, get_type (), hb_forward (ds)...)); } + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { dispatch (c); } + /* This is called from may_dispatch() above with hb_sanitize_context_t. */ bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - extensionOffset != 0 && extensionLookupType != T::SubTable::Extension); } @@ -2547,7 +3118,7 @@ struct ExtensionFormat1 HBUINT16 extensionLookupType; /* Lookup type of subtable referenced * by ExtensionOffset (i.e. the * extension subtable). */ - HBUINT32 extensionOffset; /* Offset to the extension subtable, + Offset32 extensionOffset; /* Offset to the extension subtable, * of lookup type subtable. */ public: DEFINE_SIZE_STATIC (8); @@ -2568,17 +3139,17 @@ struct Extension { switch (u.format) { case 1: return u.format1.template get_subtable (); - default:return Null(typename T::SubTable); + default:return Null (typename T::SubTable); } } - template - typename context_t::return_t dispatch (context_t *c) const + template + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { TRACE_DISPATCH (this, u.format); if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ()); switch (u.format) { - case 1: return_trace (u.format1.dispatch (c)); + case 1: return_trace (u.format1.dispatch (c, hb_forward (ds)...)); default:return_trace (c->default_return_value ()); } } @@ -2601,7 +3172,7 @@ struct hb_ot_layout_lookup_accelerator_t void init (const TLookup &lookup) { digest.init (); - lookup.add_coverage (&digest); + lookup.collect_coverage (&digest); subtables.init (); OT::hb_get_subtables_context_t c_get_subtables (subtables); @@ -2661,11 +3232,18 @@ struct GSUBGPOS bool find_variations_index (const int *coords, unsigned int num_coords, unsigned int *index) const - { return (version.to_int () >= 0x00010001u ? this+featureVars : Null(FeatureVariations)) - .find_index (coords, num_coords, index); } + { +#ifdef HB_NO_VAR + *index = FeatureVariations::NOT_FOUND_INDEX; + return false; +#endif + return (version.to_int () >= 0x00010001u ? this+featureVars : Null (FeatureVariations)) + .find_index (coords, num_coords, index); + } const Feature& get_feature_variation (unsigned int feature_index, unsigned int variations_index) const { +#ifndef HB_NO_VAR if (FeatureVariations::NOT_FOUND_INDEX != variations_index && version.to_int () >= 0x00010001u) { @@ -2674,32 +3252,90 @@ struct GSUBGPOS if (feature) return *feature; } +#endif return get_feature (feature_index); } + void feature_variation_collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { +#ifndef HB_NO_VAR + if (version.to_int () >= 0x00010001u) + (this+featureVars).collect_lookups (feature_indexes, lookup_indexes); +#endif + } + template - bool subset (hb_subset_context_t *c) const + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const { - TRACE_SUBSET (this); - struct GSUBGPOS *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); + hb_set_t visited_lookups, inactive_lookups; + OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups); - out->scriptList.serialize_subset (c, this+scriptList, out); - out->featureList.serialize_subset (c, this+featureList, out); + for (unsigned lookup_index : + hb_iter (lookup_indexes)) + reinterpret_cast (get_lookup (lookup_index)).closure_lookups (&c, lookup_index); - typedef OffsetListOf TLookupList; - /* TODO Use intersects() to count how many subtables survive? */ - CastR > (out->lookupList) - .serialize_subset (c, - this+CastR > (lookupList), - out); + hb_set_union (lookup_indexes, &visited_lookups); + hb_set_subtract (lookup_indexes, &inactive_lookups); + } + + template + bool subset (hb_subset_layout_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + typedef LookupOffsetList TLookupList; + reinterpret_cast &> (out->lookupList) + .serialize_subset (c->subset_context, + reinterpret_cast &> (lookupList), + this, + c); + + reinterpret_cast &> (out->featureList) + .serialize_subset (c->subset_context, + reinterpret_cast &> (featureList), + this, + c); + + out->scriptList.serialize_subset (c->subset_context, + scriptList, + this, + c); + +#ifndef HB_NO_VAR if (version.to_int () >= 0x00010001u) - out->featureVars.serialize_subset (c, this+featureVars, out); + { + bool ret = out->featureVars.serialize_subset (c->subset_context, featureVars, this, c); + if (!ret) + { + out->version.major = 1; + out->version.minor = 0; + } + } +#endif return_trace (true); } + void closure_features (const hb_map_t *lookup_indexes, /* IN */ + hb_set_t *feature_indexes /* OUT */) const + { + unsigned int feature_count = hb_min (get_feature_count (), (unsigned) HB_MAX_FEATURES); + for (unsigned i = 0; i < feature_count; i++) + { + const Feature& f = get_feature (i); + if ((!f.featureParams.is_null ()) || f.intersects_lookup_indexes (lookup_indexes)) + feature_indexes->add (i); + } +#ifndef HB_NO_VAR + if (version.to_int () >= 0x00010001u) + (this+featureVars).closure_features (lookup_indexes, feature_indexes); +#endif + } + unsigned int get_size () const { return min_size + @@ -2711,12 +3347,19 @@ struct GSUBGPOS { TRACE_SANITIZE (this); typedef OffsetListOf TLookupList; - return_trace (version.sanitize (c) && - likely (version.major == 1) && - scriptList.sanitize (c, this) && - featureList.sanitize (c, this) && - CastR > (lookupList).sanitize (c, this) && - (version.to_int () < 0x00010001u || featureVars.sanitize (c, this))); + if (unlikely (!(version.sanitize (c) && + likely (version.major == 1) && + scriptList.sanitize (c, this) && + featureList.sanitize (c, this) && + reinterpret_cast &> (lookupList).sanitize (c, this)))) + return_trace (false); + +#ifndef HB_NO_VAR + if (unlikely (!(version.to_int () < 0x00010001u || featureVars.sanitize (c, this)))) + return_trace (false); +#endif + + return_trace (true); } template @@ -2724,8 +3367,8 @@ struct GSUBGPOS { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); - if (unlikely (this->table->is_blacklisted (this->table.get_blob (), face))) + this->table = hb_sanitize_context_t ().reference_table (face); + if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) { hb_blob_destroy (this->table.get_blob ()); this->table = hb_blob_get_empty (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh index 7a543b0aa4059..f8cd27f922141 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout-jstf-table.hh @@ -136,7 +136,7 @@ struct JstfLangSys : OffsetListOf * ExtenderGlyphs -- Extender Glyph Table */ -typedef SortedArrayOf ExtenderGlyphs; +typedef SortedArrayOf ExtenderGlyphs; /* diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc index a8e579b447291..c326120628b3b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.cc @@ -28,6 +28,14 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_LAYOUT + +#ifdef HB_NO_OT_TAG +#error "Cannot compile hb-ot-layout.cc with HB_NO_OT_TAG." +#endif + #include "hb-open-type.hh" #include "hb-ot-layout.hh" #include "hb-ot-face.hh" @@ -35,7 +43,6 @@ #include "hb-map.hh" #include "hb-ot-kern-table.hh" -#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. #include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" @@ -44,9 +51,8 @@ #include "hb-ot-name-table.hh" #include "hb-ot-os2-table.hh" -#include "hb-aat-layout-lcar-table.hh" #include "hb-aat-layout-morx-table.hh" - +#include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise. /** * SECTION:hb-ot-layout @@ -62,18 +68,53 @@ * kern */ +#ifndef HB_NO_OT_KERN +/** + * hb_ot_layout_has_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any kerning data in the 'kern' table. + * Does NOT test for kerning lookups in the GPOS table. + * + * Return value: true if data found, false otherwise + * + **/ bool hb_ot_layout_has_kerning (hb_face_t *face) { return face->table.kern->has_data (); } +/** + * hb_ot_layout_has_machine_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any state-machine kerning in the 'kern' table. + * Does NOT examine the GPOS table. + * + * Return value: true if data found, false otherwise + * + **/ bool hb_ot_layout_has_machine_kerning (hb_face_t *face) { return face->table.kern->has_state_machine (); } +/** + * hb_ot_layout_has_cross_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face has any cross-stream kerning (i.e., kerns + * that make adjustments perpendicular to the direction of the text + * flow: Y adjustments in horizontal text or X adjustments in + * vertical text) in the 'kern' table. + * + * Does NOT examine the GPOS table. + * + * Return value: true is data found, false otherwise + * + **/ bool hb_ot_layout_has_cross_kerning (hb_face_t *face) { @@ -92,6 +133,7 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, kern.apply (&c); } +#endif /* @@ -99,10 +141,13 @@ hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, */ bool -OT::GDEF::is_blacklisted (hb_blob_t *blob, +OT::GDEF::is_blocklisted (hb_blob_t *blob, hb_face_t *face) const { - /* The ugly business of blacklisting individual fonts' tables happen here! +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif + /* The ugly business of blocklisting individual fonts' tables happen here! * See this thread for why we finally had to bend in and do this: * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html * @@ -119,84 +164,82 @@ OT::GDEF::is_blacklisted (hb_blob_t *blob, * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 */ -#define ENCODE(x,y,z) (((uint64_t) (x) << 48) | ((uint64_t) (y) << 24) | (uint64_t) (z)) - switch ENCODE(blob->length, - face->table.GSUB->table.get_length (), - face->table.GPOS->table.get_length ()) + switch HB_CODEPOINT_ENCODE3(blob->length, + face->table.GSUB->table.get_length (), + face->table.GPOS->table.get_length ()) { /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ - case ENCODE (442, 2874, 42038): + case HB_CODEPOINT_ENCODE3 (442, 2874, 42038): /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ - case ENCODE (430, 2874, 40662): + case HB_CODEPOINT_ENCODE3 (430, 2874, 40662): /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ - case ENCODE (442, 2874, 39116): + case HB_CODEPOINT_ENCODE3 (442, 2874, 39116): /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ - case ENCODE (430, 2874, 39374): + case HB_CODEPOINT_ENCODE3 (430, 2874, 39374): /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ - case ENCODE (490, 3046, 41638): + case HB_CODEPOINT_ENCODE3 (490, 3046, 41638): /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ - case ENCODE (478, 3046, 41902): + case HB_CODEPOINT_ENCODE3 (478, 3046, 41902): /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ - case ENCODE (898, 12554, 46470): + case HB_CODEPOINT_ENCODE3 (898, 12554, 46470): /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ - case ENCODE (910, 12566, 47732): + case HB_CODEPOINT_ENCODE3 (910, 12566, 47732): /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ - case ENCODE (928, 23298, 59332): + case HB_CODEPOINT_ENCODE3 (928, 23298, 59332): /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ - case ENCODE (940, 23310, 60732): + case HB_CODEPOINT_ENCODE3 (940, 23310, 60732): /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (964, 23836, 60072): + case HB_CODEPOINT_ENCODE3 (964, 23836, 60072): /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (976, 23832, 61456): + case HB_CODEPOINT_ENCODE3 (976, 23832, 61456): /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ - case ENCODE (994, 24474, 60336): + case HB_CODEPOINT_ENCODE3 (994, 24474, 60336): /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ - case ENCODE (1006, 24470, 61740): + case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740): /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (1006, 24576, 61346): + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346): /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ - case ENCODE (1018, 24572, 62828): + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828): /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ - case ENCODE (1006, 24576, 61352): + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352): /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ - case ENCODE (1018, 24572, 62834): + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834): /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ - case ENCODE (832, 7324, 47162): + case HB_CODEPOINT_ENCODE3 (832, 7324, 47162): /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ - case ENCODE (844, 7302, 45474): + case HB_CODEPOINT_ENCODE3 (844, 7302, 45474): /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ - case ENCODE (180, 13054, 7254): + case HB_CODEPOINT_ENCODE3 (180, 13054, 7254): /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ - case ENCODE (192, 12638, 7254): + case HB_CODEPOINT_ENCODE3 (192, 12638, 7254): /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ - case ENCODE (192, 12690, 7254): + case HB_CODEPOINT_ENCODE3 (192, 12690, 7254): /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ - case ENCODE (188, 248, 3852): + case HB_CODEPOINT_ENCODE3 (188, 248, 3852): /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ - case ENCODE (188, 264, 3426): + case HB_CODEPOINT_ENCODE3 (188, 264, 3426): /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */ - case ENCODE (1058, 47032, 11818): + case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818): /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/ - case ENCODE (1046, 47030, 12600): + case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600): /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */ - case ENCODE (1058, 71796, 16770): + case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770): /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */ - case ENCODE (1046, 71790, 17862): + case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862): /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ - case ENCODE (1046, 71788, 17112): + case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112): /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ - case ENCODE (1058, 71794, 17514): + case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514): /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ - case ENCODE (1330, 109904, 57938): + case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938): /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ - case ENCODE (1330, 109904, 58972): + case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972): /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ - case ENCODE (1004, 59092, 14836): + case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836): return true; -#undef ENCODE } return false; } @@ -219,6 +262,15 @@ _hb_ot_layout_set_glyph_props (hb_font_t *font, /* Public API */ +/** + * hb_ot_layout_has_glyph_classes: + * @face: #hb_face_t to work upon + * + * Tests whether a face has any glyph classes defined in its GDEF table. + * + * Return value: true if data found, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_glyph_classes (hb_face_t *face) { @@ -227,6 +279,13 @@ hb_ot_layout_has_glyph_classes (hb_face_t *face) /** * hb_ot_layout_get_glyph_class: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * + * Fetches the GDEF class of the requested glyph in the specified face. + * + * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code + * point in the GDEF table of the face. * * Since: 0.9.7 **/ @@ -239,6 +298,13 @@ hb_ot_layout_get_glyph_class (hb_face_t *face, /** * hb_ot_layout_get_glyphs_in_class: + * @face: The #hb_face_t to work on + * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve + * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested + * class. + * + * Retrieves the set of all glyphs from the face that belong to the requested + * glyph class in the face's GDEF table. * * Since: 0.9.7 **/ @@ -250,6 +316,22 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs); } +#ifndef HB_NO_LAYOUT_UNUSED +/** + * hb_ot_layout_get_attach_points: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first attachment point to retrieve + * @point_count: (inout) (allow-none): Input = the maximum number of attachment points to return; + * Output = the actual number of attachment points returned (may be zero) + * @point_array: (out) (array length=point_count): The array of attachment points found for the query + * + * Fetches a list of all attachment points for the specified glyph in the GDEF + * table of the face. The list returned will begin at the offset provided. + * + * Useful if the client program wishes to cache the list. + * + **/ unsigned int hb_ot_layout_get_attach_points (hb_face_t *face, hb_codepoint_t glyph, @@ -262,7 +344,20 @@ hb_ot_layout_get_attach_points (hb_face_t *face, point_count, point_array); } - +/** + * hb_ot_layout_get_ligature_carets: + * @font: The #hb_font_t to work on + * @direction: The #hb_direction_t text direction to use + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first caret position to retrieve + * @caret_count: (inout) (allow-none): Input = the maximum number of caret positions to return; + * Output = the actual number of caret positions returned (may be zero) + * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query + * + * Fetches a list of the caret positions defined for a ligature glyph in the GDEF + * table of the font. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_get_ligature_carets (hb_font_t *font, hb_direction_t direction, @@ -271,16 +366,9 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, unsigned int *caret_count /* IN/OUT */, hb_position_t *caret_array /* OUT */) { - unsigned int result_caret_count = 0; - unsigned int result = font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, &result_caret_count, caret_array); - if (result) - { - if (caret_count) *caret_count = result_caret_count; - } - else - result = font->face->table.lcar->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); - return result; + return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); } +#endif /* @@ -288,34 +376,22 @@ hb_ot_layout_get_ligature_carets (hb_font_t *font, */ bool -OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED, +OT::GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face) const { - /* Mac OS X prefers morx over GSUB. It also ships with various Indic fonts, - * all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken - * GSUB/GPOS tables. Some have GSUB with zero scripts, those are ignored by - * our morx/GSUB preference code. But if GSUB has non-zero scripts, we tend - * to prefer it over morx because we want to be consistent with other OpenType - * shapers. - * - * To work around broken Indic Mac system fonts, we ignore GSUB table if - * OS/2 VendorId is 'MUTF' and font has morx table as well. - * - * https://github.com/harfbuzz/harfbuzz/issues/1410 - * https://github.com/harfbuzz/harfbuzz/issues/1348 - * https://github.com/harfbuzz/harfbuzz/issues/1391 - */ - if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') && - face->table.morx->has_data ())) - return true; - +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif return false; } bool -OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED, +OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED, hb_face_t *face HB_UNUSED) const { +#ifdef HB_NO_OT_LAYOUT_BLACKLIST + return false; +#endif return false; } @@ -326,11 +402,24 @@ get_gsubgpos_table (hb_face_t *face, switch (table_tag) { case HB_OT_TAG_GSUB: return *face->table.GSUB->table; case HB_OT_TAG_GPOS: return *face->table.GPOS->table; - default: return Null(OT::GSUBGPOS); + default: return Null (OT::GSUBGPOS); } } +/** + * hb_ot_layout_table_get_script_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @start_offset: offset of the first script tag to retrieve + * @script_count: (inout) (allow-none): Input = the maximum number of script tags to return; + * Output = the actual number of script tags returned (may be zero) + * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query + * + * Fetches a list of all scripts enumerated in the specified face's GSUB table + * or GPOS table. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_table_get_script_tags (hb_face_t *face, hb_tag_t table_tag, @@ -345,11 +434,24 @@ hb_ot_layout_table_get_script_tags (hb_face_t *face, #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') +/** + * hb_ot_layout_table_find_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_tag: #hb_tag_t of the script tag requested + * @script_index: (out): The index of the requested script tag + * + * Fetches the index if a given script tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: true if the script is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_table_find_script (hb_face_t *face, hb_tag_t table_tag, hb_tag_t script_tag, - unsigned int *script_index) + unsigned int *script_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -375,20 +477,38 @@ hb_ot_layout_table_find_script (hb_face_t *face, return false; } +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_table_choose_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out): The index of the requested script tag + * @chosen_script: (out): #hb_tag_t of the script tag requested + * + * Deprecated since 2.0.0 + **/ hb_bool_t hb_ot_layout_table_choose_script (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *script_tags, - unsigned int *script_index, - hb_tag_t *chosen_script) + unsigned int *script_index /* OUT */, + hb_tag_t *chosen_script /* OUT */) { const hb_tag_t *t; for (t = script_tags; *t; t++); return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script); } +#endif /** * hb_ot_layout_table_select_script: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_count: Number of script tags in the array + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out): The index of the requested script + * @chosen_script: (out): #hb_tag_t of the requested script * * Since: 2.0.0 **/ @@ -442,6 +562,19 @@ hb_ot_layout_table_select_script (hb_face_t *face, return false; } + +/** + * hb_ot_layout_table_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table + * + * Fetches a list of all feature tags in the given face's GSUB or GPOS table. + * + **/ unsigned int hb_ot_layout_table_get_feature_tags (hb_face_t *face, hb_tag_t table_tag, @@ -454,11 +587,24 @@ hb_ot_layout_table_get_feature_tags (hb_face_t *face, return g.get_feature_tags (start_offset, feature_count, feature_tags); } + +/** + * hb_ot_layout_table_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_tag: The #hb_tag_t og the requested feature tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index for a given feature tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: true if the feature is found, false otherwise + **/ bool hb_ot_layout_table_find_feature (hb_face_t *face, hb_tag_t table_tag, hb_tag_t feature_tag, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -477,6 +623,20 @@ hb_ot_layout_table_find_feature (hb_face_t *face, } +/** + * hb_ot_layout_script_get_language_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @start_offset: offset of the first language tag to retrieve + * @language_count: (inout) (allow-none): Input = the maximum number of language tags to return; + * Output = the actual number of language tags returned (may be zero) + * @language_tags: (out) (array length=language_count): Array of language tags found in the table + * + * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath + * the specified script index. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_script_get_language_tags (hb_face_t *face, hb_tag_t table_tag, @@ -490,6 +650,24 @@ hb_ot_layout_script_get_language_tags (hb_face_t *face, return s.get_lang_sys_tags (start_offset, language_count, language_tags); } + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_script_find_language: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_tag: The #hb_tag_t of the requested language + * @language_index: The index of the requested language + * + * Fetches the index of a given language tag in the specified face's GSUB table + * or GPOS table, underneath the specified script tag. + * + * Return value: true if the language tag is found, false otherwise + * + * Since: ?? + * Deprecated: ?? + **/ hb_bool_t hb_ot_layout_script_find_language (hb_face_t *face, hb_tag_t table_tag, @@ -504,9 +682,22 @@ hb_ot_layout_script_find_language (hb_face_t *face, &language_tag, language_index); } +#endif + /** * hb_ot_layout_script_select_language: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_count: The number of languages in the specified script + * @language_tags: The array of language tags + * @language_index: (out): The index of the requested language + * + * Fetches the index of a given language tag in the specified face's GSUB table + * or GPOS table, underneath the specified script index. + * + * Return value: true if the language tag is found, false otherwise * * Since: 2.0.0 **/ @@ -536,12 +727,27 @@ hb_ot_layout_script_select_language (hb_face_t *face, return false; } + +/** + * hb_ot_layout_language_get_required_feature_index: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a requested feature in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_language_get_required_feature_index (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { return hb_ot_layout_language_get_required_feature (face, table_tag, @@ -551,8 +757,20 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, nullptr); } + /** * hb_ot_layout_language_get_required_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * @feature_tag: (out): The #hb_tag_t of the requested feature + * + * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise * * Since: 0.9.30 **/ @@ -561,8 +779,8 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index, - hb_tag_t *feature_tag) + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */) { const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); @@ -574,6 +792,22 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face, return l.has_required_feature (); } + +/** + * hb_ot_layout_language_get_feature_indexes: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output: the actual number of feature tags returned (may be zero) + * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + **/ unsigned int hb_ot_layout_language_get_feature_indexes (hb_face_t *face, hb_tag_t table_tag, @@ -589,6 +823,23 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face, return l.get_feature_indexes (start_offset, feature_count, feature_indexes); } + +/** + * hb_ot_layout_language_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_language_get_feature_tags (hb_face_t *face, hb_tag_t table_tag, @@ -614,13 +865,28 @@ hb_ot_layout_language_get_feature_tags (hb_face_t *face, } +/** + * hb_ot_layout_language_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_tag: #hb_tag_t of the feature tag requested + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a given feature tag in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. + * + * Return value: true if the feature is found, false otherwise + * + **/ hb_bool_t hb_ot_layout_language_find_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, hb_tag_t feature_tag, - unsigned int *feature_index) + unsigned int *feature_index /* OUT */) { static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); @@ -640,8 +906,20 @@ hb_ot_layout_language_find_feature (hb_face_t *face, return false; } + /** * hb_ot_layout_feature_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_index: The index of the requested feature + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table. The list returned will + * begin at the offset provided. * * Since: 0.9.7 **/ @@ -662,8 +940,14 @@ hb_ot_layout_feature_get_lookups (hb_face_t *face, lookup_indexes); } + /** * hb_ot_layout_table_get_lookup_count: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * + * Fetches the total number of lookups enumerated in the specified + * face's GSUB table or GPOS table. * * Since: 0.9.22 **/ @@ -677,12 +961,12 @@ hb_ot_layout_table_get_lookup_count (hb_face_t *face, struct hb_collect_features_context_t { - hb_collect_features_context_t (hb_face_t *face, - hb_tag_t table_tag, - hb_set_t *feature_indexes_) + hb_collect_features_context_t (hb_face_t *face, + hb_tag_t table_tag, + hb_set_t *feature_indexes_) : g (get_gsubgpos_table (face, table_tag)), feature_indexes (feature_indexes_), - script_count(0),langsys_count(0) {} + script_count (0),langsys_count (0), feature_index_count (0) {} bool visited (const OT::Script &s) { @@ -711,6 +995,12 @@ struct hb_collect_features_context_t return visited (l, visited_langsys); } + bool visited_feature_indices (unsigned count) + { + feature_index_count += count; + return feature_index_count > HB_MAX_FEATURE_INDICES; + } + private: template bool visited (const T &p, hb_set_t &visited_set) @@ -732,6 +1022,7 @@ struct hb_collect_features_context_t hb_set_t visited_langsys; unsigned int script_count; unsigned int langsys_count; + unsigned int feature_index_count; }; static void @@ -744,10 +1035,11 @@ langsys_collect_features (hb_collect_features_context_t *c, if (!features) { /* All features. */ - if (l.has_required_feature ()) + if (l.has_required_feature () && !c->visited_feature_indices (1)) c->feature_indexes->add (l.get_required_feature_index ()); - l.add_feature_indexes_to (c->feature_indexes); + if (!c->visited_feature_indices (l.featureIndex.len)) + l.add_feature_indexes_to (c->feature_indexes); } else { @@ -805,8 +1097,21 @@ script_collect_features (hb_collect_features_context_t *c, } } + /** * hb_ot_layout_collect_features: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @scripts: The array of scripts to collect features for + * @languages: The array of languages to collect features for + * @features: The array of features to collect + * @feature_indexes: (out): The array of feature indexes found for the query + * + * Fetches a list of all feature indexes in the specified face's GSUB table + * or GPOS table, underneath the specified scripts, languages, and features. + * If no list of scripts is provided, all scripts will be queried. If no list + * of languages is provided, all languages will be queried. If no list of + * features is provided, all features will be queried. * * Since: 1.8.5 **/ @@ -843,8 +1148,21 @@ hb_ot_layout_collect_features (hb_face_t *face, } } + /** * hb_ot_layout_collect_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @scripts: The array of scripts to collect lookups for + * @languages: The array of languages to collect lookups for + * @features: The array of features to collect lookups for + * @lookup_indexes: (out): The array of lookup indexes found for the query + * + * Fetches a list of all feature-lookup indexes in the specified face's GSUB + * table or GPOS table, underneath the specified scripts, languages, and + * features. If no list of scripts is provided, all scripts will be queried. + * If no list of languages is provided, all languages will be queried. If no + * list of features is provided, all features will be queried. * * Since: 0.9.8 **/ @@ -864,10 +1182,24 @@ hb_ot_layout_collect_lookups (hb_face_t *face, for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; hb_set_next (&feature_indexes, &feature_index);) g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes); + + g.feature_variation_collect_lookups (&feature_indexes, lookup_indexes); } + +#ifndef HB_NO_LAYOUT_COLLECT_GLYPHS /** * hb_ot_layout_lookup_collect_glyphs: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @lookup_index: The index of the feature lookup to query + * @glyphs_before: (out): Array of glyphs preceding the substitution range + * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup + * @glyphs_after: (out): Array of glyphs following the substitution range + * @glyphs_output: (out): Array of glyphs that would be the substitued output of the lookup + * + * Fetches a list of all glyphs affected by the specified lookup in the + * specified face's GSUB table or GPOS table. * * Since: 0.9.7 **/ @@ -902,10 +1234,24 @@ hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, } } } +#endif /* Variations support */ + +/** + * hb_ot_layout_table_find_feature_variations: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @coords: The variation coordinates to query + * @num_coords: The number of variation coorinates + * @variations_index: (out): The array of feature variations found for the query + * + * Fetches a list of feature variations in the specified face's GSUB table + * or GPOS table, at the specified variation coordinates. + * + **/ hb_bool_t hb_ot_layout_table_find_feature_variations (hb_face_t *face, hb_tag_t table_tag, @@ -918,6 +1264,23 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face, return g.find_variations_index (coords, num_coords, variations_index); } + +/** + * hb_ot_layout_feature_with_variations_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS + * @feature_index: The index of the feature to query + * @variations_index: The index of the feature variation to query + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table, enabled at the specified + * variations index. The list returned will begin at the offset provided. + * + **/ unsigned int hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, hb_tag_t table_tag, @@ -940,14 +1303,35 @@ hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, * OT::GSUB */ + +/** + * hb_ot_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any GSUB substitutions. + * + * Return value: true if data found, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face) { return face->table.GSUB->table->has_data (); } + /** * hb_ot_layout_lookup_would_substitute: + * @face: #hb_face_t to work upon + * @lookup_index: The index of the lookup to query + * @glyphs: The sequence of glyphs to query for substitution + * @glyphs_length: The length of the glyph sequence + * @zero_context: #hb_bool_t indicating whether substitutions should be context-free + * + * Tests whether a specified lookup in the specified face would + * trigger a substitution on the given glyph sequence. + * + * Return value: true if a substitution would be triggered, false otherwise * * Since: 0.9.7 **/ @@ -957,33 +1341,29 @@ hb_ot_layout_lookup_would_substitute (hb_face_t *face, const hb_codepoint_t *glyphs, unsigned int glyphs_length, hb_bool_t zero_context) -{ - return hb_ot_layout_lookup_would_substitute_fast (face, - lookup_index, - glyphs, glyphs_length, - zero_context); -} - -bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context) { if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false; OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); - return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]); } + +/** + * hb_ot_layout_substitute_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before substitution lookups are performed, to ensure that glyph + * class and other properties are set on the glyphs in the buffer. + * + **/ void hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) { -_hb_ot_layout_set_glyph_props (font, buffer); + _hb_ot_layout_set_glyph_props (font, buffer); } void @@ -1038,13 +1418,19 @@ hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer, /** * hb_ot_layout_lookup_substitute_closure: + * @face: #hb_face_t to work upon + * @lookup_index: index of the feature lookup to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup + * + * Compute the transitive closure of glyphs needed for a + * specified lookup. * * Since: 0.9.7 **/ void hb_ot_layout_lookup_substitute_closure (hb_face_t *face, unsigned int lookup_index, - hb_set_t *glyphs) + hb_set_t *glyphs /* OUT */) { hb_map_t done_lookups; OT::hb_closure_context_t c (face, glyphs, &done_lookups); @@ -1056,6 +1442,9 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t *face, /** * hb_ot_layout_lookups_substitute_closure: + * @face: #hb_face_t to work upon + * @lookups: The set of lookups to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups * * Compute the transitive closure of glyphs needed for all of the * provided lookups. @@ -1065,7 +1454,7 @@ hb_ot_layout_lookup_substitute_closure (hb_face_t *face, void hb_ot_layout_lookups_substitute_closure (hb_face_t *face, const hb_set_t *lookups, - hb_set_t *glyphs) + hb_set_t *glyphs /* OUT */) { hb_map_t done_lookups; OT::hb_closure_context_t c (face, glyphs, &done_lookups); @@ -1076,7 +1465,7 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face, do { glyphs_length = glyphs->get_population (); - if (lookups != nullptr) + if (lookups) { for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);) gsub.get_lookup (lookup_index).closure (&c, lookup_index); @@ -1094,32 +1483,85 @@ hb_ot_layout_lookups_substitute_closure (hb_face_t *face, * OT::GPOS */ + +/** + * hb_ot_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Return value: true if the face has GPOS data, false otherwise + * + **/ hb_bool_t hb_ot_layout_has_positioning (hb_face_t *face) { return face->table.GPOS->table->has_data (); } +/** + * hb_ot_layout_position_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before positioning lookups are performed, to ensure that glyph + * attachment types and glyph-attachment chains are set for the glyphs in the buffer. + * + **/ void hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_start (font, buffer); } + +/** + * hb_ot_layout_position_finish_advances: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph advances. + * + **/ void hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_finish_advances (font, buffer); } +/** + * hb_ot_layout_position_finish_offsets: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph offsets. + * + **/ void hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) { OT::GPOS::position_finish_offsets (font, buffer); } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS /** * hb_ot_layout_get_size_params: + * @face: #hb_face_t to work upon + * @design_size: (out): The design size of the face + * @subfamily_id: (out): The identifier of the face within the font subfamily + * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily + * @range_start: (out): The minimum size of the recommended size range for the face + * @range_end: (out): The maximum size of the recommended size range for the face + * + * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that + * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id) + * as used here are defined as pertaining only to fonts within a font family that differ + * specifically in their respective size ranges; other ways to differentiate fonts within + * a subfamily are not covered by the `size` feature. + * + * For more information on this distinction, see the [`size` feature documentation]( + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size). + * + * Return value: true if data found, false otherwise * * Since: 0.9.10 **/ @@ -1163,7 +1605,6 @@ hb_ot_layout_get_size_params (hb_face_t *face, return false; } - /** * hb_ot_layout_feature_get_name_ids: * @face: #hb_face_t to work upon @@ -1238,24 +1679,20 @@ hb_ot_layout_feature_get_name_ids (hb_face_t *face, if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; return false; } - /** * hb_ot_layout_feature_get_characters: * @face: #hb_face_t to work upon * @table_tag: table tag to query, "GSUB" or "GPOS". * @feature_index: index of feature to query. - * @start_offset: In case the resulting char_count was equal to its input value, there - * is a chance there were more characters on the tag so this API can be - * called with an offset till resulting char_count gets to a number - * lower than input buffer (or consider using just a bigger buffer for - * one shot copying). - * @char_count: (inout) (allow-none): The count of characters for which this feature - * provides glyph variants. (May be zero.) - * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. The Unicode codepoints - * of the characters for which this feature provides glyph variants. - * - * Fetches characters listed by designer under feature parameters for "Character - * Variant" ("cvXX") features. + * @start_offset: offset of the first character to retrieve + * @char_count: (inout) (allow-none): Input = the maximum number of characters to return; + * Output = the actual number of characters returned (may be zero) + * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. + * The Unicode codepoints of the characters for which this feature provides + * glyph variants. + * + * Fetches a list of the characters defined as having a variant under the specified + * "Character Variant" ("cvXX") feature tag. * * Return value: Number of total sample characters in the cvXX feature. * @@ -1270,25 +1707,12 @@ hb_ot_layout_feature_get_characters (hb_face_t *face, hb_codepoint_t *characters /* OUT. May be NULL */) { const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); - - hb_tag_t feature_tag = g.get_feature_tag (feature_index); - const OT::Feature &f = g.get_feature (feature_index); - - const OT::FeatureParams &feature_params = f.get_feature_params (); - - const OT::FeatureParamsCharacterVariants& cv_params = - feature_params.get_character_variants_params(feature_tag); - - unsigned int len = 0; - if (char_count && characters && start_offset < cv_params.characters.len) - { - len = MIN (cv_params.characters.len - start_offset, *char_count); - for (unsigned int i = 0; i < len; ++i) - characters[i] = cv_params.characters[start_offset + i]; - } - if (char_count) *char_count = len; - return cv_params.characters.len; + return g.get_feature (feature_index) + .get_feature_params () + .get_character_variants_params(g.get_feature_tag (feature_index)) + .get_characters (start_offset, char_count, characters); } +#endif /* @@ -1455,13 +1879,17 @@ inline void hb_ot_map_t::apply (const Proxy &proxy, void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const { GSUBProxy proxy (font->face); + if (!buffer->message (font, "start table GSUB")) return; apply (proxy, plan, font, buffer); + (void)buffer->message (font, "end table GSUB"); } void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const { GPOSProxy proxy (font->face); + if (!buffer->message (font, "start table GPOS")) return; apply (proxy, plan, font, buffer); + (void)buffer->message (font, "end table GPOS"); } void @@ -1472,60 +1900,94 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, apply_string (c, lookup, accel); } -#if 0 -static const OT::BASE& _get_base (hb_face_t *face) -{ - return *face->table.BASE; -} - +#ifndef HB_NO_BASE +/** + * hb_ot_layout_get_baseline: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag. + * @coord: (out): baseline value if found. + * + * Fetches a baseline value from the face. + * + * Return value: if found baseline value in the font. + * + * Since: 2.6.0 + **/ hb_bool_t -hb_ot_layout_get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *coord /* OUT. May be NULL. */) +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */) { - const OT::BASE &base = _get_base (font->face); - bool result = base.get_baseline (font, baseline, direction, script_tag, - language_tag, coord); + bool result = font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord); - /* TODO: Simulate https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags#ideographic-em-box */ - if (!result && coord) *coord = 0; - - if (coord) *coord = font->em_scale_dir (*coord, direction); + if (result && coord) + *coord = HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (*coord) : font->em_scale_x (*coord); return result; } +#endif -/* To be moved to public header */ -/* - * BASE - */ + +struct hb_get_glyph_alternates_dispatch_t : + hb_dispatch_context_t +{ + static return_t default_return_value () { return 0; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + hb_face_t *face; + + hb_get_glyph_alternates_dispatch_t (hb_face_t *face) : + face (face) {} + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.get_glyph_alternates (hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) +}; /** - * hb_ot_layout_baseline_t: + * hb_ot_layout_lookup_get_glyph_alternates: + * @face: a face. + * @lookup_index: index of the feature lookup to query. + * @glyph: a glyph id. + * @start_offset: starting offset. + * @alternate_count: (inout) (allow-none): Input = the maximum number of alternate glyphs to return; + * Output = the actual number of alternate glyphs returned (may be zero). + * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. + * Alternate glyphs associated with the glyph id. * - * https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags + * Fetches alternates of a glyph from a given GSUB lookup index. * - * Since: DONTREPLACEME - */ -typedef enum { - HB_OT_LAYOUT_BASELINE_HANG = HB_TAG('h','a','n','g'), - HB_OT_LAYOUT_BASELINE_ICFB = HB_TAG('i','c','f','b'), - HB_OT_LAYOUT_BASELINE_ICFT = HB_TAG('i','c','f','t'), - HB_OT_LAYOUT_BASELINE_IDEO = HB_TAG('i','d','e','o'), - HB_OT_LAYOUT_BASELINE_IDTB = HB_TAG('i','d','t','b'), - HB_OT_LAYOUT_BASELINE_MATH = HB_TAG('m','a','t','h'), - HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n') -} hb_ot_layout_baseline_t; - -HB_EXTERN hb_bool_t -hb_ot_layout_get_baseline (hb_font_t *font, - hb_ot_layout_baseline_t baseline, - hb_direction_t direction, - hb_tag_t script_tag, - hb_tag_t language_tag, - hb_position_t *coord /* OUT. May be NULL. */); + * Return value: total number of alternates found in the specific lookup index for the given glyph id. + * + * Since: 2.6.8 + **/ +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) +{ + hb_get_glyph_alternates_dispatch_t c (face); + const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); + auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs); + if (!ret && alternate_count) *alternate_count = 0; + return ret; +} #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h index 0419f60391698..cdb1955317c60 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.h @@ -93,6 +93,17 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, HB_EXTERN hb_bool_t hb_ot_layout_has_glyph_classes (hb_face_t *face); +/** + * hb_ot_layout_glyph_class_t: + * @HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: Glyphs not matching the other classifications + * @HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: Spacing, single characters, capable of accepting marks + * @HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: Glyphs that represent ligation of multiple characters + * @HB_OT_LAYOUT_GLYPH_CLASS_MARK: Non-spacing, combining glyphs that represent marks + * @HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: Spacing glyphs that represent part of a single character + * + * The GDEF classes defined for glyphs. + * + **/ typedef enum { HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0, HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 1, @@ -110,7 +121,6 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face, hb_ot_layout_glyph_class_t klass, hb_set_t *glyphs /* OUT */); - /* Not that useful. Provides list of attach points for a glyph that a * client may want to cache */ HB_EXTERN unsigned int @@ -150,7 +160,7 @@ HB_EXTERN hb_bool_t hb_ot_layout_table_find_script (hb_face_t *face, hb_tag_t table_tag, hb_tag_t script_tag, - unsigned int *script_index); + unsigned int *script_index /* OUT */); HB_EXTERN hb_bool_t hb_ot_layout_table_select_script (hb_face_t *face, @@ -188,15 +198,15 @@ hb_ot_layout_language_get_required_feature_index (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index); + unsigned int *feature_index /* OUT */); HB_EXTERN hb_bool_t hb_ot_layout_language_get_required_feature (hb_face_t *face, hb_tag_t table_tag, unsigned int script_index, unsigned int language_index, - unsigned int *feature_index, - hb_tag_t *feature_tag); + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */); HB_EXTERN unsigned int hb_ot_layout_language_get_feature_indexes (hb_face_t *face, @@ -222,7 +232,7 @@ hb_ot_layout_language_find_feature (hb_face_t *face, unsigned int script_index, unsigned int language_index, hb_tag_t feature_tag, - unsigned int *feature_index); + unsigned int *feature_index /* OUT */); HB_EXTERN unsigned int hb_ot_layout_feature_get_lookups (hb_face_t *face, @@ -313,6 +323,14 @@ hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, HB_EXTERN hb_bool_t hb_ot_layout_has_substitution (hb_face_t *face); +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT */, + hb_codepoint_t *alternate_glyphs /* OUT */); + HB_EXTERN hb_bool_t hb_ot_layout_lookup_would_substitute (hb_face_t *face, unsigned int lookup_index, @@ -391,6 +409,54 @@ hb_ot_layout_feature_get_characters (hb_face_t *face, unsigned int *char_count /* IN/OUT. May be NULL */, hb_codepoint_t *characters /* OUT. May be NULL */); +/* + * BASE + */ + +/** + * hb_ot_layout_baseline_tag_t: + * @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek. + * In vertical writing mode, the alphabetic baseline for characters rotated 90 degrees clockwise. + * (This would not apply to alphabetic characters that remain upright in vertical writing mode, since these + * characters are not rotated.) + * @HB_OT_LAYOUT_BASELINE_TAG_HANGING: The hanging baseline. In horizontal direction, this is the horizontal + * line from which syllables seem, to hang in Tibetan and other similar scripts. In vertical writing mode, + * for Tibetan (or some other similar script) characters rotated 90 degrees clockwise. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT: Ideographic character face bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT: Ideographic character face top or right edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT: Ideographic em-box bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT: Ideographic em-box top or right edge baseline, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_MATH: The baseline about which mathematical characters are centered. + * In vertical writing mode when mathematical characters rotated 90 degrees clockwise, are centered. + * + * Baseline tags from https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags + * + * Since: 2.6.0 + */ +typedef enum { + HB_OT_LAYOUT_BASELINE_TAG_ROMAN = HB_TAG ('r','o','m','n'), + HB_OT_LAYOUT_BASELINE_TAG_HANGING = HB_TAG ('h','a','n','g'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT = HB_TAG ('i','c','f','b'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT = HB_TAG ('i','c','f','t'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT = HB_TAG ('i','d','e','o'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT = HB_TAG ('i','d','t','p'), + HB_OT_LAYOUT_BASELINE_TAG_MATH = HB_TAG ('m','a','t','h'), + + _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_layout_baseline_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */); + HB_END_DECLS #endif /* HB_OT_LAYOUT_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh index 9025bc5a8a0e9..07447f913c83d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-layout.hh @@ -96,13 +96,6 @@ HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t); * GSUB/GPOS */ -HB_INTERNAL bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context); - /* Should be called before all the substitute_lookup's are done. */ HB_INTERNAL void @@ -175,6 +168,17 @@ _hb_next_syllable (hb_buffer_t *buffer, unsigned int start) return start; } +static inline void +_hb_clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + info[i].syllable() = 0; +} + /* unicode_props */ @@ -215,7 +219,7 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) unsigned int gen_cat = (unsigned int) unicode->general_category (u); unsigned int props = gen_cat; - if (u >= 0x80) + if (u >= 0x80u) { buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; @@ -232,10 +236,10 @@ _hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) * FVSes are GC=Mn, we have use a separate bit to remember them. * Fixes: * https://github.com/harfbuzz/harfbuzz/issues/234 */ - else if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN; + else if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN; /* TAG characters need similar treatment. Fixes: * https://github.com/harfbuzz/harfbuzz/issues/463 */ - else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; + else if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; /* COMBINING GRAPHEME JOINER should not be skipped; at least some times. * https://github.com/harfbuzz/harfbuzz/issues/554 */ else if (unlikely (u == 0x034Fu)) @@ -558,6 +562,17 @@ _hb_glyph_info_clear_substituted (hb_glyph_info_t *info) info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); } +static inline void +_hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_clear_substituted (&info[i]); +} + /* Allocation / deallocation. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc index a428b4bdbe30d..d256a04ca5a40 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.cc @@ -26,6 +26,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-map.hh" #include "hb-ot-shape.hh" #include "hb-ot-layout.hh" @@ -34,7 +38,7 @@ void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const { for (unsigned int i = 0; i < lookups[table_index].length; i++) - hb_set_add (lookups_out, lookups[table_index][i].index); + lookups_out->add (lookups[table_index][i].index); } @@ -187,13 +191,14 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, feature_infos[j].max_value = feature_infos[i].max_value; feature_infos[j].default_value = feature_infos[i].default_value; } else { - feature_infos[j].flags &= ~F_GLOBAL; - feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value); + if (feature_infos[j].flags & F_GLOBAL) + feature_infos[j].flags ^= F_GLOBAL; + feature_infos[j].max_value = hb_max (feature_infos[j].max_value, feature_infos[i].max_value); /* Inherit default_value from j */ } feature_infos[j].flags |= (feature_infos[i].flags & F_HAS_FALLBACK); - feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]); - feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]); + feature_infos[j].stage[0] = hb_min (feature_infos[j].stage[0], feature_infos[i].stage[0]); + feature_infos[j].stage[1] = hb_min (feature_infos[j].stage[1], feature_infos[i].stage[1]); } feature_infos.shrink (j + 1); } @@ -213,34 +218,34 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, bits_needed = 0; else /* Limit bits per feature. */ - bits_needed = MIN(HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); + bits_needed = hb_min (HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); if (!info->max_value || next_bit + bits_needed > 8 * sizeof (hb_mask_t)) continue; /* Feature disabled, or not enough bits. */ - hb_bool_t found = false; + bool found = false; unsigned int feature_index[2]; for (unsigned int table_index = 0; table_index < 2; table_index++) { if (required_feature_tag[table_index] == info->tag) required_feature_stage[table_index] = info->stage[table_index]; - found |= hb_ot_layout_language_find_feature (face, - table_tags[table_index], - script_index[table_index], - language_index[table_index], - info->tag, - &feature_index[table_index]); + found |= (bool) hb_ot_layout_language_find_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + info->tag, + &feature_index[table_index]); } if (!found && (info->flags & F_GLOBAL_SEARCH)) { for (unsigned int table_index = 0; table_index < 2; table_index++) { - found |= hb_ot_layout_table_find_feature (face, - table_tags[table_index], - info->tag, - &feature_index[table_index]); + found |= (bool) hb_ot_layout_table_find_feature (face, + table_tags[table_index], + info->tag, + &feature_index[table_index]); } } if (!found && !(info->flags & F_HAS_FALLBACK)) @@ -332,3 +337,6 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m, } } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh index 7505adfe12742..5c23eb57814b9 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-map.hh @@ -68,7 +68,7 @@ struct hb_ot_map_t unsigned short random : 1; hb_mask_t mask; - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const lookup_map_t *a = (const lookup_map_t *) pa; const lookup_map_t *b = (const lookup_map_t *) pb; @@ -134,13 +134,13 @@ struct hb_ot_map_t unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const { const feature_map_t *map = features.bsearch (feature_tag); - return map ? map->stage[table_index] : (unsigned int) -1; + return map ? map->stage[table_index] : UINT_MAX; } void get_stage_lookups (unsigned int table_index, unsigned int stage, const struct lookup_map_t **plookups, unsigned int *lookup_count) const { - if (unlikely (stage == (unsigned int) -1)) { + if (unlikely (stage == UINT_MAX)) { *plookups = nullptr; *lookup_count = 0; return; @@ -154,8 +154,8 @@ struct hb_ot_map_t HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const; template - HB_INTERNAL inline void apply (const Proxy &proxy, - const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void apply (const Proxy &proxy, + const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; @@ -167,7 +167,7 @@ struct hb_ot_map_t hb_mask_t global_mask; - hb_vector_t features; + hb_sorted_vector_t features; hb_vector_t lookups[2]; /* GSUB/GPOS */ hb_vector_t stages[2]; /* GSUB/GPOS */ }; @@ -213,8 +213,8 @@ struct hb_ot_map_builder_t { add_feature (feat.tag, feat.flags); } void enable_feature (hb_tag_t tag, - hb_ot_map_feature_flags_t flags=F_NONE, - unsigned int value=1) + hb_ot_map_feature_flags_t flags=F_NONE, + unsigned int value=1) { add_feature (tag, F_GLOBAL | flags, value); } void disable_feature (hb_tag_t tag) @@ -247,7 +247,7 @@ struct hb_ot_map_builder_t unsigned int default_value; /* for non-global features, what should the unset glyphs take */ unsigned int stage[2]; /* GSUB/GPOS */ - static int cmp (const void *pa, const void *pb) + HB_INTERNAL static int cmp (const void *pa, const void *pb) { const feature_info_t *a = (const feature_info_t *) pa; const feature_info_t *b = (const feature_info_t *) pb; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh index 07112e0f420de..6cacf8eadb7bf 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math-table.hh @@ -48,7 +48,7 @@ struct MathValueRecord } protected: - HBINT16 value; /* The X or Y value in design units */ + HBINT16 value; /* The X or Y value in design units */ OffsetTo deviceTable; /* Offset to the device table - from the * beginning of parent table. May be NULL. * Suggested format for device table is 1. */ @@ -78,7 +78,7 @@ struct MathConstants } hb_position_t get_value (hb_ot_math_constant_t constant, - hb_font_t *font) const + hb_font_t *font) const { switch (constant) { @@ -279,14 +279,15 @@ struct MathKern protected: HBUINT16 heightCount; UnsizedArrayOf - mathValueRecordsZ; /* Array of correction heights at - * which the kern value changes. - * Sorted by the height value in - * design units (heightCount entries), - * Followed by: - * Array of kern values corresponding - * to heights. (heightCount+1 entries). - */ + mathValueRecordsZ; + /* Array of correction heights at + * which the kern value changes. + * Sorted by the height value in + * design units (heightCount entries), + * Followed by: + * Array of kern values corresponding + * to heights. (heightCount+1 entries). + */ public: DEFINE_SIZE_ARRAY (2, mathValueRecordsZ); @@ -345,15 +346,18 @@ struct MathKernInfo } protected: - OffsetTo mathKernCoverage; /* Offset to Coverage table - - * from the beginning of the - * MathKernInfo table. */ - ArrayOf mathKernInfoRecords; /* Array of - * MathKernInfoRecords, - * per-glyph information for - * mathematical positioning - * of subscripts and - * superscripts. */ + OffsetTo + mathKernCoverage; + /* Offset to Coverage table - + * from the beginning of the + * MathKernInfo table. */ + ArrayOf + mathKernInfoRecords; + /* Array of MathKernInfoRecords, + * per-glyph information for + * mathematical positioning + * of subscripts and + * superscripts. */ public: DEFINE_SIZE_ARRAY (4, mathKernInfoRecords); @@ -423,7 +427,7 @@ struct MathGlyphVariantRecord } protected: - GlyphID variantGlyph; /* Glyph ID for the variant. */ + HBGlyphID variantGlyph; /* Glyph ID for the variant. */ HBUINT16 advanceMeasurement; /* Advance width/height, in design units, of the * variant, in the direction of requested * glyph extension. */ @@ -453,16 +457,16 @@ struct MathGlyphPartRecord } void extract (hb_ot_math_glyph_part_t &out, - int scale, + int64_t mult, hb_font_t *font) const { out.glyph = glyph; - out.start_connector_length = font->em_scale (startConnectorLength, scale); - out.end_connector_length = font->em_scale (endConnectorLength, scale); - out.full_advance = font->em_scale (fullAdvance, scale); + out.start_connector_length = font->em_mult (startConnectorLength, mult); + out.end_connector_length = font->em_mult (endConnectorLength, mult); + out.full_advance = font->em_mult (fullAdvance, mult); - static_assert ((unsigned int) HB_MATH_GLYPH_PART_FLAG_EXTENDER == + static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER == (unsigned int) PartFlags::Extender, ""); out.flags = (hb_ot_math_glyph_part_flags_t) @@ -471,19 +475,21 @@ struct MathGlyphPartRecord } protected: - GlyphID glyph; /* Glyph ID for the part. */ - HBUINT16 startConnectorLength; /* Advance width/ height of the straight bar - * connector material, in design units, is at - * the beginning of the glyph, in the - * direction of the extension. */ - HBUINT16 endConnectorLength; /* Advance width/ height of the straight bar - * connector material, in design units, is at - * the end of the glyph, in the direction of - * the extension. */ - HBUINT16 fullAdvance; /* Full advance width/height for this part, - * in the direction of the extension. - * In design units. */ - PartFlags partFlags; /* Part qualifiers. */ + HBGlyphID glyph; /* Glyph ID for the part. */ + HBUINT16 startConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the beginning of the glyph, in the + * direction of the extension. */ + HBUINT16 endConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the end of the glyph, in the direction of + * the extension. */ + HBUINT16 fullAdvance; /* Full advance width/height for this part, + * in the direction of the extension. + * In design units. */ + PartFlags partFlags; /* Part qualifiers. */ public: DEFINE_SIZE_STATIC (10); @@ -508,11 +514,10 @@ struct MathGlyphAssembly { if (parts_count) { - int scale = font->dir_scale (direction); - hb_array_t arr = partRecords.sub_array (start_offset, parts_count); - unsigned int count = arr.length; - for (unsigned int i = 0; i < count; i++) - arr[i].extract (parts[i], scale, font); + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (partRecords.sub_array (start_offset, parts_count), + hb_array (parts, *parts_count))) + _.first.extract (_.second, mult, font); } if (italics_correction) @@ -522,12 +527,15 @@ struct MathGlyphAssembly } protected: - MathValueRecord italicsCorrection; /* Italics correction of this - * MathGlyphAssembly. Should not - * depend on the assembly size. */ - ArrayOf partRecords; /* Array of part records, from - * left to right and bottom to - * top. */ + MathValueRecord + italicsCorrection; + /* Italics correction of this + * MathGlyphAssembly. Should not + * depend on the assembly size. */ + ArrayOf + partRecords; /* Array of part records, from + * left to right and bottom to + * top. */ public: DEFINE_SIZE_ARRAY (6, partRecords); @@ -553,14 +561,10 @@ struct MathGlyphConstruction { if (variants_count) { - int scale = font->dir_scale (direction); - hb_array_t arr = mathGlyphVariantRecord.sub_array (start_offset, variants_count); - unsigned int count = arr.length; - for (unsigned int i = 0; i < count; i++) - { - variants[i].glyph = arr[i].variantGlyph; - variants[i].advance = font->em_scale (arr[i].advanceMeasurement, scale); - } + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (mathGlyphVariantRecord.sub_array (start_offset, variants_count), + hb_array (variants, *variants_count))) + _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)}; } return mathGlyphVariantRecord.len; } @@ -612,12 +616,12 @@ struct MathVariants .get_variants (direction, font, start_offset, variants_count, variants); } unsigned int get_glyph_parts (hb_codepoint_t glyph, - hb_direction_t direction, - hb_font_t *font, - unsigned int start_offset, - unsigned int *parts_count, /* IN/OUT */ - hb_ot_math_glyph_part_t *parts /* OUT */, - hb_position_t *italics_correction /* OUT */) const + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const { return get_glyph_construction (glyph, direction, font) .get_assembly () .get_parts (direction, font, @@ -645,26 +649,29 @@ struct MathVariants } protected: - HBUINT16 minConnectorOverlap; /* Minimum overlap of connecting - * glyphs during glyph construction, - * in design units. */ - OffsetTo vertGlyphCoverage; /* Offset to Coverage table - - * from the beginning of MathVariants - * table. */ - OffsetTo horizGlyphCoverage; /* Offset to Coverage table - - * from the beginning of MathVariants - * table. */ - HBUINT16 vertGlyphCount; /* Number of glyphs for which - * information is provided for - * vertically growing variants. */ - HBUINT16 horizGlyphCount; /* Number of glyphs for which - * information is provided for - * horizontally growing variants. */ + HBUINT16 minConnectorOverlap; + /* Minimum overlap of connecting + * glyphs during glyph construction, + * in design units. */ + OffsetTo vertGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + OffsetTo horizGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + HBUINT16 vertGlyphCount; /* Number of glyphs for which + * information is provided for + * vertically growing variants. */ + HBUINT16 horizGlyphCount;/* Number of glyphs for which + * information is provided for + * horizontally growing variants. */ /* Array of offsets to MathGlyphConstruction tables - from the beginning of the MathVariants table, for shapes growing in vertical/horizontal direction. */ - UnsizedArrayOf > + UnsizedArrayOf> glyphConstruction; public: @@ -694,7 +701,7 @@ struct MATH } hb_position_t get_constant (hb_ot_math_constant_t constant, - hb_font_t *font) const + hb_font_t *font) const { return (this+mathConstants).get_value (constant, font); } const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; } @@ -702,11 +709,14 @@ struct MATH const MathVariants &get_variants () const { return this+mathVariants; } protected: - FixedVersion<>version; /* Version of the MATH table - * initially set to 0x00010000u */ - OffsetTo mathConstants;/* MathConstants table */ - OffsetTo mathGlyphInfo;/* MathGlyphInfo table */ - OffsetTo mathVariants; /* MathVariants table */ + FixedVersion<>version; /* Version of the MATH table + * initially set to 0x00010000u */ + OffsetTo + mathConstants; /* MathConstants table */ + OffsetTo + mathGlyphInfo; /* MathGlyphInfo table */ + OffsetTo + mathVariants; /* MathVariants table */ public: DEFINE_SIZE_STATIC (10); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc index e10cf3844ba87..94a5720187198 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.cc @@ -24,9 +24,10 @@ * Igalia Author(s): Frédéric Wang */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_MATH -#include "hb-ot-face.hh" #include "hb-ot-math-table.hh" @@ -37,6 +38,11 @@ * @include: hb-ot.h * * Functions for fetching mathematics layout data from OpenType fonts. + * + * HarfBuzz itself does not implement a math layout solution. The + * functions and types provided can be used by client programs to access + * the font data necessary for typesetting OpenType Math layout. + * **/ @@ -48,10 +54,9 @@ * hb_ot_math_has_data: * @face: #hb_face_t to test * - * This function allows to verify the presence of an OpenType MATH table on the - * face. + * Tests whether a face has a `MATH` table. * - * Return value: true if face has a MATH table, false otherwise + * Return value: true if the table is found, false otherwise * * Since: 1.3.3 **/ @@ -63,16 +68,18 @@ hb_ot_math_has_data (hb_face_t *face) /** * hb_ot_math_get_constant: - * @font: #hb_font_t from which to retrieve the value + * @font: #hb_font_t to work upon * @constant: #hb_ot_math_constant_t the constant to retrieve * - * This function returns the requested math constants as a #hb_position_t. - * If the request constant is HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, - * HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or - * HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN then the return value is - * actually an integer between 0 and 100 representing that percentage. + * Fetches the specified math constant. For most constants, the value returned + * is an #hb_position_t. + * + * However, if the requested constant is #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, + * #HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or + * #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, then the return value is + * an integer between 0 and 100 representing that percentage. * - * Return value: the requested constant or 0 + * Return value: the requested constant or zero * * Since: 1.3.3 **/ @@ -85,10 +92,13 @@ hb_ot_math_get_constant (hb_font_t *font, /** * hb_ot_math_get_glyph_italics_correction: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value * - * Return value: the italics correction of the glyph or 0 + * Fetches an italics-correction value (if one exists) for the specified + * glyph index. + * + * Return value: the italics correction of the glyph or zero * * Since: 1.3.3 **/ @@ -101,10 +111,20 @@ hb_ot_math_get_glyph_italics_correction (hb_font_t *font, /** * hb_ot_math_get_glyph_top_accent_attachment: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * + * Fetches a top-accent-attachment value (if one exists) for the specified + * glyph index. + * + * For any glyph that does not have a top-accent-attachment value - that is, + * a glyph not covered by the `MathTopAccentAttachment` table (or, when + * @font has no `MathTopAccentAttachment` table or no `MATH` table, any + * glyph) - the function synthesizes a value, returning the position at + * one-half the glyph's advance width. * - * Return value: the top accent attachment of the glyph or 0 + * Return value: the top accent attachment of the glyph or 0.5 * the advance + * width of @glyph * * Since: 1.3.3 **/ @@ -117,8 +137,10 @@ hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, /** * hb_ot_math_is_glyph_extended_shape: - * @face: a #hb_face_t to test - * @glyph: a glyph index to test + * @face: #hb_face_t to work upon + * @glyph: The glyph index to test + * + * Tests whether the given glyph index is an extended shape in the face. * * Return value: true if the glyph is an extended shape, false otherwise * @@ -133,18 +155,20 @@ hb_ot_math_is_glyph_extended_shape (hb_face_t *face, /** * hb_ot_math_get_glyph_kerning: - * @font: #hb_font_t from which to retrieve the value - * @glyph: glyph index from which to retrieve the value - * @kern: the #hb_ot_math_kern_t from which to retrieve the value + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * @kern: The #hb_ot_math_kern_t from which to retrieve the value * @correction_height: the correction height to use to determine the kerning. * - * This function tries to retrieve the MathKern table for the specified font, - * glyph and #hb_ot_math_kern_t. Then it browses the list of heights from the - * MathKern table to find one value that is greater or equal to specified - * correction_height. If one is found the corresponding value from the list of - * kerns is returned and otherwise the last kern value is returned. + * Fetches the math kerning (cut-ins) value for the specified font, glyph index, and + * @kern. + * + * If the MathKern table is found, the function examines it to find a height + * value that is greater or equal to @correction_height. If such a height + * value is found, corresponding kerning value from the table is returned. If + * no such height value is found, the last kerning value is returned. * - * Return value: requested kerning or 0 + * Return value: requested kerning value or zero * * Since: 1.3.3 **/ @@ -162,20 +186,24 @@ hb_ot_math_get_glyph_kerning (hb_font_t *font, /** * hb_ot_math_get_glyph_variants: - * @font: #hb_font_t from which to retrieve the values - * @glyph: index of the glyph to stretch - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: The direction of the stretching (horizontal or vertical) * @start_offset: offset of the first variant to retrieve - * @variants_count: maximum number of variants to retrieve after start_offset - * (IN) and actual number of variants retrieved (OUT) - * @variants: array of size at least @variants_count to store the result + * @variants_count: (inout): Input = the maximum number of variants to return; + * Output = the actual number of variants returned + * @variants: (out) (array length=variants_count): array of variants returned * - * This function tries to retrieve the MathGlyphConstruction for the specified - * font, glyph and direction. Note that only the value of - * #HB_DIRECTION_IS_HORIZONTAL is considered. It provides the corresponding list - * of size variants as an array of hb_ot_math_glyph_variant_t structs. + * Fetches the MathGlyphConstruction for the specified font, glyph index, and + * direction. The corresponding list of size variants is returned as a list of + * #hb_ot_math_glyph_variant_t structs. * - * Return value: the total number of size variants available or 0 + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. + * + * Return value: the total number of size variants available or zero * * Since: 1.3.3 **/ @@ -195,15 +223,19 @@ hb_ot_math_get_glyph_variants (hb_font_t *font, /** * hb_ot_math_get_min_connector_overlap: - * @font: #hb_font_t from which to retrieve the value - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @direction: direction of the stretching (horizontal or vertical) + * + * Fetches the MathVariants table for the specified font and returns the + * minimum overlap of connecting glyphs that are required to draw a glyph + * assembly in the specified direction. * - * This function tries to retrieve the MathVariants table for the specified - * font and returns the minimum overlap of connecting glyphs to draw a glyph - * assembly in the specified direction. Note that only the value of - * #HB_DIRECTION_IS_HORIZONTAL is considered. + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. * - * Return value: requested min connector overlap or 0 + * Return value: requested minimum connector overlap or zero * * Since: 1.3.3 **/ @@ -216,19 +248,24 @@ hb_ot_math_get_min_connector_overlap (hb_font_t *font, /** * hb_ot_math_get_glyph_assembly: - * @font: #hb_font_t from which to retrieve the values - * @glyph: index of the glyph to stretch - * @direction: direction of the stretching + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: direction of the stretching (horizontal or vertical) * @start_offset: offset of the first glyph part to retrieve - * @parts_count: maximum number of glyph parts to retrieve after start_offset - * (IN) and actual number of parts retrieved (OUT) - * @parts: array of size at least @parts_count to store the result - * @italics_correction: italic correction of the glyph assembly + * @parts_count: (inout): Input = maximum number of glyph parts to return; + * Output = actual number of parts returned + * @parts: (out) (array length=parts_count): the glyph parts returned + * @italics_correction: (out): italics correction of the glyph assembly * - * This function tries to retrieve the GlyphAssembly for the specified font, - * glyph and direction. Note that only the value of #HB_DIRECTION_IS_HORIZONTAL - * is considered. It provides the information necessary to draw the glyph - * assembly as an array of #hb_ot_math_glyph_part_t. + * Fetches the GlyphAssembly for the specified font, glyph index, and direction. + * Returned are a list of #hb_ot_math_glyph_part_t glyph parts that can be + * used to draw the glyph and an italics-correction value (if one is defined + * in the font). + * + * The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered. * * Return value: the total number of parts in the glyph assembly * @@ -251,3 +288,6 @@ hb_ot_math_get_glyph_assembly (hb_font_t *font, parts, italics_correction); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h index 3cbdb8b3e5df0..0d92ffb4147ad 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-math.h @@ -50,6 +50,9 @@ HB_BEGIN_DECLS /** * hb_ot_math_constant_t: * + * The 'MATH' table constants specified at + * https://docs.microsoft.com/en-us/typography/opentype/spec/math + * * Since: 1.3.3 */ typedef enum { @@ -114,6 +117,9 @@ typedef enum { /** * hb_ot_math_kern_t: * + * The math kerning-table types defined for the four corners + * of a glyph. + * * Since: 1.3.3 */ typedef enum { @@ -125,6 +131,10 @@ typedef enum { /** * hb_ot_math_glyph_variant_t: + * @glyph: The glyph index of the variant + * @advance: The advance width of the variant + * + * Data type to hold math-variant information for a glyph. * * Since: 1.3.3 */ @@ -136,14 +146,25 @@ typedef struct hb_ot_math_glyph_variant_t { /** * hb_ot_math_glyph_part_flags_t: * + * Flags for math glyph parts. + * * Since: 1.3.3 */ typedef enum { /*< flags >*/ - HB_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ + HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ } hb_ot_math_glyph_part_flags_t; /** * hb_ot_math_glyph_part_t: + * @glyph: The glyph index of the variant part + * @start_connector_length: The length of the connector on the starting side of the variant part + * @end_connector_length: The length of the connector on the ending side of the variant part + * @full_advance: The total advance of the part + * @flags: #hb_ot_math_glyph_part_flags_t flags for the part + * + * Data type to hold information for a "part" component of a math-variant glyph. + * Large variants for stretchable math glyphs (such as parentheses) can be constructed + * on the fly from parts. * * Since: 1.3.3 */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh index 2940617b1b804..0afa53a882f74 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-maxp-table.hh @@ -77,7 +77,7 @@ struct maxp void set_num_glyphs (unsigned int count) { - numGlyphs.set (count); + numGlyphs = count; } bool sanitize (hb_sanitize_context_t *c) const @@ -94,46 +94,43 @@ struct maxp return_trace (likely (version.major == 0 && version.minor == 0x5000u)); } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *maxp_blob = hb_sanitize_context_t().reference_table (plan->source); - hb_blob_t *maxp_prime_blob = hb_blob_copy_writable_or_fail (maxp_blob); - hb_blob_destroy (maxp_blob); + TRACE_SUBSET (this); + maxp *maxp_prime = c->serializer->embed (this); + if (unlikely (!maxp_prime)) return_trace (false); - if (unlikely (!maxp_prime_blob)) { - return false; - } - maxp *maxp_prime = (maxp *) hb_blob_get_data (maxp_prime_blob, nullptr); + maxp_prime->numGlyphs = c->plan->num_output_glyphs (); + if (maxp_prime->version.major == 1) + { + const maxpV1Tail *src_v1 = &StructAfter (*this); + maxpV1Tail *dest_v1 = c->serializer->embed (src_v1); + if (unlikely (!dest_v1)) return_trace (false); - maxp_prime->set_num_glyphs (plan->glyphs.length); - if (plan->drop_hints) - drop_hint_fields (plan, maxp_prime); + if (c->plan->drop_hints) + drop_hint_fields (dest_v1); + } - bool result = plan->add_table (HB_OT_TAG_maxp, maxp_prime_blob); - hb_blob_destroy (maxp_prime_blob); - return result; + return_trace (true); } - static void drop_hint_fields (hb_subset_plan_t *plan HB_UNUSED, maxp *maxp_prime) + static void drop_hint_fields (maxpV1Tail* dest_v1) { - if (maxp_prime->version.major == 1) - { - maxpV1Tail &v1 = StructAfter (*maxp_prime); - v1.maxZones.set (1); - v1.maxTwilightPoints.set (0); - v1.maxStorage.set (0); - v1.maxFunctionDefs.set (0); - v1.maxInstructionDefs.set (0); - v1.maxStackElements.set (0); - v1.maxSizeOfInstructions.set (0); - } + dest_v1->maxZones = 1; + dest_v1->maxTwilightPoints = 0; + dest_v1->maxStorage = 0; + dest_v1->maxFunctionDefs = 0; + dest_v1->maxInstructionDefs = 0; + dest_v1->maxStackElements = 0; + dest_v1->maxSizeOfInstructions = 0; } protected: - FixedVersion<>version; /* Version of the maxp table (0.5 or 1.0), - * 0x00005000u or 0x00010000u. */ - HBUINT16 numGlyphs; /* The number of glyphs in the font. */ -/*maxpV1Tail v1Tail[VAR]; */ + FixedVersion<>version;/* Version of the maxp table (0.5 or 1.0), + * 0x00005000u or 0x00010000u. */ + HBUINT16 numGlyphs; + /* The number of glyphs in the font. */ +/*maxpV1Tail v1Tail[HB_VAR_ARRAY]; */ public: DEFINE_SIZE_STATIC (6); }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh new file mode 100644 index 0000000000000..2b45f42572f16 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta-table.hh @@ -0,0 +1,127 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_META_TABLE_HH +#define HB_OT_META_TABLE_HH + +#include "hb-open-type.hh" + +/* + * meta -- Metadata Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html + */ +#define HB_OT_TAG_meta HB_TAG ('m','e','t','a') + + +namespace OT { + + +struct DataMap +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + hb_tag_t get_tag () const { return tag; } + + hb_blob_t *reference_entry (hb_blob_t *meta_blob) const + { return hb_blob_create_sub_blob (meta_blob, dataZ, dataLength); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + dataZ.sanitize (c, base, dataLength))); + } + + protected: + Tag tag; /* A tag indicating the type of metadata. */ + LNNOffsetTo> + dataZ; /* Offset in bytes from the beginning of the + * metadata table to the data for this tag. */ + HBUINT32 dataLength; /* Length of the data. The data is not required to + * be padded to any byte boundary. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct meta +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_meta; + + struct accelerator_t + { + void init (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + void fini () { table.destroy (); } + + hb_blob_t *reference_entry (hb_tag_t tag) const + { return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); } + + unsigned int get_entries (unsigned int start_offset, + unsigned int *count, + hb_ot_meta_tag_t *entries) const + { + if (count) + { + + table->dataMaps.sub_array (start_offset, count) + | hb_map (&DataMap::get_tag) + | hb_map ([](hb_tag_t tag) { return (hb_ot_meta_tag_t) tag; }) + | hb_sink (hb_array (entries, *count)) + ; + } + return table->dataMaps.len; + } + + private: + hb_blob_ptr_t table; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + version == 1 && + dataMaps.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Version number of the metadata table — set to 1. */ + HBUINT32 flags; /* Flags — currently unused; set to 0. */ + HBUINT32 dataOffset; + /* Per Apple specification: + * Offset from the beginning of the table to the data. + * Per OT specification: + * Reserved. Not used; should be set to 0. */ + LArrayOf + dataMaps;/* Array of data map records. */ + public: + DEFINE_SIZE_ARRAY (16, dataMaps); +}; + +struct meta_accelerator_t : meta::accelerator_t {}; + +} /* namespace OT */ + + +#endif /* HB_OT_META_TABLE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc new file mode 100644 index 0000000000000..f2d7d337cf347 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.cc @@ -0,0 +1,77 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_META + +#include "hb-ot-meta-table.hh" + +/** + * SECTION:hb-ot-meta + * @title: hb-ot-meta + * @short_description: OpenType Metadata + * @include: hb-ot.h + * + * Functions for fetching metadata from fonts. + **/ + +/** + * hb_ot_meta_get_entry_tags: + * @face: a face object + * @start_offset: iteration's start offset + * @entries_count:(inout) (allow-none): buffer size as input, filled size as output + * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer + * + * Return value: Number of all available feature types. + * + * Since: 2.6.0 + **/ +unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */) +{ + return face->table.meta->get_entries (start_offset, entries_count, entries); +} + +/** + * hb_ot_meta_reference_entry: + * @face: a #hb_face_t object. + * @meta_tag: tag of metadata you like to have. + * + * It fetches metadata entry of a given tag from a font. + * + * Returns: (transfer full): A blob containing the blob. + * + * Since: 2.6.0 + **/ +hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag) +{ + return face->table.meta->reference_entry (meta_tag); +} + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h new file mode 100644 index 0000000000000..71e1b781970fd --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-meta.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_META_H +#define HB_OT_META_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_ot_meta_tag_t: + * @HB_OT_META_TAG_DESIGN_LANGUAGES: Design languages. Text, using only + * Basic Latin (ASCII) characters. Indicates languages and/or scripts + * for the user audiences that the font was primarily designed for. + * @HB_OT_META_TAG_SUPPORTED_LANGUAGES: Supported languages. Text, using + * only Basic Latin (ASCII) characters. Indicates languages and/or scripts + * that the font is declared to be capable of supporting. + * + * Known metadata tags from https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * + * Since: 2.6.0 + **/ +typedef enum { +/* + HB_OT_META_TAG_APPL = HB_TAG ('a','p','p','l'), + HB_OT_META_TAG_BILD = HB_TAG ('b','i','l','d'), +*/ + HB_OT_META_TAG_DESIGN_LANGUAGES = HB_TAG ('d','l','n','g'), + HB_OT_META_TAG_SUPPORTED_LANGUAGES = HB_TAG ('s','l','n','g'), + + _HB_OT_META_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_meta_tag_t; + +HB_EXTERN unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */); + +HB_EXTERN hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag); + +HB_END_DECLS + +#endif /* HB_OT_META_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc new file mode 100644 index 0000000000000..9c0920a8085e3 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.cc @@ -0,0 +1,231 @@ +/* + * Copyright © 2018-2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-metrics.hh" +#include "hb-ot-face.hh" + + +static float +_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag) +{ + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER) + return fabs ((double) value); + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER) + return -fabs ((double) value); + return value; +} + +/* The common part of _get_position logic needed on hb-ot-font and here + to be able to have slim builds without the not always needed parts */ +bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { +#ifndef HB_NO_VAR +#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords) +#else +#define GET_VAR .0f +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag))), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) || + GET_METRIC_Y (hhea, ascender); + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) || + GET_METRIC_Y (hhea, descender); + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) || + GET_METRIC_Y (hhea, lineGap); + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: return GET_METRIC_X (vhea, ascender); + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender); + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return GET_METRIC_X (vhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: assert (0); return false; + } +} + +#ifndef HB_NO_METRICS + +#if 0 +static bool +_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag) +{ + const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0')); + if (&range == &Null (OT::GaspRange)) return false; + if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); + return true; +} +#endif + +/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */ +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2 HB_TAG ('O','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA HB_TAG ('H','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2 HB_TAG ('O','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2 HB_TAG ('O','l','g','p') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA HB_TAG ('H','l','g','p') + +/** + * hb_ot_metrics_get_position: + * @font: a #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * It fetches metrics value corresponding to a given tag from a font. + * + * Returns: Whether found the requested metrics in the font. + * Since: 2.6.0 + **/ +hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return _hb_ot_metrics_get_position_common (font, metrics_tag, position); +#ifndef HB_NO_VAR +#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag) +#else +#define GET_VAR 0 +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR)), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR)), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent); + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: return GET_METRIC_Y (hhea, caretSlopeRise); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: return GET_METRIC_X (hhea, caretSlopeRun); + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun); + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: return GET_METRIC_Y (vhea, caretOffset); + case HB_OT_METRICS_TAG_X_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sxHeight); + case HB_OT_METRICS_TAG_CAP_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sCapHeight); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySubscriptXSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySubscriptYSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySubscriptXOffset); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySubscriptYOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySuperscriptXSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySuperscriptYSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySuperscriptXOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySuperscriptYOffset); + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: return GET_METRIC_Y (OS2, yStrikeoutSize); + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: return GET_METRIC_Y (OS2, yStrikeoutPosition); + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: return GET_METRIC_Y (post->table, underlineThickness); + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: return GET_METRIC_Y (post->table, underlinePosition); + + /* Private tags */ + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2: return GET_METRIC_Y (OS2, sTypoAscender); + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA: return GET_METRIC_Y (hhea, ascender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2: return GET_METRIC_Y (OS2, sTypoDescender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA: return GET_METRIC_Y (hhea, descender); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2: return GET_METRIC_Y (OS2, sTypoLineGap); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA: return GET_METRIC_Y (hhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: return false; + } +} + +#ifndef HB_NO_VAR +/** + * hb_ot_metrics_get_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); +} + +/** + * hb_ot_metrics_get_x_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag)); +} + +/** + * hb_ot_metrics_get_y_variation: + * @font: + * @metrics_tag: + * + * Returns: + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag)); +} +#endif + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h new file mode 100644 index 0000000000000..c1be1d85b3c94 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.h @@ -0,0 +1,122 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_OT_H_IN +#error "Include instead." +#endif + +#ifndef HB_OT_METRICS_H +#define HB_OT_METRICS_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/** + * hb_ot_metrics_tag_t: + * @HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: horizontal ascender. + * @HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: horizontal descender. + * @HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: horizontal line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: horizontal clipping ascent. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: horizontal clipping descent. + * @HB_OT_METRICS_TAG_VERTICAL_ASCENDER: vertical ascender. + * @HB_OT_METRICS_TAG_VERTICAL_DESCENDER: vertical descender. + * @HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: vertical line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: horizontal caret rise. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: horizontal caret run. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: horizontal caret offset. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: vertical caret rise. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: vertical caret run. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: vertical caret offset. + * @HB_OT_METRICS_TAG_X_HEIGHT: x height. + * @HB_OT_METRICS_TAG_CAP_HEIGHT: cap height. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: subscript em x size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: subscript em y size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: subscript em x offset. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: subscript em y offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: superscript em x size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: superscript em y size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: superscript em x offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: superscript em y offset. + * @HB_OT_METRICS_TAG_STRIKEOUT_SIZE: strikeout size. + * @HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: strikeout offset. + * @HB_OT_METRICS_TAG_UNDERLINE_SIZE: underline size. + * @HB_OT_METRICS_TAG_UNDERLINE_OFFSET: underline offset. + * + * From https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags + * + * Since: 2.6.0 + **/ +typedef enum { + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER = HB_TAG ('h','a','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER = HB_TAG ('h','d','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP = HB_TAG ('h','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT = HB_TAG ('h','c','l','a'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT = HB_TAG ('h','c','l','d'), + HB_OT_METRICS_TAG_VERTICAL_ASCENDER = HB_TAG ('v','a','s','c'), + HB_OT_METRICS_TAG_VERTICAL_DESCENDER = HB_TAG ('v','d','s','c'), + HB_OT_METRICS_TAG_VERTICAL_LINE_GAP = HB_TAG ('v','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE = HB_TAG ('h','c','r','s'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN = HB_TAG ('h','c','r','n'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET = HB_TAG ('h','c','o','f'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RISE = HB_TAG ('v','c','r','s'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RUN = HB_TAG ('v','c','r','n'), + HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET = HB_TAG ('v','c','o','f'), + HB_OT_METRICS_TAG_X_HEIGHT = HB_TAG ('x','h','g','t'), + HB_OT_METRICS_TAG_CAP_HEIGHT = HB_TAG ('c','p','h','t'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE = HB_TAG ('s','b','x','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE = HB_TAG ('s','b','y','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET = HB_TAG ('s','b','x','o'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET = HB_TAG ('s','b','y','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE = HB_TAG ('s','p','x','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE = HB_TAG ('s','p','y','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET = HB_TAG ('s','p','x','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET = HB_TAG ('s','p','y','o'), + HB_OT_METRICS_TAG_STRIKEOUT_SIZE = HB_TAG ('s','t','r','s'), + HB_OT_METRICS_TAG_STRIKEOUT_OFFSET = HB_TAG ('s','t','r','o'), + HB_OT_METRICS_TAG_UNDERLINE_SIZE = HB_TAG ('u','n','d','s'), + HB_OT_METRICS_TAG_UNDERLINE_OFFSET = HB_TAG ('u','n','d','o'), + + _HB_OT_METRICS_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_metrics_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +HB_EXTERN float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_END_DECLS + +#endif /* HB_OT_METRICS_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh similarity index 70% rename from src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh rename to src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh index 385a8f9d564c3..2c75f32d99965 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-metrics.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Google, Inc. + * Copyright © 2018 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -20,21 +20,16 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Garret Rieger */ -#ifndef HB_SUBSET_GLYF_HH -#define HB_SUBSET_GLYF_HH +#ifndef HB_OT_METRICS_HH +#define HB_OT_METRICS_HH #include "hb.hh" -#include "hb-subset.hh" - HB_INTERNAL bool -hb_subset_glyf_and_loca (hb_subset_plan_t *plan, - bool *use_short_loca, /* OUT */ - hb_blob_t **glyf_prime /* OUT */, - hb_blob_t **loca_prime /* OUT */); +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); -#endif /* HB_SUBSET_GLYF_HH */ +#endif /* HB_OT_METRICS_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh similarity index 96% rename from src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc rename to src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh index 1606a13cec96a..5312f7b3a6ecd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-language-static.hh @@ -24,6 +24,9 @@ * Google Author(s): Behdad Esfahbod */ +#ifndef HB_OT_NAME_LANGUAGE_STATIC_HH +#define HB_OT_NAME_LANGUAGE_STATIC_HH + #include "hb-ot-name-language.hh" /* Following two tables were generated by joining FreeType, FontConfig, @@ -34,12 +37,8 @@ struct hb_ot_language_map_t { - static int cmp (const void *key, const void *item) - { - unsigned int a = * (unsigned int *) key; - unsigned int b = ((const hb_ot_language_map_t *) item)->code; - return a < b ? -1 : a > b ? +1 : 0; - } + int cmp (unsigned int key) const + { return key < code ? -1 : key > code ? +1 : 0; } uint16_t code; char lang[6]; @@ -427,12 +426,10 @@ _hb_ot_name_language_for (unsigned int code, const hb_ot_language_map_t *array, unsigned int len) { - const hb_ot_language_map_t *entry = (const hb_ot_language_map_t *) - hb_bsearch (&code, - array, - len, - sizeof (array[0]), - hb_ot_language_map_t::cmp); +#ifdef HB_NO_OT_NAME_LANGUAGE + return HB_LANGUAGE_INVALID; +#endif + auto *entry = hb_bsearch (code, array, len); if (entry) return hb_language_from_string (entry->lang, -1); @@ -455,3 +452,5 @@ _hb_ot_name_language_for_mac_code (unsigned int code) hb_mac_language_map, ARRAY_LENGTH (hb_mac_language_map)); } + +#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh index 96600cb40062e..fb2300d068884 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name-table.hh @@ -51,6 +51,7 @@ struct NameRecord { hb_language_t language (hb_face_t *face) const { +#ifndef HB_NO_OT_NAME_LANGUAGE unsigned int p = platformID; unsigned int l = languageID; @@ -60,9 +61,12 @@ struct NameRecord if (p == 1) return _hb_ot_name_language_for_mac_code (l); +#ifndef HB_NO_OT_NAME_LANGUAGE_AAT if (p == 0) - return _hb_aat_language_get (face, l); + return face->table.ltag->get_language (l); +#endif +#endif return HB_LANGUAGE_INVALID; } @@ -93,11 +97,51 @@ struct NameRecord return UNSUPPORTED; } + NameRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length); + return_trace (out); + } + + bool isUnicode () const + { + unsigned int p = platformID; + unsigned int e = encodingID; + + return (p == 0 || + (p == 3 && (e == 0 || e == 1 || e == 10))); + } + + static int cmp (const void *pa, const void *pb) + { + const NameRecord *a = (const NameRecord *)pa; + const NameRecord *b = (const NameRecord *)pb; + + if (a->platformID != b->platformID) + return a->platformID - b->platformID; + + if (a->encodingID != b->encodingID) + return a->encodingID - b->encodingID; + + if (a->languageID != b->languageID) + return a->languageID - b->languageID; + + if (a->nameID != b->nameID) + return a->nameID - b->nameID; + + if (a->length != b->length) + return a->length - b->length; + + return 0; + } + bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - /* We can check from base all the way up to the end of string... */ - return_trace (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset)); + return_trace (c->check_struct (this) && offset.sanitize (c, base, length)); } HBUINT16 platformID; /* Platform ID. */ @@ -105,7 +149,8 @@ struct NameRecord HBUINT16 languageID; /* Language ID. */ HBUINT16 nameID; /* Name ID. */ HBUINT16 length; /* String length (in bytes). */ - HBUINT16 offset; /* String offset from start of storage area (in bytes). */ + NNOffsetTo> + offset; /* String offset from start of storage area (in bytes). */ public: DEFINE_SIZE_STATIC (12); }; @@ -119,7 +164,7 @@ _hb_ot_name_entry_cmp_key (const void *pa, const void *pb) /* Compare by name_id, then language. */ if (a->name_id != b->name_id) - return a->name_id < b->name_id ? -1 : +1; + return a->name_id - b->name_id; if (a->language == b->language) return 0; if (!a->language) return -1; @@ -141,10 +186,10 @@ _hb_ot_name_entry_cmp (const void *pa, const void *pb) const hb_ot_name_entry_t *b = (const hb_ot_name_entry_t *) pb; if (a->entry_score != b->entry_score) - return a->entry_score < b->entry_score ? -1 : +1; + return a->entry_score - b->entry_score; if (a->entry_index != b->entry_index) - return a->entry_index < b->entry_index ? -1 : +1; + return a->entry_index - b->entry_index; return 0; } @@ -156,15 +201,65 @@ struct name unsigned int get_size () const { return min_size + count * nameRecordZ.item_size; } + template + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool) + { + TRACE_SERIALIZE (this); + + if (unlikely (!c->extend_min ((*this)))) return_trace (false); + + this->format = 0; + this->count = it.len (); + + NameRecord *name_records = (NameRecord *) calloc (it.len (), NameRecord::static_size); + if (unlikely (!name_records)) return_trace (false); + + hb_array_t records (name_records, it.len ()); + + for (const NameRecord& record : it) + { + memcpy (name_records, &record, NameRecord::static_size); + name_records++; + } + + records.qsort (); + + c->copy_all (records, src_string_pool); + free (records.arrayZ); + + if (unlikely (c->ran_out_of_room)) return_trace (false); + + this->stringOffset = c->length (); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + name *name_prime = c->serializer->start_embed (); + if (unlikely (!name_prime)) return_trace (false); + + auto it = + + nameRecordZ.as_array (count) + | hb_filter (c->plan->name_ids, &NameRecord::nameID) + | hb_filter (c->plan->name_languages, &NameRecord::languageID) + | hb_filter ([&] (const NameRecord& namerecord) { return c->plan->name_legacy || namerecord.isUnicode (); }) + ; + + name_prime->serialize (c->serializer, it, hb_addressof (this + stringOffset)); + return_trace (name_prime->count); + } + bool sanitize_records (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); const void *string_pool = (this+stringOffset).arrayZ; - unsigned int _count = count; - /* Move to run-time?! */ - for (unsigned int i = 0; i < _count; i++) - if (!nameRecordZ[i].sanitize (c, string_pool)) return_trace (false); - return_trace (true); + return_trace (nameRecordZ.sanitize (c, count, string_pool)); } bool sanitize (hb_sanitize_context_t *c) const @@ -173,14 +268,15 @@ struct name return_trace (c->check_struct (this) && likely (format == 0 || format == 1) && c->check_array (nameRecordZ.arrayZ, count) && - c->check_range (this, stringOffset)); + c->check_range (this, stringOffset) && + sanitize_records (c)); } struct accelerator_t { void init (hb_face_t *face) { - this->table = hb_sanitize_context_t().reference_table (face); + this->table = hb_sanitize_context_t ().reference_table (face); assert (this->table.get_length () >= this->table->stringOffset); this->pool = (const char *) (const void *) (this->table+this->table->stringOffset); this->pool_len = this->table.get_length () - this->table->stringOffset; @@ -224,16 +320,14 @@ struct name this->table.destroy (); } - int get_index (hb_ot_name_id_t name_id, - hb_language_t language, - unsigned int *width=nullptr) const + int get_index (hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *width=nullptr) const { const hb_ot_name_entry_t key = {name_id, {0}, language}; - const hb_ot_name_entry_t *entry = (const hb_ot_name_entry_t *) - hb_bsearch (&key, - (const hb_ot_name_entry_t *) this->names, + const hb_ot_name_entry_t *entry = hb_bsearch (key, (const hb_ot_name_entry_t *) this->names, this->names.length, - sizeof (key), + sizeof (hb_ot_name_entry_t), _hb_ot_name_entry_cmp_key); if (!entry) return -1; @@ -261,16 +355,19 @@ struct name }; /* We only implement format 0 for now. */ - HBUINT16 format; /* Format selector (=0/1). */ - HBUINT16 count; /* Number of name records. */ - NNOffsetTo > - stringOffset; /* Offset to start of string storage (from start of table). */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffsetTo> + stringOffset; /* Offset to start of string storage (from start of table). */ UnsizedArrayOf - nameRecordZ; /* The name records where count is the number of records. */ + nameRecordZ; /* The name records where count is the number of records. */ public: DEFINE_SIZE_ARRAY (6, nameRecordZ); }; +#undef entry_index +#undef entry_score + struct name_accelerator_t : name::accelerator_t {}; } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc index 83a332e77d275..56bfd39168b25 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-name.cc @@ -26,9 +26,10 @@ #include "hb.hh" +#ifndef HB_NO_NAME + #include "hb-ot-name-table.hh" -#include "hb-ot-face.hh" #include "hb-utf.hh" @@ -93,7 +94,7 @@ hb_ot_name_convert_utf (hb_bytes_t bytes, dst = dst_next; src = src_next; - }; + } *text_size = dst - text; *dst = 0; /* NUL-terminate. */ @@ -105,7 +106,7 @@ hb_ot_name_convert_utf (hb_bytes_t bytes, { src = in_utf_t::next (src, src_end, &unicode, replacement); dst_len += out_utf_t::encode_len (unicode); - }; + } return dst_len; } @@ -222,3 +223,6 @@ hb_ot_name_get_utf32 (hb_face_t *face, { return hb_ot_name_get_utf (face, name_id, language, text_size, text); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh index 6fbffb1451288..3639a06e9be06 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-table.hh @@ -30,6 +30,7 @@ #include "hb-open-type.hh" #include "hb-ot-os2-unicode-ranges.hh" +#include "hb-ot-cmap-table.hh" #include "hb-set.hh" @@ -59,6 +60,10 @@ struct OS2V1Tail struct OS2V2Tail { + bool has_data () const { return sxHeight || sCapHeight; } + + const OS2V2Tail * operator -> () const { return this; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -77,6 +82,23 @@ struct OS2V2Tail struct OS2V5Tail { + inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const + { + unsigned int lower_optical_size = usLowerOpticalPointSize; + unsigned int upper_optical_size = usUpperOpticalPointSize; + + /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */ + if (lower_optical_size < upper_optical_size && + lower_optical_size >= 1 && lower_optical_size <= 0xFFFE && + upper_optical_size >= 2 && upper_optical_size <= 0xFFFF) + { + *lower = lower_optical_size; + *upper = upper_optical_size; + return true; + } + return false; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -94,7 +116,7 @@ struct OS2 { static constexpr hb_tag_t tableTag = HB_OT_TAG_OS2; - bool has_data () const { return this != &Null (OS2); } + bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; } const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); } const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); } @@ -113,9 +135,9 @@ struct OS2 OBLIQUE = 1u<<9 }; - bool is_italic () const { return fsSelection & ITALIC; } - bool is_oblique () const { return fsSelection & OBLIQUE; } - bool is_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } + bool is_italic () const { return fsSelection & ITALIC; } + bool is_oblique () const { return fsSelection & OBLIQUE; } + bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } enum width_class_t { FWIDTH_ULTRA_CONDENSED = 1, /* 50% */ @@ -145,36 +167,49 @@ struct OS2 } } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *os2_blob = hb_sanitize_context_t ().reference_table (plan->source); - hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1); - // TODO(grieger): move to hb_blob_copy_writable_or_fail - hb_blob_destroy (os2_blob); - - OS2 *os2_prime = (OS2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr); - if (unlikely (!os2_prime)) { - hb_blob_destroy (os2_prime_blob); - return false; + TRACE_SUBSET (this); + OS2 *os2_prime = c->serializer->embed (this); + if (unlikely (!os2_prime)) return_trace (false); + + hb_set_t unicodes; + if (!c->plan->glyphs_requested->is_empty ()) + { + hb_map_t unicode_glyphid_map; + + OT::cmap::accelerator_t cmap; + cmap.init (c->plan->source); + cmap.collect_mapping (&unicodes, &unicode_glyphid_map); + cmap.fini (); + + if (c->plan->unicodes->is_empty ()) unicodes.clear (); + else hb_set_set (&unicodes, c->plan->unicodes); + + + unicode_glyphid_map.iter () + | hb_filter (c->plan->glyphs_requested, hb_second) + | hb_map (hb_first) + | hb_sink (unicodes) + ; } - + /* when --gids option is not used, no need to do collect_mapping that is + * iterating all codepoints in each subtable, which is not efficient */ uint16_t min_cp, max_cp; - find_min_and_max_codepoint (plan->unicodes, &min_cp, &max_cp); - os2_prime->usFirstCharIndex.set (min_cp); - os2_prime->usLastCharIndex.set (max_cp); + find_min_and_max_codepoint (unicodes.is_empty () ? c->plan->unicodes : &unicodes, &min_cp, &max_cp); + os2_prime->usFirstCharIndex = min_cp; + os2_prime->usLastCharIndex = max_cp; - _update_unicode_ranges (plan->unicodes, os2_prime->ulUnicodeRange); - bool result = plan->add_table (HB_OT_TAG_OS2, os2_prime_blob); + _update_unicode_ranges (unicodes.is_empty () ? c->plan->unicodes : &unicodes, os2_prime->ulUnicodeRange); - hb_blob_destroy (os2_prime_blob); - return result; + return_trace (true); } void _update_unicode_ranges (const hb_set_t *codepoints, HBUINT32 ulUnicodeRange[4]) const { + HBUINT32 newBits[4]; for (unsigned int i = 0; i < 4; i++) - ulUnicodeRange[i].set (0); + newBits[i] = 0; hb_codepoint_t cp = HB_SET_VALUE_INVALID; while (codepoints->next (&cp)) { @@ -184,40 +219,52 @@ struct OS2 unsigned int block = bit / 32; unsigned int bit_in_block = bit % 32; unsigned int mask = 1 << bit_in_block; - ulUnicodeRange[block].set (ulUnicodeRange[block] | mask); + newBits[block] = newBits[block] | mask; } if (cp >= 0x10000 && cp <= 0x110000) { /* the spec says that bit 57 ("Non Plane 0") implies that there's at least one codepoint beyond the BMP; so I also include all the non-BMP codepoints here */ - ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25)); + newBits[1] = newBits[1] | (1 << 25); } } + + for (unsigned int i = 0; i < 4; i++) + ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original } static void find_min_and_max_codepoint (const hb_set_t *codepoints, - uint16_t *min_cp, /* OUT */ - uint16_t *max_cp /* OUT */) + uint16_t *min_cp, /* OUT */ + uint16_t *max_cp /* OUT */) { - *min_cp = codepoints->get_min (); - *max_cp = codepoints->get_max (); + *min_cp = hb_min (0xFFFFu, codepoints->get_min ()); + *max_cp = hb_min (0xFFFFu, codepoints->get_max ()); } - enum font_page_t { - HEBREW_FONT_PAGE = 0xB100, // Hebrew Windows 3.1 font page - SIMP_ARABIC_FONT_PAGE = 0xB200, // Simplified Arabic Windows 3.1 font page - TRAD_ARABIC_FONT_PAGE = 0xB300, // Traditional Arabic Windows 3.1 font page - OEM_ARABIC_FONT_PAGE = 0xB400, // OEM Arabic Windows 3.1 font page - SIMP_FARSI_FONT_PAGE = 0xBA00, // Simplified Farsi Windows 3.1 font page - TRAD_FARSI_FONT_PAGE = 0xBB00, // Traditional Farsi Windows 3.1 font page - THAI_FONT_PAGE = 0xDE00 // Thai Windows 3.1 font page + /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 */ + enum font_page_t + { + FONT_PAGE_HEBREW = 0xB100, /* Hebrew Windows 3.1 font page */ + FONT_PAGE_SIMP_ARABIC = 0xB200, /* Simplified Arabic Windows 3.1 font page */ + FONT_PAGE_TRAD_ARABIC = 0xB300, /* Traditional Arabic Windows 3.1 font page */ + FONT_PAGE_OEM_ARABIC = 0xB400, /* OEM Arabic Windows 3.1 font page */ + FONT_PAGE_SIMP_FARSI = 0xBA00, /* Simplified Farsi Windows 3.1 font page */ + FONT_PAGE_TRAD_FARSI = 0xBB00, /* Traditional Farsi Windows 3.1 font page */ + FONT_PAGE_THAI = 0xDE00 /* Thai Windows 3.1 font page */ }; - - // https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 font_page_t get_font_page () const { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } + unsigned get_size () const + { + unsigned result = min_size; + if (version >= 1) result += v1X.get_size (); + if (version >= 2) result += v2X.get_size (); + if (version >= 5) result += v5X.get_size (); + return result; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh index 7e573b33206ec..9613d2d186df0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-os2-unicode-ranges.hh @@ -33,19 +33,8 @@ namespace OT { struct OS2Range { - static int - cmp (const void *_key, const void *_item) - { - hb_codepoint_t cp = *((hb_codepoint_t *) _key); - const OS2Range *range = (OS2Range *) _item; - - if (cp < range->start) - return -1; - else if (cp <= range->end) - return 0; - else - return +1; - } + int cmp (hb_codepoint_t key) const + { return (key < start) ? -1 : key <= end ? 0 : +1; } hb_codepoint_t start; hb_codepoint_t end; @@ -233,13 +222,8 @@ static const OS2Range _hb_os2_unicode_ranges[] = static unsigned int _hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp) { - OS2Range *range = (OS2Range*) hb_bsearch (&cp, _hb_os2_unicode_ranges, - ARRAY_LENGTH (_hb_os2_unicode_ranges), - sizeof (OS2Range), - OS2Range::cmp); - if (range != nullptr) - return range->bit; - return -1; + auto *range = hb_sorted_array (_hb_os2_unicode_ranges).bsearch (cp); + return range ? range->bit : -1; } } /* namespace OT */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh index 40134b40f13c8..08449ac2488f2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-post-table.hh @@ -35,8 +35,6 @@ #undef HB_STRING_ARRAY_LIST #undef HB_STRING_ARRAY_NAME -#define NUM_FORMAT1_NAMES 258 - /* * post -- PostScript * https://docs.microsoft.com/en-us/typography/opentype/spec/post @@ -73,26 +71,25 @@ struct post { static constexpr hb_tag_t tableTag = HB_OT_TAG_post; - bool subset (hb_subset_plan_t *plan) const + void serialize (hb_serialize_context_t *c) const { - unsigned int post_prime_length; - hb_blob_t *post_blob = hb_sanitize_context_t ().reference_table(plan->source); - hb_blob_t *post_prime_blob = hb_blob_create_sub_blob (post_blob, 0, post::min_size); - post *post_prime = (post *) hb_blob_get_data_writable (post_prime_blob, &post_prime_length); - hb_blob_destroy (post_blob); + post *post_prime = c->allocate_min (); + if (unlikely (!post_prime)) return; - if (unlikely (!post_prime || post_prime_length != post::min_size)) - { - hb_blob_destroy (post_prime_blob); - DEBUG_MSG(SUBSET, nullptr, "Invalid source post table with length %d.", post_prime_length); - return false; - } + memcpy (post_prime, this, post::min_size); + post_prime->version.major = 3; // Version 3 does not have any glyph names. + } - post_prime->version.major.set (3); // Version 3 does not have any glyph names. - bool result = plan->add_table (HB_OT_TAG_post, post_prime_blob); - hb_blob_destroy (post_prime_blob); + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + post *post_prime = c->serializer->start_embed (); + if (unlikely (!post_prime)) return_trace (false); - return result; + serialize (c->serializer); + if (c->serializer->in_error () || c->serializer->ran_out_of_room) return_trace (false); + + return_trace (true); } struct accelerator_t @@ -131,7 +128,7 @@ struct post hb_bytes_t s = find_glyph_name (glyph); if (!s.length) return false; if (!buf_len) return true; - unsigned int len = MIN (buf_len - 1, s.length); + unsigned int len = hb_min (buf_len - 1, s.length); strncpy (buf, s.arrayZ, len); buf[len] = '\0'; return true; @@ -158,7 +155,7 @@ struct post for (unsigned int i = 0; i < count; i++) gids[i] = i; - hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); + hb_qsort (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) { @@ -168,8 +165,7 @@ struct post } hb_bytes_t st (name, len); - const uint16_t *gid = (const uint16_t *) hb_bsearch_r (hb_addressof (st), gids, count, - sizeof (gids[0]), cmp_key, (void *) this); + auto* gid = hb_bsearch (st, gids, count, sizeof (gids[0]), cmp_key, (void *) this); if (gid) { *glyph = *gid; @@ -179,12 +175,14 @@ struct post return false; } + hb_blob_ptr_t table; + protected: unsigned int get_glyph_count () const { if (version == 0x00010000) - return NUM_FORMAT1_NAMES; + return format1_names_length; if (version == 0x00020000) return glyphNameIndex->len; @@ -212,7 +210,7 @@ struct post { if (version == 0x00010000) { - if (glyph >= NUM_FORMAT1_NAMES) + if (glyph >= format1_names_length) return hb_bytes_t (); return format1_names (glyph); @@ -222,9 +220,9 @@ struct post return hb_bytes_t (); unsigned int index = glyphNameIndex->arrayZ[glyph]; - if (index < NUM_FORMAT1_NAMES) + if (index < format1_names_length) return format1_names (index); - index -= NUM_FORMAT1_NAMES; + index -= format1_names_length; if (index >= index_to_offset.length) return hb_bytes_t (); @@ -238,7 +236,6 @@ struct post } private: - hb_blob_ptr_t table; uint32_t version; const ArrayOf *glyphNameIndex; hb_vector_t index_to_offset; @@ -246,6 +243,8 @@ struct post hb_atomic_ptr_t gids_sorted_by_name; }; + bool has_data () const { return version.to_int (); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -260,7 +259,7 @@ struct post * 0x00020000 for version 2.0 * 0x00025000 for version 2.5 (deprecated) * 0x00030000 for version 3.0 */ - Fixed italicAngle; /* Italic angle in counter-clockwise degrees + HBFixed italicAngle; /* Italic angle in counter-clockwise degrees * from the vertical. Zero for upright text, * negative for text that leans to the right * (forward). */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh index f0d104c63d841..8a29d54d96111 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-fallback.hh @@ -49,8 +49,8 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS hb_font_t *font, unsigned int feature_index) { - OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; - OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; unsigned int num_glyphs = 0; /* Populate arrays */ @@ -66,8 +66,8 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) continue; - glyphs[num_glyphs].set (u_glyph); - substitutes[num_glyphs].set (s_glyph); + glyphs[num_glyphs] = u_glyph; + substitutes[num_glyphs] = s_glyph; num_glyphs++; } @@ -77,7 +77,9 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS /* Bubble-sort or something equally good! * May not be good-enough for presidential candidate interviews, but good-enough for us... */ - hb_stable_sort (&glyphs[0], num_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &substitutes[0]); + hb_stable_sort (&glyphs[0], num_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp, + &substitutes[0]); /* Each glyph takes four bytes max, and there's some overhead. */ @@ -86,27 +88,26 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS OT::SubstLookup *lookup = c.start_serialize (); bool ret = lookup->serialize_single (&c, OT::LookupFlag::IgnoreMarks, - hb_array (glyphs, num_glyphs), + hb_sorted_array (glyphs, num_glyphs), hb_array (substitutes, num_glyphs)); c.end_serialize (); - /* TODO sanitize the results? */ - return ret ? c.copy () : nullptr; + return ret && !c.in_error () ? c.copy () : nullptr; } static OT::SubstLookup * arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font) { - OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + OT::HBGlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; unsigned int num_first_glyphs = 0; /* We know that all our ligatures are 2-component */ - OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + OT::HBGlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; - OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; + OT::HBGlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */]; unsigned int num_ligatures = 0; /* Populate arrays */ @@ -118,12 +119,14 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN hb_codepoint_t first_glyph; if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) continue; - first_glyphs[num_first_glyphs].set (first_glyph); + first_glyphs[num_first_glyphs] = first_glyph; ligature_per_first_glyph_count_list[num_first_glyphs] = 0; first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; num_first_glyphs++; } - hb_stable_sort (&first_glyphs[0], num_first_glyphs, (int(*)(const OT::GlyphID*, const OT::GlyphID *)) OT::GlyphID::cmp, &first_glyphs_indirection[0]); + hb_stable_sort (&first_glyphs[0], num_first_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID::cmp, + &first_glyphs_indirection[0]); /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ for (unsigned int i = 0; i < num_first_glyphs; i++) @@ -142,9 +145,9 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN ligature_per_first_glyph_count_list[i]++; - ligature_list[num_ligatures].set (ligature_glyph); + ligature_list[num_ligatures] = ligature_glyph; component_count_list[num_ligatures] = 2; - component_list[num_ligatures].set (second_glyph); + component_list[num_ligatures] = second_glyph; num_ligatures++; } } @@ -159,7 +162,7 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN OT::SubstLookup *lookup = c.start_serialize (); bool ret = lookup->serialize_ligature (&c, OT::LookupFlag::IgnoreMarks, - hb_array (first_glyphs, num_first_glyphs), + hb_sorted_array (first_glyphs, num_first_glyphs), hb_array (ligature_per_first_glyph_count_list, num_first_glyphs), hb_array (ligature_list, num_ligatures), hb_array (component_count_list, num_ligatures), @@ -167,7 +170,7 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN c.end_serialize (); /* TODO sanitize the results? */ - return ret ? c.copy () : nullptr; + return ret && !c.in_error () ? c.copy () : nullptr; } static OT::SubstLookup * @@ -227,8 +230,8 @@ arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUS return false; const Manifest &manifest = reinterpret_cast (arabic_win1256_gsub_lookups.manifest); - static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup) - <= ARABIC_FALLBACK_MAX_LOOKUPS, ""); + static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) == + ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), ""); /* TODO sanitize the table? */ unsigned j = 0; @@ -289,7 +292,7 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, { arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t)); if (unlikely (!fallback_plan)) - return const_cast (&Null(arabic_fallback_plan_t)); + return const_cast (&Null (arabic_fallback_plan_t)); fallback_plan->num_lookups = 0; fallback_plan->free_lookups = false; @@ -306,7 +309,7 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, assert (fallback_plan->num_lookups == 0); free (fallback_plan); - return const_cast (&Null(arabic_fallback_plan_t)); + return const_cast (&Null (arabic_fallback_plan_t)); } static void diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh new file mode 100644 index 0000000000000..c022d4bb06bcc --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-joining-list.hh @@ -0,0 +1,46 @@ +/* == Start of generated function == */ +/* + * The following function is generated by running: + * + * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + * + * on files with these headers: + * + * # ArabicShaping-13.0.0.txt + * # Date: 2020-01-31, 23:55:00 GMT [KW, RP] + * # Scripts-13.0.0.txt + * # Date: 2020-01-22, 00:07:43 GMT + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH +#define HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH + +static bool +has_arabic_joining (hb_script_t script) +{ + /* List of scripts that have data in arabic-table. */ + switch ((int) script) + { + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MANDAIC: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SOGDIAN: + case HB_SCRIPT_SYRIAC: + return true; + + default: + return false; + } +} + + +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH */ + +/* == End of generated function == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh index 7bb0aeb5220f2..d47d0367700e7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-11.0.0.txt - * # Date: 2018-02-21, 14:50:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # ArabicShaping-13.0.0.txt + * # Date: 2020-01-31, 23:55:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] * UnicodeData.txt does not have a header. */ @@ -17,15 +17,15 @@ #define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH -#define X JOINING_TYPE_X -#define R JOINING_TYPE_R -#define T JOINING_TYPE_T -#define U JOINING_TYPE_U #define A JOINING_GROUP_ALAPH #define DR JOINING_GROUP_DALATH_RISH -#define L JOINING_TYPE_L #define C JOINING_TYPE_C #define D JOINING_TYPE_D +#define L JOINING_TYPE_L +#define R JOINING_TYPE_R +#define T JOINING_TYPE_T +#define U JOINING_TYPE_U +#define X JOINING_TYPE_X static const uint8_t joining_table[] = { @@ -71,7 +71,7 @@ static const uint8_t joining_table[] = /* Mandaic */ - /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,U,U,U,X,X,X,X,X,X,X, + /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X, /* Syriac Supplement */ @@ -80,8 +80,8 @@ static const uint8_t joining_table[] = /* Arabic Extended-A */ - /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,X,D,D,D,R,D,D,D,D,X,X, - /* 08C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,X,D,D,D,R,D,D,D,D,D,D, + /* 08C0 */ D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, /* 08E0 */ X,X,U, #define joining_offset_0x1806u 739 @@ -139,22 +139,29 @@ static const uint8_t joining_table[] = /* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D, /* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R, -#define joining_offset_0x110bdu 1219 +#define joining_offset_0x10fb0u 1219 + + /* Chorasmian */ + + /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, + /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, + +#define joining_offset_0x110bdu 1247 /* Kaithi */ /* 110A0 */ U,X,X, /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, -#define joining_offset_0x1e900u 1236 +#define joining_offset_0x1e900u 1264 /* Adlam */ /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1E940 */ D,D,D,D, + /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1304; occupancy: 56% */ +}; /* Table items: 1340; occupancy: 57% */ static unsigned int @@ -183,6 +190,7 @@ joining_type (hb_codepoint_t u) if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; if (hb_in_range (u, 0x10F30u, 0x10F54u)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; + if (hb_in_range (u, 0x10FB0u, 0x10FCBu)) return joining_table[u - 0x10FB0u + joining_offset_0x10fb0u]; break; case 0x11u: @@ -190,7 +198,7 @@ joining_type (hb_codepoint_t u) break; case 0x1Eu: - if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + if (hb_in_range (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; break; default: @@ -199,15 +207,15 @@ joining_type (hb_codepoint_t u) return X; } -#undef X -#undef R -#undef T -#undef U #undef A #undef DR -#undef L #undef C #undef D +#undef L +#undef R +#undef T +#undef U +#undef X static const uint16_t shaping_table[][4] = @@ -406,16 +414,16 @@ static const struct ligature_set_t { } ligature_table[] = { { 0xFEDFu, { - { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ { 0xFE82u, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ - { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ { 0xFE84u, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + { 0xFE88u, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { 0xFE8Eu, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ }}, { 0xFEE0u, { - { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ { 0xFE82u, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ - { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ { 0xFE84u, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + { 0xFE88u, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { 0xFE8Eu, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ }}, }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc index a25ced192bd99..c8ad501bca69c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-arabic.cc @@ -25,6 +25,9 @@ */ #include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-arabic.hh" #include "hb-ot-shape.hh" @@ -225,8 +228,6 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ); map->add_gsub_pause (nullptr); - /* And undo here. */ - /* The spec includes 'cswh'. Earlier versions of Windows * used to enable this by default, but testing suggests * that Windows 8 and later do not enable it by default, @@ -289,7 +290,7 @@ arabic_joining (hb_buffer_t *buffer) { unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; - unsigned int prev = (unsigned int) -1, state = 0; + unsigned int prev = UINT_MAX, state = 0; /* Check pre-context */ for (unsigned int i = 0; i < buffer->context_len[0]; i++) @@ -315,7 +316,7 @@ arabic_joining (hb_buffer_t *buffer) const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; - if (entry->prev_action != NONE && prev != (unsigned int) -1) + if (entry->prev_action != NONE && prev != UINT_MAX) { info[prev].arabic_shaping_action() = entry->prev_action; buffer->unsafe_to_break (prev, i + 1); @@ -335,7 +336,7 @@ arabic_joining (hb_buffer_t *buffer) continue; const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; - if (entry->prev_action != NONE && prev != (unsigned int) -1) + if (entry->prev_action != NONE && prev != UINT_MAX) info[prev].arabic_shaping_action() = entry->prev_action; break; } @@ -383,6 +384,10 @@ arabic_fallback_shape (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK + return; +#endif + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; if (!arabic_plan->do_fallback) @@ -467,7 +472,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int j = new_len; for (unsigned int i = count; i; i--) { - if (!hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + if (!hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) { if (step == CUT) { @@ -488,7 +493,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int end = i; while (i && - hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + hb_in_range (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) { i--; hb_position_t width = font->get_glyph_h_advance (info[i].codepoint); @@ -506,7 +511,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, unsigned int start = i; unsigned int context = i; while (context && - !hb_in_range (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && + !hb_in_range (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) { @@ -597,7 +602,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); } -/* http://www.unicode.org/reports/tr53/ */ +/* https://www.unicode.org/reports/tr53/ */ static hb_codepoint_t modifier_combining_marks[] = @@ -706,3 +711,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc index 97923ecf69208..a755aea098b6c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-default.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -44,3 +48,26 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + +/* Same as default but no mark advance zeroing / fallback positioning. + * Dumbest shaper ever, basically. */ +const hb_ot_complex_shaper_t _hb_ot_complex_shaper_dumber = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + HB_TAG_NONE, /* gpos_tag */ + nullptr, /* reorder_marks */ + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc index c77cac163e3c3..d7db43755ec92 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hangul.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -214,7 +218,8 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, else { /* No valid syllable as base for tone mark; try to insert dotted circle. */ - if (font->has_glyph (0x25CCu)) + if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) && + font->has_glyph (0x25CCu)) { hb_codepoint_t chars[2]; if (!is_zero_width_char (font, u)) { @@ -429,3 +434,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc index 1d80a46bb233b..5754080586847 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-hebrew.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -70,6 +74,10 @@ compose_hebrew (const hb_ot_shape_normalize_context_t *c, bool found = (bool) c->unicode->compose (a, b, ab); +#ifdef HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK + return found; +#endif + if (!found && !c->plan->has_gpos_mark) { /* Special-case Hebrew presentation forms that are excluded from @@ -172,3 +180,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, true, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh index 9fc63157ed952..4c04e0ca6ecd1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-machine.hh @@ -34,711 +34,284 @@ #line 36 "hb-ot-shape-complex-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { - 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, - 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, - 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, - 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 4u, 8u, - 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 8u, 8u, 4u, 8u, 5u, 7u, 7u, 7u, - 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, - 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, - 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, - 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 8u, 8u, 4u, 8u, 5u, 7u, - 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, 5u, 7u, 7u, 7u, 5u, 8u, - 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, - 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, - 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 13u, - 5u, 8u, 8u, 8u, 1u, 19u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, - 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, - 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, - 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 10u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, - 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, - 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, - 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, - 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 13u, - 3u, 10u, 4u, 8u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, - 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, - 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 1u, 16u, 3u, 13u, - 1u, 16u, 4u, 13u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 3u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 0 + 8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, + 4u, 13u, 4u, 8u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, + 4u, 8u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, + 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, + 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 5u, 8u, 8u, 8u, 1u, 19u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, + 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, + 3u, 10u, 5u, 10u, 3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, + 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, + 5u, 10u, 3u, 17u, 3u, 17u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, + 3u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, + 4u, 10u, 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, + 3u, 17u, 4u, 13u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, + 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 1u, 17u, + 3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u, + 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 0 }; static const char _indic_syllable_machine_key_spans[] = { - 1, 5, 3, 1, 4, 3, 1, 4, - 3, 1, 4, 3, 1, 5, 1, 1, - 5, 1, 1, 5, 1, 1, 5, 1, - 1, 10, 5, 10, 5, 10, 5, 10, - 5, 10, 1, 5, 3, 1, 4, 3, - 1, 4, 3, 1, 4, 3, 1, 5, - 1, 1, 5, 1, 1, 5, 1, 1, - 5, 1, 1, 10, 5, 10, 5, 10, - 5, 10, 5, 10, 1, 5, 3, 1, - 4, 3, 1, 4, 3, 1, 4, 3, - 1, 5, 1, 1, 5, 1, 1, 5, - 1, 1, 5, 1, 1, 10, 5, 10, - 5, 10, 5, 10, 5, 1, 5, 3, - 1, 4, 3, 1, 4, 3, 1, 4, - 3, 1, 5, 1, 1, 5, 1, 1, - 5, 1, 1, 5, 1, 1, 10, 5, - 10, 5, 10, 5, 10, 5, 10, 10, - 4, 1, 19, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 6, 6, - 6, 1, 1, 1, 6, 8, 6, 8, - 7, 6, 8, 7, 6, 8, 7, 6, - 8, 7, 8, 11, 16, 16, 16, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 6, 6, 6, 1, 1, - 1, 6, 8, 6, 8, 7, 6, 8, - 7, 6, 8, 7, 6, 8, 7, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 5, 8, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 6, - 6, 6, 1, 1, 1, 6, 8, 6, - 8, 7, 6, 8, 7, 6, 8, 7, - 6, 8, 7, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 11, 16, - 16, 16, 8, 11, 16, 16, 16, 10, - 8, 5, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 6, 6, 6, - 1, 1, 1, 6, 8, 6, 8, 7, - 6, 8, 7, 6, 8, 7, 6, 8, - 7, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 16, 11, - 16, 10, 6, 1, 1, 1, 6, 16, - 8, 6, 6, 1, 1, 1, 6, 16 + 1, 5, 3, 4, 5, 1, 1, 5, + 10, 5, 1, 3, 4, 5, 1, 1, + 5, 10, 10, 10, 1, 3, 4, 5, + 1, 1, 5, 5, 10, 1, 3, 4, + 5, 1, 1, 5, 5, 4, 1, 19, + 15, 15, 14, 16, 6, 6, 1, 6, + 16, 16, 16, 8, 7, 6, 7, 6, + 8, 6, 15, 15, 15, 15, 14, 16, + 15, 15, 14, 16, 6, 1, 6, 16, + 16, 8, 7, 6, 7, 6, 6, 8, + 6, 15, 15, 5, 15, 15, 14, 16, + 15, 16, 6, 1, 6, 16, 16, 8, + 7, 6, 15, 7, 6, 6, 8, 6, + 15, 10, 5, 15, 15, 14, 16, 15, + 16, 6, 1, 6, 16, 16, 8, 7, + 6, 15, 7, 6, 6, 8, 6, 17, + 15, 17, 10, 6, 1, 6, 16, 8, + 6, 6, 1, 6, 16 }; static const short _indic_syllable_machine_index_offsets[] = { - 0, 2, 8, 12, 14, 19, 23, 25, - 30, 34, 36, 41, 45, 47, 53, 55, - 57, 63, 65, 67, 73, 75, 77, 83, - 85, 87, 98, 104, 115, 121, 132, 138, - 149, 155, 166, 168, 174, 178, 180, 185, - 189, 191, 196, 200, 202, 207, 211, 213, - 219, 221, 223, 229, 231, 233, 239, 241, - 243, 249, 251, 253, 264, 270, 281, 287, - 298, 304, 315, 321, 332, 334, 340, 344, - 346, 351, 355, 357, 362, 366, 368, 373, - 377, 379, 385, 387, 389, 395, 397, 399, - 405, 407, 409, 415, 417, 419, 430, 436, - 447, 453, 464, 470, 481, 487, 489, 495, - 499, 501, 506, 510, 512, 517, 521, 523, - 528, 532, 534, 540, 542, 544, 550, 552, - 554, 560, 562, 564, 570, 572, 574, 585, - 591, 602, 608, 619, 625, 636, 642, 653, - 664, 669, 671, 691, 703, 712, 720, 737, - 749, 758, 766, 783, 795, 804, 812, 829, - 841, 850, 858, 875, 887, 896, 904, 911, - 918, 925, 927, 929, 931, 938, 947, 954, - 963, 971, 978, 987, 995, 1002, 1011, 1019, - 1026, 1035, 1043, 1052, 1064, 1081, 1098, 1115, - 1124, 1136, 1153, 1170, 1187, 1196, 1208, 1225, - 1242, 1259, 1268, 1280, 1297, 1314, 1331, 1340, - 1352, 1364, 1373, 1381, 1398, 1410, 1419, 1427, - 1444, 1456, 1465, 1473, 1490, 1502, 1511, 1519, - 1536, 1548, 1557, 1565, 1572, 1579, 1586, 1588, - 1590, 1592, 1599, 1608, 1615, 1624, 1632, 1639, - 1648, 1656, 1663, 1672, 1680, 1687, 1696, 1704, - 1713, 1725, 1742, 1759, 1776, 1785, 1797, 1814, - 1831, 1848, 1857, 1869, 1886, 1903, 1920, 1929, - 1941, 1958, 1975, 1992, 1998, 2007, 2016, 2024, - 2041, 2053, 2062, 2070, 2087, 2099, 2108, 2116, - 2133, 2145, 2154, 2162, 2179, 2191, 2200, 2208, - 2215, 2222, 2229, 2231, 2233, 2235, 2242, 2251, - 2258, 2267, 2275, 2282, 2291, 2299, 2306, 2315, - 2323, 2330, 2339, 2347, 2356, 2368, 2385, 2402, - 2419, 2428, 2440, 2457, 2474, 2491, 2500, 2512, - 2529, 2546, 2563, 2572, 2584, 2601, 2618, 2635, - 2646, 2655, 2661, 2673, 2682, 2690, 2707, 2719, - 2728, 2736, 2753, 2765, 2774, 2782, 2799, 2811, - 2820, 2828, 2845, 2857, 2866, 2874, 2881, 2888, - 2895, 2897, 2899, 2901, 2908, 2917, 2924, 2933, - 2941, 2948, 2957, 2965, 2972, 2981, 2989, 2996, - 3005, 3013, 3022, 3034, 3051, 3068, 3085, 3094, - 3106, 3123, 3140, 3157, 3166, 3178, 3195, 3212, - 3229, 3238, 3250, 3267, 3284, 3301, 3310, 3327, - 3339, 3356, 3367, 3374, 3376, 3378, 3380, 3387, - 3404, 3413, 3420, 3427, 3429, 3431, 3433, 3440 + 0, 2, 8, 12, 17, 23, 25, 27, + 33, 44, 50, 52, 56, 61, 67, 69, + 71, 77, 88, 99, 110, 112, 116, 121, + 127, 129, 131, 137, 143, 154, 156, 160, + 165, 171, 173, 175, 181, 187, 192, 194, + 214, 230, 246, 261, 278, 285, 292, 294, + 301, 318, 335, 352, 361, 369, 376, 384, + 391, 400, 407, 423, 439, 455, 471, 486, + 503, 519, 535, 550, 567, 574, 576, 583, + 600, 617, 626, 634, 641, 649, 656, 663, + 672, 679, 695, 711, 717, 733, 749, 764, + 781, 797, 814, 821, 823, 830, 847, 864, + 873, 881, 888, 904, 912, 919, 926, 935, + 942, 958, 969, 975, 991, 1007, 1022, 1039, + 1055, 1072, 1079, 1081, 1088, 1105, 1122, 1131, + 1139, 1146, 1162, 1170, 1177, 1184, 1193, 1200, + 1218, 1234, 1252, 1263, 1270, 1272, 1279, 1296, + 1305, 1312, 1319, 1321, 1328 }; -static const short _indic_syllable_machine_indicies[] = { +static const unsigned char _indic_syllable_machine_indicies[] = { 1, 0, 2, 3, 3, 4, 1, 0, - 5, 5, 4, 0, 4, 0, 6, 6, - 7, 1, 0, 8, 8, 7, 0, 7, - 0, 9, 9, 10, 1, 0, 11, 11, - 10, 0, 10, 0, 12, 12, 13, 1, - 0, 14, 14, 13, 0, 13, 0, 15, - 0, 0, 0, 1, 0, 16, 0, 17, - 0, 18, 12, 12, 13, 1, 0, 19, - 0, 20, 0, 21, 9, 9, 10, 1, - 0, 22, 0, 23, 0, 24, 6, 6, - 7, 1, 0, 25, 0, 26, 0, 2, - 3, 3, 4, 1, 0, 0, 0, 0, - 27, 0, 28, 3, 3, 4, 1, 0, - 28, 3, 3, 4, 1, 0, 0, 0, - 0, 29, 0, 30, 3, 3, 4, 1, - 0, 30, 3, 3, 4, 1, 0, 0, - 0, 0, 31, 0, 32, 3, 3, 4, - 1, 0, 32, 3, 3, 4, 1, 0, - 0, 0, 0, 33, 0, 34, 3, 3, - 4, 1, 0, 34, 3, 3, 4, 1, - 0, 0, 0, 0, 35, 0, 37, 36, - 38, 39, 39, 40, 37, 36, 41, 41, - 40, 36, 40, 36, 42, 42, 43, 37, - 36, 44, 44, 43, 36, 43, 36, 45, - 45, 46, 37, 36, 47, 47, 46, 36, - 46, 36, 48, 48, 49, 37, 36, 50, - 50, 49, 36, 49, 36, 51, 36, 36, - 36, 37, 36, 52, 36, 53, 36, 54, - 48, 48, 49, 37, 36, 55, 36, 56, - 36, 57, 45, 45, 46, 37, 36, 58, - 36, 59, 36, 60, 42, 42, 43, 37, - 36, 61, 36, 62, 36, 38, 39, 39, - 40, 37, 36, 36, 36, 36, 63, 36, - 64, 39, 39, 40, 37, 36, 64, 39, - 39, 40, 37, 36, 36, 36, 36, 65, - 36, 66, 39, 39, 40, 37, 36, 66, - 39, 39, 40, 37, 36, 36, 36, 36, - 67, 36, 68, 39, 39, 40, 37, 36, - 68, 39, 39, 40, 37, 36, 36, 36, - 36, 69, 36, 70, 39, 39, 40, 37, - 36, 70, 39, 39, 40, 37, 36, 36, - 36, 36, 71, 36, 73, 72, 74, 75, - 75, 76, 73, 72, 78, 78, 76, 77, - 76, 77, 79, 79, 80, 73, 72, 81, - 81, 80, 72, 80, 72, 82, 82, 83, - 73, 72, 84, 84, 83, 72, 83, 72, - 85, 85, 86, 73, 72, 87, 87, 86, - 72, 86, 72, 88, 72, 72, 72, 73, - 72, 89, 72, 90, 72, 91, 85, 85, - 86, 73, 72, 92, 72, 93, 72, 94, - 82, 82, 83, 73, 72, 95, 72, 96, - 72, 97, 79, 79, 80, 73, 72, 98, - 72, 99, 72, 74, 75, 75, 76, 73, - 72, 72, 72, 72, 100, 72, 101, 75, - 75, 76, 73, 72, 101, 75, 75, 76, - 73, 72, 72, 72, 72, 102, 72, 103, - 75, 75, 76, 73, 72, 103, 75, 75, - 76, 73, 72, 72, 72, 72, 104, 72, - 105, 75, 75, 76, 73, 72, 105, 75, - 75, 76, 73, 72, 72, 72, 72, 106, - 72, 107, 75, 75, 76, 73, 72, 109, - 108, 110, 111, 111, 112, 109, 108, 113, - 113, 112, 108, 112, 108, 114, 114, 115, - 109, 108, 116, 116, 115, 108, 115, 108, - 117, 117, 118, 109, 108, 119, 119, 118, - 108, 118, 108, 120, 120, 121, 109, 108, - 122, 122, 121, 108, 121, 108, 123, 108, - 108, 108, 109, 108, 124, 108, 125, 108, - 126, 120, 120, 121, 109, 108, 127, 108, - 128, 108, 129, 117, 117, 118, 109, 108, - 130, 108, 131, 108, 132, 114, 114, 115, - 109, 108, 133, 108, 134, 108, 110, 111, - 111, 112, 109, 108, 108, 108, 108, 135, - 108, 136, 111, 111, 112, 109, 108, 136, - 111, 111, 112, 109, 108, 108, 108, 108, - 137, 108, 138, 111, 111, 112, 109, 108, - 138, 111, 111, 112, 109, 108, 108, 108, - 108, 139, 108, 140, 111, 111, 112, 109, - 108, 140, 111, 111, 112, 109, 108, 108, - 108, 108, 141, 108, 142, 111, 111, 112, - 109, 108, 142, 111, 111, 112, 109, 108, - 108, 108, 108, 143, 108, 107, 75, 75, - 76, 73, 72, 72, 72, 72, 144, 72, - 78, 78, 76, 1, 0, 146, 145, 148, - 149, 150, 151, 152, 153, 76, 73, 147, - 154, 155, 155, 144, 147, 156, 157, 147, - 158, 159, 147, 161, 162, 163, 164, 4, - 1, 160, 165, 160, 160, 35, 160, 166, - 162, 167, 167, 4, 1, 160, 165, 160, - 162, 167, 167, 4, 1, 160, 165, 160, - 168, 160, 160, 160, 17, 169, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 170, 171, 172, 173, 4, 1, 160, - 165, 160, 160, 33, 160, 174, 171, 175, - 175, 4, 1, 160, 165, 160, 171, 175, - 175, 4, 1, 160, 165, 160, 176, 160, - 160, 160, 17, 177, 160, 1, 160, 165, - 160, 160, 160, 160, 160, 176, 160, 178, - 179, 180, 181, 4, 1, 160, 165, 160, - 160, 31, 160, 182, 179, 183, 183, 4, - 1, 160, 165, 160, 179, 183, 183, 4, - 1, 160, 165, 160, 184, 160, 160, 160, - 17, 185, 160, 1, 160, 165, 160, 160, - 160, 160, 160, 184, 160, 186, 187, 188, - 189, 4, 1, 160, 165, 160, 160, 29, - 160, 190, 187, 191, 191, 4, 1, 160, - 165, 160, 187, 191, 191, 4, 1, 160, - 165, 160, 192, 160, 160, 160, 17, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 194, 195, 196, 197, 4, - 1, 160, 165, 160, 160, 27, 160, 198, - 195, 199, 199, 4, 1, 160, 165, 160, - 195, 199, 199, 4, 1, 160, 165, 160, - 17, 200, 160, 1, 160, 165, 160, 201, - 201, 160, 1, 160, 165, 160, 202, 160, - 160, 203, 160, 165, 160, 165, 160, 204, - 160, 205, 160, 202, 160, 160, 160, 160, - 165, 160, 17, 160, 201, 201, 160, 1, - 160, 165, 160, 201, 200, 160, 1, 160, - 165, 160, 206, 26, 207, 208, 7, 1, - 160, 165, 160, 26, 207, 208, 7, 1, - 160, 165, 160, 207, 207, 7, 1, 160, - 165, 160, 209, 23, 210, 211, 10, 1, - 160, 165, 160, 23, 210, 211, 10, 1, - 160, 165, 160, 210, 210, 10, 1, 160, - 165, 160, 212, 20, 213, 214, 13, 1, - 160, 165, 160, 20, 213, 214, 13, 1, - 160, 165, 160, 213, 213, 13, 1, 160, - 165, 160, 215, 17, 201, 216, 160, 1, - 160, 165, 160, 17, 201, 216, 160, 1, - 160, 165, 160, 194, 195, 199, 199, 4, - 1, 160, 165, 160, 194, 195, 196, 199, - 4, 1, 160, 165, 160, 160, 27, 160, - 192, 160, 217, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 192, - 160, 192, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 192, 160, 192, 160, 160, 160, 201, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 186, 187, 191, 191, 4, - 1, 160, 165, 160, 186, 187, 188, 191, - 4, 1, 160, 165, 160, 160, 29, 160, - 184, 160, 218, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 184, - 160, 184, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 184, 160, 184, 160, 160, 160, 201, 185, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 184, 160, 178, 179, 183, 183, 4, - 1, 160, 165, 160, 178, 179, 180, 183, - 4, 1, 160, 165, 160, 160, 31, 160, - 176, 160, 219, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 176, - 160, 176, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 176, 160, 176, 160, 160, 160, 201, 177, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 176, 160, 170, 171, 175, 175, 4, - 1, 160, 165, 160, 170, 171, 172, 175, - 4, 1, 160, 165, 160, 160, 33, 160, - 168, 160, 220, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 168, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 168, 160, 168, 160, 160, 160, 201, 169, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 168, 160, 161, 162, 167, 167, 4, - 1, 160, 165, 160, 161, 162, 163, 167, - 4, 1, 160, 165, 160, 160, 35, 160, - 222, 223, 224, 225, 40, 37, 221, 226, - 221, 221, 71, 221, 227, 223, 228, 225, - 40, 37, 221, 226, 221, 223, 228, 225, - 40, 37, 221, 226, 221, 229, 221, 221, - 221, 53, 230, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 231, 232, - 233, 234, 40, 37, 221, 226, 221, 221, - 69, 221, 235, 232, 236, 236, 40, 37, - 221, 226, 221, 232, 236, 236, 40, 37, - 221, 226, 221, 237, 221, 221, 221, 53, - 238, 221, 37, 221, 226, 221, 221, 221, - 221, 221, 237, 221, 239, 240, 241, 242, - 40, 37, 221, 226, 221, 221, 67, 221, - 243, 240, 244, 244, 40, 37, 221, 226, - 221, 240, 244, 244, 40, 37, 221, 226, - 221, 245, 221, 221, 221, 53, 246, 221, - 37, 221, 226, 221, 221, 221, 221, 221, - 245, 221, 247, 248, 249, 250, 40, 37, - 221, 226, 221, 221, 65, 221, 251, 248, - 252, 252, 40, 37, 221, 226, 221, 248, - 252, 252, 40, 37, 221, 226, 221, 253, - 221, 221, 221, 53, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 255, 256, 257, 258, 40, 37, 221, 226, - 221, 221, 63, 221, 259, 256, 260, 260, - 40, 37, 221, 226, 221, 256, 260, 260, - 40, 37, 221, 226, 221, 53, 261, 221, - 37, 221, 226, 221, 262, 262, 221, 37, - 221, 226, 221, 263, 221, 221, 264, 221, - 226, 221, 226, 221, 265, 221, 266, 221, - 263, 221, 221, 221, 221, 226, 221, 53, - 221, 262, 262, 221, 37, 221, 226, 221, - 262, 261, 221, 37, 221, 226, 221, 267, - 62, 268, 269, 43, 37, 221, 226, 221, - 62, 268, 269, 43, 37, 221, 226, 221, - 268, 268, 43, 37, 221, 226, 221, 270, - 59, 271, 272, 46, 37, 221, 226, 221, - 59, 271, 272, 46, 37, 221, 226, 221, - 271, 271, 46, 37, 221, 226, 221, 273, - 56, 274, 275, 49, 37, 221, 226, 221, - 56, 274, 275, 49, 37, 221, 226, 221, - 274, 274, 49, 37, 221, 226, 221, 276, - 53, 262, 277, 221, 37, 221, 226, 221, - 53, 262, 277, 221, 37, 221, 226, 221, - 255, 256, 260, 260, 40, 37, 221, 226, - 221, 255, 256, 257, 260, 40, 37, 221, - 226, 221, 221, 63, 221, 253, 221, 278, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 253, 221, 253, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 253, 221, 253, - 221, 221, 221, 262, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 247, 248, 252, 252, 40, 37, 221, 226, - 221, 247, 248, 249, 252, 40, 37, 221, - 226, 221, 221, 65, 221, 245, 221, 279, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 245, 221, 245, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 245, 221, 245, - 221, 221, 221, 262, 246, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 245, 221, - 239, 240, 244, 244, 40, 37, 221, 226, - 221, 239, 240, 241, 244, 40, 37, 221, - 226, 221, 221, 67, 221, 237, 221, 280, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 237, 221, 237, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 237, 221, 237, - 221, 221, 221, 262, 238, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 237, 221, - 231, 232, 236, 236, 40, 37, 221, 226, - 221, 231, 232, 233, 236, 40, 37, 221, - 226, 221, 221, 69, 221, 229, 221, 281, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 229, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 229, 221, 229, - 221, 221, 221, 262, 230, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 229, 221, - 70, 39, 39, 40, 37, 221, 222, 223, - 228, 225, 40, 37, 221, 226, 221, 283, - 151, 284, 284, 76, 73, 282, 154, 282, - 151, 284, 284, 76, 73, 282, 154, 282, - 285, 282, 282, 282, 90, 286, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 287, 288, 289, 290, 76, 73, 282, - 154, 282, 282, 106, 282, 291, 288, 292, - 292, 76, 73, 282, 154, 282, 288, 292, - 292, 76, 73, 282, 154, 282, 293, 282, - 282, 282, 90, 294, 282, 73, 282, 154, - 282, 282, 282, 282, 282, 293, 282, 295, - 296, 297, 298, 76, 73, 282, 154, 282, - 282, 104, 282, 299, 296, 300, 300, 76, - 73, 282, 154, 282, 296, 300, 300, 76, - 73, 282, 154, 282, 301, 282, 282, 282, - 90, 302, 282, 73, 282, 154, 282, 282, - 282, 282, 282, 301, 282, 303, 304, 305, - 306, 76, 73, 282, 154, 282, 282, 102, - 282, 307, 304, 308, 308, 76, 73, 282, - 154, 282, 304, 308, 308, 76, 73, 282, - 154, 282, 309, 282, 282, 282, 90, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 311, 312, 313, 314, 76, - 73, 282, 154, 282, 282, 100, 282, 315, - 312, 316, 316, 76, 73, 282, 154, 282, - 312, 316, 316, 76, 73, 282, 154, 282, - 90, 317, 282, 73, 282, 154, 282, 318, - 318, 282, 73, 282, 154, 282, 319, 282, - 282, 320, 282, 154, 282, 154, 282, 321, - 282, 322, 282, 319, 282, 282, 282, 282, - 154, 282, 90, 282, 318, 318, 282, 73, - 282, 154, 282, 318, 317, 282, 73, 282, - 154, 282, 323, 99, 324, 325, 80, 73, - 282, 154, 282, 99, 324, 325, 80, 73, - 282, 154, 282, 324, 324, 80, 73, 282, - 154, 282, 326, 96, 327, 328, 83, 73, - 282, 154, 282, 96, 327, 328, 83, 73, - 282, 154, 282, 327, 327, 83, 73, 282, - 154, 282, 329, 93, 330, 331, 86, 73, - 282, 154, 282, 93, 330, 331, 86, 73, - 282, 154, 282, 330, 330, 86, 73, 282, - 154, 282, 332, 90, 318, 333, 282, 73, - 282, 154, 282, 90, 318, 333, 282, 73, - 282, 154, 282, 311, 312, 316, 316, 76, - 73, 282, 154, 282, 311, 312, 313, 316, - 76, 73, 282, 154, 282, 282, 100, 282, - 309, 282, 334, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 309, - 282, 309, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 309, 282, 309, 282, 282, 282, 318, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 303, 304, 308, 308, 76, - 73, 282, 154, 282, 303, 304, 305, 308, - 76, 73, 282, 154, 282, 282, 102, 282, - 301, 282, 335, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 301, - 282, 301, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 301, 282, 301, 282, 282, 282, 318, 302, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 301, 282, 295, 296, 300, 300, 76, - 73, 282, 154, 282, 295, 296, 297, 300, - 76, 73, 282, 154, 282, 282, 104, 282, - 293, 282, 336, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 293, - 282, 293, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 293, 282, 293, 282, 282, 282, 318, 294, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 293, 282, 287, 288, 292, 292, 76, - 73, 282, 154, 282, 287, 288, 289, 292, - 76, 73, 282, 154, 282, 282, 106, 282, - 285, 282, 337, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 285, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 285, 282, 285, 282, 282, 282, 318, 286, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 285, 282, 107, 75, 75, 76, 73, - 338, 338, 338, 338, 144, 338, 150, 151, - 284, 284, 76, 73, 282, 154, 282, 107, - 75, 75, 76, 73, 338, 340, 341, 342, - 343, 112, 109, 339, 344, 339, 339, 143, - 339, 345, 341, 343, 343, 112, 109, 339, - 344, 339, 341, 343, 343, 112, 109, 339, - 344, 339, 346, 339, 339, 339, 125, 347, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 348, 349, 350, 351, 112, - 109, 339, 344, 339, 339, 141, 339, 352, - 349, 353, 353, 112, 109, 339, 344, 339, - 349, 353, 353, 112, 109, 339, 344, 339, - 354, 339, 339, 339, 125, 355, 339, 109, - 339, 344, 339, 339, 339, 339, 339, 354, - 339, 356, 357, 358, 359, 112, 109, 339, - 344, 339, 339, 139, 339, 360, 357, 361, - 361, 112, 109, 339, 344, 339, 357, 361, - 361, 112, 109, 339, 344, 339, 362, 339, - 339, 339, 125, 363, 339, 109, 339, 344, - 339, 339, 339, 339, 339, 362, 339, 364, - 365, 366, 367, 112, 109, 339, 344, 339, - 339, 137, 339, 368, 365, 369, 369, 112, - 109, 339, 344, 339, 365, 369, 369, 112, - 109, 339, 344, 339, 370, 339, 339, 339, - 125, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 372, 373, 374, - 375, 112, 109, 339, 344, 339, 339, 135, - 339, 376, 373, 377, 377, 112, 109, 339, - 344, 339, 373, 377, 377, 112, 109, 339, - 344, 339, 125, 378, 339, 109, 339, 344, - 339, 379, 379, 339, 109, 339, 344, 339, - 380, 339, 339, 381, 339, 344, 339, 344, - 339, 382, 339, 383, 339, 380, 339, 339, - 339, 339, 344, 339, 125, 339, 379, 379, - 339, 109, 339, 344, 339, 379, 378, 339, - 109, 339, 344, 339, 384, 134, 385, 386, - 115, 109, 339, 344, 339, 134, 385, 386, - 115, 109, 339, 344, 339, 385, 385, 115, - 109, 339, 344, 339, 387, 131, 388, 389, - 118, 109, 339, 344, 339, 131, 388, 389, - 118, 109, 339, 344, 339, 388, 388, 118, - 109, 339, 344, 339, 390, 128, 391, 392, - 121, 109, 339, 344, 339, 128, 391, 392, - 121, 109, 339, 344, 339, 391, 391, 121, - 109, 339, 344, 339, 393, 125, 379, 394, - 339, 109, 339, 344, 339, 125, 379, 394, - 339, 109, 339, 344, 339, 372, 373, 377, - 377, 112, 109, 339, 344, 339, 372, 373, - 374, 377, 112, 109, 339, 344, 339, 339, - 135, 339, 370, 339, 395, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 370, 339, 370, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 370, 339, 370, 339, 339, 339, - 379, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 364, 365, 369, - 369, 112, 109, 339, 344, 339, 364, 365, - 366, 369, 112, 109, 339, 344, 339, 339, - 137, 339, 362, 339, 396, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 362, 339, 362, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 362, 339, 362, 339, 339, 339, - 379, 363, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 362, 339, 356, 357, 361, - 361, 112, 109, 339, 344, 339, 356, 357, - 358, 361, 112, 109, 339, 344, 339, 339, - 139, 339, 354, 339, 397, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 354, 339, 354, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 354, 339, 354, 339, 339, 339, - 379, 355, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 354, 339, 348, 349, 353, - 353, 112, 109, 339, 344, 339, 348, 349, - 350, 353, 112, 109, 339, 344, 339, 339, - 141, 339, 346, 339, 398, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 346, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 346, 339, 346, 339, 339, 339, - 379, 347, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 346, 339, 340, 341, 343, - 343, 112, 109, 339, 344, 339, 148, 149, - 150, 151, 399, 284, 76, 73, 282, 154, - 155, 155, 144, 282, 282, 148, 282, 161, - 400, 163, 164, 4, 1, 160, 165, 160, - 160, 35, 160, 168, 149, 150, 151, 401, - 402, 76, 403, 160, 404, 160, 155, 144, - 160, 160, 168, 160, 107, 405, 405, 76, - 403, 160, 165, 160, 160, 144, 160, 406, - 160, 160, 407, 160, 404, 160, 404, 160, - 408, 160, 205, 160, 406, 160, 160, 160, - 160, 404, 160, 168, 160, 220, 107, 405, - 405, 76, 403, 160, 165, 160, 160, 160, - 160, 160, 168, 160, 410, 409, 411, 411, - 409, 146, 409, 412, 409, 411, 411, 409, - 146, 409, 412, 409, 413, 409, 409, 414, - 409, 412, 409, 412, 409, 415, 409, 416, - 409, 413, 409, 409, 409, 409, 412, 409, - 148, 338, 338, 338, 338, 338, 338, 338, - 338, 338, 155, 338, 338, 338, 338, 148, - 338, 0 + 3, 3, 4, 0, 3, 3, 4, 1, + 0, 5, 3, 3, 4, 1, 0, 6, + 0, 7, 0, 8, 3, 3, 4, 1, + 0, 2, 3, 3, 4, 1, 0, 0, + 0, 0, 9, 0, 11, 12, 12, 13, + 14, 10, 14, 10, 12, 12, 13, 10, + 12, 12, 13, 14, 10, 15, 12, 12, + 13, 14, 10, 16, 10, 17, 10, 18, + 12, 12, 13, 14, 10, 11, 12, 12, + 13, 14, 10, 10, 10, 10, 19, 10, + 11, 12, 12, 13, 14, 10, 10, 10, + 10, 20, 10, 22, 23, 23, 24, 25, + 21, 21, 21, 21, 26, 21, 25, 21, + 23, 23, 24, 27, 23, 23, 24, 25, + 21, 28, 23, 23, 24, 25, 21, 29, + 21, 30, 21, 22, 23, 23, 24, 25, + 21, 31, 23, 23, 24, 25, 21, 33, + 34, 34, 35, 36, 32, 32, 32, 32, + 37, 32, 36, 32, 34, 34, 35, 32, + 34, 34, 35, 36, 32, 38, 34, 34, + 35, 36, 32, 39, 32, 40, 32, 33, + 34, 34, 35, 36, 32, 41, 34, 34, + 35, 36, 32, 23, 23, 24, 1, 0, + 43, 42, 45, 46, 47, 48, 49, 50, + 24, 25, 44, 51, 52, 52, 26, 44, + 53, 54, 55, 56, 57, 44, 59, 60, + 61, 62, 4, 1, 58, 63, 58, 58, + 9, 58, 58, 58, 64, 58, 65, 60, + 66, 66, 4, 1, 58, 63, 58, 58, + 58, 58, 58, 58, 64, 58, 60, 66, + 66, 4, 1, 58, 63, 58, 58, 58, + 58, 58, 58, 64, 58, 45, 58, 58, + 58, 67, 68, 58, 1, 58, 63, 58, + 58, 58, 58, 58, 45, 58, 69, 69, + 58, 1, 58, 63, 58, 63, 58, 58, + 70, 58, 63, 58, 63, 58, 63, 58, + 58, 58, 58, 63, 58, 45, 58, 71, + 58, 69, 69, 58, 1, 58, 63, 58, + 58, 58, 58, 58, 45, 58, 45, 58, + 58, 58, 69, 69, 58, 1, 58, 63, + 58, 58, 58, 58, 58, 45, 58, 45, + 58, 58, 58, 69, 68, 58, 1, 58, + 63, 58, 58, 58, 58, 58, 45, 58, + 72, 7, 73, 74, 4, 1, 58, 63, + 58, 7, 73, 74, 4, 1, 58, 63, + 58, 73, 73, 4, 1, 58, 63, 58, + 75, 76, 76, 4, 1, 58, 63, 58, + 67, 77, 58, 1, 58, 63, 58, 67, + 58, 69, 69, 58, 1, 58, 63, 58, + 69, 77, 58, 1, 58, 63, 58, 59, + 60, 66, 66, 4, 1, 58, 63, 58, + 58, 58, 58, 58, 58, 64, 58, 59, + 60, 61, 66, 4, 1, 58, 63, 58, + 58, 9, 58, 58, 58, 64, 58, 79, + 80, 81, 82, 13, 14, 78, 83, 78, + 78, 20, 78, 78, 78, 84, 78, 85, + 80, 86, 82, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 80, + 86, 82, 13, 14, 78, 83, 78, 78, + 78, 78, 78, 78, 84, 78, 87, 78, + 78, 78, 88, 89, 78, 14, 78, 83, + 78, 78, 78, 78, 78, 87, 78, 90, + 80, 91, 92, 13, 14, 78, 83, 78, + 78, 19, 78, 78, 78, 84, 78, 93, + 80, 86, 86, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 80, + 86, 86, 13, 14, 78, 83, 78, 78, + 78, 78, 78, 78, 84, 78, 87, 78, + 78, 78, 94, 89, 78, 14, 78, 83, + 78, 78, 78, 78, 78, 87, 78, 83, + 78, 78, 95, 78, 83, 78, 83, 78, + 83, 78, 78, 78, 78, 83, 78, 87, + 78, 96, 78, 94, 94, 78, 14, 78, + 83, 78, 78, 78, 78, 78, 87, 78, + 87, 78, 78, 78, 94, 94, 78, 14, + 78, 83, 78, 78, 78, 78, 78, 87, + 78, 97, 17, 98, 99, 13, 14, 78, + 83, 78, 17, 98, 99, 13, 14, 78, + 83, 78, 98, 98, 13, 14, 78, 83, + 78, 100, 101, 101, 13, 14, 78, 83, + 78, 88, 102, 78, 14, 78, 83, 78, + 94, 94, 78, 14, 78, 83, 78, 88, + 78, 94, 94, 78, 14, 78, 83, 78, + 94, 102, 78, 14, 78, 83, 78, 90, + 80, 86, 86, 13, 14, 78, 83, 78, + 78, 78, 78, 78, 78, 84, 78, 90, + 80, 91, 86, 13, 14, 78, 83, 78, + 78, 19, 78, 78, 78, 84, 78, 11, + 12, 12, 13, 14, 78, 79, 80, 86, + 82, 13, 14, 78, 83, 78, 78, 78, + 78, 78, 78, 84, 78, 104, 48, 105, + 105, 24, 25, 103, 51, 103, 103, 103, + 103, 103, 103, 55, 103, 48, 105, 105, + 24, 25, 103, 51, 103, 103, 103, 103, + 103, 103, 55, 103, 106, 103, 103, 103, + 107, 108, 103, 25, 103, 51, 103, 103, + 103, 103, 103, 106, 103, 47, 48, 109, + 110, 24, 25, 103, 51, 103, 103, 26, + 103, 103, 103, 55, 103, 106, 103, 103, + 103, 111, 108, 103, 25, 103, 51, 103, + 103, 103, 103, 103, 106, 103, 51, 103, + 103, 112, 103, 51, 103, 51, 103, 51, + 103, 103, 103, 103, 51, 103, 106, 103, + 113, 103, 111, 111, 103, 25, 103, 51, + 103, 103, 103, 103, 103, 106, 103, 106, + 103, 103, 103, 111, 111, 103, 25, 103, + 51, 103, 103, 103, 103, 103, 106, 103, + 114, 30, 115, 116, 24, 25, 103, 51, + 103, 30, 115, 116, 24, 25, 103, 51, + 103, 115, 115, 24, 25, 103, 51, 103, + 47, 48, 105, 105, 24, 25, 103, 51, + 103, 103, 103, 103, 103, 103, 55, 103, + 117, 118, 118, 24, 25, 103, 51, 103, + 107, 119, 103, 25, 103, 51, 103, 111, + 111, 103, 25, 103, 51, 103, 107, 103, + 111, 111, 103, 25, 103, 51, 103, 111, + 119, 103, 25, 103, 51, 103, 47, 48, + 109, 105, 24, 25, 103, 51, 103, 103, + 26, 103, 103, 103, 55, 103, 22, 23, + 23, 24, 25, 120, 120, 120, 120, 26, + 120, 22, 23, 23, 24, 25, 120, 122, + 123, 124, 125, 35, 36, 121, 126, 121, + 121, 37, 121, 121, 121, 127, 121, 128, + 123, 125, 125, 35, 36, 121, 126, 121, + 121, 121, 121, 121, 121, 127, 121, 123, + 125, 125, 35, 36, 121, 126, 121, 121, + 121, 121, 121, 121, 127, 121, 129, 121, + 121, 121, 130, 131, 121, 36, 121, 126, + 121, 121, 121, 121, 121, 129, 121, 122, + 123, 124, 52, 35, 36, 121, 126, 121, + 121, 37, 121, 121, 121, 127, 121, 129, + 121, 121, 121, 132, 131, 121, 36, 121, + 126, 121, 121, 121, 121, 121, 129, 121, + 126, 121, 121, 133, 121, 126, 121, 126, + 121, 126, 121, 121, 121, 121, 126, 121, + 129, 121, 134, 121, 132, 132, 121, 36, + 121, 126, 121, 121, 121, 121, 121, 129, + 121, 129, 121, 121, 121, 132, 132, 121, + 36, 121, 126, 121, 121, 121, 121, 121, + 129, 121, 135, 40, 136, 137, 35, 36, + 121, 126, 121, 40, 136, 137, 35, 36, + 121, 126, 121, 136, 136, 35, 36, 121, + 126, 121, 122, 123, 125, 125, 35, 36, + 121, 126, 121, 121, 121, 121, 121, 121, + 127, 121, 138, 139, 139, 35, 36, 121, + 126, 121, 130, 140, 121, 36, 121, 126, + 121, 132, 132, 121, 36, 121, 126, 121, + 130, 121, 132, 132, 121, 36, 121, 126, + 121, 132, 140, 121, 36, 121, 126, 121, + 45, 46, 47, 48, 109, 105, 24, 25, + 103, 51, 52, 52, 26, 103, 103, 45, + 55, 103, 59, 141, 61, 62, 4, 1, + 58, 63, 58, 58, 9, 58, 58, 58, + 64, 58, 45, 46, 47, 48, 142, 143, + 24, 144, 58, 145, 58, 52, 26, 58, + 58, 45, 55, 58, 22, 146, 146, 24, + 144, 58, 63, 58, 58, 26, 58, 145, + 58, 58, 147, 58, 145, 58, 145, 58, + 145, 58, 58, 58, 58, 145, 58, 45, + 58, 71, 22, 146, 146, 24, 144, 58, + 63, 58, 58, 58, 58, 58, 45, 58, + 149, 148, 150, 150, 148, 43, 148, 151, + 148, 150, 150, 148, 43, 148, 151, 148, + 151, 148, 148, 152, 148, 151, 148, 151, + 148, 151, 148, 148, 148, 148, 151, 148, + 45, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 52, 120, 120, 120, 120, 45, + 120, 0 }; -static const short _indic_syllable_machine_trans_targs[] = { - 138, 160, 166, 2, 167, 3, 5, 170, - 6, 8, 173, 9, 11, 176, 12, 14, - 15, 159, 17, 18, 175, 20, 21, 172, - 23, 24, 169, 178, 182, 183, 187, 188, - 192, 193, 197, 198, 138, 221, 227, 36, - 228, 37, 39, 231, 40, 42, 234, 43, - 45, 237, 46, 48, 49, 220, 51, 52, - 236, 54, 55, 233, 57, 58, 230, 239, - 243, 244, 248, 249, 253, 254, 258, 260, - 138, 281, 287, 70, 288, 138, 71, 73, - 291, 74, 76, 294, 77, 79, 297, 80, - 82, 83, 280, 85, 86, 296, 88, 89, - 293, 91, 92, 290, 299, 303, 304, 308, - 309, 313, 314, 318, 138, 343, 349, 103, - 350, 104, 106, 353, 107, 109, 356, 110, - 112, 359, 113, 115, 116, 342, 118, 119, - 358, 121, 122, 355, 124, 125, 352, 361, - 365, 366, 370, 371, 375, 376, 380, 381, - 320, 138, 394, 138, 139, 200, 261, 263, - 319, 321, 283, 322, 382, 383, 392, 399, - 138, 140, 142, 33, 199, 162, 141, 32, - 143, 195, 144, 146, 31, 194, 145, 30, - 147, 190, 148, 150, 29, 189, 149, 28, - 151, 185, 152, 154, 27, 184, 153, 26, - 155, 180, 156, 158, 25, 179, 157, 1, - 165, 0, 161, 164, 163, 138, 168, 4, - 22, 171, 7, 19, 174, 10, 16, 177, - 13, 181, 186, 191, 196, 138, 201, 203, - 67, 259, 223, 202, 66, 204, 256, 205, - 207, 65, 255, 206, 64, 208, 251, 209, - 211, 63, 250, 210, 62, 212, 246, 213, - 215, 61, 245, 214, 60, 216, 241, 217, - 219, 59, 240, 218, 35, 226, 34, 222, - 225, 224, 138, 229, 38, 56, 232, 41, - 53, 235, 44, 50, 238, 47, 242, 247, - 252, 257, 138, 262, 100, 264, 316, 265, - 267, 99, 315, 266, 98, 268, 311, 269, - 271, 97, 310, 270, 96, 272, 306, 273, - 275, 95, 305, 274, 94, 276, 301, 277, - 279, 93, 300, 278, 69, 286, 68, 282, - 285, 284, 138, 289, 72, 90, 292, 75, - 87, 295, 78, 84, 298, 81, 302, 307, - 312, 317, 138, 138, 323, 325, 134, 133, - 345, 324, 326, 378, 327, 329, 132, 377, - 328, 131, 330, 373, 331, 333, 130, 372, - 332, 129, 334, 368, 335, 337, 128, 367, - 336, 127, 338, 363, 339, 341, 126, 362, - 340, 102, 348, 101, 344, 347, 346, 138, - 351, 105, 123, 354, 108, 120, 357, 111, - 117, 360, 114, 364, 369, 374, 379, 135, - 384, 385, 391, 386, 388, 136, 387, 390, - 389, 138, 393, 137, 396, 395, 398, 397, - 138 +static const unsigned char _indic_syllable_machine_trans_targs[] = { + 39, 45, 50, 2, 51, 5, 6, 53, + 57, 58, 39, 67, 11, 73, 68, 14, + 15, 75, 80, 81, 84, 39, 89, 21, + 95, 90, 98, 39, 24, 25, 97, 103, + 39, 112, 30, 118, 113, 121, 33, 34, + 120, 126, 39, 137, 39, 40, 60, 85, + 87, 105, 106, 91, 107, 127, 128, 99, + 135, 140, 39, 41, 43, 8, 59, 46, + 54, 42, 1, 44, 48, 0, 47, 49, + 52, 3, 4, 55, 7, 56, 39, 61, + 63, 18, 83, 69, 76, 62, 9, 64, + 78, 71, 65, 17, 82, 66, 10, 70, + 72, 74, 12, 13, 77, 16, 79, 39, + 86, 26, 88, 101, 93, 19, 104, 20, + 92, 94, 96, 22, 23, 100, 27, 102, + 39, 39, 108, 110, 28, 35, 114, 122, + 109, 111, 124, 116, 29, 115, 117, 119, + 31, 32, 123, 36, 125, 129, 130, 134, + 131, 132, 37, 133, 39, 136, 38, 138, + 139 }; static const char _indic_syllable_machine_trans_actions[] = { 1, 0, 2, 0, 2, 0, 0, 2, - 0, 0, 2, 0, 0, 2, 0, 0, - 0, 2, 0, 0, 2, 0, 0, 2, - 0, 0, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 0, 2, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 4, 0, 2, 0, 2, 5, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 0, 2, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 6, 2, 6, 2, - 6, 2, 6, 2, 7, 0, 2, 0, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 0, 2, 0, 0, - 2, 0, 0, 2, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 6, 8, 0, 11, 2, 2, 6, 0, - 12, 12, 0, 2, 6, 2, 2, 0, - 13, 2, 0, 0, 2, 0, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 0, 0, 0, 0, 14, 2, 0, - 0, 2, 0, 0, 2, 0, 0, 2, - 0, 2, 2, 2, 2, 15, 2, 0, - 0, 2, 0, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 0, 0, - 0, 0, 16, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 17, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 2, 0, 0, - 0, 0, 18, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 19, 20, 2, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 0, 0, 0, 0, 21, - 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 2, 2, 2, 2, 0, - 0, 22, 22, 0, 0, 0, 0, 0, - 0, 23, 2, 0, 0, 0, 0, 0, - 24 + 2, 2, 3, 2, 0, 2, 0, 0, + 0, 2, 2, 2, 2, 4, 2, 0, + 5, 0, 5, 6, 0, 0, 5, 2, + 7, 2, 0, 2, 0, 2, 0, 0, + 2, 2, 8, 0, 11, 2, 2, 5, + 0, 12, 12, 0, 2, 5, 2, 5, + 2, 0, 13, 2, 0, 0, 2, 0, + 2, 2, 0, 2, 2, 0, 0, 2, + 2, 0, 0, 0, 0, 2, 14, 2, + 0, 0, 2, 0, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 0, 0, + 2, 2, 0, 0, 0, 0, 2, 15, + 5, 0, 5, 2, 2, 0, 5, 0, + 0, 2, 5, 0, 0, 0, 0, 2, + 16, 17, 2, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 0, 0, 2, 2, + 0, 0, 0, 0, 2, 0, 18, 18, + 0, 0, 0, 0, 19, 2, 0, 0, + 0 }; static const char _indic_syllable_machine_to_state_actions[] = { @@ -746,6 +319,7 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -758,40 +332,7 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const char _indic_syllable_machine_from_state_actions[] = { @@ -799,6 +340,7 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -811,126 +353,61 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 10, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0 }; static const short _indic_syllable_machine_eof_trans[] = { 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 73, 73, 78, 78, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 73, 73, 73, - 73, 73, 73, 73, 73, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 109, - 109, 109, 109, 109, 109, 109, 109, 73, - 1, 146, 0, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 339, - 283, 339, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 283, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 410, 410, 410, 410, 410, 410, 410, 339 + 1, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 22, 22, 28, 22, 22, + 22, 22, 22, 22, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 1, 43, 0, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 121, 121, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 122, + 122, 122, 122, 122, 122, 122, 122, 104, + 59, 59, 59, 59, 59, 59, 59, 149, + 149, 149, 149, 149, 121 }; -static const int indic_syllable_machine_start = 138; -static const int indic_syllable_machine_first_final = 138; +static const int indic_syllable_machine_start = 39; +static const int indic_syllable_machine_first_final = 39; static const int indic_syllable_machine_error = -1; -static const int indic_syllable_machine_en_main = 138; +static const int indic_syllable_machine_en_main = 39; #line 36 "hb-ot-shape-complex-indic-machine.rl" -#line 92 "hb-ot-shape-complex-indic-machine.rl" +#line 93 "hb-ot-shape-complex-indic-machine.rl" #define found_syllable(syllable_type) \ HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_indic (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act; int cs; hb_glyph_info_t *info = buffer->info; -#line 934 "hb-ot-shape-complex-indic-machine.hh" +#line 411 "hb-ot-shape-complex-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -938,7 +415,7 @@ find_syllables (hb_buffer_t *buffer) act = 0; } -#line 112 "hb-ot-shape-complex-indic-machine.rl" +#line 113 "hb-ot-shape-complex-indic-machine.rl" p = 0; @@ -946,12 +423,12 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 950 "hb-ot-shape-complex-indic-machine.hh" +#line 427 "hb-ot-shape-complex-indic-machine.hh" { int _slen; int _trans; const unsigned char *_keys; - const short *_inds; + const unsigned char *_inds; if ( p == pe ) goto _test_eof; _resume: @@ -960,7 +437,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 964 "hb-ot-shape-complex-indic-machine.hh" +#line 441 "hb-ot-shape-complex-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -982,75 +459,55 @@ _eof_trans: #line 1 "NONE" {te = p+1;} break; - case 14: -#line 83 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (consonant_syllable); }} - break; - case 16: -#line 84 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (vowel_syllable); }} - break; - case 21: -#line 85 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (standalone_cluster); }} - break; - case 24: -#line 86 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (symbol_cluster); }} - break; - case 18: -#line 87 "hb-ot-shape-complex-indic-machine.rl" - {te = p+1;{ found_syllable (broken_cluster); }} - break; case 11: -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (non_indic_cluster); }} break; case 13: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (consonant_syllable); }} break; - case 15: -#line 84 "hb-ot-shape-complex-indic-machine.rl" + case 14: +#line 85 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (vowel_syllable); }} break; - case 20: -#line 85 "hb-ot-shape-complex-indic-machine.rl" + case 17: +#line 86 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (standalone_cluster); }} break; - case 23: -#line 86 "hb-ot-shape-complex-indic-machine.rl" + case 19: +#line 87 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (symbol_cluster); }} break; - case 17: -#line 87 "hb-ot-shape-complex-indic-machine.rl" + case 15: +#line 88 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; - case 19: -#line 88 "hb-ot-shape-complex-indic-machine.rl" + case 16: +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (non_indic_cluster); }} break; case 1: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (consonant_syllable); }} break; case 3: -#line 84 "hb-ot-shape-complex-indic-machine.rl" +#line 85 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (vowel_syllable); }} break; case 7: -#line 85 "hb-ot-shape-complex-indic-machine.rl" +#line 86 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (standalone_cluster); }} break; case 8: -#line 86 "hb-ot-shape-complex-indic-machine.rl" +#line 87 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (symbol_cluster); }} break; case 4: -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (broken_cluster); }} break; - case 5: + case 6: #line 1 "NONE" { switch( act ) { case 1: @@ -1065,25 +522,25 @@ _eof_trans: } } break; - case 22: + case 18: #line 1 "NONE" {te = p+1;} -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {act = 1;} break; - case 6: + case 5: #line 1 "NONE" {te = p+1;} -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {act = 5;} break; case 12: #line 1 "NONE" {te = p+1;} -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {act = 6;} break; -#line 1087 "hb-ot-shape-complex-indic-machine.hh" +#line 544 "hb-ot-shape-complex-indic-machine.hh" } _again: @@ -1092,7 +549,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1096 "hb-ot-shape-complex-indic-machine.hh" +#line 553 "hb-ot-shape-complex-indic-machine.hh" } if ( ++p != pe ) @@ -1108,8 +565,10 @@ _again: } -#line 120 "hb-ot-shape-complex-indic-machine.rl" +#line 121 "hb-ot-shape-complex-indic-machine.rl" } +#undef found_syllable + #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc index c061ed1a78c90..a150fd2486650 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic-table.cc @@ -6,71 +6,77 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-13.0.0.txt + * # Date: 2019-07-22, 19:55:00 GMT [KW, RP] + * # IndicPositionalCategory-13.0.0.txt + * # Date: 2019-07-23, 00:01:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-indic.hh" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" -#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 16 chars; Avagraha */ -#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 83 chars; Bindu */ -#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ -#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 58 chars; Cantillation_Mark */ -#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2110 chars; Consonant */ -#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 10 chars; Consonant_Dead */ -#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */ -#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ -#define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */ -#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ -#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 28 chars; Consonant_Medial */ -#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 21 chars; Consonant_Placeholder */ -#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 2 chars; Consonant_Preceding_Repha */ -#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 7 chars; Consonant_Prefixed */ -#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 95 chars; Consonant_Subjoined */ -#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ -#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 6 chars; Consonant_With_Stacker */ -#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */ -#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 11 chars; Invisible_Stacker */ -#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */ -#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ -#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ -#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 30 chars; Nukta */ -#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 480 chars; Number */ -#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ -#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ -#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 21 chars; Pure_Killer */ -#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */ -#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */ -#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ -#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ -#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 25 chars; Virama */ -#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 36 chars; Visarga */ -#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ -#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 660 chars; Vowel_Dependent */ -#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 464 chars; Vowel_Independent */ - -#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 340 chars; Bottom */ -#define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */ -#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ -#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 59 chars; Left */ -#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 21 chars; Left_And_Right */ -#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ -#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ -#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 276 chars; Right */ -#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 393 chars; Top */ -#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */ -#define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ -#define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */ -#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */ -#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */ -#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */ +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 17 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 91 chars; Bindu */ +#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ +#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 59 chars; Cantillation_Mark */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2195 chars; Consonant */ +#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 12 chars; Consonant_Dead */ +#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */ +#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ +#define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */ +#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ +#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 31 chars; Consonant_Medial */ +#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 22 chars; Consonant_Placeholder */ +#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 3 chars; Consonant_Preceding_Repha */ +#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 10 chars; Consonant_Prefixed */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 94 chars; Consonant_Subjoined */ +#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ +#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 8 chars; Consonant_With_Stacker */ +#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */ +#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 12 chars; Invisible_Stacker */ +#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */ +#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ +#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ +#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 31 chars; Nukta */ +#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 491 chars; Number */ +#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ +#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ +#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 23 chars; Pure_Killer */ +#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */ +#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */ +#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ +#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 27 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 35 chars; Visarga */ +#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 683 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 484 chars; Vowel_Independent */ + +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 351 chars; Bottom */ +#define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */ +#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 4 chars; Bottom_And_Right */ +#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 64 chars; Left */ +#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 22 chars; Left_And_Right */ +#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ +#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 288 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 415 chars; Top */ +#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */ +#define IMC_TBL INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT /* 2 chars; Top_And_Bottom_And_Left */ +#define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ +#define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */ +#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */ +#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */ +#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */ + #pragma GCC diagnostic pop #define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M) @@ -152,7 +158,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0A38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(x,x), _(M,R), _(M,L), /* 0A40 */ _(M,R), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), /* 0A48 */ _(M,T), _(x,x), _(x,x), _(M,T), _(M,T), _(V,B), _(x,x), _(x,x), - /* 0A50 */ _(x,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0A50 */ _(x,x), _(Ca,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 0A58 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), /* 0A60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), @@ -190,7 +196,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0B38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T), /* 0B40 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L), /* 0B48 */ _(M,TL), _(x,x), _(x,x), _(M,LR),_(M,TLR), _(V,B), _(x,x), _(x,x), - /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,TR), + /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,T), _(M,TR), /* 0B58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), /* 0B60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), /* 0B68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), @@ -237,7 +243,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Kannada */ - /* 0C80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C80 */ _(Bi,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), /* 0C90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), /* 0C98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -256,7 +262,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Malayalam */ - /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D00 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(Bi,x), _(VI,x), _(VI,x), _(VI,x), /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), /* 0D10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), /* 0D18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -265,7 +271,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0D30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), /* 0D38 */ _(C,x), _(C,x), _(C,x), _(PK,T), _(PK,T), _(A,x), _(M,R), _(M,R), /* 0D40 */ _(M,R), _(M,R), _(M,R), _(M,B), _(M,B), _(x,x), _(M,L), _(M,L), - /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,x), _(x,x), + /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,T), _(x,x), /* 0D50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(M,R), /* 0D58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x), /* 0D60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x), @@ -275,7 +281,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Sinhala */ - /* 0D80 */ _(x,x), _(x,x), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0D80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 0D88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 0D90 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), /* 0D98 */ _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -303,7 +309,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1020 */ _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), /* 1028 */ _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), _(M,T), _(M,T), _(M,B), /* 1030 */ _(M,B), _(M,L), _(M,T), _(M,T), _(M,T), _(M,T), _(Bi,T), _(TM,B), - /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R), _(CM,x), _(CM,B), _(CM,B), _(C,x), + /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R),_(CM,TBL), _(CM,B), _(CM,B), _(C,x), /* 1040 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), /* 1048 */ _(Nd,x), _(Nd,x), _(x,x), _(CP,x), _(x,x), _(x,x), _(CP,x), _(x,x), /* 1050 */ _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), @@ -346,8 +352,8 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B), /* 1CE0 */ _(Ca,T), _(Ca,R), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), /* 1CE8 */ _(x,O), _(x,x), _(x,x), _(x,x), _(x,x), _(x,B), _(x,x), _(x,x), - /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), - /* 1CF8 */ _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), + /* 1CF8 */ _(Ca,x), _(Ca,x), _(CP,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), #define indic_offset_0x2008u 1656 @@ -435,6 +441,7 @@ hb_indic_get_categories (hb_codepoint_t u) } #undef _ + #undef ISC_A #undef ISC_Bi #undef ISC_BJN @@ -471,6 +478,7 @@ hb_indic_get_categories (hb_codepoint_t u) #undef ISC_Vo #undef ISC_M #undef ISC_VI + #undef IMC_B #undef IMC_BL #undef IMC_BR @@ -481,10 +489,13 @@ hb_indic_get_categories (hb_codepoint_t u) #undef IMC_R #undef IMC_T #undef IMC_TB +#undef IMC_TBL #undef IMC_TBR #undef IMC_TL #undef IMC_TLR #undef IMC_TR #undef IMC_VOL +#endif + /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc index 38b47fa63e493..e011f510d3271 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-indic.hh" #include "hb-ot-shape-complex-vowel-constraints.hh" #include "hb-ot-layout.hh" @@ -127,62 +131,47 @@ indic_features[] = {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS}, - /* - * Positioning features. - * We don't care about the types. - */ - {HB_TAG('d','i','s','t'), F_GLOBAL}, - {HB_TAG('a','b','v','m'), F_GLOBAL}, - {HB_TAG('b','l','w','m'), F_GLOBAL}, }; /* * Must be in the same order as the indic_features array. */ enum { - _NUKT, - _AKHN, - RPHF, - _RKRF, - PREF, - BLWF, - ABVF, - HALF, - PSTF, - _VATU, - _CJCT, - - INIT, - _PRES, - _ABVS, - _BLWS, - _PSTS, - _HALN, - - _DIST, - _ABVM, - _BLWM, + _INDIC_NUKT, + _INDIC_AKHN, + INDIC_RPHF, + _INDIC_RKRF, + INDIC_PREF, + INDIC_BLWF, + INDIC_ABVF, + INDIC_HALF, + INDIC_PSTF, + _INDIC_VATU, + _INDIC_CJCT, + + INDIC_INIT, + _INDIC_PRES, + _INDIC_ABVS, + _INDIC_BLWS, + _INDIC_PSTS, + _INDIC_HALN, INDIC_NUM_FEATURES, - INDIC_BASIC_FEATURES = INIT, /* Don't forget to update this! */ + INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */ }; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -initial_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -final_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void collect_features_indic (hb_ot_shape_planner_t *plan) @@ -190,7 +179,7 @@ collect_features_indic (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_indic); map->enable_feature (HB_TAG('l','o','c','l')); /* The Indic specs do not require ccmp, but we apply it here since if @@ -199,14 +188,14 @@ collect_features_indic (hb_ot_shape_planner_t *plan) unsigned int i = 0; - map->add_gsub_pause (initial_reordering); + map->add_gsub_pause (initial_reordering_indic); for (; i < INDIC_BASIC_FEATURES; i++) { map->add_feature (indic_features[i]); map->add_gsub_pause (nullptr); } - map->add_gsub_pause (final_reordering); + map->add_gsub_pause (final_reordering_indic); for (; i < INDIC_NUM_FEATURES; i++) map->add_feature (indic_features[i]); @@ -214,7 +203,7 @@ collect_features_indic (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','a','l','t')); map->enable_feature (HB_TAG('c','l','i','g')); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); } static void @@ -224,32 +213,6 @@ override_features_indic (hb_ot_shape_planner_t *plan) } -struct would_substitute_feature_t -{ - void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) - { - zero_context = zero_context_; - map->get_stage_lookups (0/*GSUB*/, - map->get_feature_stage (0/*GSUB*/, feature_tag), - &lookups, &count); - } - - bool would_substitute (const hb_codepoint_t *glyphs, - unsigned int glyphs_count, - hb_face_t *face) const - { - for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) - return true; - return false; - } - - private: - const hb_ot_map_t::lookup_map_t *lookups; - unsigned int count; - bool zero_context; -}; - struct indic_shape_plan_t { bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const @@ -274,13 +237,18 @@ struct indic_shape_plan_t const indic_config_t *config; bool is_old_spec; +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE bool uniscribe_bug_compatible; +#else + static constexpr bool uniscribe_bug_compatible = false; +#endif mutable hb_atomic_int_t virama_glyph; - would_substitute_feature_t rphf; - would_substitute_feature_t pref; - would_substitute_feature_t blwf; - would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t rphf; + hb_indic_would_substitute_feature_t pref; + hb_indic_would_substitute_feature_t blwf; + hb_indic_would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t vatu; hb_mask_t mask_array[INDIC_NUM_FEATURES]; }; @@ -300,7 +268,9 @@ data_create_indic (const hb_ot_shape_plan_t *plan) } indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; +#endif indic_plan->virama_glyph.set_relaxed (-1); /* Use zero-context would_substitute() matching for new-spec of the main @@ -317,6 +287,7 @@ data_create_indic (const hb_ot_shape_plan_t *plan) indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); + indic_plan->vatu.init (&plan->map, HB_TAG('v','a','t','u'), zero_context); for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? @@ -346,10 +317,16 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan, * base at 0. The font however, only has lookups matching * 930,94D in 'blwf', not the expected 94D,930 (with new-spec * table). As such, we simply match both sequences. Seems - * to work. */ + * to work. + * + * Vatu is done as well, for: + * https://github.com/harfbuzz/harfbuzz/issues/1587 + */ hb_codepoint_t glyphs[3] = {virama, consonant, virama}; if (indic_plan->blwf.would_substitute (glyphs , 2, face) || - indic_plan->blwf.would_substitute (glyphs+1, 2, face)) + indic_plan->blwf.would_substitute (glyphs+1, 2, face) || + indic_plan->vatu.would_substitute (glyphs , 2, face) || + indic_plan->vatu.would_substitute (glyphs+1, 2, face)) return POS_BELOW_C; if (indic_plan->pstf.would_substitute (glyphs , 2, face) || indic_plan->pstf.would_substitute (glyphs+1, 2, face)) @@ -361,13 +338,13 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan, } -enum syllable_type_t { - consonant_syllable, - vowel_syllable, - standalone_cluster, - symbol_cluster, - broken_cluster, - non_indic_cluster, +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, }; #include "hb-ot-shape-complex-indic-machine.hh" @@ -391,11 +368,11 @@ setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_indic (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -412,9 +389,9 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) static void -update_consonant_positions (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +update_consonant_positions_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; @@ -487,7 +464,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, * and has more than one consonant, Ra is excluded from candidates for * base consonants. */ unsigned int limit = start; - if (indic_plan->mask_array[RPHF] && + if (indic_plan->mask_array[INDIC_RPHF] && start + 3 <= end && ( (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || @@ -645,7 +622,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Reorder characters */ for (unsigned int i = start; i < base; i++) - info[i].indic_position() = MIN (POS_PRE_C, (indic_position_t) info[i].indic_position()); + info[i].indic_position() = hb_min (POS_PRE_C, (indic_position_t) info[i].indic_position()); if (base < end) info[base].indic_position() = POS_BASE_C; @@ -673,7 +650,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, * is *not* a Halant after last consonant already. We know that is the * case for Kannada, while it reorders unconditionally in other scripts, * eg. Malayalam, Bengali, and Devanagari. We don't currently know about - * other scripts, so we blacklist Kannada. + * other scripts, so we block Kannada. * * Kannada test case: * U+0C9A,U+0CCD,U+0C9A,U+0CCD @@ -720,7 +697,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, indic_position_t last_pos = POS_START; for (unsigned int i = start; i < end; i++) { - if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | FLAG (OT_H)))) + if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H)))) { info[i].indic_position() = last_pos; if (unlikely (info[i].indic_category() == OT_H && @@ -801,7 +778,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, unsigned int j = start + info[i].syllable(); while (j != i) { - max = MAX (max, j); + max = hb_max (max, j); unsigned int next = start + info[j].syllable(); info[j].syllable() = 255; /* So we don't process j later again. */ j = next; @@ -823,13 +800,13 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Reph */ for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) - info[i].mask |= indic_plan->mask_array[RPHF]; + info[i].mask |= indic_plan->mask_array[INDIC_RPHF]; /* Pre-base */ - mask = indic_plan->mask_array[HALF]; + mask = indic_plan->mask_array[INDIC_HALF]; if (!indic_plan->is_old_spec && indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) - mask |= indic_plan->mask_array[BLWF]; + mask |= indic_plan->mask_array[INDIC_BLWF]; for (unsigned int i = start; i < base; i++) info[i].mask |= mask; /* Base */ @@ -837,7 +814,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, if (base < end) info[base].mask |= mask; /* Post-base */ - mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF]; + mask = indic_plan->mask_array[INDIC_BLWF] | + indic_plan->mask_array[INDIC_ABVF] | + indic_plan->mask_array[INDIC_PSTF]; for (unsigned int i = base + 1; i < end; i++) info[i].mask |= mask; } @@ -869,13 +848,13 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, (i + 2 == base || info[i+2].indic_category() != OT_ZWJ)) { - info[i ].mask |= indic_plan->mask_array[BLWF]; - info[i+1].mask |= indic_plan->mask_array[BLWF]; + info[i ].mask |= indic_plan->mask_array[INDIC_BLWF]; + info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF]; } } unsigned int pref_len = 2; - if (indic_plan->mask_array[PREF] && base + pref_len < end) + if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end) { /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */ for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { @@ -885,7 +864,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) { for (unsigned int j = 0; j < pref_len; j++) - info[i++].mask |= indic_plan->mask_array[PREF]; + info[i++].mask |= indic_plan->mask_array[INDIC_PREF]; break; } } @@ -906,7 +885,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, /* A ZWNJ disables HALF. */ if (non_joiner) - info[j].mask &= ~indic_plan->mask_array[HALF]; + info[j].mask &= ~indic_plan->mask_array[INDIC_HALF]; } while (j > start && !is_consonant (info[j])); } @@ -918,11 +897,10 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, unsigned int start, unsigned int end) { - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - /* We treat placeholder/dotted-circle as if they are consonants, so we * should just chain. Only if not in compatibility mode that is... */ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; if (indic_plan->uniscribe_bug_compatible) { /* For dotted-circle, this is what Uniscribe does: @@ -936,42 +914,45 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_face_t *face, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ - case consonant_syllable: + case indic_vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ + case indic_consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); break; - case broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ - case standalone_cluster: + case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ + case indic_standalone_cluster: initial_reordering_standalone_cluster (plan, face, buffer, start, end); break; - case symbol_cluster: - case non_indic_cluster: + case indic_symbol_cluster: + case indic_non_indic_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == indic_broken_cluster) { has_broken_syllables = true; break; @@ -996,8 +977,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == indic_broken_cluster)) { last_syllable = syllable; @@ -1005,7 +986,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -1022,21 +1002,21 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -initial_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - update_consonant_positions (plan, font, buffer); - insert_dotted_circles (plan, font, buffer); + update_consonant_positions_indic (plan, font, buffer); + insert_dotted_circles_indic (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + initial_reordering_syllable_indic (plan, font->face, buffer, start, end); } static void -final_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; hb_glyph_info_t *info = buffer->info; @@ -1072,7 +1052,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, * syllable. */ - bool try_pref = !!indic_plan->mask_array[PREF]; + bool try_pref = !!indic_plan->mask_array[INDIC_PREF]; /* Find base again */ unsigned int base; @@ -1082,7 +1062,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (try_pref && base + 1 < end) { for (unsigned int i = base + 1; i < end; i++) - if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) { if (!(_hb_glyph_info_substituted (&info[i]) && _hb_glyph_info_ligated_and_didnt_multiply (&info[i]))) @@ -1199,9 +1179,14 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, goto search; } } - /* -> If ZWNJ follows this halant, position is moved after it. */ - if (info[new_pos + 1].indic_category() == OT_ZWNJ) - new_pos++; + /* -> If ZWNJ follows this halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating + * sequence for a consonant syllable; any pre-base matras occurring after it + * will belong to the subsequent syllable. + */ } } else @@ -1224,14 +1209,14 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, /* Note: this merge_clusters() is intentionally *after* the reordering. * Indic matra reordering is special and tricky... */ - buffer->merge_clusters (new_pos, MIN (end, base + 1)); + buffer->merge_clusters (new_pos, hb_min (end, base + 1)); new_pos--; } } else { for (unsigned int i = start; i < base; i++) if (info[i].indic_position () == POS_PRE_M) { - buffer->merge_clusters (i, MIN (end, base + 1)); + buffer->merge_clusters (i, hb_min (end, base + 1)); break; } } @@ -1348,6 +1333,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, goto reph_move; } } + /* See https://github.com/harfbuzz/harfbuzz/issues/2298#issuecomment-615318654 */ /* 6. Otherwise, reorder reph to the end of the syllable. */ @@ -1364,13 +1350,15 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ if (!indic_plan->uniscribe_bug_compatible && - unlikely (is_halant (info[new_reph_pos]))) { + unlikely (is_halant (info[new_reph_pos]))) + { for (unsigned int i = base + 1; i < new_reph_pos; i++) if (info[i].indic_category() == OT_M) { /* Ok, got it. */ new_reph_pos--; } } + goto reph_move; } @@ -1397,7 +1385,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */ { for (unsigned int i = base + 1; i < end; i++) - if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) { /* 1. Only reorder a glyph produced by substitution during application * of the feature. (Note that a font may shape a Ra consonant with @@ -1460,7 +1448,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, if (!start || !(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) & FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) - info[start].mask |= indic_plan->mask_array[INIT]; + info[start].mask |= indic_plan->mask_array[INDIC_INIT]; else buffer->unsafe_to_break (start - 1, start + 1); } @@ -1490,33 +1478,21 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, static void -final_reordering (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { unsigned int count = buffer->len; if (unlikely (!count)) return; foreach_syllable (buffer, start, end) - final_reordering_syllable (plan, buffer, start, end); + final_reordering_syllable_indic (plan, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - - static void preprocess_text_indic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, @@ -1583,11 +1559,10 @@ decompose_indic (const hb_ot_shape_normalize_context_t *c, * https://docs.microsoft.com/en-us/typography/script-development/sinhala#shaping */ - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data; + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data; hb_codepoint_t glyph; - - if (hb_options ().uniscribe_bug_compatible || + if (indic_plan->uniscribe_bug_compatible || (c->font->get_nominal_glyph (ab, &glyph) && indic_plan->pstf.would_substitute (&glyph, 1, c->font->face))) { @@ -1635,3 +1610,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh index 0aae0d3953a10..5b3347b9f52d7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-indic.hh @@ -62,17 +62,26 @@ enum indic_category_t { OT_Coeng = 14, /* Khmer-style Virama. */ OT_Repha = 15, /* Atomically-encoded logical or visual repha. */ OT_Ra = 16, - OT_CM = 17, /* Consonant-Medial; Unused by Indic shaper. */ + OT_CM = 17, /* Consonant-Medial. */ OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */ - OT_CS = 19 + OT_CS = 19, + + /* The following are used by Khmer & Myanmar shapers. Defined + * here for them to share. */ + OT_VAbv = 26, + OT_VBlw = 27, + OT_VPre = 28, + OT_VPst = 29, }; +#define MEDIAL_FLAGS (FLAG (OT_CM)) + /* Note: * * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels * cannot happen in a consonant syllable. The plus side however is, we can call the * consonant syllable logic from the vowel syllable function and get it all right! */ -#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) +#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)) @@ -156,6 +165,7 @@ enum indic_matra_category_t { INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM, + INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT = INDIC_MATRA_CATEGORY_BOTTOM, INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_TOP, INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT, @@ -276,7 +286,7 @@ matra_position_indic (hb_codepoint_t u, indic_position_t side) case POS_POST_C: return MATRA_POS_RIGHT (u); case POS_ABOVE_C: return MATRA_POS_TOP (u); case POS_BELOW_C: return MATRA_POS_BOTTOM (u); - }; + } return side; } @@ -357,11 +367,12 @@ set_indic_properties (hb_glyph_info_t &info) /* According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil, * so the Indic shaper needs to know their categories. */ else if (unlikely (u == 0x11301u || u == 0x11303u)) cat = OT_SM; - else if (unlikely (u == 0x1133cu)) cat = OT_N; + else if (unlikely (u == 0x1133Bu || u == 0x1133Cu)) cat = OT_N; else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */ else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */ + else if (unlikely (u == 0x09FCu)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/1613 */ else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */ else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u))) cat = OT_PLACEHOLDER; @@ -395,5 +406,31 @@ set_indic_properties (hb_glyph_info_t &info) info.indic_position() = pos; } +struct hb_indic_would_substitute_feature_t +{ + void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) + { + zero_context = zero_context_; + map->get_stage_lookups (0/*GSUB*/, + map->get_feature_stage (0/*GSUB*/, feature_tag), + &lookups, &count); + } + + bool would_substitute (const hb_codepoint_t *glyphs, + unsigned int glyphs_count, + hb_face_t *face) const + { + for (unsigned int i = 0; i < count; i++) + if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context)) + return true; + return false; + } + + private: + const hb_ot_map_t::lookup_map_t *lookups; + unsigned int count; + bool zero_context; +}; + #endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh index 2b59a50e72400..4d737dad05c14 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer-machine.hh @@ -35,29 +35,27 @@ #line 36 "hb-ot-shape-complex-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, - 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, - 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 16u, 1u, 29u, 5u, 29u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 5u, 26u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, - 5u, 29u, 0 + 5u, 26u, 5u, 21u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, + 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 29u, 5u, 29u, 5u, 29u, 5u, 29u, + 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 26u, 5u, 29u, + 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 5u, 29u, + 0 }; static const char _khmer_syllable_machine_key_spans[] = { 22, 17, 22, 17, 16, 17, 22, 17, - 22, 17, 16, 17, 22, 17, 16, 17, - 22, 17, 22, 17, 22, 16, 29, 25, - 25, 25, 1, 18, 25, 25, 25, 22, - 25, 25, 1, 18, 25, 25, 16, 25, - 25 + 22, 17, 17, 22, 17, 16, 17, 22, + 17, 22, 17, 22, 29, 25, 25, 25, + 1, 18, 25, 25, 25, 16, 22, 25, + 25, 1, 18, 25, 25, 16, 25, 25 }; static const short _khmer_syllable_machine_index_offsets[] = { 0, 23, 41, 64, 82, 99, 117, 140, - 158, 181, 199, 216, 234, 257, 275, 292, - 310, 333, 351, 374, 392, 415, 432, 462, - 488, 514, 540, 542, 561, 587, 613, 639, - 662, 688, 714, 716, 735, 761, 787, 804, - 830 + 158, 181, 199, 217, 240, 258, 275, 293, + 316, 334, 357, 375, 398, 428, 454, 480, + 506, 508, 527, 553, 579, 605, 622, 645, + 671, 697, 699, 718, 744, 770, 787, 813 }; static const char _khmer_syllable_machine_indicies[] = { @@ -85,142 +83,136 @@ static const char _khmer_syllable_machine_indicies[] = { 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 4, 0, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 12, 0, 13, - 13, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 13, 0, - 15, 15, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 16, 14, 15, 15, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 16, 17, 17, 17, 17, 18, - 17, 19, 19, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 18, 17, 20, 20, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 20, 17, 21, 21, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 22, 17, 23, 23, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 24, 17, - 17, 17, 17, 18, 17, 23, 23, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 24, 17, 25, - 25, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 26, - 17, 17, 17, 17, 18, 17, 25, 25, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 26, 17, - 15, 15, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 27, - 16, 17, 17, 17, 17, 18, 17, 28, - 28, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 28, 17, - 13, 13, 29, 29, 30, 30, 29, 29, - 29, 29, 2, 2, 29, 31, 29, 13, - 29, 29, 29, 29, 16, 20, 29, 29, - 29, 18, 24, 26, 22, 29, 33, 33, - 32, 32, 32, 32, 32, 32, 32, 34, - 32, 32, 32, 32, 32, 2, 3, 6, - 32, 32, 32, 4, 10, 12, 8, 32, - 35, 35, 32, 32, 32, 32, 32, 32, - 32, 36, 32, 32, 32, 32, 32, 32, - 3, 6, 32, 32, 32, 4, 10, 12, - 8, 32, 5, 5, 32, 32, 32, 32, - 32, 32, 32, 36, 32, 32, 32, 32, - 32, 32, 4, 6, 32, 32, 32, 32, - 32, 32, 8, 32, 6, 32, 7, 7, - 32, 32, 32, 32, 32, 32, 32, 36, - 32, 32, 32, 32, 32, 32, 8, 6, - 32, 37, 37, 32, 32, 32, 32, 32, - 32, 32, 36, 32, 32, 32, 32, 32, - 32, 10, 6, 32, 32, 32, 4, 32, - 32, 8, 32, 38, 38, 32, 32, 32, - 32, 32, 32, 32, 36, 32, 32, 32, - 32, 32, 32, 12, 6, 32, 32, 32, - 4, 10, 32, 8, 32, 35, 35, 32, - 32, 32, 32, 32, 32, 32, 34, 32, - 32, 32, 32, 32, 32, 3, 6, 32, - 32, 32, 4, 10, 12, 8, 32, 15, - 15, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 16, - 39, 39, 39, 39, 18, 39, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 42, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 19, 19, 40, 40, 40, 40, 40, 40, - 40, 42, 40, 40, 40, 40, 40, 40, - 18, 20, 40, 40, 40, 40, 40, 40, - 22, 40, 20, 40, 21, 21, 40, 40, - 40, 40, 40, 40, 40, 42, 40, 40, - 40, 40, 40, 40, 22, 20, 40, 43, - 43, 40, 40, 40, 40, 40, 40, 40, - 42, 40, 40, 40, 40, 40, 40, 24, - 20, 40, 40, 40, 18, 40, 40, 22, - 40, 44, 44, 40, 40, 40, 40, 40, - 40, 40, 42, 40, 40, 40, 40, 40, - 40, 26, 20, 40, 40, 40, 18, 24, - 40, 22, 40, 28, 28, 39, 39, 39, + 0, 0, 0, 0, 0, 12, 0, 14, + 14, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, + 13, 14, 14, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 15, 16, 16, 16, 16, 17, 16, + 18, 18, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 17, 16, 19, 19, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 19, 16, 20, 20, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 21, 16, 22, 22, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 23, 16, 16, + 16, 16, 17, 16, 22, 22, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 23, 16, 24, 24, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 25, 16, + 16, 16, 16, 17, 16, 24, 24, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 25, 16, 14, + 14, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 26, 15, + 16, 16, 16, 16, 17, 16, 28, 28, + 27, 27, 29, 29, 27, 27, 27, 27, + 2, 2, 27, 30, 27, 28, 27, 27, + 27, 27, 15, 19, 27, 27, 27, 17, + 23, 25, 21, 27, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 33, 31, 31, + 31, 31, 31, 2, 3, 6, 31, 31, + 31, 4, 10, 12, 8, 31, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 35, + 31, 31, 31, 31, 31, 31, 3, 6, + 31, 31, 31, 4, 10, 12, 8, 31, + 5, 5, 31, 31, 31, 31, 31, 31, + 31, 35, 31, 31, 31, 31, 31, 31, + 4, 6, 31, 31, 31, 31, 31, 31, + 8, 31, 6, 31, 7, 7, 31, 31, + 31, 31, 31, 31, 31, 35, 31, 31, + 31, 31, 31, 31, 8, 6, 31, 36, + 36, 31, 31, 31, 31, 31, 31, 31, + 35, 31, 31, 31, 31, 31, 31, 10, + 6, 31, 31, 31, 4, 31, 31, 8, + 31, 37, 37, 31, 31, 31, 31, 31, + 31, 31, 35, 31, 31, 31, 31, 31, + 31, 12, 6, 31, 31, 31, 4, 10, + 31, 8, 31, 34, 34, 31, 31, 31, + 31, 31, 31, 31, 33, 31, 31, 31, + 31, 31, 31, 3, 6, 31, 31, 31, + 4, 10, 12, 8, 31, 28, 28, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 28, 31, 14, 14, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 15, 38, + 38, 38, 38, 17, 38, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 41, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 18, + 18, 39, 39, 39, 39, 39, 39, 39, + 41, 39, 39, 39, 39, 39, 39, 17, + 19, 39, 39, 39, 39, 39, 39, 21, + 39, 19, 39, 20, 20, 39, 39, 39, + 39, 39, 39, 39, 41, 39, 39, 39, + 39, 39, 39, 21, 19, 39, 42, 42, + 39, 39, 39, 39, 39, 39, 39, 41, + 39, 39, 39, 39, 39, 39, 23, 19, + 39, 39, 39, 17, 39, 39, 21, 39, + 43, 43, 39, 39, 39, 39, 39, 39, + 39, 41, 39, 39, 39, 39, 39, 39, + 25, 19, 39, 39, 39, 17, 23, 39, + 21, 39, 44, 44, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 28, 39, 45, 45, 40, 40, - 40, 40, 40, 40, 40, 46, 40, 40, - 40, 40, 40, 27, 16, 20, 40, 40, - 40, 18, 24, 26, 22, 40, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 46, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 0 + 39, 44, 39, 45, 45, 39, 39, 39, + 39, 39, 39, 39, 30, 39, 39, 39, + 39, 39, 26, 15, 19, 39, 39, 39, + 17, 23, 25, 21, 39, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 30, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 0 }; static const char _khmer_syllable_machine_trans_targs[] = { - 22, 1, 30, 24, 25, 3, 26, 5, - 27, 7, 28, 9, 29, 23, 22, 11, - 32, 22, 33, 13, 34, 15, 35, 17, - 36, 19, 37, 40, 39, 22, 31, 38, - 22, 0, 10, 2, 4, 6, 8, 22, - 22, 12, 14, 16, 18, 20, 21 + 20, 1, 28, 22, 23, 3, 24, 5, + 25, 7, 26, 9, 27, 20, 10, 31, + 20, 32, 12, 33, 14, 34, 16, 35, + 18, 36, 39, 20, 21, 30, 37, 20, + 0, 29, 2, 4, 6, 8, 20, 20, + 11, 13, 15, 17, 38, 19 }; static const char _khmer_syllable_machine_trans_actions[] = { 1, 0, 2, 2, 2, 0, 0, 0, - 2, 0, 2, 0, 2, 2, 3, 0, - 4, 5, 2, 0, 0, 0, 2, 0, - 2, 0, 2, 4, 4, 8, 9, 0, - 10, 0, 0, 0, 0, 0, 0, 11, - 12, 0, 0, 0, 0, 0, 0 + 2, 0, 2, 0, 2, 3, 0, 4, + 5, 2, 0, 0, 0, 2, 0, 2, + 0, 2, 4, 8, 2, 9, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, + 0, 0, 0, 0, 4, 0 }; static const char _khmer_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 6, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const char _khmer_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const unsigned char _khmer_syllable_machine_eof_trans[] = { 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 15, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 0, 33, - 33, 33, 33, 33, 33, 33, 33, 40, - 41, 41, 41, 41, 41, 41, 40, 41, - 41 + 1, 1, 14, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 40 }; -static const int khmer_syllable_machine_start = 22; -static const int khmer_syllable_machine_first_final = 22; +static const int khmer_syllable_machine_start = 20; +static const int khmer_syllable_machine_first_final = 20; static const int khmer_syllable_machine_error = -1; -static const int khmer_syllable_machine_en_main = 22; +static const int khmer_syllable_machine_en_main = 20; #line 36 "hb-ot-shape-complex-khmer-machine.rl" @@ -234,19 +226,19 @@ static const int khmer_syllable_machine_en_main = 22; HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_khmer (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act HB_UNUSED; int cs; hb_glyph_info_t *info = buffer->info; -#line 250 "hb-ot-shape-complex-khmer-machine.hh" +#line 242 "hb-ot-shape-complex-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -262,7 +254,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 266 "hb-ot-shape-complex-khmer-machine.hh" +#line 258 "hb-ot-shape-complex-khmer-machine.hh" { int _slen; int _trans; @@ -276,7 +268,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 280 "hb-ot-shape-complex-khmer-machine.hh" +#line 272 "hb-ot-shape-complex-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -346,7 +338,7 @@ _eof_trans: #line 76 "hb-ot-shape-complex-khmer-machine.rl" {act = 3;} break; -#line 350 "hb-ot-shape-complex-khmer-machine.hh" +#line 342 "hb-ot-shape-complex-khmer-machine.hh" } _again: @@ -355,7 +347,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 359 "hb-ot-shape-complex-khmer-machine.hh" +#line 351 "hb-ot-shape-complex-khmer-machine.hh" } if ( ++p != pe ) @@ -375,4 +367,6 @@ _again: } +#undef found_syllable + #endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc index f5437b3971360..6be5d3731bf63 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-khmer.hh" #include "hb-ot-layout.hh" @@ -52,50 +56,35 @@ khmer_features[] = {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, - /* - * Positioning features. - * We don't care about the types. - */ - {HB_TAG('d','i','s','t'), F_GLOBAL}, - {HB_TAG('a','b','v','m'), F_GLOBAL}, - {HB_TAG('b','l','w','m'), F_GLOBAL}, }; /* * Must be in the same order as the khmer_features array. */ enum { - PREF, - BLWF, - ABVF, - PSTF, - CFAR, - - _PRES, - _ABVS, - _BLWS, - _PSTS, + KHMER_PREF, + KHMER_BLWF, + KHMER_ABVF, + KHMER_PSTF, + KHMER_CFAR, - _DIST, - _ABVM, - _BLWM, + _KHMER_PRES, + _KHMER_ABVS, + _KHMER_BLWS, + _KHMER_PSTS, KHMER_NUM_FEATURES, - KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */ + KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */ }; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void collect_features_khmer (hb_ot_shape_planner_t *plan) @@ -103,8 +92,8 @@ collect_features_khmer (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); - map->add_gsub_pause (reorder); + map->add_gsub_pause (setup_syllables_khmer); + map->add_gsub_pause (reorder_khmer); /* Testing suggests that Uniscribe does NOT pause between basic * features. Test with KhmerUI.ttf and the following three @@ -123,7 +112,7 @@ collect_features_khmer (hb_ot_shape_planner_t *plan) for (; i < KHMER_BASIC_FEATURES; i++) map->add_feature (khmer_features[i]); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); for (; i < KHMER_NUM_FEATURES; i++) map->add_feature (khmer_features[i]); @@ -149,32 +138,6 @@ override_features_khmer (hb_ot_shape_planner_t *plan) } -struct would_substitute_feature_t -{ - void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) - { - zero_context = zero_context_; - map->get_stage_lookups (0/*GSUB*/, - map->get_feature_stage (0/*GSUB*/, feature_tag), - &lookups, &count); - } - - bool would_substitute (const hb_codepoint_t *glyphs, - unsigned int glyphs_count, - hb_face_t *face) const - { - for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) - return true; - return false; - } - - private: - const hb_ot_map_t::lookup_map_t *lookups; - unsigned int count; - bool zero_context; -}; - struct khmer_shape_plan_t { bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const @@ -198,8 +161,6 @@ struct khmer_shape_plan_t mutable hb_codepoint_t virama_glyph; - would_substitute_feature_t pref; - hb_mask_t mask_array[KHMER_NUM_FEATURES]; }; @@ -212,8 +173,6 @@ data_create_khmer (const hb_ot_shape_plan_t *plan) khmer_plan->virama_glyph = (hb_codepoint_t) -1; - khmer_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), true); - for (unsigned int i = 0; i < ARRAY_LENGTH (khmer_plan->mask_array); i++) khmer_plan->mask_array[i] = (khmer_features[i].flags & F_GLOBAL) ? 0 : plan->map.get_1_mask (khmer_features[i].tag); @@ -228,10 +187,10 @@ data_destroy_khmer (void *data) } -enum syllable_type_t { - consonant_syllable, - broken_cluster, - non_khmer_cluster, +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, }; #include "hb-ot-shape-complex-khmer-machine.hh" @@ -253,11 +212,11 @@ setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_khmer (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -278,7 +237,9 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, /* Setup masks. */ { /* Post-base */ - hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF]; + hb_mask_t mask = khmer_plan->mask_array[KHMER_BLWF] | + khmer_plan->mask_array[KHMER_ABVF] | + khmer_plan->mask_array[KHMER_PSTF]; for (unsigned int i = start + 1; i < end; i++) info[i].mask |= mask; } @@ -305,7 +266,7 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, if (info[i + 1].khmer_category() == OT_Ra) { for (unsigned int j = 0; j < 2; j++) - info[i + j].mask |= khmer_plan->mask_array[PREF]; + info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF]; /* Move the Coeng,Ro sequence to the start. */ buffer->merge_clusters (start, i + 2); @@ -321,9 +282,9 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, * U+1784,U+17D2,U+179A,U+17D2,U+1782 * U+1784,U+17D2,U+1782,U+17D2,U+179A */ - if (khmer_plan->mask_array[CFAR]) + if (khmer_plan->mask_array[KHMER_CFAR]) for (unsigned int j = i + 2; j < end; j++) - info[j].mask |= khmer_plan->mask_array[CFAR]; + info[j].mask |= khmer_plan->mask_array[KHMER_CFAR]; num_coengs = 2; /* Done. */ } @@ -342,35 +303,39 @@ reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan, - hb_face_t *face, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +reorder_syllable_khmer (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ - case consonant_syllable: + case khmer_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case khmer_consonant_syllable: reorder_consonant_syllable (plan, face, buffer, start, end); break; - case non_khmer_cluster: + case khmer_non_khmer_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == khmer_broken_cluster) { has_broken_syllables = true; break; @@ -395,8 +360,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == khmer_broken_cluster)) { last_syllable = syllable; @@ -404,7 +369,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -421,29 +385,18 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_khmer (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + reorder_syllable_khmer (plan, font->face, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - static bool decompose_khmer (const hb_ot_shape_normalize_context_t *c, @@ -499,3 +452,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_khmer = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh index 4b94cae1f134f..b809d8e4f6672 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-khmer.hh @@ -43,11 +43,10 @@ enum khmer_category_t OT_Robatic = 20, OT_Xgroup = 21, OT_Ygroup = 22, - - OT_VAbv = 26, - OT_VBlw = 27, - OT_VPre = 28, - OT_VPst = 29, + //OT_VAbv = 26, + //OT_VBlw = 27, + //OT_VPre = 28, + //OT_VPst = 29, }; static inline void @@ -100,12 +99,12 @@ set_khmer_properties (hb_glyph_info_t &info) if (cat == (khmer_category_t) OT_M) switch ((int) pos) { - case POS_PRE_C: cat = OT_VPre; break; - case POS_BELOW_C: cat = OT_VBlw; break; - case POS_ABOVE_C: cat = OT_VAbv; break; - case POS_POST_C: cat = OT_VPst; break; + case POS_PRE_C: cat = (khmer_category_t) OT_VPre; break; + case POS_BELOW_C: cat = (khmer_category_t) OT_VBlw; break; + case POS_ABOVE_C: cat = (khmer_category_t) OT_VAbv; break; + case POS_POST_C: cat = (khmer_category_t) OT_VPst; break; default: assert (0); - }; + } info.khmer_category() = cat; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh index 7364d6b400d70..c011c9f6a38b5 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar-machine.hh @@ -36,29 +36,31 @@ static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 32u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, - 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 30u, 3u, 29u, 1u, 32u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, - 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 32u, 8u, 8u, - 0 + 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 29u, 1u, 16u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, + 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 30u, + 3u, 29u, 1u, 32u, 1u, 32u, 8u, 8u, 0 }; static const char _myanmar_syllable_machine_key_spans[] = { 32, 28, 25, 4, 25, 23, 21, 21, 27, 27, 27, 27, 16, 27, 27, 27, - 27, 27, 28, 27, 27, 27, 27, 25, - 4, 25, 23, 21, 21, 27, 27, 27, - 27, 28, 27, 32, 27, 27, 27, 27, - 27, 28, 27, 27, 27, 27, 32, 1 + 27, 27, 28, 27, 27, 27, 27, 27, + 25, 4, 25, 23, 21, 21, 27, 27, + 27, 27, 16, 28, 27, 27, 27, 27, + 27, 28, 27, 27, 27, 27, 27, 28, + 27, 32, 32, 1 }; static const short _myanmar_syllable_machine_index_offsets[] = { 0, 33, 62, 88, 93, 119, 143, 165, 187, 215, 243, 271, 299, 316, 344, 372, 400, 428, 456, 485, 513, 541, 569, 597, - 623, 628, 654, 678, 700, 722, 750, 778, - 806, 834, 863, 891, 924, 952, 980, 1008, - 1036, 1064, 1093, 1121, 1149, 1177, 1205, 1238 + 625, 651, 656, 682, 706, 728, 750, 778, + 806, 834, 862, 879, 908, 936, 964, 992, + 1020, 1048, 1077, 1105, 1133, 1161, 1189, 1217, + 1246, 1274, 1307, 1340 }; static const char _myanmar_syllable_machine_indicies[] = { @@ -124,120 +126,134 @@ static const char _myanmar_syllable_machine_indicies[] = { 21, 21, 21, 21, 21, 21, 32, 33, 34, 35, 36, 43, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, - 21, 21, 21, 21, 43, 21, 21, 28, + 21, 21, 21, 21, 21, 21, 21, 28, 21, 30, 21, 32, 33, 34, 35, 36, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, 21, 21, 21, 21, 43, 21, 21, 28, 21, 21, 21, 32, 33, 34, 35, 36, 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, - 21, 21, 21, 21, 43, 21, 21, 28, + 21, 21, 21, 21, 44, 21, 21, 28, 29, 30, 21, 32, 33, 34, 35, 36, - 21, 22, 23, 24, 24, 21, 25, 21, + 21, 22, 21, 24, 24, 21, 25, 21, 26, 21, 21, 21, 21, 21, 21, 21, - 27, 21, 21, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 21, 3, 3, 44, - 5, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 45, 44, 44, 44, 44, 44, - 44, 14, 44, 44, 44, 18, 44, 3, - 3, 44, 5, 44, 3, 3, 44, 5, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 14, 44, 44, 44, 18, 44, 46, 44, - 3, 3, 44, 5, 44, 14, 44, 44, - 44, 44, 44, 44, 44, 47, 44, 44, - 44, 44, 44, 44, 14, 44, 3, 3, - 44, 5, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 47, 44, 44, 44, 44, - 44, 44, 14, 44, 3, 3, 44, 5, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 14, 44, 2, 44, 3, 3, 44, 5, - 44, 6, 44, 44, 44, 44, 44, 44, - 44, 48, 44, 44, 48, 44, 44, 44, - 14, 49, 44, 44, 18, 44, 2, 44, - 3, 3, 44, 5, 44, 6, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 14, 44, 44, 44, - 18, 44, 2, 44, 3, 3, 44, 5, - 44, 6, 44, 44, 44, 44, 44, 44, - 44, 48, 44, 44, 44, 44, 44, 44, - 14, 49, 44, 44, 18, 44, 2, 44, - 3, 3, 44, 5, 44, 6, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 14, 49, 44, 44, - 18, 44, 22, 23, 24, 24, 21, 25, - 21, 26, 21, 21, 21, 21, 21, 21, - 21, 50, 21, 21, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 21, 22, - 51, 24, 24, 21, 25, 21, 26, 21, - 21, 21, 21, 21, 21, 21, 27, 21, - 21, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 21, 1, 1, 2, 3, 3, - 3, 44, 5, 44, 6, 1, 44, 44, - 44, 44, 1, 44, 8, 44, 44, 10, + 21, 21, 21, 28, 29, 30, 21, 32, + 33, 34, 35, 36, 21, 22, 23, 24, + 24, 21, 25, 21, 26, 21, 21, 21, + 21, 21, 21, 21, 27, 21, 21, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 21, 46, 46, 45, 5, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 47, 45, + 45, 45, 45, 45, 45, 14, 45, 45, + 45, 18, 45, 46, 46, 45, 5, 45, + 46, 46, 45, 5, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 14, 45, 45, 45, + 18, 45, 48, 45, 46, 46, 45, 5, + 45, 14, 45, 45, 45, 45, 45, 45, + 45, 49, 45, 45, 45, 45, 45, 45, + 14, 45, 46, 46, 45, 5, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 49, + 45, 45, 45, 45, 45, 45, 14, 45, + 46, 46, 45, 5, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 14, 45, 2, 45, + 46, 46, 45, 5, 45, 6, 45, 45, + 45, 45, 45, 45, 45, 50, 45, 45, + 50, 45, 45, 45, 14, 51, 45, 45, + 18, 45, 2, 45, 46, 46, 45, 5, + 45, 6, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 14, 45, 45, 45, 18, 45, 2, 45, + 46, 46, 45, 5, 45, 6, 45, 45, + 45, 45, 45, 45, 45, 50, 45, 45, + 45, 45, 45, 45, 14, 51, 45, 45, + 18, 45, 2, 45, 46, 46, 45, 5, + 45, 6, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 14, 51, 45, 45, 18, 45, 52, 52, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 52, 45, 2, + 3, 46, 46, 45, 5, 45, 6, 45, + 45, 45, 45, 45, 45, 45, 8, 45, + 45, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 8, 45, 45, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 53, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 17, 18, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 14, 15, 16, 17, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 45, 18, 45, 2, 45, 46, 46, + 45, 5, 45, 6, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 14, 45, 16, 45, 18, 45, + 2, 45, 46, 46, 45, 5, 45, 6, + 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 14, 15, + 16, 17, 18, 53, 45, 2, 45, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 10, + 45, 12, 45, 14, 15, 16, 17, 18, + 45, 2, 45, 46, 46, 45, 5, 45, + 6, 45, 45, 45, 45, 45, 45, 45, + 53, 45, 45, 10, 45, 45, 45, 14, + 15, 16, 17, 18, 45, 2, 45, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 54, 45, 45, 10, + 11, 12, 45, 14, 15, 16, 17, 18, + 45, 2, 45, 46, 46, 45, 5, 45, + 6, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 10, 11, 12, 45, 14, + 15, 16, 17, 18, 45, 2, 3, 46, + 46, 45, 5, 45, 6, 45, 45, 45, + 45, 45, 45, 45, 8, 45, 45, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 44, 1, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 8, 44, 44, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 52, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 17, 18, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 14, 15, 16, 17, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 44, 18, 44, 2, 44, 3, 3, - 44, 5, 44, 6, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 14, 44, 16, 44, 18, 44, - 2, 44, 3, 3, 44, 5, 44, 6, - 44, 44, 44, 44, 44, 44, 44, 44, - 44, 44, 44, 44, 44, 44, 14, 15, - 16, 17, 18, 52, 44, 2, 44, 3, - 3, 44, 5, 44, 6, 44, 44, 44, - 44, 44, 44, 44, 52, 44, 44, 10, - 44, 12, 44, 14, 15, 16, 17, 18, - 44, 2, 44, 3, 3, 44, 5, 44, - 6, 44, 44, 44, 44, 44, 44, 44, - 52, 44, 44, 10, 44, 44, 44, 14, - 15, 16, 17, 18, 44, 2, 44, 3, - 3, 44, 5, 44, 6, 44, 44, 44, - 44, 44, 44, 44, 52, 44, 44, 10, - 11, 12, 44, 14, 15, 16, 17, 18, - 44, 2, 3, 3, 3, 44, 5, 44, - 6, 44, 44, 44, 44, 44, 44, 44, - 8, 44, 44, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 44, 1, 1, 53, - 53, 53, 53, 53, 53, 53, 53, 1, - 53, 53, 53, 53, 1, 53, 53, 53, - 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 1, 53, 54, 53, - 0 + 45, 22, 23, 24, 24, 21, 25, 21, + 26, 21, 21, 21, 21, 21, 21, 21, + 55, 21, 21, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 21, 22, 56, + 24, 24, 21, 25, 21, 26, 21, 21, + 21, 21, 21, 21, 21, 27, 21, 21, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 21, 1, 1, 2, 3, 46, 46, + 45, 5, 45, 6, 1, 45, 45, 45, + 45, 1, 45, 8, 45, 45, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 45, 1, 45, 1, 1, 57, 57, 57, + 57, 57, 57, 57, 57, 1, 57, 57, + 57, 57, 1, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 1, 57, 58, 57, 0 }; static const char _myanmar_syllable_machine_trans_targs[] = { - 0, 1, 23, 0, 0, 24, 30, 33, - 36, 46, 37, 42, 43, 44, 26, 39, - 40, 41, 29, 45, 47, 0, 2, 12, + 0, 1, 24, 34, 0, 25, 31, 47, + 36, 50, 37, 42, 43, 44, 27, 39, + 40, 41, 30, 46, 51, 0, 2, 12, 0, 3, 9, 13, 14, 19, 20, 21, - 5, 16, 17, 18, 8, 22, 4, 6, - 7, 10, 11, 15, 0, 25, 27, 28, - 31, 32, 34, 35, 38, 0, 0 + 5, 16, 17, 18, 8, 23, 4, 6, + 7, 10, 11, 15, 22, 0, 0, 26, + 28, 29, 32, 33, 35, 38, 45, 48, + 49, 0, 0 }; static const char _myanmar_syllable_machine_trans_actions[] = { - 3, 0, 0, 4, 5, 0, 0, 0, + 3, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 6, 0, 0, - 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, - 0, 0, 0, 0, 0, 9, 10 + 0, 9, 10 }; static const char _myanmar_syllable_machine_to_state_actions[] = { @@ -246,7 +262,8 @@ static const char _myanmar_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const char _myanmar_syllable_machine_from_state_actions[] = { @@ -255,16 +272,18 @@ static const char _myanmar_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const short _myanmar_syllable_machine_eof_trans[] = { 0, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 45, - 45, 45, 45, 45, 45, 45, 45, 45, - 45, 22, 22, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 54, 54 + 22, 22, 22, 22, 22, 22, 22, 22, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 22, + 22, 46, 58, 58 }; static const int myanmar_syllable_machine_start = 0; @@ -285,19 +304,19 @@ static const int myanmar_syllable_machine_en_main = 0; HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_myanmar (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act HB_UNUSED; int cs; hb_glyph_info_t *info = buffer->info; -#line 301 "hb-ot-shape-complex-myanmar-machine.hh" +#line 320 "hb-ot-shape-complex-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -313,7 +332,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 317 "hb-ot-shape-complex-myanmar-machine.hh" +#line 336 "hb-ot-shape-complex-myanmar-machine.hh" { int _slen; int _trans; @@ -327,7 +346,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 331 "hb-ot-shape-complex-myanmar-machine.hh" +#line 350 "hb-ot-shape-complex-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -345,11 +364,11 @@ _eof_trans: goto _again; switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { - case 7: + case 6: #line 86 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (consonant_syllable); }} break; - case 5: + case 4: #line 87 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (non_myanmar_cluster); }} break; @@ -357,7 +376,7 @@ _eof_trans: #line 88 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (punctuation_cluster); }} break; - case 4: + case 8: #line 89 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (broken_cluster); }} break; @@ -365,11 +384,11 @@ _eof_trans: #line 90 "hb-ot-shape-complex-myanmar-machine.rl" {te = p+1;{ found_syllable (non_myanmar_cluster); }} break; - case 6: + case 5: #line 86 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (consonant_syllable); }} break; - case 8: + case 7: #line 89 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; @@ -377,7 +396,7 @@ _eof_trans: #line 90 "hb-ot-shape-complex-myanmar-machine.rl" {te = p;p--;{ found_syllable (non_myanmar_cluster); }} break; -#line 381 "hb-ot-shape-complex-myanmar-machine.hh" +#line 400 "hb-ot-shape-complex-myanmar-machine.hh" } _again: @@ -386,7 +405,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 390 "hb-ot-shape-complex-myanmar-machine.hh" +#line 409 "hb-ot-shape-complex-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc index e84c8f4adc664..03b93a6354832 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-myanmar.hh" @@ -32,7 +36,7 @@ */ static const hb_tag_t -basic_features[] = +myanmar_basic_features[] = { /* * Basic features. @@ -44,7 +48,7 @@ basic_features[] = HB_TAG('p','s','t','f'), }; static const hb_tag_t -other_features[] = +myanmar_other_features[] = { /* * Other features. @@ -55,36 +59,13 @@ other_features[] = HB_TAG('b','l','w','s'), HB_TAG('p','s','t','s'), }; -static const hb_tag_t -positioning_features[] = -{ - /* - * Positioning features. - * We don't care about the types. - */ - HB_TAG('d','i','s','t'), - /* Pre-release version of Windows 8 Myanmar font had abvm,blwm - * features. The released Windows 8 version of the font (as well - * as the released spec) used 'mark' instead. The Windows 8 - * shaper however didn't apply 'mark' but did apply 'mkmk'. - * Perhaps it applied abvm/blwm. This was fixed in a Windows 8 - * update, so now it applies mark/mkmk. We are guessing that - * it still applies abvm/blwm too. - */ - HB_TAG('a','b','v','m'), - HB_TAG('b','l','w','m'), -}; static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -clear_syllables (const hb_ot_shape_plan_t *plan, +reorder_myanmar (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); @@ -94,7 +75,7 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_myanmar); map->enable_feature (HB_TAG('l','o','c','l')); /* The Indic specs do not require ccmp, but we apply it here since if @@ -102,21 +83,18 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('c','c','m','p')); - map->add_gsub_pause (reorder); + map->add_gsub_pause (reorder_myanmar); - for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++) { - map->enable_feature (basic_features[i], F_MANUAL_ZWJ); + map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ); map->add_gsub_pause (nullptr); } - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (_hb_clear_syllables); - for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) - map->enable_feature (other_features[i], F_MANUAL_ZWJ); - - for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) - map->enable_feature (positioning_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++) + map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ); } static void @@ -126,11 +104,11 @@ override_features_myanmar (hb_ot_shape_planner_t *plan) } -enum syllable_type_t { - consonant_syllable, - punctuation_cluster, - broken_cluster, - non_myanmar_cluster, +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_punctuation_cluster, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, }; #include "hb-ot-shape-complex-myanmar-machine.hh" @@ -138,8 +116,8 @@ enum syllable_type_t { static void setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_buffer_t *buffer, - hb_font_t *font HB_UNUSED) + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) { HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category); HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position); @@ -154,11 +132,11 @@ setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_myanmar (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); } @@ -274,36 +252,40 @@ initial_reordering_consonant_syllable (hb_buffer_t *buffer, } static void -initial_reordering_syllable (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_face_t *face HB_UNUSED, - hb_buffer_t *buffer, - unsigned int start, unsigned int end) +reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F); switch (syllable_type) { - case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ - case consonant_syllable: + case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case myanmar_consonant_syllable: initial_reordering_consonant_syllable (buffer, start, end); break; - case punctuation_cluster: - case non_myanmar_cluster: + case myanmar_punctuation_cluster: + case myanmar_non_myanmar_cluster: break; } } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == myanmar_broken_cluster) { has_broken_syllables = true; break; @@ -328,8 +310,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == myanmar_broken_cluster)) { last_syllable = syllable; @@ -347,30 +329,19 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_myanmar (plan, font, buffer); foreach_syllable (buffer, start, end) - initial_reordering_syllable (plan, font->face, buffer, start, end); + reorder_syllable_myanmar (plan, font->face, buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar = { @@ -411,3 +382,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi = HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh index 86717a950049d..4adbc5ae3d3a3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-myanmar.hh @@ -49,10 +49,10 @@ enum myanmar_category_t { OT_MW = 23, /* Various consonant medial types */ OT_MY = 24, /* Various consonant medial types */ OT_PT = 25, /* Pwo and other tones */ - OT_VAbv = 26, - OT_VBlw = 27, - OT_VPre = 28, - OT_VPst = 29, + //OT_VAbv = 26, + //OT_VBlw = 27, + //OT_VPre = 28, + //OT_VPst = 29, OT_VS = 30, /* Variation selectors */ OT_P = 31, /* Punctuation */ OT_D = 32, /* Digits except zero */ @@ -146,7 +146,7 @@ set_myanmar_properties (hb_glyph_info_t &info) break; case 0xAA74u: case 0xAA75u: case 0xAA76u: - /* https://github.com/roozbehp/unicode-data/issues/3 */ + /* https://github.com/harfbuzz/harfbuzz/issues/218 */ cat = OT_C; break; } @@ -155,11 +155,11 @@ set_myanmar_properties (hb_glyph_info_t &info) { switch ((int) pos) { - case POS_PRE_C: cat = OT_VPre; + case POS_PRE_C: cat = (myanmar_category_t) OT_VPre; pos = POS_PRE_M; break; - case POS_ABOVE_C: cat = OT_VAbv; break; - case POS_BELOW_C: cat = OT_VBlw; break; - case POS_POST_C: cat = OT_VPst; break; + case POS_ABOVE_C: cat = (myanmar_category_t) OT_VAbv; break; + case POS_BELOW_C: cat = (myanmar_category_t) OT_VBlw; break; + case POS_POST_C: cat = (myanmar_category_t) OT_VPst; break; } } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc index b13697b9e184f..e18a06baa12af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-thai.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex.hh" @@ -218,6 +222,10 @@ do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK + return; +#endif + thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT]; thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT]; unsigned int base = 0; @@ -381,3 +389,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, false,/* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh index 3d0271b1ee4b3..796b611ba66ac 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-machine.hh @@ -36,36 +36,42 @@ #line 38 "hb-ot-shape-complex-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { - 12u, 44u, 1u, 15u, 1u, 1u, 12u, 44u, 0u, 44u, 21u, 21u, 8u, 44u, 8u, 44u, - 1u, 15u, 1u, 1u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, - 8u, 39u, 8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, - 8u, 44u, 8u, 44u, 8u, 44u, 1u, 39u, 8u, 44u, 13u, 21u, 4u, 4u, 13u, 13u, - 8u, 44u, 8u, 44u, 8u, 44u, 8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, - 8u, 39u, 8u, 39u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, 8u, 44u, - 8u, 44u, 8u, 44u, 1u, 39u, 1u, 15u, 12u, 44u, 1u, 44u, 8u, 44u, 21u, 42u, - 41u, 42u, 42u, 42u, 1u, 5u, 0 + 12u, 48u, 1u, 15u, 1u, 1u, 12u, 48u, 1u, 1u, 0u, 48u, 21u, 21u, 11u, 48u, + 11u, 48u, 1u, 15u, 1u, 1u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, + 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u, + 23u, 48u, 23u, 48u, 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, + 1u, 48u, 11u, 48u, 13u, 21u, 4u, 4u, 13u, 13u, 11u, 48u, 11u, 48u, 41u, 42u, + 42u, 42u, 11u, 48u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u, + 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 24u, 48u, 23u, 48u, 23u, 48u, + 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, 1u, 48u, 1u, 15u, + 4u, 4u, 13u, 21u, 13u, 13u, 12u, 48u, 1u, 48u, 11u, 48u, 41u, 42u, 42u, 42u, + 21u, 42u, 1u, 5u, 0 }; static const char _use_syllable_machine_key_spans[] = { - 33, 15, 1, 33, 45, 1, 37, 37, - 15, 1, 37, 37, 32, 19, 19, 19, - 32, 32, 32, 37, 37, 37, 37, 37, - 37, 37, 37, 39, 37, 9, 1, 1, - 37, 37, 37, 32, 19, 19, 19, 32, - 32, 32, 37, 37, 37, 37, 37, 37, - 37, 37, 39, 15, 33, 44, 37, 22, - 2, 1, 5 + 37, 15, 1, 37, 1, 49, 1, 38, + 38, 15, 1, 38, 27, 26, 24, 23, + 22, 2, 1, 25, 25, 25, 1, 25, + 26, 26, 26, 27, 27, 27, 27, 38, + 48, 38, 9, 1, 1, 38, 38, 2, + 1, 38, 38, 27, 26, 24, 23, 22, + 2, 1, 25, 25, 25, 25, 26, 26, + 26, 27, 27, 27, 27, 38, 48, 15, + 1, 9, 1, 37, 48, 38, 2, 1, + 22, 5 }; static const short _use_syllable_machine_index_offsets[] = { - 0, 34, 50, 52, 86, 132, 134, 172, - 210, 226, 228, 266, 304, 337, 357, 377, - 397, 430, 463, 496, 534, 572, 610, 648, - 686, 724, 762, 800, 840, 878, 888, 890, - 892, 930, 968, 1006, 1039, 1059, 1079, 1099, - 1132, 1165, 1198, 1236, 1274, 1312, 1350, 1388, - 1426, 1464, 1502, 1542, 1558, 1592, 1637, 1675, - 1698, 1701, 1703 + 0, 38, 54, 56, 94, 96, 146, 148, + 187, 226, 242, 244, 283, 311, 338, 363, + 387, 410, 413, 415, 441, 467, 493, 495, + 521, 548, 575, 602, 630, 658, 686, 714, + 753, 802, 841, 851, 853, 855, 894, 933, + 936, 938, 977, 1016, 1044, 1071, 1096, 1120, + 1143, 1146, 1148, 1174, 1200, 1226, 1252, 1279, + 1306, 1333, 1361, 1389, 1417, 1445, 1484, 1533, + 1549, 1551, 1561, 1563, 1601, 1650, 1689, 1692, + 1694, 1717 }; static const char _use_syllable_machine_indicies[] = { @@ -73,308 +79,320 @@ static const char _use_syllable_machine_indicies[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 3, 2, 2, 2, 2, 2, + 1, 0, 0, 0, 1, 0, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 4, 2, 3, 2, 6, 5, 5, 5, + 2, 2, 2, 2, 4, 2, 3, 2, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 6, 5, 7, 8, - 9, 7, 10, 8, 9, 9, 11, 9, - 9, 3, 12, 9, 9, 13, 7, 7, - 14, 15, 9, 9, 16, 17, 18, 19, - 20, 21, 22, 16, 23, 24, 25, 26, - 27, 28, 9, 29, 30, 31, 9, 9, - 9, 32, 33, 9, 35, 34, 37, 36, - 36, 38, 1, 36, 36, 39, 36, 36, - 36, 36, 36, 40, 41, 42, 43, 44, - 45, 46, 47, 41, 48, 40, 49, 50, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 56, 36, 37, 36, 36, 38, - 1, 36, 36, 39, 36, 36, 36, 36, - 36, 57, 41, 42, 43, 44, 45, 46, - 47, 41, 48, 49, 49, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 56, 36, 38, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, - 59, 58, 38, 58, 37, 36, 36, 38, - 1, 36, 36, 39, 36, 36, 36, 36, - 36, 36, 41, 42, 43, 44, 45, 46, - 47, 41, 48, 49, 49, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 56, 36, 37, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 36, 36, 36, - 36, 36, 36, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 42, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 42, - 43, 44, 45, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 53, 54, 55, - 36, 37, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 43, 44, 45, 36, 37, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 44, 45, - 36, 37, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 45, 36, 37, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 43, 44, 45, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 53, 54, 55, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 43, 44, - 45, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 54, 55, 36, 37, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 43, - 44, 45, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 55, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 42, - 43, 44, 45, 36, 36, 36, 36, 36, - 36, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 42, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 42, 43, 44, - 45, 36, 36, 36, 36, 36, 36, 36, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 42, 36, 37, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 42, 43, 44, 45, 36, - 36, 36, 36, 36, 36, 36, 36, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 42, 36, 37, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 36, 47, 41, - 36, 36, 36, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 42, 36, - 37, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 41, 42, - 43, 44, 45, 36, 60, 41, 36, 36, - 36, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 42, 36, 37, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 41, 42, 43, 44, - 45, 36, 36, 41, 36, 36, 36, 50, - 51, 52, 36, 53, 54, 55, 36, 36, - 36, 36, 42, 36, 37, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 41, 42, 43, 44, 45, 46, - 47, 41, 36, 36, 36, 50, 51, 52, - 36, 53, 54, 55, 36, 36, 36, 36, - 42, 36, 37, 36, 36, 38, 1, 36, - 36, 39, 36, 36, 36, 36, 36, 36, - 41, 42, 43, 44, 45, 46, 47, 41, - 48, 36, 49, 50, 51, 52, 36, 53, - 54, 55, 36, 36, 36, 36, 56, 36, - 38, 58, 58, 58, 58, 58, 58, 37, - 58, 58, 58, 58, 58, 58, 59, 58, - 58, 58, 58, 58, 58, 58, 42, 43, - 44, 45, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 53, 54, 55, 58, - 37, 36, 36, 38, 1, 36, 36, 39, - 36, 36, 36, 36, 36, 36, 41, 42, - 43, 44, 45, 46, 47, 41, 48, 40, - 49, 50, 51, 52, 36, 53, 54, 55, - 36, 36, 36, 36, 56, 36, 62, 61, - 61, 61, 61, 61, 61, 61, 63, 61, - 10, 64, 62, 61, 11, 65, 65, 3, - 6, 65, 65, 66, 65, 65, 65, 65, - 65, 67, 16, 17, 18, 19, 20, 21, - 22, 16, 23, 25, 25, 26, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 33, 65, 11, 65, 65, 3, 6, 65, - 65, 66, 65, 65, 65, 65, 65, 65, - 16, 17, 18, 19, 20, 21, 22, 16, - 23, 25, 25, 26, 27, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 33, 65, - 11, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 65, 65, 65, 65, 65, - 65, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 17, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 17, 18, 19, - 20, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 29, 30, 31, 65, 11, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 18, - 19, 20, 65, 11, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 19, 20, 65, 11, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 20, 65, 11, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 18, 19, 20, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 29, 30, 31, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 18, 19, 20, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 30, 31, 65, 11, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 18, 19, 20, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 31, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 17, 18, 19, - 20, 65, 65, 65, 65, 65, 65, 26, - 27, 28, 65, 29, 30, 31, 65, 65, - 65, 65, 17, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 17, 18, 19, 20, 65, - 65, 65, 65, 65, 65, 65, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 17, 65, 11, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 17, 18, 19, 20, 65, 65, 65, - 65, 65, 65, 65, 65, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 17, 65, - 11, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 65, 22, 16, 65, 65, - 65, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 17, 65, 11, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 16, 17, 18, 19, - 20, 65, 68, 16, 65, 65, 65, 26, - 27, 28, 65, 29, 30, 31, 65, 65, - 65, 65, 17, 65, 11, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 16, 17, 18, 19, 20, 65, - 65, 16, 65, 65, 65, 26, 27, 28, - 65, 29, 30, 31, 65, 65, 65, 65, - 17, 65, 11, 65, 65, 65, 65, 65, - 65, 65, 65, 65, 65, 65, 65, 65, - 16, 17, 18, 19, 20, 21, 22, 16, - 65, 65, 65, 26, 27, 28, 65, 29, - 30, 31, 65, 65, 65, 65, 17, 65, - 11, 65, 65, 3, 6, 65, 65, 66, - 65, 65, 65, 65, 65, 65, 16, 17, - 18, 19, 20, 21, 22, 16, 23, 65, - 25, 26, 27, 28, 65, 29, 30, 31, - 65, 65, 65, 65, 33, 65, 3, 65, - 65, 65, 65, 65, 65, 11, 65, 65, - 65, 65, 65, 65, 4, 65, 65, 65, - 65, 65, 65, 65, 17, 18, 19, 20, - 65, 65, 65, 65, 65, 65, 65, 65, - 65, 65, 29, 30, 31, 65, 3, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 4, 69, 6, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 6, 69, - 8, 65, 65, 65, 8, 65, 65, 11, - 65, 65, 3, 6, 65, 65, 66, 65, - 65, 65, 65, 65, 65, 16, 17, 18, - 19, 20, 21, 22, 16, 23, 24, 25, - 26, 27, 28, 65, 29, 30, 31, 65, - 65, 65, 65, 33, 65, 11, 65, 65, - 3, 6, 65, 65, 66, 65, 65, 65, - 65, 65, 65, 16, 17, 18, 19, 20, - 21, 22, 16, 23, 24, 25, 26, 27, - 28, 65, 29, 30, 31, 65, 65, 65, - 65, 33, 65, 71, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 70, - 70, 70, 70, 70, 70, 70, 70, 71, - 72, 70, 71, 72, 70, 72, 70, 8, - 69, 69, 69, 8, 69, 0 + 6, 5, 5, 5, 6, 5, 7, 5, + 8, 9, 10, 8, 11, 12, 10, 10, + 10, 10, 10, 3, 13, 14, 10, 15, + 8, 8, 16, 17, 10, 10, 18, 19, + 20, 21, 22, 23, 24, 18, 25, 26, + 27, 28, 29, 30, 10, 31, 32, 33, + 10, 34, 35, 36, 37, 38, 39, 40, + 13, 10, 42, 41, 44, 1, 43, 43, + 45, 43, 43, 43, 43, 43, 46, 47, + 48, 49, 50, 51, 52, 53, 47, 54, + 46, 55, 56, 57, 58, 43, 59, 60, + 61, 43, 43, 43, 43, 62, 63, 64, + 65, 1, 43, 44, 1, 43, 43, 45, + 43, 43, 43, 43, 43, 66, 47, 48, + 49, 50, 51, 52, 53, 47, 54, 55, + 55, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 62, 63, 64, 65, + 1, 43, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 67, + 68, 67, 44, 67, 44, 1, 43, 43, + 45, 43, 43, 43, 43, 43, 43, 47, + 48, 49, 50, 51, 52, 53, 47, 54, + 55, 55, 56, 57, 58, 43, 59, 60, + 61, 43, 43, 43, 43, 62, 63, 64, + 65, 1, 43, 47, 48, 49, 50, 51, + 43, 43, 43, 43, 43, 43, 56, 57, + 58, 43, 59, 60, 61, 43, 43, 43, + 43, 48, 63, 64, 65, 69, 43, 48, + 49, 50, 51, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 59, 60, 61, + 43, 43, 43, 43, 43, 63, 64, 65, + 69, 43, 49, 50, 51, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 63, + 64, 65, 43, 50, 51, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 63, + 64, 65, 43, 51, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 63, 64, + 65, 43, 63, 64, 43, 64, 43, 49, + 50, 51, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 59, 60, 61, 43, + 43, 43, 43, 43, 63, 64, 65, 69, + 43, 49, 50, 51, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 60, + 61, 43, 43, 43, 43, 43, 63, 64, + 65, 69, 43, 49, 50, 51, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 61, 43, 43, 43, 43, 43, + 63, 64, 65, 69, 43, 71, 70, 49, + 50, 51, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 63, 64, 65, 69, + 43, 48, 49, 50, 51, 43, 43, 43, + 43, 43, 43, 56, 57, 58, 43, 59, + 60, 61, 43, 43, 43, 43, 48, 63, + 64, 65, 69, 43, 48, 49, 50, 51, + 43, 43, 43, 43, 43, 43, 43, 57, + 58, 43, 59, 60, 61, 43, 43, 43, + 43, 48, 63, 64, 65, 69, 43, 48, + 49, 50, 51, 43, 43, 43, 43, 43, + 43, 43, 43, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 47, 48, 49, 50, 51, 43, + 53, 47, 43, 43, 43, 56, 57, 58, + 43, 59, 60, 61, 43, 43, 43, 43, + 48, 63, 64, 65, 69, 43, 47, 48, + 49, 50, 51, 43, 72, 47, 43, 43, + 43, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 47, 48, 49, 50, 51, 43, + 43, 47, 43, 43, 43, 56, 57, 58, + 43, 59, 60, 61, 43, 43, 43, 43, + 48, 63, 64, 65, 69, 43, 47, 48, + 49, 50, 51, 52, 53, 47, 43, 43, + 43, 56, 57, 58, 43, 59, 60, 61, + 43, 43, 43, 43, 48, 63, 64, 65, + 69, 43, 44, 1, 43, 43, 45, 43, + 43, 43, 43, 43, 43, 47, 48, 49, + 50, 51, 52, 53, 47, 54, 43, 55, + 56, 57, 58, 43, 59, 60, 61, 43, + 43, 43, 43, 62, 63, 64, 65, 1, + 43, 44, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 68, + 67, 67, 67, 67, 67, 67, 67, 48, + 49, 50, 51, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 59, 60, 61, + 67, 67, 67, 67, 67, 63, 64, 65, + 69, 67, 44, 1, 43, 43, 45, 43, + 43, 43, 43, 43, 43, 47, 48, 49, + 50, 51, 52, 53, 47, 54, 46, 55, + 56, 57, 58, 43, 59, 60, 61, 43, + 43, 43, 43, 62, 63, 64, 65, 1, + 43, 74, 73, 73, 73, 73, 73, 73, + 73, 75, 73, 11, 76, 74, 73, 44, + 1, 43, 43, 45, 43, 43, 43, 43, + 43, 77, 47, 48, 49, 50, 51, 52, + 53, 47, 54, 46, 55, 56, 57, 58, + 43, 59, 60, 61, 43, 78, 79, 43, + 62, 63, 64, 65, 1, 43, 44, 1, + 43, 43, 45, 43, 43, 43, 43, 43, + 43, 47, 48, 49, 50, 51, 52, 53, + 47, 54, 46, 55, 56, 57, 58, 43, + 59, 60, 61, 43, 78, 79, 43, 62, + 63, 64, 65, 1, 43, 78, 79, 80, + 79, 80, 3, 6, 81, 81, 82, 81, + 81, 81, 81, 81, 83, 18, 19, 20, + 21, 22, 23, 24, 18, 25, 27, 27, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 37, 38, 39, 40, 6, + 81, 3, 6, 81, 81, 82, 81, 81, + 81, 81, 81, 81, 18, 19, 20, 21, + 22, 23, 24, 18, 25, 27, 27, 28, + 29, 30, 81, 31, 32, 33, 81, 81, + 81, 81, 37, 38, 39, 40, 6, 81, + 18, 19, 20, 21, 22, 81, 81, 81, + 81, 81, 81, 28, 29, 30, 81, 31, + 32, 33, 81, 81, 81, 81, 19, 38, + 39, 40, 84, 81, 19, 20, 21, 22, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 31, 32, 33, 81, 81, 81, + 81, 81, 38, 39, 40, 84, 81, 20, + 21, 22, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 38, 39, 40, 81, + 21, 22, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 38, 39, 40, 81, + 22, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 38, 39, 40, 81, 38, + 39, 81, 39, 81, 20, 21, 22, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 31, 32, 33, 81, 81, 81, 81, + 81, 38, 39, 40, 84, 81, 20, 21, + 22, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 32, 33, 81, 81, + 81, 81, 81, 38, 39, 40, 84, 81, + 20, 21, 22, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 33, + 81, 81, 81, 81, 81, 38, 39, 40, + 84, 81, 20, 21, 22, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 38, + 39, 40, 84, 81, 19, 20, 21, 22, + 81, 81, 81, 81, 81, 81, 28, 29, + 30, 81, 31, 32, 33, 81, 81, 81, + 81, 19, 38, 39, 40, 84, 81, 19, + 20, 21, 22, 81, 81, 81, 81, 81, + 81, 81, 29, 30, 81, 31, 32, 33, + 81, 81, 81, 81, 19, 38, 39, 40, + 84, 81, 19, 20, 21, 22, 81, 81, + 81, 81, 81, 81, 81, 81, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 18, 19, 20, + 21, 22, 81, 24, 18, 81, 81, 81, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 19, 38, 39, 40, 84, + 81, 18, 19, 20, 21, 22, 81, 85, + 18, 81, 81, 81, 28, 29, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 18, 19, 20, + 21, 22, 81, 81, 18, 81, 81, 81, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 19, 38, 39, 40, 84, + 81, 18, 19, 20, 21, 22, 23, 24, + 18, 81, 81, 81, 28, 29, 30, 81, + 31, 32, 33, 81, 81, 81, 81, 19, + 38, 39, 40, 84, 81, 3, 6, 81, + 81, 82, 81, 81, 81, 81, 81, 81, + 18, 19, 20, 21, 22, 23, 24, 18, + 25, 81, 27, 28, 29, 30, 81, 31, + 32, 33, 81, 81, 81, 81, 37, 38, + 39, 40, 6, 81, 3, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 4, 81, 81, 81, 81, 81, + 81, 81, 19, 20, 21, 22, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, + 31, 32, 33, 81, 81, 81, 81, 81, + 38, 39, 40, 84, 81, 3, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 4, 86, 87, 81, 14, + 81, 81, 81, 81, 81, 81, 81, 88, + 81, 14, 81, 6, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 6, 86, 86, 86, 6, + 86, 9, 81, 81, 81, 9, 81, 81, + 81, 81, 81, 3, 6, 14, 81, 82, + 81, 81, 81, 81, 81, 81, 18, 19, + 20, 21, 22, 23, 24, 18, 25, 26, + 27, 28, 29, 30, 81, 31, 32, 33, + 81, 34, 35, 81, 37, 38, 39, 40, + 6, 81, 3, 6, 81, 81, 82, 81, + 81, 81, 81, 81, 81, 18, 19, 20, + 21, 22, 23, 24, 18, 25, 26, 27, + 28, 29, 30, 81, 31, 32, 33, 81, + 81, 81, 81, 37, 38, 39, 40, 6, + 81, 34, 35, 81, 35, 81, 78, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 78, 79, 80, 9, 86, 86, + 86, 9, 86, 0 }; static const char _use_syllable_machine_trans_targs[] = { - 4, 8, 4, 32, 2, 4, 1, 5, - 6, 4, 29, 4, 51, 52, 53, 55, - 34, 35, 36, 37, 38, 45, 46, 48, - 54, 49, 42, 43, 44, 39, 40, 41, - 58, 50, 4, 4, 4, 4, 7, 0, - 28, 11, 12, 13, 14, 15, 22, 23, - 25, 26, 19, 20, 21, 16, 17, 18, - 27, 10, 4, 9, 24, 4, 30, 31, - 4, 4, 3, 33, 47, 4, 4, 56, - 57 + 5, 9, 5, 41, 2, 5, 1, 53, + 6, 7, 5, 34, 37, 63, 64, 67, + 68, 72, 43, 44, 45, 46, 47, 57, + 58, 60, 69, 61, 54, 55, 56, 50, + 51, 52, 70, 71, 73, 62, 48, 49, + 5, 5, 5, 5, 8, 0, 33, 12, + 13, 14, 15, 16, 27, 28, 30, 31, + 24, 25, 26, 19, 20, 21, 32, 17, + 18, 5, 11, 5, 10, 22, 5, 23, + 29, 5, 35, 36, 5, 38, 39, 40, + 5, 5, 3, 42, 4, 59, 5, 65, + 66 }; static const char _use_syllable_machine_trans_actions[] = { - 1, 0, 2, 3, 0, 4, 0, 0, - 7, 8, 0, 9, 10, 10, 3, 0, + 1, 0, 2, 3, 0, 4, 0, 5, + 0, 5, 8, 0, 5, 9, 0, 9, + 3, 0, 5, 5, 0, 0, 0, 5, + 5, 5, 3, 3, 5, 5, 5, 5, + 5, 5, 0, 0, 0, 3, 0, 0, + 10, 11, 12, 13, 5, 0, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 3, 0, 0, 0, 0, 0, 0, - 0, 3, 11, 12, 13, 14, 7, 0, - 7, 0, 0, 0, 0, 0, 0, 0, - 0, 7, 0, 0, 0, 0, 0, 0, - 0, 7, 15, 0, 0, 16, 0, 0, - 17, 18, 0, 3, 0, 19, 20, 0, + 0, 14, 5, 15, 0, 0, 16, 0, + 0, 17, 0, 0, 18, 5, 0, 0, + 19, 20, 0, 3, 0, 5, 21, 0, 0 }; static const char _use_syllable_machine_to_state_actions[] = { - 0, 0, 0, 0, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 }; static const char _use_syllable_machine_from_state_actions[] = { - 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 + 0, 0 }; static const short _use_syllable_machine_eof_trans[] = { - 1, 3, 3, 6, 0, 35, 37, 37, - 59, 59, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 59, 37, 62, 65, 62, - 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 70, 70, 66, 66, 71, - 71, 71, 70 + 1, 3, 3, 6, 6, 0, 42, 44, + 44, 68, 68, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 71, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 68, 44, 74, 77, 74, 44, 44, 81, + 81, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 87, + 82, 82, 82, 87, 82, 82, 82, 82, + 81, 87 }; -static const int use_syllable_machine_start = 4; -static const int use_syllable_machine_first_final = 4; +static const int use_syllable_machine_start = 5; +static const int use_syllable_machine_first_final = 5; static const int use_syllable_machine_error = -1; -static const int use_syllable_machine_en_main = 4; +static const int use_syllable_machine_en_main = 5; #line 38 "hb-ot-shape-complex-use-machine.rl" -#line 143 "hb-ot-shape-complex-use-machine.rl" +#line 162 "hb-ot-shape-complex-use-machine.rl" #define found_syllable(syllable_type) \ HB_STMT_START { \ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \ for (unsigned int i = ts; i < te; i++) \ - info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \ syllable_serial++; \ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \ } HB_STMT_END static void -find_syllables (hb_buffer_t *buffer) +find_syllables_use (hb_buffer_t *buffer) { unsigned int p, pe, eof, ts, te, act; int cs; hb_glyph_info_t *info = buffer->info; -#line 378 "hb-ot-shape-complex-use-machine.hh" +#line 396 "hb-ot-shape-complex-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -382,7 +400,7 @@ find_syllables (hb_buffer_t *buffer) act = 0; } -#line 163 "hb-ot-shape-complex-use-machine.rl" +#line 182 "hb-ot-shape-complex-use-machine.rl" p = 0; @@ -390,7 +408,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 394 "hb-ot-shape-complex-use-machine.hh" +#line 412 "hb-ot-shape-complex-use-machine.hh" { int _slen; int _trans; @@ -400,11 +418,11 @@ find_syllables (hb_buffer_t *buffer) goto _test_eof; _resume: switch ( _use_syllable_machine_from_state_actions[cs] ) { - case 6: + case 7: #line 1 "NONE" {ts = p;} break; -#line 408 "hb-ot-shape-complex-use-machine.hh" +#line 426 "hb-ot-shape-complex-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -422,73 +440,77 @@ _eof_trans: goto _again; switch ( _use_syllable_machine_trans_actions[_trans] ) { - case 7: + case 5: #line 1 "NONE" {te = p+1;} break; case 12: -#line 132 "hb-ot-shape-complex-use-machine.rl" +#line 150 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (independent_cluster); }} break; case 14: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (standard_cluster); }} break; - case 9: -#line 138 "hb-ot-shape-complex-use-machine.rl" + case 10: +#line 157 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (broken_cluster); }} break; case 8: -#line 139 "hb-ot-shape-complex-use-machine.rl" +#line 158 "hb-ot-shape-complex-use-machine.rl" {te = p+1;{ found_syllable (non_cluster); }} break; case 11: -#line 132 "hb-ot-shape-complex-use-machine.rl" +#line 150 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (independent_cluster); }} break; case 15: -#line 133 "hb-ot-shape-complex-use-machine.rl" +#line 151 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (virama_terminated_cluster); }} break; + case 16: +#line 152 "hb-ot-shape-complex-use-machine.rl" + {te = p;p--;{ found_syllable (sakot_terminated_cluster); }} + break; case 13: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (standard_cluster); }} break; - case 17: -#line 135 "hb-ot-shape-complex-use-machine.rl" + case 18: +#line 154 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }} break; - case 16: -#line 136 "hb-ot-shape-complex-use-machine.rl" + case 17: +#line 155 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (numeral_cluster); }} break; - case 20: -#line 137 "hb-ot-shape-complex-use-machine.rl" + case 19: +#line 156 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (symbol_cluster); }} break; - case 18: -#line 138 "hb-ot-shape-complex-use-machine.rl" + case 20: +#line 157 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; - case 19: -#line 139 "hb-ot-shape-complex-use-machine.rl" + case 21: +#line 158 "hb-ot-shape-complex-use-machine.rl" {te = p;p--;{ found_syllable (non_cluster); }} break; case 1: -#line 134 "hb-ot-shape-complex-use-machine.rl" +#line 153 "hb-ot-shape-complex-use-machine.rl" {{p = ((te))-1;}{ found_syllable (standard_cluster); }} break; case 4: -#line 138 "hb-ot-shape-complex-use-machine.rl" +#line 157 "hb-ot-shape-complex-use-machine.rl" {{p = ((te))-1;}{ found_syllable (broken_cluster); }} break; case 2: #line 1 "NONE" { switch( act ) { - case 7: + case 8: {{p = ((te))-1;} found_syllable (broken_cluster); } break; - case 8: + case 9: {{p = ((te))-1;} found_syllable (non_cluster); } break; } @@ -497,25 +519,25 @@ _eof_trans: case 3: #line 1 "NONE" {te = p+1;} -#line 138 "hb-ot-shape-complex-use-machine.rl" - {act = 7;} +#line 157 "hb-ot-shape-complex-use-machine.rl" + {act = 8;} break; - case 10: + case 9: #line 1 "NONE" {te = p+1;} -#line 139 "hb-ot-shape-complex-use-machine.rl" - {act = 8;} +#line 158 "hb-ot-shape-complex-use-machine.rl" + {act = 9;} break; -#line 510 "hb-ot-shape-complex-use-machine.hh" +#line 532 "hb-ot-shape-complex-use-machine.hh" } _again: switch ( _use_syllable_machine_to_state_actions[cs] ) { - case 5: + case 6: #line 1 "NONE" {ts = 0;} break; -#line 519 "hb-ot-shape-complex-use-machine.hh" +#line 541 "hb-ot-shape-complex-use-machine.hh" } if ( ++p != pe ) @@ -531,7 +553,7 @@ _again: } -#line 171 "hb-ot-shape-complex-use-machine.rl" +#line 190 "hb-ot-shape-complex-use-machine.rl" } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc index 9411e28e43518..910b01a32af78 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use-table.cc @@ -6,15 +6,19 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-13.0.0.txt + * # Date: 2019-07-22, 19:55:00 GMT [KW, RP] + * # IndicPositionalCategory-13.0.0.txt + * # Date: 2019-07-23, 00:01:00 GMT [KW, RP] + * # Blocks-13.0.0.txt + * # Date: 2019-07-10, 19:06:00 GMT [KW] * UnicodeData.txt does not have a header. */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-use.hh" #pragma GCC diagnostic push @@ -22,7 +26,6 @@ #define B USE_B /* BASE */ #define CGJ USE_CGJ /* CGJ */ #define CS USE_CS /* CONS_WITH_STACKER */ -#define FM USE_FM /* CONS_FINAL_MOD */ #define GB USE_GB /* BASE_OTHER */ #define H USE_H /* HALANT */ #define HN USE_HN /* HALANT_NUM */ @@ -34,29 +37,33 @@ #define Rsv USE_Rsv /* Reserved */ #define S USE_S /* SYM */ #define SUB USE_SUB /* CONS_SUB */ +#define Sk USE_Sk /* SAKOT */ #define VS USE_VS /* VARIATION_SELECTOR */ #define WJ USE_WJ /* Word_Joiner */ #define ZWJ USE_ZWJ /* ZWJ */ #define ZWNJ USE_ZWNJ /* ZWNJ */ -#define CMBlw USE_CMBlw #define CMAbv USE_CMAbv +#define CMBlw USE_CMBlw +#define FAbv USE_FAbv #define FBlw USE_FBlw #define FPst USE_FPst -#define FAbv USE_FAbv -#define MPre USE_MPre +#define FMAbv USE_FMAbv +#define FMBlw USE_FMBlw +#define FMPst USE_FMPst +#define MAbv USE_MAbv #define MBlw USE_MBlw #define MPst USE_MPst -#define MAbv USE_MAbv -#define SMBlw USE_SMBlw +#define MPre USE_MPre #define SMAbv USE_SMAbv -#define VPre USE_VPre +#define SMBlw USE_SMBlw +#define VAbv USE_VAbv #define VBlw USE_VBlw #define VPst USE_VPst -#define VAbv USE_VAbv -#define VMPre USE_VMPre +#define VPre USE_VPre +#define VMAbv USE_VMAbv #define VMBlw USE_VMBlw #define VMPst USE_VMPst -#define VMAbv USE_VMAbv +#define VMPre USE_VMPre #pragma GCC diagnostic pop static const USE_TABLE_ELEMENT_TYPE use_table[] = { @@ -75,7 +82,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Latin-1 Supplement */ /* 00A0 */ GB, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, - /* 00B0 */ O, O, FM, FM, O, O, O, O, O, O, O, O, O, O, O, O, + /* 00B0 */ O, O, FMPst, FMPst, O, O, O, O, O, O, O, O, O, O, O, O, /* 00C0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 00D0 */ O, O, O, O, O, O, O, GB, @@ -108,7 +115,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 09C0 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, O, O, VPst, VPst, H, IND, O, /* 09D0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, B, B, O, B, /* 09E0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, - /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, B, O, FM, O, + /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, B, O, FMAbv, O, /* Gurmukhi */ @@ -139,7 +146,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 0B20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 0B30 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, /* 0B40 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPst, O, O, VPst, VPst, H, O, O, - /* 0B50 */ O, O, O, O, O, O, VAbv, VAbv, O, O, O, O, B, B, O, B, + /* 0B50 */ O, O, O, O, O, VAbv, VAbv, VAbv, O, O, O, O, B, B, O, B, /* 0B60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B, /* 0B70 */ O, B, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -167,7 +174,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Kannada */ - /* 0C80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0C80 */ B, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, /* 0C90 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0CA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 0CB0 */ B, B, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, @@ -178,7 +185,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Malayalam */ - /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst, B, B, B, B, B, B, B, B, B, O, B, B, /* 0D10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0D20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0D30 */ B, B, B, B, B, B, B, B, B, B, B, VAbv, VAbv, B, VPst, VPst, @@ -189,7 +196,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Sinhala */ - /* 0D80 */ O, O, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B, + /* 0D80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B, /* 0D90 */ B, B, B, B, B, B, B, O, O, O, B, B, B, B, B, B, /* 0DA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0DB0 */ B, B, O, B, B, B, B, B, B, B, B, B, O, B, O, O, @@ -204,7 +211,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Tibetan */ VBlw, VBlw, O, O, O, O, O, O, /* 0F20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 0F30 */ B, B, B, B, O, FM, O, FM, O, CMAbv, O, O, O, O, VPst, VPre, + /* 0F30 */ B, B, B, B, O, FMBlw, O, FMBlw, O, CMAbv, O, O, O, O, VPst, VPre, /* 0F40 */ B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, B, /* 0F50 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0F60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, @@ -213,7 +220,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 0F90 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 0FA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 0FB0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, O, O, - /* 0FC0 */ O, O, O, O, O, O, FM, O, + /* 0FC0 */ O, O, O, O, O, O, FMBlw, O, #define use_offset_0x1000u 1536 @@ -260,8 +267,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1790 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 17A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 17B0 */ B, B, B, B, O, O, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VPst, VPst, - /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FM, FAbv, CMAbv, FM, FM, - /* 17D0 */ FM, VAbv, H, FM, O, O, O, O, O, O, O, O, B, VAbv, O, O, + /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FMAbv, FAbv, CMAbv, FMAbv, FMAbv, + /* 17D0 */ FMAbv, VAbv, H, FMAbv, O, O, O, O, O, O, O, O, B, FMAbv, O, O, /* 17E0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x1900u 1936 @@ -272,7 +279,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1900 */ GB, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, /* 1920 */ VAbv, VAbv, VBlw, VPst, VPst, VAbv, VAbv, VAbv, VAbv, SUB, SUB, SUB, O, O, O, O, - /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FM, O, O, O, O, + /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FMBlw, O, O, O, O, /* 1940 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B, /* Tai Le */ @@ -288,7 +295,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 19A0 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, /* 19B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 19C0 */ B, B, B, B, B, B, B, B, VMPst, VMPst, O, O, O, O, O, O, - /* 19D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 19D0 */ B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, /* 19E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 19F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -302,9 +309,9 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1A30 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1A40 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 1A50 */ B, B, B, B, B, MPre, MBlw, SUB, FAbv, FAbv, FAbv, SUB, SUB, SUB, SUB, O, - /* 1A60 */ H, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre, - /* 1A70 */ VPre, VPre, VPre, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VAbv, FM, FM, O, O, FBlw, + /* 1A50 */ B, B, B, B, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, SUB, SUB, SUB, O, + /* 1A60 */ Sk, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre, + /* 1A70 */ VPre, VPre, VPre, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VAbv, FMAbv, FMAbv, O, O, FMBlw, /* 1A80 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 1A90 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, @@ -318,8 +325,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre, /* 1B40 */ VPst, VPst, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O, - /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, - /* 1B60 */ O, O, O, O, O, O, O, O, O, O, O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, + /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, GB, GB, O, O, GB, + /* 1B60 */ O, S, GB, S, S, S, S, S, GB, S, S, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O, /* Sundanese */ @@ -340,8 +347,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1C00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPst, VPst, VPst, VBlw, FAbv, FAbv, FAbv, - /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FM, CMBlw, O, O, O, O, O, O, O, O, + /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPre, VPst, VPst, VBlw, FAbv, FAbv, FAbv, + /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FMAbv, CMBlw, O, O, O, O, O, O, O, O, /* 1C40 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, B, B, #define use_offset_0x1cd0u 2688 @@ -351,13 +358,13 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1CD0 */ VMAbv, VMAbv, VMAbv, O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw, /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, O, O, O, O, VMBlw, O, O, - /* 1CF0 */ O, O, VMPst, VMPst, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, O, O, O, O, O, O, + /* 1CF0 */ O, O, IND, IND, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, GB, O, O, O, O, O, #define use_offset_0x1df8u 2736 /* Combining Diacritical Marks Supplement */ - O, O, O, FM, O, O, O, O, + O, O, O, FMAbv, O, O, O, O, #define use_offset_0x2008u 2744 @@ -372,8 +379,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Superscripts and Subscripts */ - /* 2070 */ O, O, O, O, FM, O, O, O, O, O, O, O, O, O, O, O, - /* 2080 */ O, O, FM, FM, FM, O, O, O, + /* 2070 */ O, O, O, O, FMPst, O, O, O, O, O, O, O, O, O, O, O, + /* 2080 */ O, O, FMPst, FMPst, FMPst, O, O, O, #define use_offset_0x20f0u 2800 @@ -393,9 +400,9 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Syloti Nagri */ - /* A800 */ B, B, O, B, B, B, VAbv, B, B, B, B, VMAbv, B, B, B, B, + /* A800 */ B, B, VAbv, B, B, B, H, B, B, B, B, VMAbv, B, B, B, B, /* A810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, O, O, O, O, + /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, VBlw, O, O, O, /* A830 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Phags-pa */ @@ -438,7 +445,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* A980 */ VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, /* A990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* A9A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, SUB, MPst, MBlw, + /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, MBlw, MBlw, MBlw, /* A9C0 */ H, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* A9D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, @@ -533,7 +540,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11110 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11120 */ B, B, B, B, B, B, B, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VBlw, VAbv, VAbv, /* 11130 */ VBlw, VAbv, VAbv, H, CMBlw, O, B, B, B, B, B, B, B, B, B, B, - /* 11140 */ O, O, O, O, B, VPst, VPst, O, O, O, O, O, O, O, O, O, + /* 11140 */ O, O, O, O, B, VPst, VPst, B, O, O, O, O, O, O, O, O, /* Mahajani */ @@ -547,7 +554,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, - /* 111C0 */ H, B, R, R, O, O, O, O, GB, FBlw, CMBlw, VAbv, VBlw, O, O, O, + /* 111C0 */ H, B, R, R, O, O, O, O, GB, FMBlw, CMBlw, VAbv, VBlw, O, VPre, VMAbv, /* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* Sinhala Archaic Numbers */ @@ -581,7 +588,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Grantha */ - /* 11300 */ VMAbv, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 11300 */ VMAbv, VMAbv, VMAbv, VMAbv, O, B, B, B, B, B, B, B, B, O, O, B, /* 11310 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11320 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 11330 */ B, O, B, B, O, B, B, B, B, B, O, CMBlw, CMBlw, B, VPst, VPst, @@ -600,8 +607,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11420 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11430 */ B, B, B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, /* 11440 */ VPst, VPst, H, VMAbv, VMAbv, VMPst, CMBlw, B, O, O, O, O, O, O, O, O, - /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FM, O, - /* 11460 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FMAbv, B, + /* 11460 */ CS, CS, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 11470 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Tirhuta */ @@ -610,7 +617,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11490 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114B0 */ VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VPre, VAbv, VPst, VPst, VPst, VPst, VMAbv, - /* 114C0 */ VMAbv, VMPst, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, + /* 114C0 */ VMAbv, VMAbv, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, /* 114D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x11580u 4720 @@ -643,7 +650,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11680 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11690 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 116A0 */ B, B, B, B, B, B, B, B, B, B, B, VMAbv, VMPst, VAbv, VPre, VPst, - /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, O, O, O, O, O, O, O, O, + /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, B, O, O, O, O, O, O, O, /* 116C0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 116D0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 116E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -666,15 +673,36 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11820 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPre, VPst, VBlw, /* 11830 */ VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VMAbv, VMPst, H, CMBlw, O, O, O, O, O, -#define use_offset_0x11a00u 5232 +#define use_offset_0x11900u 5232 + + + /* Dives Akuru */ + + /* 11900 */ B, B, B, B, B, B, B, O, O, B, O, O, B, B, B, B, + /* 11910 */ B, B, B, B, O, B, B, O, B, B, B, B, B, B, B, B, + /* 11920 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 11930 */ VPst, VPst, VPst, VPst, VPst, VPre, O, VPre, VPst, O, O, VMAbv, VMAbv, VPst, H, R, + /* 11940 */ MPst, R, MBlw, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O, + /* 11950 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, +#define use_offset_0x119a0u 5328 + + + /* Nandinagari */ + + /* 119A0 */ B, B, B, B, B, B, B, B, O, O, B, B, B, B, B, B, + /* 119B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119D0 */ B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VAbv, VAbv, VPst, VPst, VMPst, VMPst, + /* 119E0 */ H, B, O, O, VPre, O, O, O, O, O, O, O, O, O, O, O, + /* 119F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* Zanabazar Square */ /* 11A00 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VAbv, VAbv, VBlw, B, B, B, B, B, /* 11A10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 11A30 */ B, B, B, FM, VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst, R, MBlw, MBlw, MBlw, MBlw, GB, + /* 11A30 */ B, B, B, FMBlw, VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst, R, MBlw, MBlw, MBlw, MBlw, GB, /* 11A40 */ O, O, O, O, O, GB, O, H, O, O, O, O, O, O, O, O, /* Soyombo */ @@ -682,10 +710,10 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11A50 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VBlw, VBlw, VBlw, B, B, B, B, /* 11A60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11A70 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 11A80 */ B, B, B, B, O, O, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, + /* 11A80 */ B, B, B, B, R, R, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, /* 11A90 */ FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, VMAbv, VMPst, CMAbv, H, O, O, O, B, O, O, -#define use_offset_0x11c00u 5392 +#define use_offset_0x11c00u 5584 /* Bhaiksuki */ @@ -706,7 +734,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11CA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 11CB0 */ VBlw, VPre, VBlw, VAbv, VPst, VMAbv, VMAbv, O, -#define use_offset_0x11d00u 5576 +#define use_offset_0x11d00u 5768 /* Masaram Gondi */ @@ -726,7 +754,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11D90 */ VAbv, VAbv, O, VPst, VPst, VMAbv, VMPst, H, O, O, O, O, O, O, O, O, /* 11DA0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, -#define use_offset_0x11ee0u 5752 +#define use_offset_0x11ee0u 5944 /* Makasar */ @@ -734,7 +762,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11EE0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11EF0 */ B, B, GB, VAbv, VBlw, VPre, VPst, O, -}; /* Table items: 5776; occupancy: 74% */ +}; /* Table items: 5968; occupancy: 74% */ USE_TABLE_ELEMENT_TYPE hb_use_get_category (hb_codepoint_t u) @@ -785,7 +813,8 @@ hb_use_get_category (hb_codepoint_t u) if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u]; if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u]; if (hb_in_range (u, 0x11800u, 0x1183Fu)) return use_table[u - 0x11800u + use_offset_0x11800u]; - if (hb_in_range (u, 0x11A00u, 0x11A9Fu)) return use_table[u - 0x11A00u + use_offset_0x11a00u]; + if (hb_in_range (u, 0x11900u, 0x1195Fu)) return use_table[u - 0x11900u + use_offset_0x11900u]; + if (hb_in_range (u, 0x119A0u, 0x11A9Fu)) return use_table[u - 0x119A0u + use_offset_0x119a0u]; if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u]; if (hb_in_range (u, 0x11D00u, 0x11DAFu)) return use_table[u - 0x11D00u + use_offset_0x11d00u]; if (hb_in_range (u, 0x11EE0u, 0x11EF7u)) return use_table[u - 0x11EE0u + use_offset_0x11ee0u]; @@ -800,7 +829,6 @@ hb_use_get_category (hb_codepoint_t u) #undef B #undef CGJ #undef CS -#undef FM #undef GB #undef H #undef HN @@ -812,28 +840,34 @@ hb_use_get_category (hb_codepoint_t u) #undef Rsv #undef S #undef SUB +#undef Sk #undef VS #undef WJ #undef ZWJ #undef ZWNJ -#undef CMBlw #undef CMAbv +#undef CMBlw +#undef FAbv #undef FBlw #undef FPst -#undef FAbv -#undef MPre +#undef FMAbv +#undef FMBlw +#undef FMPst +#undef MAbv #undef MBlw #undef MPst -#undef MAbv -#undef SMBlw +#undef MPre #undef SMAbv -#undef VPre +#undef SMBlw +#undef VAbv #undef VBlw #undef VPst -#undef VAbv -#undef VMPre +#undef VPre +#undef VMAbv #undef VMBlw #undef VMPst -#undef VMAbv +#undef VMPre + +#endif /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc index 25b135ab58c2c..b5cf48ac2278c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.cc @@ -26,12 +26,17 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-use.hh" #include "hb-ot-shape-complex-arabic.hh" +#include "hb-ot-shape-complex-arabic-joining-list.hh" #include "hb-ot-shape-complex-vowel-constraints.hh" /* buffer var allocations */ -#define use_category() complex_var_u8_0() +#define use_category() complex_var_u8_1() /* @@ -40,7 +45,7 @@ */ static const hb_tag_t -basic_features[] = +use_basic_features[] = { /* * Basic features. @@ -55,28 +60,23 @@ basic_features[] = HB_TAG('c','j','c','t'), }; static const hb_tag_t -arabic_features[] = +use_topographical_features[] = { HB_TAG('i','s','o','l'), HB_TAG('i','n','i','t'), HB_TAG('m','e','d','i'), HB_TAG('f','i','n','a'), - /* The spec doesn't specify these but we apply anyway, since our Arabic shaper - * does. These are only used in Syriac spec. */ - HB_TAG('m','e','d','2'), - HB_TAG('f','i','n','2'), - HB_TAG('f','i','n','3'), }; -/* Same order as arabic_features. Don't need Syriac stuff.*/ +/* Same order as use_topographical_features. */ enum joining_form_t { - ISOL, - INIT, - MEDI, - FINA, - _NONE + USE_ISOL, + USE_INIT, + USE_MEDI, + USE_FINA, + _USE_NONE }; static const hb_tag_t -other_features[] = +use_other_features[] = { /* * Other features. @@ -89,42 +89,23 @@ other_features[] = HB_TAG('p','r','e','s'), HB_TAG('p','s','t','s'), }; -static const hb_tag_t -positioning_features[] = -{ - /* - * Positioning features. - * We don't care about the types. - */ - HB_TAG('d','i','s','t'), - HB_TAG('a','b','v','m'), - HB_TAG('b','l','w','m'), -}; static void -setup_syllables (const hb_ot_shape_plan_t *plan, +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static void +record_rphf_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); static void -clear_substitution_flags (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -record_rphf (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); +record_pref_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); static void -record_pref (const hb_ot_shape_plan_t *plan, +reorder_use (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); -static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); -static void -clear_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer); static void collect_features_use (hb_ot_shape_planner_t *plan) @@ -132,7 +113,7 @@ collect_features_use (hb_ot_shape_planner_t *plan) hb_ot_map_builder_t *map = &plan->map; /* Do this before any lookups have been applied. */ - map->add_gsub_pause (setup_syllables); + map->add_gsub_pause (setup_syllables_use); /* "Default glyph pre-processing group" */ map->enable_feature (HB_TAG('l','o','c','l')); @@ -141,32 +122,28 @@ collect_features_use (hb_ot_shape_planner_t *plan) map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ); /* "Reordering group" */ - map->add_gsub_pause (clear_substitution_flags); + map->add_gsub_pause (_hb_clear_substitution_flags); map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ); - map->add_gsub_pause (record_rphf); - map->add_gsub_pause (clear_substitution_flags); + map->add_gsub_pause (record_rphf_use); + map->add_gsub_pause (_hb_clear_substitution_flags); map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ); - map->add_gsub_pause (record_pref); + map->add_gsub_pause (record_pref_use); /* "Orthographic unit shaping group" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) - map->enable_feature (basic_features[i], F_MANUAL_ZWJ); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++) + map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ); - map->add_gsub_pause (reorder); - map->add_gsub_pause (clear_syllables); + map->add_gsub_pause (reorder_use); + map->add_gsub_pause (_hb_clear_syllables); /* "Topographical features" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (arabic_features); i++) - map->add_feature (arabic_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++) + map->add_feature (use_topographical_features[i]); map->add_gsub_pause (nullptr); /* "Standard typographic presentation" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) - map->enable_feature (other_features[i], F_MANUAL_ZWJ); - - /* "Positional feature application" */ - for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) - map->enable_feature (positioning_features[i]); + for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++) + map->enable_feature (use_other_features[i], F_MANUAL_ZWJ); } struct use_shape_plan_t @@ -176,40 +153,6 @@ struct use_shape_plan_t arabic_shape_plan_t *arabic_plan; }; -static bool -has_arabic_joining (hb_script_t script) -{ - /* List of scripts that have data in arabic-table. */ - switch ((int) script) - { - /* Unicode-1.1 additions */ - case HB_SCRIPT_ARABIC: - - /* Unicode-3.0 additions */ - case HB_SCRIPT_MONGOLIAN: - case HB_SCRIPT_SYRIAC: - - /* Unicode-5.0 additions */ - case HB_SCRIPT_NKO: - case HB_SCRIPT_PHAGS_PA: - - /* Unicode-6.0 additions */ - case HB_SCRIPT_MANDAIC: - - /* Unicode-7.0 additions */ - case HB_SCRIPT_MANICHAEAN: - case HB_SCRIPT_PSALTER_PAHLAVI: - - /* Unicode-9.0 additions */ - case HB_SCRIPT_ADLAM: - - return true; - - default: - return false; - } -} - static void * data_create_use (const hb_ot_shape_plan_t *plan) { @@ -243,15 +186,16 @@ data_destroy_use (void *data) free (data); } -enum syllable_type_t { - independent_cluster, - virama_terminated_cluster, - standard_cluster, - number_joiner_terminated_cluster, - numeral_cluster, - symbol_cluster, - broken_cluster, - non_cluster, +enum use_syllable_type_t { + use_independent_cluster, + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_broken_cluster, + use_non_cluster, }; #include "hb-ot-shape-complex-use-machine.hh" @@ -294,7 +238,7 @@ setup_rphf_mask (const hb_ot_shape_plan_t *plan, foreach_syllable (buffer, start, end) { - unsigned int limit = info[start].use_category() == USE_R ? 1 : MIN (3u, end - start); + unsigned int limit = info[start].use_category() == USE_R ? 1 : hb_min (3u, end - start); for (unsigned int i = start; i < start + limit; i++) info[i].mask |= mask; } @@ -308,11 +252,11 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, if (use_plan->arabic_plan) return; - static_assert ((INIT < 4 && ISOL < 4 && MEDI < 4 && FINA < 4), ""); + static_assert ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_FINA < 4), ""); hb_mask_t masks[4], all_masks = 0; for (unsigned int i = 0; i < 4; i++) { - masks[i] = plan->map.get_1_mask (arabic_features[i]); + masks[i] = plan->map.get_1_mask (use_topographical_features[i]); if (masks[i] == plan->map.get_global_mask ()) masks[i] = 0; all_masks |= masks[i]; @@ -322,38 +266,39 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, hb_mask_t other_masks = ~all_masks; unsigned int last_start = 0; - joining_form_t last_form = _NONE; + joining_form_t last_form = _USE_NONE; hb_glyph_info_t *info = buffer->info; foreach_syllable (buffer, start, end) { - syllable_type_t syllable_type = (syllable_type_t) (info[start].syllable() & 0x0F); + use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); switch (syllable_type) { - case independent_cluster: - case symbol_cluster: - case non_cluster: + case use_independent_cluster: + case use_symbol_cluster: + case use_non_cluster: /* These don't join. Nothing to do. */ - last_form = _NONE; + last_form = _USE_NONE; break; - case virama_terminated_cluster: - case standard_cluster: - case number_joiner_terminated_cluster: - case numeral_cluster: - case broken_cluster: + case use_virama_terminated_cluster: + case use_sakot_terminated_cluster: + case use_standard_cluster: + case use_number_joiner_terminated_cluster: + case use_numeral_cluster: + case use_broken_cluster: - bool join = last_form == FINA || last_form == ISOL; + bool join = last_form == USE_FINA || last_form == USE_ISOL; if (join) { /* Fixup previous syllable's form. */ - last_form = last_form == FINA ? MEDI : INIT; + last_form = last_form == USE_FINA ? USE_MEDI : USE_INIT; for (unsigned int i = last_start; i < start; i++) info[i].mask = (info[i].mask & other_masks) | masks[last_form]; } /* Form for this syllable. */ - last_form = join ? FINA : ISOL; + last_form = join ? USE_FINA : USE_ISOL; for (unsigned int i = start; i < end; i++) info[i].mask = (info[i].mask & other_masks) | masks[last_form]; @@ -365,11 +310,11 @@ setup_topographical_masks (const hb_ot_shape_plan_t *plan, } static void -setup_syllables (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { - find_syllables (buffer); + find_syllables_use (buffer); foreach_syllable (buffer, start, end) buffer->unsafe_to_break (start, end); setup_rphf_mask (plan, buffer); @@ -377,20 +322,9 @@ setup_syllables (const hb_ot_shape_plan_t *plan, } static void -clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - _hb_glyph_info_clear_substituted (&info[i]); -} - -static void -record_rphf (const hb_ot_shape_plan_t *plan, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; @@ -411,9 +345,9 @@ record_rphf (const hb_ot_shape_plan_t *plan, } static void -record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) +record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) { hb_glyph_info_t *info = buffer->info; @@ -430,21 +364,22 @@ record_pref (const hb_ot_shape_plan_t *plan HB_UNUSED, } static inline bool -is_halant (const hb_glyph_info_t &info) +is_halant_use (const hb_glyph_info_t &info) { return (info.use_category() == USE_H || info.use_category() == USE_HVM) && !_hb_glyph_info_ligated (&info); } static void -reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) +reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) { - syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F); + use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); /* Only a few syllable types need reordering. */ if (unlikely (!(FLAG_UNSAFE (syllable_type) & - (FLAG (virama_terminated_cluster) | - FLAG (standard_cluster) | - FLAG (broken_cluster) | + (FLAG (use_virama_terminated_cluster) | + FLAG (use_sakot_terminated_cluster) | + FLAG (use_standard_cluster) | + FLAG (use_broken_cluster) | 0)))) return; @@ -475,7 +410,7 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) for (unsigned int i = start + 1; i < end; i++) { bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || - is_halant (info[i]); + is_halant_use (info[i]); if (is_post_base_glyph || i == end - 1) { /* If we hit a post-base glyph, move before it; otherwise move to the @@ -499,7 +434,7 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) for (unsigned int i = start; i < end; i++) { uint32_t flag = FLAG_UNSAFE (info[i].use_category()); - if (is_halant (info[i])) + if (is_halant_use (info[i])) { /* If we hit a halant, move after it; otherwise move to the beginning, and * shift things in between forward. */ @@ -519,16 +454,20 @@ reorder_syllable (hb_buffer_t *buffer, unsigned int start, unsigned int end) } static inline void -insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font, - hb_buffer_t *buffer) +insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) { - /* Note: This loop is extra overhead, but should not be measurable. */ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + /* Note: This loop is extra overhead, but should not be measurable. + * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) - if ((info[i].syllable() & 0x0F) == broken_cluster) + if ((info[i].syllable() & 0x0F) == use_broken_cluster) { has_broken_syllables = true; break; @@ -548,8 +487,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, while (buffer->idx < buffer->len && buffer->successful) { unsigned int syllable = buffer->cur().syllable(); - syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F); - if (unlikely (last_syllable != syllable && syllable_type == broken_cluster)) + use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F); + if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster)) { last_syllable = syllable; @@ -557,7 +496,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, ginfo.cluster = buffer->cur().cluster; ginfo.mask = buffer->cur().mask; ginfo.syllable() = buffer->cur().syllable(); - /* TODO Set glyph_props? */ /* Insert dottedcircle after possible Repha. */ while (buffer->idx < buffer->len && buffer->successful && @@ -574,29 +512,18 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, } static void -reorder (const hb_ot_shape_plan_t *plan, - hb_font_t *font, - hb_buffer_t *buffer) +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) { - insert_dotted_circles (plan, font, buffer); + insert_dotted_circles_use (plan, font, buffer); foreach_syllable (buffer, start, end) - reorder_syllable (buffer, start, end); + reorder_syllable_use (buffer, start, end); HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); } -static void -clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, - hb_font_t *font HB_UNUSED, - hb_buffer_t *buffer) -{ - hb_glyph_info_t *info = buffer->info; - unsigned int count = buffer->len; - for (unsigned int i = 0; i < count; i++) - info[i].syllable() = 0; -} - static void preprocess_text_use (const hb_ot_shape_plan_t *plan, @@ -637,3 +564,6 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_use = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, false, /* fallback_position */ }; + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh index d7edef40ff2d4..6a42d58c5a8aa 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-use.hh @@ -68,6 +68,12 @@ enum use_category_t { USE_VS = 21, /* VARIATION_SELECTOR */ // USE_V = 36, /* VOWEL */ // USE_VM = 40, /* VOWEL_MOD */ + USE_CS = 43, /* CONS_WITH_STACKER */ + + /* https://github.com/harfbuzz/harfbuzz/issues/1102 */ + USE_HVM = 44, /* HALANT_OR_VOWEL_MODIFIER */ + + USE_Sk = 48, /* SAKOT */ USE_FAbv = 24, /* CONS_FINAL_ABOVE */ USE_FBlw = 25, /* CONS_FINAL_BELOW */ @@ -88,10 +94,9 @@ enum use_category_t { USE_VMPre = 23, /* VOWEL_MOD_PRE */ USE_SMAbv = 41, /* SYM_MOD_ABOVE */ USE_SMBlw = 42, /* SYM_MOD_BELOW */ - USE_CS = 43, /* CONS_WITH_STACKER */ - - /* https://github.com/harfbuzz/harfbuzz/issues/1102 */ - USE_HVM = 44, /* HALANT_OR_VOWEL_MODIFIER */ + USE_FMAbv = 45, /* CONS_FINAL_MOD UIPC = Top */ + USE_FMBlw = 46, /* CONS_FINAL_MOD UIPC = Bottom */ + USE_FMPst = 47, /* CONS_FINAL_MOD UIPC = Not_Applicable */ }; HB_INTERNAL USE_TABLE_ELEMENT_TYPE diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc index 366751efee2ce..adfaa3010491a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex-vowel-constraints.cc @@ -2,17 +2,22 @@ /* * The following functions are generated by running: * - * ./gen-vowel-constraints.py use Scripts.txt + * ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt * * on files with these headers: * - * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use - * # On October 23, 2018; with documentd dated 02/07/2018. + * # IndicShapingInvalidCluster.txt + * # Date: 2015-03-12, 21:17:00 GMT [AG] + * # Date: 2019-11-08, 23:22:00 GMT [AG] * - * # Scripts-11.0.0.txt - * # Date: 2018-02-21, 05:34:31 GMT + * # Scripts-13.0.0.txt + * # Date: 2020-01-22, 00:07:43 GMT */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-complex-vowel-constraints.hh" static void @@ -34,6 +39,12 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font HB_UNUSED) { +#ifdef HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS + return; +#endif + if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) + return; + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of * vowel-sequences that look like another vowel. Data for each script * collected from the USE script development spec. @@ -87,8 +98,7 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, 0x0907u == buffer->cur (2).codepoint) { buffer->next_glyph (); - buffer->next_glyph (); - _output_dotted_circle (buffer); + matched = true; } break; } @@ -201,6 +211,21 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, processed = true; break; + case HB_SCRIPT_TAMIL: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + if (0x0B85u == buffer->cur ().codepoint && + 0x0BC2u == buffer->cur (1).codepoint) + { + matched = true; + } + buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + processed = true; + break; + case HB_SCRIPT_TELUGU: for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) { @@ -434,4 +459,6 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, } } + +#endif /* == End of generated functions == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh index 2756ae1aeeb8f..59165c46c7031 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-complex.hh @@ -50,8 +50,9 @@ enum hb_ot_shape_zero_width_marks_type_t { /* Master OT shaper list */ #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \ - HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \ HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \ + HB_COMPLEX_SHAPER_IMPLEMENT (default) \ + HB_COMPLEX_SHAPER_IMPLEMENT (dumber) \ HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \ HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \ HB_COMPLEX_SHAPER_IMPLEMENT (indic) \ @@ -60,7 +61,7 @@ enum hb_ot_shape_zero_width_marks_type_t { HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \ HB_COMPLEX_SHAPER_IMPLEMENT (use) \ - /* ^--- Add new shapers here */ + /* ^--- Add new shapers here; keep sorted. */ struct hb_ot_complex_shaper_t @@ -377,6 +378,13 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner) case HB_SCRIPT_MAKASAR: //case HB_SCRIPT_SOGDIAN: + /* Unicode-12.0 additions */ + case HB_SCRIPT_NANDINAGARI: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_DIVES_AKURU: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc index d29bc9b9ef522..b6b6f0b153521 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-fallback.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-fallback.hh" #include "hb-kern.hh" @@ -166,6 +170,10 @@ _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t hb_font_t *font HB_UNUSED, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) @@ -414,12 +422,12 @@ position_cluster (const hb_ot_shape_plan_t *plan, /* Find the base glyph */ hb_glyph_info_t *info = buffer->info; for (unsigned int i = start; i < end; i++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))) + if (!_hb_glyph_info_is_unicode_mark (&info[i])) { /* Find mark glyphs */ unsigned int j; for (j = i + 1; j < end; j++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[j]))) + if (!_hb_glyph_info_is_unicode_mark (&info[j])) break; position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); @@ -434,13 +442,17 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, bool adjust_offsets_when_zeroing) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + _hb_buffer_assert_gsubgpos_vars (buffer); unsigned int start = 0; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) - if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) { + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); start = i; } @@ -448,6 +460,7 @@ _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, } +#ifndef HB_DISABLE_DEPRECATED struct hb_ot_shape_fallback_kern_driver_t { hb_ot_shape_fallback_kern_driver_t (hb_font_t *font_, @@ -466,6 +479,7 @@ struct hb_ot_shape_fallback_kern_driver_t hb_font_t *font; hb_direction_t direction; }; +#endif /* Performs font-assisted kerning. */ void @@ -473,6 +487,11 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) { +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + +#ifndef HB_DISABLE_DEPRECATED if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ? !font->has_glyph_h_kerning_func () : !font->has_glyph_v_kerning_func ()) @@ -489,6 +508,7 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, if (reverse) buffer->reverse (); +#endif } @@ -571,3 +591,6 @@ _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED, } } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc index ed73abb153043..aa88b2681bcf7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape-normalize.cc @@ -24,6 +24,10 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + #include "hb-ot-shape-normalize.hh" #include "hb-ot-shape-complex.hh" #include "hb-ot-shape.hh" @@ -330,7 +334,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, { unsigned int end; for (end = buffer->idx + 1; end < count; end++) - if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end])))) + if (unlikely (_hb_glyph_info_is_unicode_mark (&buffer->info[end]))) break; if (end < count) @@ -356,7 +360,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, /* Find all the marks now. */ for (end = buffer->idx + 1; end < count; end++) - if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))) + if (!_hb_glyph_info_is_unicode_mark(&buffer->info[end])) break; /* idx to end is one non-simple cluster. */ @@ -431,7 +435,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, * This is both an optimization to avoid trying to compose every two neighboring * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ - HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) + _hb_glyph_info_is_unicode_mark(&buffer->cur())) { if (/* If there's anything between the starter and this char, they should have CCC * smaller than this character's. */ @@ -469,3 +473,6 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, buffer->swap_buffers (); } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc index e9c8f882162bd..154725598ba1a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.cc @@ -26,6 +26,14 @@ * Google Author(s): Behdad Esfahbod */ +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#ifdef HB_NO_OT_LAYOUT +#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." +#endif + #include "hb-shaper-impl.hh" #include "hb-ot-shape.hh" @@ -40,6 +48,16 @@ #include "hb-aat-layout.hh" +#ifndef HB_NO_AAT_SHAPE +static inline bool +_hb_apply_morx (hb_face_t *face, const hb_segment_properties_t *props) +{ + /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ + return hb_aat_layout_has_substitution (face) && + (HB_DIRECTION_IS_HORIZONTAL (props->direction) || !hb_ot_layout_has_substitution (face)); +} +#endif + /** * SECTION:hb-ot-shape * @title: hb-ot-shape @@ -55,36 +73,24 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, const hb_feature_t *user_features, unsigned int num_user_features); -static bool -_hb_apply_morx (hb_face_t *face) -{ - if (hb_options ().aat && - hb_aat_layout_has_substitution (face)) - return true; - - /* Ignore empty GSUB tables. */ - return (!hb_ot_layout_has_substitution (face) || - !hb_ot_layout_table_get_script_tags (face, - HB_OT_TAG_GSUB, - 0, nullptr, nullptr)) && - hb_aat_layout_has_substitution (face); -} - hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, const hb_segment_properties_t *props) : face (face), props (*props), map (face, props), - aat_map (face, props), - apply_morx (_hb_apply_morx (face)) + aat_map (face, props) +#ifndef HB_NO_AAT_SHAPE + , apply_morx (_hb_apply_morx (face, props)) +#endif { shaper = hb_ot_shape_complex_categorize (this); script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; script_fallback_mark_positioning = shaper->fallback_position; - if (apply_morx) - shaper = &_hb_ot_complex_shaper_default; + /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ + if (apply_morx && shaper != &_hb_ot_complex_shaper_default) + shaper = &_hb_ot_complex_shaper_dumber; } void @@ -94,21 +100,32 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, plan.props = props; plan.shaper = shaper; map.compile (plan.map, key); +#ifndef HB_NO_AAT_SHAPE if (apply_morx) aat_map.compile (plan.aat_map); +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); +#endif + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); + hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); +#ifndef HB_NO_OT_KERN plan.kern_mask = plan.map.get_mask (kern_tag); - plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); - plan.requested_kerning = !!plan.kern_mask; +#endif +#ifndef HB_NO_AAT_SHAPE + plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); plan.requested_tracking = !!plan.trak_mask; +#endif + bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; bool disable_gpos = plan.shaper->gpos_tag && plan.shaper->gpos_tag != plan.map.chosen_script[1]; @@ -124,42 +141,61 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, * Decide who does substitutions. GSUB, morx, or fallback. */ +#ifndef HB_NO_AAT_SHAPE plan.apply_morx = apply_morx; +#endif /* * Decide who does positioning. GPOS, kerx, kern, or fallback. */ - if (hb_options ().aat && hb_aat_layout_has_positioning (face)) + if (0) + ; +#ifndef HB_NO_AAT_SHAPE + else if (hb_aat_layout_has_positioning (face)) plan.apply_kerx = true; +#endif else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face)) plan.apply_gpos = true; - else if (hb_aat_layout_has_positioning (face)) - plan.apply_kerx = true; - if (!plan.apply_kerx && !has_gpos_kern) + if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) { /* Apparently Apple applies kerx if GPOS kern was not applied. */ +#ifndef HB_NO_AAT_SHAPE if (hb_aat_layout_has_positioning (face)) plan.apply_kerx = true; - else if (hb_ot_layout_has_kerning (face)) + else +#endif +#ifndef HB_NO_OT_KERN + if (hb_ot_layout_has_kerning (face)) plan.apply_kern = true; +#endif } plan.zero_marks = script_zero_marks && !plan.apply_kerx && - (!plan.apply_kern || !hb_ot_layout_has_machine_kerning (face)); + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_machine_kerning (face) +#endif + ); plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && !plan.apply_kerx && - (!plan.apply_kern || !hb_ot_layout_has_cross_kerning (face)); + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_cross_kerning (face) +#endif + ); plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && script_fallback_mark_positioning; +#ifndef HB_NO_AAT_SHAPE /* Currently we always apply trak. */ plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); +#endif } bool @@ -167,7 +203,9 @@ hb_ot_shape_plan_t::init0 (hb_face_t *face, const hb_shape_plan_key_t *key) { map.init (); +#ifndef HB_NO_AAT_SHAPE aat_map.init (); +#endif hb_ot_shape_planner_t planner (face, &key->props); @@ -182,7 +220,13 @@ hb_ot_shape_plan_t::init0 (hb_face_t *face, { data = shaper->data_create (this); if (unlikely (!data)) + { + map.fini (); +#ifndef HB_NO_AAT_SHAPE + aat_map.fini (); +#endif return false; + } } return true; @@ -195,16 +239,20 @@ hb_ot_shape_plan_t::fini () shaper->data_destroy (const_cast (data)); map.fini (); +#ifndef HB_NO_AAT_SHAPE aat_map.fini (); +#endif } void hb_ot_shape_plan_t::substitute (hb_font_t *font, hb_buffer_t *buffer) const { +#ifndef HB_NO_AAT_SHAPE if (unlikely (apply_morx)) hb_aat_layout_substitute (this, font, buffer); else +#endif map.substitute (this, font, buffer); } @@ -214,21 +262,29 @@ hb_ot_shape_plan_t::position (hb_font_t *font, { if (this->apply_gpos) map.position (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE else if (this->apply_kerx) hb_aat_layout_position (this, font, buffer); +#endif +#ifndef HB_NO_OT_KERN else if (this->apply_kern) hb_ot_layout_kern (this, font, buffer); +#endif else _hb_ot_shape_fallback_kern (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE if (this->apply_trak) hb_aat_layout_track (this, font, buffer); +#endif } static const hb_ot_map_feature_t common_features[] = { + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, {HB_TAG('c','c','m','p'), F_GLOBAL}, {HB_TAG('l','o','c','l'), F_GLOBAL}, {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, @@ -243,6 +299,7 @@ horizontal_features[] = {HB_TAG('c','a','l','t'), F_GLOBAL}, {HB_TAG('c','l','i','g'), F_GLOBAL}, {HB_TAG('c','u','r','s'), F_GLOBAL}, + {HB_TAG('d','i','s','t'), F_GLOBAL}, {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, {HB_TAG('l','i','g','a'), F_GLOBAL}, {HB_TAG('r','c','l','t'), F_GLOBAL}, @@ -274,18 +331,22 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, break; } +#ifndef HB_NO_OT_SHAPE_FRACTIONS /* Automatic fractions. */ map->add_feature (HB_TAG ('f','r','a','c')); map->add_feature (HB_TAG ('n','u','m','r')); map->add_feature (HB_TAG ('d','n','o','m')); +#endif /* Random! */ map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); +#ifndef HB_NO_AAT_SHAPE /* Tracking. We enable dummy feature here just to allow disabling * AAT 'trak' table using features. * https://github.com/harfbuzz/harfbuzz/issues/1303 */ map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); +#endif map->enable_feature (HB_TAG ('H','A','R','F')); @@ -318,6 +379,7 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, feature->value); } +#ifndef HB_NO_AAT_SHAPE if (planner->apply_morx) { hb_aat_map_builder_t *aat_map = &planner->aat_map; @@ -327,6 +389,7 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, aat_map->add_feature (feature->tag, feature->value); } } +#endif if (planner->shaper->override_features) planner->shaper->override_features (planner); @@ -417,6 +480,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) { _hb_glyph_info_set_continuation (&info[i]); } +#ifndef HB_NO_EMOJI_SEQUENCES else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) { _hb_glyph_info_set_continuation (&info[i]); @@ -428,6 +492,7 @@ hb_set_unicode_props (hb_buffer_t *buffer) _hb_glyph_info_set_continuation (&info[i]); } } +#endif /* Or part of the Other_Grapheme_Extend that is not marks. * As of Unicode 11 that is just: * @@ -448,6 +513,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) static void hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || buffer->context_len[0] || !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) @@ -524,30 +592,95 @@ hb_ensure_native_direction (hb_buffer_t *buffer) * Substitute */ -static inline void -hb_ot_mirror_chars (const hb_ot_shape_context_t *c) +static hb_codepoint_t +hb_vert_char_for (hb_codepoint_t u) { - if (HB_DIRECTION_IS_FORWARD (c->target_direction)) - return; + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } - hb_buffer_t *buffer = c->buffer; - hb_unicode_funcs_t *unicode = buffer->unicode; - hb_mask_t rtlm_mask = c->plan->rtlm_mask; + return u; +} +static inline void +hb_ot_rotate_chars (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; - for (unsigned int i = 0; i < count; i++) { - hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); - if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint))) - info[i].mask |= rtlm_mask; - else - info[i].codepoint = codepoint; + + if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + hb_mask_t rtlm_mask = c->plan->rtlm_mask; + + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + else + info[i].mask |= rtlm_mask; + } + } + + if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) + { + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + } } } static inline void hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) { +#ifdef HB_NO_OT_SHAPE_FRACTIONS + return; +#endif + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || !c->plan->has_frac) return; @@ -619,7 +752,7 @@ hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) for (unsigned int i = 0; i < c->num_user_features; i++) { const hb_feature_t *feature = &c->user_features[i]; - if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { + if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { unsigned int shift; hb_mask_t mask = map->get_mask (feature->tag, &shift); buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); @@ -714,7 +847,7 @@ hb_ot_substitute_default (const hb_ot_shape_context_t *c) { hb_buffer_t *buffer = c->buffer; - hb_ot_mirror_chars (c); + hb_ot_rotate_chars (c); HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); @@ -758,8 +891,10 @@ static inline void hb_ot_substitute_post (const hb_ot_shape_context_t *c) { hb_ot_hide_default_ignorables (c->buffer, c->font); +#ifndef HB_NO_AAT_SHAPE if (c->plan->apply_morx) hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif if (c->plan->shaper->postprocess_glyphs) c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); @@ -893,8 +1028,10 @@ hb_ot_position_complex (const hb_ot_shape_context_t *c) /* Finish off. Has to follow a certain order. */ hb_ot_layout_position_finish_advances (c->font, c->buffer); hb_ot_zero_width_default_ignorables (c->buffer); +#ifndef HB_NO_AAT_SHAPE if (c->plan->apply_morx) hb_aat_layout_zero_width_deleted_glyphs (c->buffer); +#endif hb_ot_layout_position_finish_offsets (c->font, c->buffer); /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ @@ -959,13 +1096,13 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c) c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) { - c->buffer->max_len = MAX (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, - (unsigned) HB_BUFFER_MAX_LEN_MIN); + c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, + (unsigned) HB_BUFFER_MAX_LEN_MIN); } if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) { - c->buffer->max_ops = MAX (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, - (unsigned) HB_BUFFER_MAX_OPS_MIN); + c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, + (unsigned) HB_BUFFER_MAX_OPS_MIN); } /* Save the original direction, we use it later. */ @@ -1081,3 +1218,6 @@ hb_ot_shape_glyphs_closure (hb_font_t *font, hb_shape_plan_destroy (shape_plan); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh index 5adc05827e6dc..7823126ee46e0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-shape.hh @@ -37,9 +37,9 @@ struct hb_ot_shape_plan_key_t { unsigned int variations_index[2]; - void init (hb_face_t *face, - const int *coords, - unsigned int num_coords) + void init (hb_face_t *face, + const int *coords, + unsigned num_coords) { for (unsigned int table_index = 0; table_index < 2; table_index++) hb_ot_layout_table_find_feature_variations (face, @@ -65,14 +65,41 @@ struct hb_ot_shape_plan_t hb_ot_map_t map; hb_aat_map_t aat_map; const void *data; +#ifndef HB_NO_OT_SHAPE_FRACTIONS hb_mask_t frac_mask, numr_mask, dnom_mask; +#else + static constexpr hb_mask_t frac_mask = 0; + static constexpr hb_mask_t numr_mask = 0; + static constexpr hb_mask_t dnom_mask = 0; +#endif hb_mask_t rtlm_mask; +#ifndef HB_NO_OT_KERN hb_mask_t kern_mask; +#else + static constexpr hb_mask_t kern_mask = 0; +#endif +#ifndef HB_NO_AAT_SHAPE hb_mask_t trak_mask; +#else + static constexpr hb_mask_t trak_mask = 0; +#endif +#ifndef HB_NO_OT_KERN bool requested_kerning : 1; +#else + static constexpr bool requested_kerning = false; +#endif +#ifndef HB_NO_AAT_SHAPE bool requested_tracking : 1; +#else + static constexpr bool requested_tracking = false; +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS bool has_frac : 1; +#else + static constexpr bool has_frac = false; +#endif + bool has_vert : 1; bool has_gpos_mark : 1; bool zero_marks : 1; bool fallback_glyph_classes : 1; @@ -80,10 +107,20 @@ struct hb_ot_shape_plan_t bool adjust_mark_positioning_when_zeroing : 1; bool apply_gpos : 1; - bool apply_kerx : 1; +#ifndef HB_NO_OT_KERN bool apply_kern : 1; +#else + static constexpr bool apply_kern = false; +#endif +#ifndef HB_NO_AAT_SHAPE + bool apply_kerx : 1; bool apply_morx : 1; bool apply_trak : 1; +#else + static constexpr bool apply_kerx = false; + static constexpr bool apply_morx = false; + static constexpr bool apply_trak = false; +#endif void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const { @@ -113,7 +150,11 @@ struct hb_ot_shape_planner_t hb_segment_properties_t props; hb_ot_map_builder_t map; hb_aat_map_builder_t aat_map; +#ifndef HB_NO_AAT_SHAPE bool apply_morx : 1; +#else + static constexpr bool apply_morx = false; +#endif bool script_zero_marks : 1; bool script_fallback_mark_positioning : 1; const struct hb_ot_complex_shaper_t *shaper; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh index 63dbfce4551da..ca97affc9a644 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-stat-table.hh @@ -59,6 +59,11 @@ enum struct AxisValueFormat1 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -75,13 +80,18 @@ struct AxisValueFormat1 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed value; /* A numeric value for this attribute value. */ + HBFixed value; /* A numeric value for this attribute value. */ public: DEFINE_SIZE_STATIC (12); }; struct AxisValueFormat2 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return nominalValue.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -98,10 +108,10 @@ struct AxisValueFormat2 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed nominalValue; /* A numeric value for this attribute value. */ - Fixed rangeMinValue; /* The minimum value for a range associated + HBFixed nominalValue; /* A numeric value for this attribute value. */ + HBFixed rangeMinValue; /* The minimum value for a range associated * with the specified name ID. */ - Fixed rangeMaxValue; /* The maximum value for a range associated + HBFixed rangeMaxValue; /* The maximum value for a range associated * with the specified name ID. */ public: DEFINE_SIZE_STATIC (20); @@ -109,6 +119,11 @@ struct AxisValueFormat2 struct AxisValueFormat3 { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -125,8 +140,8 @@ struct AxisValueFormat3 NameID valueNameID; /* The name ID for entries in the 'name' table * that provide a display string for this * attribute value. */ - Fixed value; /* A numeric value for this attribute value. */ - Fixed linkedValue; /* The numeric value for a style-linked mapping + HBFixed value; /* A numeric value for this attribute value. */ + HBFixed linkedValue; /* The numeric value for a style-linked mapping * from this value. */ public: DEFINE_SIZE_STATIC (16); @@ -134,6 +149,9 @@ struct AxisValueFormat3 struct AxisValueRecord { + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -144,13 +162,18 @@ struct AxisValueRecord HBUINT16 axisIndex; /* Zero-base index into the axis record array * identifying the axis to which this value * applies. Must be less than designAxisCount. */ - Fixed value; /* A numeric value for this attribute value. */ + HBFixed value; /* A numeric value for this attribute value. */ public: DEFINE_SIZE_STATIC (6); }; struct AxisValueFormat4 { + const AxisValueRecord &get_axis_record (unsigned int axis_index) const + { return axisValues.as_array (axisCount)[axis_index]; } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -175,19 +198,55 @@ struct AxisValueFormat4 struct AxisValue { + bool get_value (unsigned int axis_index) const + { + switch (u.format) + { + case 1: return u.format1.get_value (); + case 2: return u.format2.get_value (); + case 3: return u.format3.get_value (); + case 4: return u.format4.get_axis_record (axis_index).get_value (); + default:return 0; + } + } + + unsigned int get_axis_index () const + { + switch (u.format) + { + case 1: return u.format1.get_axis_index (); + case 2: return u.format2.get_axis_index (); + case 3: return u.format3.get_axis_index (); + /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ + default:return -1; + } + } + + hb_ot_name_id_t get_value_name_id () const + { + switch (u.format) + { + case 1: return u.format1.get_value_name_id (); + case 2: return u.format2.get_value_name_id (); + case 3: return u.format3.get_value_name_id (); + case 4: return u.format4.get_value_name_id (); + default:return HB_OT_NAME_ID_INVALID; + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (c->check_struct (this))) + if (unlikely (!c->check_struct (this))) return_trace (false); switch (u.format) { - case 1: return_trace (likely (u.format1.sanitize (c))); - case 2: return_trace (likely (u.format2.sanitize (c))); - case 3: return_trace (likely (u.format3.sanitize (c))); - case 4: return_trace (likely (u.format4.sanitize (c))); - default: return_trace (true); + case 1: return_trace (u.format1.sanitize (c)); + case 2: return_trace (u.format2.sanitize (c)); + case 3: return_trace (u.format3.sanitize (c)); + case 4: return_trace (u.format4.sanitize (c)); + default:return_trace (true); } } @@ -206,6 +265,10 @@ struct AxisValue struct StatAxisRecord { + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + hb_ot_name_id_t get_name_id () const { return nameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -227,21 +290,82 @@ struct STAT { static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT; + bool has_data () const { return version.to_int (); } + + bool get_value (hb_tag_t tag, float *value) const + { + unsigned int axis_index; + if (!get_design_axes ().lfind (tag, &axis_index)) return false; + + hb_array_t> axis_values = get_axis_value_offsets (); + for (unsigned int i = 0; i < axis_values.length; i++) + { + const AxisValue& axis_value = this+axis_values[i]; + if (axis_value.get_axis_index () == axis_index) + { + if (value) + *value = axis_value.get_value (axis_index); + return true; + } + } + return false; + } + + unsigned get_design_axis_count () const { return designAxisCount; } + + hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const + { + if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID; + const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index]; + return axis_record.get_name_id (); + } + + unsigned get_axis_value_count () const { return axisValueCount; } + + hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const + { + if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID; + const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]); + return axis_value.get_value_name_id (); + } + + void collect_name_ids (hb_set_t *nameids_to_retain) const + { + if (!has_data ()) return; + + + get_design_axes () + | hb_map (&StatAxisRecord::get_name_id) + | hb_sink (nameids_to_retain) + ; + + + get_axis_value_offsets () + | hb_map (hb_add (&(this + offsetToAxisValueOffsets))) + | hb_map (&AxisValue::get_value_name_id) + | hb_sink (nameids_to_retain) + ; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && - majorVersion == 1 && - minorVersion > 0 && + version.major == 1 && + version.minor > 0 && designAxesOffset.sanitize (c, this, designAxisCount) && offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); } protected: - HBUINT16 majorVersion; /* Major version number of the style attributes - * table — set to 1. */ - HBUINT16 minorVersion; /* Minor version number of the style attributes - * table — set to 2. */ + hb_array_t const get_design_axes () const + { return (this+designAxesOffset).as_array (designAxisCount); } + + hb_array_t> const get_axis_value_offsets () const + { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); } + + + protected: + FixedVersion<>version; /* Version of the stat table + * initially set to 0x00010002u */ HBUINT16 designAxisSize; /* The size in bytes of each axis record. */ HBUINT16 designAxisCount;/* The number of design axis records. In a * font with an 'fvar' table, this value must be @@ -249,7 +373,7 @@ struct STAT * in the 'fvar' table. In all fonts, must * be greater than zero if axisValueCount * is greater than zero. */ - LNNOffsetTo > + LNNOffsetTo> designAxesOffset; /* Offset in bytes from the beginning of * the STAT table to the start of the design @@ -257,7 +381,7 @@ struct STAT * set to zero; if designAxisCount is greater * than zero, must be greater than zero. */ HBUINT16 axisValueCount; /* The number of axis value tables. */ - LNNOffsetTo > > + LNNOffsetTo>> offsetToAxisValueOffsets; /* Offset in bytes from the beginning of * the STAT table to the start of the design diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh index 93333bb3ff08e..9109b487b91c2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag-table.hh @@ -6,1055 +6,1058 @@ * * on files with these headers: * - * - * File-Date: 2018-08-08 + * + * File-Date: 2020-05-12 */ #ifndef HB_OT_TAG_TABLE_HH #define HB_OT_TAG_TABLE_HH static const LangTag ot_languages[] = { - {"aa", {HB_TAG('A','F','R',' ')}}, /* Afar */ - {"aae", {HB_TAG('S','Q','I',' ')}}, /* Arbëreshë Albanian -> Albanian */ - {"aao", {HB_TAG('A','R','A',' ')}}, /* Algerian Saharan Arabic -> Arabic */ - {"aat", {HB_TAG('S','Q','I',' ')}}, /* Arvanitika Albanian -> Albanian */ - {"ab", {HB_TAG('A','B','K',' ')}}, /* Abkhazian */ - {"abh", {HB_TAG('A','R','A',' ')}}, /* Tajiki Arabic -> Arabic */ - {"abq", {HB_TAG('A','B','A',' ')}}, /* Abaza */ - {"abv", {HB_TAG('A','R','A',' ')}}, /* Baharna Arabic -> Arabic */ - {"acf", {HB_TAG('F','A','N',' ')}}, /* Saint Lucian Creole French -> French Antillean */ - {"ach", {HB_TAG('A','C','H',' ')}}, /* Acoli -> Acholi */ - {"acm", {HB_TAG('A','R','A',' ')}}, /* Mesopotamian Arabic -> Arabic */ - {"acq", {HB_TAG('A','R','A',' ')}}, /* Ta'izzi-Adeni Arabic -> Arabic */ - {"acr", {HB_TAG('A','C','R',' ')}}, /* Achi */ - {"acw", {HB_TAG('A','R','A',' ')}}, /* Hijazi Arabic -> Arabic */ - {"acx", {HB_TAG('A','R','A',' ')}}, /* Omani Arabic -> Arabic */ - {"acy", {HB_TAG('A','R','A',' ')}}, /* Cypriot Arabic -> Arabic */ - {"ada", {HB_TAG('D','N','G',' ')}}, /* Adangme -> Dangme */ - {"adf", {HB_TAG('A','R','A',' ')}}, /* Dhofari Arabic -> Arabic */ - {"adp", {HB_TAG('D','Z','N',' ')}}, /* Adap (retired code) -> Dzongkha */ - {"ady", {HB_TAG('A','D','Y',' ')}}, /* Adyghe */ - {"aeb", {HB_TAG('A','R','A',' ')}}, /* Tunisian Arabic -> Arabic */ - {"aec", {HB_TAG('A','R','A',' ')}}, /* Saidi Arabic -> Arabic */ - {"af", {HB_TAG('A','F','K',' ')}}, /* Afrikaans */ - {"afb", {HB_TAG('A','R','A',' ')}}, /* Gulf Arabic -> Arabic */ - {"ahg", {HB_TAG('A','G','W',' ')}}, /* Qimant -> Agaw */ - {"aht", {HB_TAG('A','T','H',' ')}}, /* Ahtena -> Athapaskan */ - {"aii", {HB_TAG('S','W','A',' '), /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ - HB_TAG('S','Y','R',' ')}}, /* Assyrian Neo-Aramaic -> Syriac */ - {"aio", {HB_TAG('A','I','O',' ')}}, /* Aiton */ - {"aiw", {HB_TAG('A','R','I',' ')}}, /* Aari */ - {"ajp", {HB_TAG('A','R','A',' ')}}, /* South Levantine Arabic -> Arabic */ - {"ak", {HB_TAG('A','K','A',' '), /* Akan [macrolanguage] */ - HB_TAG('T','W','I',' ')}}, /* Akan [macrolanguage] -> Twi */ - {"aln", {HB_TAG('S','Q','I',' ')}}, /* Gheg Albanian -> Albanian */ - {"als", {HB_TAG('S','Q','I',' ')}}, /* Tosk Albanian -> Albanian */ - {"alt", {HB_TAG('A','L','T',' ')}}, /* Southern Altai -> Altai */ - {"am", {HB_TAG('A','M','H',' ')}}, /* Amharic */ - {"amf", {HB_TAG('H','B','N',' ')}}, /* Hamer-Banna -> Hammer-Banna */ - {"amw", {HB_TAG('S','Y','R',' ')}}, /* Western Neo-Aramaic -> Syriac */ - {"an", {HB_TAG('A','R','G',' ')}}, /* Aragonese */ - {"ang", {HB_TAG('A','N','G',' ')}}, /* Old English (ca. 450-1100) -> Anglo-Saxon */ - {"apc", {HB_TAG('A','R','A',' ')}}, /* North Levantine Arabic -> Arabic */ - {"apd", {HB_TAG('A','R','A',' ')}}, /* Sudanese Arabic -> Arabic */ - {"apj", {HB_TAG('A','T','H',' ')}}, /* Jicarilla Apache -> Athapaskan */ - {"apk", {HB_TAG('A','T','H',' ')}}, /* Kiowa Apache -> Athapaskan */ - {"apl", {HB_TAG('A','T','H',' ')}}, /* Lipan Apache -> Athapaskan */ - {"apm", {HB_TAG('A','T','H',' ')}}, /* Mescalero-Chiricahua Apache -> Athapaskan */ - {"apw", {HB_TAG('A','T','H',' ')}}, /* Western Apache -> Athapaskan */ - {"ar", {HB_TAG('A','R','A',' ')}}, /* Arabic [macrolanguage] */ - {"arb", {HB_TAG('A','R','A',' ')}}, /* Standard Arabic -> Arabic */ - {"arn", {HB_TAG('M','A','P',' ')}}, /* Mapudungun */ - {"arq", {HB_TAG('A','R','A',' ')}}, /* Algerian Arabic -> Arabic */ - {"ars", {HB_TAG('A','R','A',' ')}}, /* Najdi Arabic -> Arabic */ - {"ary", {HB_TAG('M','O','R',' ')}}, /* Moroccan Arabic -> Moroccan */ - {"arz", {HB_TAG('A','R','A',' ')}}, /* Egyptian Arabic -> Arabic */ - {"as", {HB_TAG('A','S','M',' ')}}, /* Assamese */ - {"ast", {HB_TAG('A','S','T',' ')}}, /* Asturian */ - {"ath", {HB_TAG('A','T','H',' ')}}, /* Athapascan [family] -> Athapaskan */ - {"atj", {HB_TAG('R','C','R',' ')}}, /* Atikamekw -> R-Cree */ - {"atv", {HB_TAG('A','L','T',' ')}}, /* Northern Altai -> Altai */ - {"auz", {HB_TAG('A','R','A',' ')}}, /* Uzbeki Arabic -> Arabic */ - {"av", {HB_TAG('A','V','R',' ')}}, /* Avaric -> Avar */ - {"avl", {HB_TAG('A','R','A',' ')}}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ - {"awa", {HB_TAG('A','W','A',' ')}}, /* Awadhi */ - {"ay", {HB_TAG('A','Y','M',' ')}}, /* Aymara [macrolanguage] */ - {"ayc", {HB_TAG('A','Y','M',' ')}}, /* Southern Aymara -> Aymara */ - {"ayh", {HB_TAG('A','R','A',' ')}}, /* Hadrami Arabic -> Arabic */ - {"ayl", {HB_TAG('A','R','A',' ')}}, /* Libyan Arabic -> Arabic */ - {"ayn", {HB_TAG('A','R','A',' ')}}, /* Sanaani Arabic -> Arabic */ - {"ayp", {HB_TAG('A','R','A',' ')}}, /* North Mesopotamian Arabic -> Arabic */ - {"ayr", {HB_TAG('A','Y','M',' ')}}, /* Central Aymara -> Aymara */ - {"az", {HB_TAG('A','Z','E',' ')}}, /* Azerbaijani [macrolanguage] */ - {"azb", {HB_TAG('A','Z','B',' ')}}, /* South Azerbaijani -> Torki */ - {"azj", {HB_TAG('A','Z','E',' ')}}, /* North Azerbaijani -> Azerbaijani */ - {"ba", {HB_TAG('B','S','H',' ')}}, /* Bashkir */ - {"bad", {HB_TAG('B','A','D','0')}}, /* Banda [family] */ - {"bai", {HB_TAG('B','M','L',' ')}}, /* Bamileke [family] */ - {"bal", {HB_TAG('B','L','I',' ')}}, /* Baluchi [macrolanguage] */ - {"ban", {HB_TAG('B','A','N',' ')}}, /* Balinese */ - {"bar", {HB_TAG('B','A','R',' ')}}, /* Bavarian */ - {"bbc", {HB_TAG('B','B','C',' ')}}, /* Batak Toba */ - {"bbz", {HB_TAG('A','R','A',' ')}}, /* Babalia Creole Arabic -> Arabic */ - {"bcc", {HB_TAG('B','L','I',' ')}}, /* Southern Balochi -> Baluchi */ - {"bci", {HB_TAG('B','A','U',' ')}}, /* Baoulé -> Baulé */ - {"bcl", {HB_TAG('B','I','K',' ')}}, /* Central Bikol -> Bikol */ - {"bcq", {HB_TAG('B','C','H',' ')}}, /* Bench */ - {"bcr", {HB_TAG('A','T','H',' ')}}, /* Babine -> Athapaskan */ - {"bdy", {HB_TAG('B','D','Y',' ')}}, /* Bandjalang */ - {"be", {HB_TAG('B','E','L',' ')}}, /* Belarusian -> Belarussian */ - {"bea", {HB_TAG('A','T','H',' ')}}, /* Beaver -> Athapaskan */ - {"beb", {HB_TAG('B','T','I',' ')}}, /* Bebele -> Beti */ - {"bem", {HB_TAG('B','E','M',' ')}}, /* Bemba (Zambia) */ - {"ber", {HB_TAG('B','B','R',' ')}}, /* Berber [family] */ - {"bfq", {HB_TAG('B','A','D',' ')}}, /* Badaga */ - {"bft", {HB_TAG('B','L','T',' ')}}, /* Balti */ - {"bfu", {HB_TAG('L','A','H',' ')}}, /* Gahri -> Lahuli */ - {"bfy", {HB_TAG('B','A','G',' ')}}, /* Bagheli -> Baghelkhandi */ - {"bg", {HB_TAG('B','G','R',' ')}}, /* Bulgarian */ - {"bgc", {HB_TAG('B','G','C',' ')}}, /* Haryanvi */ - {"bgn", {HB_TAG('B','L','I',' ')}}, /* Western Balochi -> Baluchi */ - {"bgp", {HB_TAG('B','L','I',' ')}}, /* Eastern Balochi -> Baluchi */ - {"bgq", {HB_TAG('B','G','Q',' ')}}, /* Bagri */ - {"bgr", {HB_TAG('Q','I','N',' ')}}, /* Bawm Chin -> Chin */ - {"bhb", {HB_TAG('B','H','I',' ')}}, /* Bhili */ - {"bhi", {HB_TAG('B','H','I',' ')}}, /* Bhilali -> Bhili */ - {"bhk", {HB_TAG('B','I','K',' ')}}, /* Albay Bicolano (retired code) -> Bikol */ - {"bho", {HB_TAG('B','H','O',' ')}}, /* Bhojpuri */ - {"bhr", {HB_TAG('M','L','G',' ')}}, /* Bara Malagasy -> Malagasy */ - {"bi", {HB_TAG('B','I','S',' ')}}, /* Bislama */ - {"bik", {HB_TAG('B','I','K',' ')}}, /* Bikol [macrolanguage] */ - {"bin", {HB_TAG('E','D','O',' ')}}, /* Edo */ - {"bjj", {HB_TAG('B','J','J',' ')}}, /* Kanauji */ - {"bjn", {HB_TAG('M','L','Y',' ')}}, /* Banjar -> Malay */ - {"bjq", {HB_TAG('M','L','G',' ')}}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ - {"bjt", {HB_TAG('B','L','N',' ')}}, /* Balanta-Ganja -> Balante */ - {"bla", {HB_TAG('B','K','F',' ')}}, /* Siksika -> Blackfoot */ - {"ble", {HB_TAG('B','L','N',' ')}}, /* Balanta-Kentohe -> Balante */ - {"blk", {HB_TAG('B','L','K',' ')}}, /* Pa'o Karen */ - {"bln", {HB_TAG('B','I','K',' ')}}, /* Southern Catanduanes Bikol -> Bikol */ - {"bm", {HB_TAG('B','M','B',' ')}}, /* Bambara (Bamanankan) */ - {"bmm", {HB_TAG('M','L','G',' ')}}, /* Northern Betsimisaraka Malagasy -> Malagasy */ - {"bn", {HB_TAG('B','E','N',' ')}}, /* Bengali */ - {"bo", {HB_TAG('T','I','B',' ')}}, /* Tibetan */ - {"bpy", {HB_TAG('B','P','Y',' ')}}, /* Bishnupriya -> Bishnupriya Manipuri */ - {"bqi", {HB_TAG('L','R','C',' ')}}, /* Bakhtiari -> Luri */ - {"br", {HB_TAG('B','R','E',' ')}}, /* Breton */ - {"bra", {HB_TAG('B','R','I',' ')}}, /* Braj -> Braj Bhasha */ - {"brh", {HB_TAG('B','R','H',' ')}}, /* Brahui */ - {"brx", {HB_TAG('B','R','X',' ')}}, /* Bodo (India) */ - {"bs", {HB_TAG('B','O','S',' ')}}, /* Bosnian */ - {"bsk", {HB_TAG('B','S','K',' ')}}, /* Burushaski */ - {"btb", {HB_TAG('B','T','I',' ')}}, /* Beti (Cameroon) (retired code) */ - {"btj", {HB_TAG('M','L','Y',' ')}}, /* Bacanese Malay -> Malay */ - {"bto", {HB_TAG('B','I','K',' ')}}, /* Rinconada Bikol -> Bikol */ - {"bts", {HB_TAG('B','T','S',' ')}}, /* Batak Simalungun */ - {"bug", {HB_TAG('B','U','G',' ')}}, /* Buginese -> Bugis */ - {"bum", {HB_TAG('B','T','I',' ')}}, /* Bulu (Cameroon) -> Beti */ - {"bve", {HB_TAG('M','L','Y',' ')}}, /* Berau Malay -> Malay */ - {"bvu", {HB_TAG('M','L','Y',' ')}}, /* Bukit Malay -> Malay */ - {"bxk", {HB_TAG('L','U','H',' ')}}, /* Bukusu -> Luyia */ - {"bxp", {HB_TAG('B','T','I',' ')}}, /* Bebil -> Beti */ - {"bxr", {HB_TAG('R','B','U',' ')}}, /* Russia Buriat -> Russian Buriat */ - {"byn", {HB_TAG('B','I','L',' ')}}, /* Bilin -> Bilen */ - {"byv", {HB_TAG('B','Y','V',' ')}}, /* Medumba */ - {"bzc", {HB_TAG('M','L','G',' ')}}, /* Southern Betsimisaraka Malagasy -> Malagasy */ - {"ca", {HB_TAG('C','A','T',' ')}}, /* Catalan */ - {"caf", {HB_TAG('C','R','R',' '), /* Southern Carrier -> Carrier */ - HB_TAG('A','T','H',' ')}}, /* Southern Carrier -> Athapaskan */ - {"cak", {HB_TAG('C','A','K',' ')}}, /* Kaqchikel */ - {"cbk", {HB_TAG('C','B','K',' ')}}, /* Chavacano -> Zamboanga Chavacano */ - {"cbl", {HB_TAG('Q','I','N',' ')}}, /* Bualkhaw Chin -> Chin */ - {"cco", {HB_TAG('C','C','H','N')}}, /* Comaltepec Chinantec -> Chinantec */ - {"ccq", {HB_TAG('A','R','K',' ')}}, /* Chaungtha (retired code) -> Rakhine */ - {"cdo", {HB_TAG('Z','H','S',' ')}}, /* Min Dong Chinese -> Chinese Simplified */ - {"ce", {HB_TAG('C','H','E',' ')}}, /* Chechen */ - {"ceb", {HB_TAG('C','E','B',' ')}}, /* Cebuano */ - {"cfm", {HB_TAG('H','A','L',' ')}}, /* Halam (Falam Chin) */ - {"cgg", {HB_TAG('C','G','G',' ')}}, /* Chiga */ - {"ch", {HB_TAG('C','H','A',' ')}}, /* Chamorro */ - {"chj", {HB_TAG('C','C','H','N')}}, /* Ojitlán Chinantec -> Chinantec */ - {"chk", {HB_TAG('C','H','K','0')}}, /* Chuukese */ - {"cho", {HB_TAG('C','H','O',' ')}}, /* Choctaw */ - {"chp", {HB_TAG('C','H','P',' '), /* Chipewyan */ - HB_TAG('S','A','Y',' '), /* Chipewyan -> Sayisi */ - HB_TAG('A','T','H',' ')}}, /* Chipewyan -> Athapaskan */ - {"chq", {HB_TAG('C','C','H','N')}}, /* Quiotepec Chinantec -> Chinantec */ - {"chr", {HB_TAG('C','H','R',' ')}}, /* Cherokee */ - {"chy", {HB_TAG('C','H','Y',' ')}}, /* Cheyenne */ - {"chz", {HB_TAG('C','C','H','N')}}, /* Ozumacín Chinantec -> Chinantec */ - {"ciw", {HB_TAG('O','J','B',' ')}}, /* Chippewa -> Ojibway */ - {"cja", {HB_TAG('C','J','A',' ')}}, /* Western Cham */ - {"cjm", {HB_TAG('C','J','M',' ')}}, /* Eastern Cham */ - {"cjy", {HB_TAG('Z','H','S',' ')}}, /* Jinyu Chinese -> Chinese Simplified */ - {"cka", {HB_TAG('Q','I','N',' ')}}, /* Khumi Awa Chin (retired code) -> Chin */ - {"ckb", {HB_TAG('K','U','R',' ')}}, /* Central Kurdish -> Kurdish */ - {"ckt", {HB_TAG('C','H','K',' ')}}, /* Chukot -> Chukchi */ - {"clc", {HB_TAG('A','T','H',' ')}}, /* Chilcotin -> Athapaskan */ - {"cld", {HB_TAG('S','Y','R',' ')}}, /* Chaldean Neo-Aramaic -> Syriac */ - {"cle", {HB_TAG('C','C','H','N')}}, /* Lealao Chinantec -> Chinantec */ - {"cmn", {HB_TAG('Z','H','S',' ')}}, /* Mandarin Chinese -> Chinese Simplified */ - {"cmr", {HB_TAG('Q','I','N',' ')}}, /* Mro-Khimi Chin -> Chin */ - {"cnb", {HB_TAG('Q','I','N',' ')}}, /* Chinbon Chin -> Chin */ - {"cnh", {HB_TAG('Q','I','N',' ')}}, /* Hakha Chin -> Chin */ - {"cnk", {HB_TAG('Q','I','N',' ')}}, /* Khumi Chin -> Chin */ - {"cnl", {HB_TAG('C','C','H','N')}}, /* Lalana Chinantec -> Chinantec */ - {"cnt", {HB_TAG('C','C','H','N')}}, /* Tepetotutla Chinantec -> Chinantec */ - {"cnw", {HB_TAG('Q','I','N',' ')}}, /* Ngawn Chin -> Chin */ - {"co", {HB_TAG('C','O','S',' ')}}, /* Corsican */ - {"coa", {HB_TAG('M','L','Y',' ')}}, /* Cocos Islands Malay -> Malay */ - {"cop", {HB_TAG('C','O','P',' ')}}, /* Coptic */ - {"coq", {HB_TAG('A','T','H',' ')}}, /* Coquille -> Athapaskan */ - {"cpa", {HB_TAG('C','C','H','N')}}, /* Palantla Chinantec -> Chinantec */ - {"cpe", {HB_TAG('C','P','P',' ')}}, /* English-based creoles and pidgins [family] -> Creoles */ - {"cpf", {HB_TAG('C','P','P',' ')}}, /* French-based creoles and pidgins [family] -> Creoles */ - {"cpp", {HB_TAG('C','P','P',' ')}}, /* Portuguese-based creoles and pidgins [family] -> Creoles */ - {"cpx", {HB_TAG('Z','H','S',' ')}}, /* Pu-Xian Chinese -> Chinese Simplified */ - {"cqd", {HB_TAG('H','M','N',' ')}}, /* Chuanqiandian Cluster Miao -> Hmong */ - {"cqu", {HB_TAG('Q','U','H',' ')}}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ - {"cr", {HB_TAG('C','R','E',' '), /* Cree [macrolanguage] */ - HB_TAG('Y','C','R',' ')}}, /* Cree [macrolanguage] -> Y-Cree */ - {"crh", {HB_TAG('C','R','T',' ')}}, /* Crimean Tatar */ - {"crj", {HB_TAG('E','C','R',' ')}}, /* Southern East Cree -> Eastern Cree */ - {"crk", {HB_TAG('W','C','R',' ')}}, /* Plains Cree -> West-Cree */ - {"crl", {HB_TAG('E','C','R',' ')}}, /* Northern East Cree -> Eastern Cree */ - {"crm", {HB_TAG('M','C','R',' '), /* Moose Cree */ - HB_TAG('L','C','R',' ')}}, /* Moose Cree -> L-Cree */ - {"crp", {HB_TAG('C','P','P',' ')}}, /* Creoles and pidgins [family] -> Creoles */ - {"crx", {HB_TAG('C','R','R',' '), /* Carrier */ - HB_TAG('A','T','H',' ')}}, /* Carrier -> Athapaskan */ - {"cs", {HB_TAG('C','S','Y',' ')}}, /* Czech */ - {"csa", {HB_TAG('C','C','H','N')}}, /* Chiltepec Chinantec -> Chinantec */ - {"csb", {HB_TAG('C','S','B',' ')}}, /* Kashubian */ - {"csh", {HB_TAG('Q','I','N',' ')}}, /* Asho Chin -> Chin */ - {"cso", {HB_TAG('C','C','H','N')}}, /* Sochiapam Chinantec -> Chinantec */ - {"csw", {HB_TAG('N','C','R',' '), /* Swampy Cree -> N-Cree */ - HB_TAG('N','H','C',' ')}}, /* Swampy Cree -> Norway House Cree */ - {"csy", {HB_TAG('Q','I','N',' ')}}, /* Siyin Chin -> Chin */ - {"ctc", {HB_TAG('A','T','H',' ')}}, /* Chetco -> Athapaskan */ - {"ctd", {HB_TAG('Q','I','N',' ')}}, /* Tedim Chin -> Chin */ - {"cte", {HB_TAG('C','C','H','N')}}, /* Tepinapa Chinantec -> Chinantec */ - {"ctg", {HB_TAG('C','T','G',' ')}}, /* Chittagonian */ - {"ctl", {HB_TAG('C','C','H','N')}}, /* Tlacoatzintepec Chinantec -> Chinantec */ - {"cts", {HB_TAG('B','I','K',' ')}}, /* Northern Catanduanes Bikol -> Bikol */ - {"cu", {HB_TAG('C','S','L',' ')}}, /* Church Slavonic */ - {"cuc", {HB_TAG('C','C','H','N')}}, /* Usila Chinantec -> Chinantec */ - {"cuk", {HB_TAG('C','U','K',' ')}}, /* San Blas Kuna */ - {"cv", {HB_TAG('C','H','U',' ')}}, /* Chuvash */ - {"cvn", {HB_TAG('C','C','H','N')}}, /* Valle Nacional Chinantec -> Chinantec */ - {"cwd", {HB_TAG('D','C','R',' '), /* Woods Cree */ - HB_TAG('T','C','R',' ')}}, /* Woods Cree -> TH-Cree */ - {"cy", {HB_TAG('W','E','L',' ')}}, /* Welsh */ - {"czh", {HB_TAG('Z','H','S',' ')}}, /* Huizhou Chinese -> Chinese Simplified */ - {"czo", {HB_TAG('Z','H','S',' ')}}, /* Min Zhong Chinese -> Chinese Simplified */ - {"czt", {HB_TAG('Q','I','N',' ')}}, /* Zotung Chin -> Chin */ - {"da", {HB_TAG('D','A','N',' ')}}, /* Danish */ - {"dao", {HB_TAG('Q','I','N',' ')}}, /* Daai Chin -> Chin */ - {"dap", {HB_TAG('N','I','S',' ')}}, /* Nisi (India) (retired code) */ - {"dar", {HB_TAG('D','A','R',' ')}}, /* Dargwa */ - {"dax", {HB_TAG('D','A','X',' ')}}, /* Dayi */ - {"de", {HB_TAG('D','E','U',' ')}}, /* German */ - {"den", {HB_TAG('S','L','A',' '), /* Slave (Athapascan) [macrolanguage] -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ - {"dgo", {HB_TAG('D','G','O',' ')}}, /* Dogri */ - {"dgr", {HB_TAG('A','T','H',' ')}}, /* Dogrib -> Athapaskan */ - {"dhd", {HB_TAG('M','A','W',' ')}}, /* Dhundari -> Marwari */ - {"dhg", {HB_TAG('D','H','G',' ')}}, /* Dhangu */ - {"dib", {HB_TAG('D','N','K',' ')}}, /* South Central Dinka -> Dinka */ - {"dik", {HB_TAG('D','N','K',' ')}}, /* Southwestern Dinka -> Dinka */ - {"din", {HB_TAG('D','N','K',' ')}}, /* Dinka [macrolanguage] */ - {"dip", {HB_TAG('D','N','K',' ')}}, /* Northeastern Dinka -> Dinka */ - {"diq", {HB_TAG('D','I','Q',' ')}}, /* Dimli */ - {"diw", {HB_TAG('D','N','K',' ')}}, /* Northwestern Dinka -> Dinka */ - {"dje", {HB_TAG('D','J','R',' ')}}, /* Zarma */ - {"djr", {HB_TAG('D','J','R','0')}}, /* Djambarrpuyngu */ - {"dks", {HB_TAG('D','N','K',' ')}}, /* Southeastern Dinka -> Dinka */ - {"dng", {HB_TAG('D','U','N',' ')}}, /* Dungan */ - {"dnj", {HB_TAG('D','N','J',' ')}}, /* Dan */ - {"doi", {HB_TAG('D','G','R',' ')}}, /* Dogri [macrolanguage] */ - {"drh", {HB_TAG('M','N','G',' ')}}, /* Darkhat (retired code) -> Mongolian */ - {"drw", {HB_TAG('D','R','I',' ')}}, /* Darwazi (retired code) -> Dari */ - {"dsb", {HB_TAG('L','S','B',' ')}}, /* Lower Sorbian */ - {"dty", {HB_TAG('N','E','P',' ')}}, /* Dotyali -> Nepali */ - {"duj", {HB_TAG('D','U','J',' ')}}, /* Dhuwal (retired code) */ - {"dup", {HB_TAG('M','L','Y',' ')}}, /* Duano -> Malay */ - {"dv", {HB_TAG('D','I','V',' '), /* Divehi (Dhivehi, Maldivian) */ - HB_TAG('D','H','V',' ')}}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ - {"dwu", {HB_TAG('D','U','J',' ')}}, /* Dhuwal */ - {"dwy", {HB_TAG('D','U','J',' ')}}, /* Dhuwaya -> Dhuwal */ - {"dyu", {HB_TAG('J','U','L',' ')}}, /* Dyula -> Jula */ - {"dz", {HB_TAG('D','Z','N',' ')}}, /* Dzongkha */ - {"ee", {HB_TAG('E','W','E',' ')}}, /* Ewe */ - {"efi", {HB_TAG('E','F','I',' ')}}, /* Efik */ - {"ekk", {HB_TAG('E','T','I',' ')}}, /* Standard Estonian -> Estonian */ - {"el", {HB_TAG('E','L','L',' ')}}, /* Modern Greek (1453-) -> Greek */ - {"emk", {HB_TAG('E','M','K',' '), /* Eastern Maninkakan */ - HB_TAG('M','N','K',' ')}}, /* Eastern Maninkakan -> Maninka */ - {"en", {HB_TAG('E','N','G',' ')}}, /* English */ - {"enb", {HB_TAG('K','A','L',' ')}}, /* Markweeta -> Kalenjin */ - {"enf", {HB_TAG('F','N','E',' ')}}, /* Forest Enets -> Forest Nenets */ - {"enh", {HB_TAG('T','N','E',' ')}}, /* Tundra Enets -> Tundra Nenets */ - {"eo", {HB_TAG('N','T','O',' ')}}, /* Esperanto */ - {"es", {HB_TAG('E','S','P',' ')}}, /* Spanish */ - {"esg", {HB_TAG('G','O','N',' ')}}, /* Aheri Gondi -> Gondi */ - {"esi", {HB_TAG('I','P','K',' ')}}, /* North Alaskan Inupiatun -> Inupiat */ - {"esk", {HB_TAG('I','P','K',' ')}}, /* Northwest Alaska Inupiatun -> Inupiat */ - {"esu", {HB_TAG('E','S','U',' ')}}, /* Central Yupik */ - {"et", {HB_TAG('E','T','I',' ')}}, /* Estonian [macrolanguage] */ - {"eto", {HB_TAG('B','T','I',' ')}}, /* Eton (Cameroon) -> Beti */ - {"eu", {HB_TAG('E','U','Q',' ')}}, /* Basque */ - {"eve", {HB_TAG('E','V','N',' ')}}, /* Even */ - {"evn", {HB_TAG('E','V','K',' ')}}, /* Evenki */ - {"ewo", {HB_TAG('B','T','I',' ')}}, /* Ewondo -> Beti */ - {"eyo", {HB_TAG('K','A','L',' ')}}, /* Keiyo -> Kalenjin */ - {"fa", {HB_TAG('F','A','R',' ')}}, /* Persian [macrolanguage] */ - {"fan", {HB_TAG('F','A','N','0')}}, /* Fang (Equatorial Guinea) */ - {"fat", {HB_TAG('F','A','T',' ')}}, /* Fanti */ - {"fbl", {HB_TAG('B','I','K',' ')}}, /* West Albay Bikol -> Bikol */ - {"ff", {HB_TAG('F','U','L',' ')}}, /* Fulah [macrolanguage] */ - {"ffm", {HB_TAG('F','U','L',' ')}}, /* Maasina Fulfulde -> Fulah */ - {"fi", {HB_TAG('F','I','N',' ')}}, /* Finnish */ - {"fil", {HB_TAG('P','I','L',' ')}}, /* Filipino */ - {"fj", {HB_TAG('F','J','I',' ')}}, /* Fijian */ - {"flm", {HB_TAG('H','A','L',' '), /* Halam (Falam Chin) (retired code) */ - HB_TAG('Q','I','N',' ')}}, /* Falam Chin (retired code) -> Chin */ - {"fmp", {HB_TAG('F','M','P',' ')}}, /* Fe'fe' */ - {"fo", {HB_TAG('F','O','S',' ')}}, /* Faroese */ - {"fon", {HB_TAG('F','O','N',' ')}}, /* Fon */ - {"fr", {HB_TAG('F','R','A',' ')}}, /* French */ - {"frc", {HB_TAG('F','R','C',' ')}}, /* Cajun French */ - {"frp", {HB_TAG('F','R','P',' ')}}, /* Arpitan */ - {"fub", {HB_TAG('F','U','L',' ')}}, /* Adamawa Fulfulde -> Fulah */ - {"fuc", {HB_TAG('F','U','L',' ')}}, /* Pulaar -> Fulah */ - {"fue", {HB_TAG('F','U','L',' ')}}, /* Borgu Fulfulde -> Fulah */ - {"fuf", {HB_TAG('F','T','A',' ')}}, /* Pular -> Futa */ - {"fuh", {HB_TAG('F','U','L',' ')}}, /* Western Niger Fulfulde -> Fulah */ - {"fui", {HB_TAG('F','U','L',' ')}}, /* Bagirmi Fulfulde -> Fulah */ - {"fuq", {HB_TAG('F','U','L',' ')}}, /* Central-Eastern Niger Fulfulde -> Fulah */ - {"fur", {HB_TAG('F','R','L',' ')}}, /* Friulian */ - {"fuv", {HB_TAG('F','U','V',' ')}}, /* Nigerian Fulfulde */ - {"fy", {HB_TAG('F','R','I',' ')}}, /* Western Frisian -> Frisian */ - {"ga", {HB_TAG('I','R','I',' ')}}, /* Irish */ - {"gaa", {HB_TAG('G','A','D',' ')}}, /* Ga */ - {"gag", {HB_TAG('G','A','G',' ')}}, /* Gagauz */ - {"gan", {HB_TAG('Z','H','S',' ')}}, /* Gan Chinese -> Chinese Simplified */ - {"gax", {HB_TAG('O','R','O',' ')}}, /* Borana-Arsi-Guji Oromo -> Oromo */ - {"gaz", {HB_TAG('O','R','O',' ')}}, /* West Central Oromo -> Oromo */ - {"gbm", {HB_TAG('G','A','W',' ')}}, /* Garhwali */ - {"gce", {HB_TAG('A','T','H',' ')}}, /* Galice -> Athapaskan */ - {"gd", {HB_TAG('G','A','E',' ')}}, /* Scottish Gaelic (Gaelic) */ - {"gda", {HB_TAG('R','A','J',' ')}}, /* Gade Lohar -> Rajasthani */ - {"gez", {HB_TAG('G','E','Z',' ')}}, /* Geez */ - {"ggo", {HB_TAG('G','O','N',' ')}}, /* Southern Gondi (retired code) -> Gondi */ - {"gih", {HB_TAG('G','I','H',' ')}}, /* Githabul */ - {"gil", {HB_TAG('G','I','L','0')}}, /* Kiribati (Gilbertese) */ - {"gju", {HB_TAG('R','A','J',' ')}}, /* Gujari -> Rajasthani */ - {"gkp", {HB_TAG('G','K','P',' ')}}, /* Guinea Kpelle -> Kpelle (Guinea) */ - {"gl", {HB_TAG('G','A','L',' ')}}, /* Galician */ - {"gld", {HB_TAG('N','A','N',' ')}}, /* Nanai */ - {"glk", {HB_TAG('G','L','K',' ')}}, /* Gilaki */ - {"gn", {HB_TAG('G','U','A',' ')}}, /* Guarani [macrolanguage] */ - {"gnn", {HB_TAG('G','N','N',' ')}}, /* Gumatj */ - {"gno", {HB_TAG('G','O','N',' ')}}, /* Northern Gondi -> Gondi */ - {"gnw", {HB_TAG('G','U','A',' ')}}, /* Western Bolivian Guaraní -> Guarani */ - {"gog", {HB_TAG('G','O','G',' ')}}, /* Gogo */ - {"gom", {HB_TAG('K','O','K',' ')}}, /* Goan Konkani -> Konkani */ - {"gon", {HB_TAG('G','O','N',' ')}}, /* Gondi [macrolanguage] */ - {"grt", {HB_TAG('G','R','O',' ')}}, /* Garo */ - {"gru", {HB_TAG('S','O','G',' ')}}, /* Kistane -> Sodo Gurage */ - {"gsw", {HB_TAG('A','L','S',' ')}}, /* Alsatian */ - {"gu", {HB_TAG('G','U','J',' ')}}, /* Gujarati */ - {"guc", {HB_TAG('G','U','C',' ')}}, /* Wayuu */ - {"guf", {HB_TAG('G','U','F',' ')}}, /* Gupapuyngu */ - {"gug", {HB_TAG('G','U','A',' ')}}, /* Paraguayan Guaraní -> Guarani */ - {"gui", {HB_TAG('G','U','A',' ')}}, /* Eastern Bolivian Guaraní -> Guarani */ - {"guk", {HB_TAG('G','M','Z',' '), /* Gumuz */ - HB_TAG('G','U','K',' ')}}, /* Gumuz (SIL fonts) */ - {"gun", {HB_TAG('G','U','A',' ')}}, /* Mbyá Guaraní -> Guarani */ - {"guz", {HB_TAG('G','U','Z',' ')}}, /* Gusii */ - {"gv", {HB_TAG('M','N','X',' ')}}, /* Manx */ - {"gwi", {HB_TAG('A','T','H',' ')}}, /* Gwichʼin -> Athapaskan */ - {"ha", {HB_TAG('H','A','U',' ')}}, /* Hausa */ - {"haa", {HB_TAG('A','T','H',' ')}}, /* Han -> Athapaskan */ - {"hae", {HB_TAG('O','R','O',' ')}}, /* Eastern Oromo -> Oromo */ - {"hak", {HB_TAG('Z','H','S',' ')}}, /* Hakka Chinese -> Chinese Simplified */ - {"har", {HB_TAG('H','R','I',' ')}}, /* Harari */ - {"haw", {HB_TAG('H','A','W',' ')}}, /* Hawaiian */ - {"hay", {HB_TAG('H','A','Y',' ')}}, /* Haya */ - {"haz", {HB_TAG('H','A','Z',' ')}}, /* Hazaragi */ - {"he", {HB_TAG('I','W','R',' ')}}, /* Hebrew */ - {"hea", {HB_TAG('H','M','N',' ')}}, /* Northern Qiandong Miao -> Hmong */ - {"hi", {HB_TAG('H','I','N',' ')}}, /* Hindi */ - {"hil", {HB_TAG('H','I','L',' ')}}, /* Hiligaynon */ - {"hji", {HB_TAG('M','L','Y',' ')}}, /* Haji -> Malay */ - {"hlt", {HB_TAG('Q','I','N',' ')}}, /* Matu Chin -> Chin */ - {"hma", {HB_TAG('H','M','N',' ')}}, /* Southern Mashan Hmong -> Hmong */ - {"hmc", {HB_TAG('H','M','N',' ')}}, /* Central Huishui Hmong -> Hmong */ - {"hmd", {HB_TAG('H','M','N',' ')}}, /* Large Flowery Miao -> Hmong */ - {"hme", {HB_TAG('H','M','N',' ')}}, /* Eastern Huishui Hmong -> Hmong */ - {"hmg", {HB_TAG('H','M','N',' ')}}, /* Southwestern Guiyang Hmong -> Hmong */ - {"hmh", {HB_TAG('H','M','N',' ')}}, /* Southwestern Huishui Hmong -> Hmong */ - {"hmi", {HB_TAG('H','M','N',' ')}}, /* Northern Huishui Hmong -> Hmong */ - {"hmj", {HB_TAG('H','M','N',' ')}}, /* Ge -> Hmong */ - {"hml", {HB_TAG('H','M','N',' ')}}, /* Luopohe Hmong -> Hmong */ - {"hmm", {HB_TAG('H','M','N',' ')}}, /* Central Mashan Hmong -> Hmong */ - {"hmn", {HB_TAG('H','M','N',' ')}}, /* Hmong [macrolanguage] */ - {"hmp", {HB_TAG('H','M','N',' ')}}, /* Northern Mashan Hmong -> Hmong */ - {"hmq", {HB_TAG('H','M','N',' ')}}, /* Eastern Qiandong Miao -> Hmong */ - {"hms", {HB_TAG('H','M','N',' ')}}, /* Southern Qiandong Miao -> Hmong */ - {"hmw", {HB_TAG('H','M','N',' ')}}, /* Western Mashan Hmong -> Hmong */ - {"hmy", {HB_TAG('H','M','N',' ')}}, /* Southern Guiyang Hmong -> Hmong */ - {"hmz", {HB_TAG('H','M','N',' ')}}, /* Hmong Shua -> Hmong */ - {"hnd", {HB_TAG('H','N','D',' ')}}, /* Southern Hindko -> Hindko */ - {"hne", {HB_TAG('C','H','H',' ')}}, /* Chhattisgarhi -> Chattisgarhi */ - {"hnj", {HB_TAG('H','M','N',' ')}}, /* Hmong Njua -> Hmong */ - {"hno", {HB_TAG('H','N','D',' ')}}, /* Northern Hindko -> Hindko */ - {"ho", {HB_TAG('H','M','O',' ')}}, /* Hiri Motu */ - {"hoc", {HB_TAG('H','O',' ',' ')}}, /* Ho */ - {"hoi", {HB_TAG('A','T','H',' ')}}, /* Holikachuk -> Athapaskan */ - {"hoj", {HB_TAG('H','A','R',' ')}}, /* Hadothi -> Harauti */ - {"hr", {HB_TAG('H','R','V',' ')}}, /* Croatian */ - {"hrm", {HB_TAG('H','M','N',' ')}}, /* Horned Miao -> Hmong */ - {"hsb", {HB_TAG('U','S','B',' ')}}, /* Upper Sorbian */ - {"hsn", {HB_TAG('Z','H','S',' ')}}, /* Xiang Chinese -> Chinese Simplified */ - {"ht", {HB_TAG('H','A','I',' ')}}, /* Haitian (Haitian Creole) */ - {"hu", {HB_TAG('H','U','N',' ')}}, /* Hungarian */ - {"huj", {HB_TAG('H','M','N',' ')}}, /* Northern Guiyang Hmong -> Hmong */ - {"hup", {HB_TAG('A','T','H',' ')}}, /* Hupa -> Athapaskan */ - {"hy", {HB_TAG('H','Y','E','0'), /* Armenian -> Armenian East */ - HB_TAG('H','Y','E',' ')}}, /* Armenian */ - {"hyw", {HB_TAG('H','Y','E',' ')}}, /* Western Armenian -> Armenian */ - {"hz", {HB_TAG('H','E','R',' ')}}, /* Herero */ - {"ia", {HB_TAG('I','N','A',' ')}}, /* Interlingua (International Auxiliary Language Association) */ - {"iba", {HB_TAG('I','B','A',' ')}}, /* Iban */ - {"ibb", {HB_TAG('I','B','B',' ')}}, /* Ibibio */ - {"id", {HB_TAG('I','N','D',' ')}}, /* Indonesian */ - {"ida", {HB_TAG('L','U','H',' ')}}, /* Idakho-Isukha-Tiriki -> Luyia */ - {"ie", {HB_TAG('I','L','E',' ')}}, /* Interlingue */ - {"ig", {HB_TAG('I','B','O',' ')}}, /* Igbo */ - {"igb", {HB_TAG('E','B','I',' ')}}, /* Ebira */ - {"ii", {HB_TAG('Y','I','M',' ')}}, /* Sichuan Yi -> Yi Modern */ - {"ijc", {HB_TAG('I','J','O',' ')}}, /* Izon -> Ijo */ - {"ijo", {HB_TAG('I','J','O',' ')}}, /* Ijo [family] */ - {"ik", {HB_TAG('I','P','K',' ')}}, /* Inupiaq [macrolanguage] -> Inupiat */ - {"ike", {HB_TAG('I','N','U',' ')}}, /* Eastern Canadian Inuktitut -> Inuktitut */ - {"ikt", {HB_TAG('I','N','U',' ')}}, /* Inuinnaqtun -> Inuktitut */ - {"ilo", {HB_TAG('I','L','O',' ')}}, /* Iloko -> Ilokano */ - {"in", {HB_TAG('I','N','D',' ')}}, /* Indonesian (retired code) */ - {"ing", {HB_TAG('A','T','H',' ')}}, /* Degexit'an -> Athapaskan */ - {"inh", {HB_TAG('I','N','G',' ')}}, /* Ingush */ - {"io", {HB_TAG('I','D','O',' ')}}, /* Ido */ - {"is", {HB_TAG('I','S','L',' ')}}, /* Icelandic */ - {"it", {HB_TAG('I','T','A',' ')}}, /* Italian */ - {"iu", {HB_TAG('I','N','U',' ')}}, /* Inuktitut [macrolanguage] */ - {"iw", {HB_TAG('I','W','R',' ')}}, /* Hebrew (retired code) */ - {"ja", {HB_TAG('J','A','N',' ')}}, /* Japanese */ - {"jak", {HB_TAG('M','L','Y',' ')}}, /* Jakun -> Malay */ - {"jam", {HB_TAG('J','A','M',' ')}}, /* Jamaican Creole English -> Jamaican Creole */ - {"jax", {HB_TAG('M','L','Y',' ')}}, /* Jambi Malay -> Malay */ - {"jbo", {HB_TAG('J','B','O',' ')}}, /* Lojban */ - {"jct", {HB_TAG('J','C','T',' ')}}, /* Krymchak */ - {"ji", {HB_TAG('J','I','I',' ')}}, /* Yiddish (retired code) */ - {"jv", {HB_TAG('J','A','V',' ')}}, /* Javanese */ - {"jw", {HB_TAG('J','A','V',' ')}}, /* Javanese (retired code) */ - {"ka", {HB_TAG('K','A','T',' ')}}, /* Georgian */ - {"kaa", {HB_TAG('K','R','K',' ')}}, /* Kara-Kalpak -> Karakalpak */ - {"kab", {HB_TAG('K','A','B','0')}}, /* Kabyle */ - {"kam", {HB_TAG('K','M','B',' ')}}, /* Kamba (Kenya) */ - {"kar", {HB_TAG('K','R','N',' ')}}, /* Karen [family] */ - {"kbd", {HB_TAG('K','A','B',' ')}}, /* Kabardian */ - {"kby", {HB_TAG('K','N','R',' ')}}, /* Manga Kanuri -> Kanuri */ - {"kca", {HB_TAG('K','H','K',' '), /* Khanty -> Khanty-Kazim */ - HB_TAG('K','H','S',' '), /* Khanty -> Khanty-Shurishkar */ - HB_TAG('K','H','V',' ')}}, /* Khanty -> Khanty-Vakhi */ - {"kde", {HB_TAG('K','D','E',' ')}}, /* Makonde */ - {"kdr", {HB_TAG('K','R','M',' ')}}, /* Karaim */ - {"kdt", {HB_TAG('K','U','Y',' ')}}, /* Kuy */ - {"kea", {HB_TAG('K','E','A',' ')}}, /* Kabuverdianu (Crioulo) */ - {"kek", {HB_TAG('K','E','K',' ')}}, /* Kekchi */ - {"kex", {HB_TAG('K','K','N',' ')}}, /* Kukna -> Kokni */ - {"kfa", {HB_TAG('K','O','D',' ')}}, /* Kodava -> Kodagu */ - {"kfr", {HB_TAG('K','A','C',' ')}}, /* Kachhi -> Kachchi */ - {"kfx", {HB_TAG('K','U','L',' ')}}, /* Kullu Pahari -> Kulvi */ - {"kfy", {HB_TAG('K','M','N',' ')}}, /* Kumaoni */ - {"kg", {HB_TAG('K','O','N','0')}}, /* Kongo [macrolanguage] */ - {"kha", {HB_TAG('K','S','I',' ')}}, /* Khasi */ - {"khb", {HB_TAG('X','B','D',' ')}}, /* Lü */ - {"khk", {HB_TAG('M','N','G',' ')}}, /* Halh Mongolian -> Mongolian */ - {"kht", {HB_TAG('K','H','N',' '), /* Khamti -> Khamti Shan (Microsoft fonts) */ - HB_TAG('K','H','T',' ')}}, /* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */ - {"khw", {HB_TAG('K','H','W',' ')}}, /* Khowar */ - {"ki", {HB_TAG('K','I','K',' ')}}, /* Kikuyu (Gikuyu) */ - {"kiu", {HB_TAG('K','I','U',' ')}}, /* Kirmanjki */ - {"kj", {HB_TAG('K','U','A',' ')}}, /* Kuanyama */ - {"kjd", {HB_TAG('K','J','D',' ')}}, /* Southern Kiwai */ - {"kjh", {HB_TAG('K','H','A',' ')}}, /* Khakas -> Khakass */ - {"kjp", {HB_TAG('K','J','P',' ')}}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ - {"kjz", {HB_TAG('K','J','Z',' ')}}, /* Bumthangkha */ - {"kk", {HB_TAG('K','A','Z',' ')}}, /* Kazakh */ - {"kkz", {HB_TAG('A','T','H',' ')}}, /* Kaska -> Athapaskan */ - {"kl", {HB_TAG('G','R','N',' ')}}, /* Greenlandic */ - {"kln", {HB_TAG('K','A','L',' ')}}, /* Kalenjin [macrolanguage] */ - {"km", {HB_TAG('K','H','M',' ')}}, /* Khmer */ - {"kmb", {HB_TAG('M','B','N',' ')}}, /* Kimbundu -> Mbundu */ - {"kmr", {HB_TAG('K','U','R',' ')}}, /* Northern Kurdish -> Kurdish */ - {"kmw", {HB_TAG('K','M','O',' ')}}, /* Komo (Democratic Republic of Congo) */ - {"kmz", {HB_TAG('K','M','Z',' ')}}, /* Khorasani Turkish -> Khorasani Turkic */ - {"kn", {HB_TAG('K','A','N',' ')}}, /* Kannada */ - {"knc", {HB_TAG('K','N','R',' ')}}, /* Central Kanuri -> Kanuri */ - {"kng", {HB_TAG('K','O','N','0')}}, /* Koongo -> Kongo */ - {"knn", {HB_TAG('K','O','K',' ')}}, /* Konkani */ - {"ko", {HB_TAG('K','O','R',' ')}}, /* Korean */ - {"koi", {HB_TAG('K','O','P',' ')}}, /* Komi-Permyak */ - {"kok", {HB_TAG('K','O','K',' ')}}, /* Konkani [macrolanguage] */ - {"kos", {HB_TAG('K','O','S',' ')}}, /* Kosraean */ - {"koy", {HB_TAG('A','T','H',' ')}}, /* Koyukon -> Athapaskan */ - {"kpe", {HB_TAG('K','P','L',' ')}}, /* Kpelle [macrolanguage] */ - {"kpv", {HB_TAG('K','O','Z',' ')}}, /* Komi-Zyrian */ - {"kpy", {HB_TAG('K','Y','K',' ')}}, /* Koryak */ - {"kqs", {HB_TAG('K','I','S',' ')}}, /* Northern Kissi -> Kisii */ - {"kqy", {HB_TAG('K','R','T',' ')}}, /* Koorete */ - {"kr", {HB_TAG('K','N','R',' ')}}, /* Kanuri [macrolanguage] */ - {"krc", {HB_TAG('K','A','R',' '), /* Karachay-Balkar -> Karachay */ - HB_TAG('B','A','L',' ')}}, /* Karachay-Balkar -> Balkar */ - {"kri", {HB_TAG('K','R','I',' ')}}, /* Krio */ - {"krl", {HB_TAG('K','R','L',' ')}}, /* Karelian */ - {"krt", {HB_TAG('K','N','R',' ')}}, /* Tumari Kanuri -> Kanuri */ - {"kru", {HB_TAG('K','U','U',' ')}}, /* Kurukh */ - {"ks", {HB_TAG('K','S','H',' ')}}, /* Kashmiri */ - {"ksh", {HB_TAG('K','S','H','0')}}, /* Kölsch -> Ripuarian */ - {"kss", {HB_TAG('K','I','S',' ')}}, /* Southern Kisi -> Kisii */ - {"ksw", {HB_TAG('K','S','W',' ')}}, /* S’gaw Karen */ - {"ktb", {HB_TAG('K','E','B',' ')}}, /* Kambaata -> Kebena */ - {"ktu", {HB_TAG('K','O','N',' ')}}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ - {"ktw", {HB_TAG('A','T','H',' ')}}, /* Kato -> Athapaskan */ - {"ku", {HB_TAG('K','U','R',' ')}}, /* Kurdish [macrolanguage] */ - {"kum", {HB_TAG('K','U','M',' ')}}, /* Kumyk */ - {"kuu", {HB_TAG('A','T','H',' ')}}, /* Upper Kuskokwim -> Athapaskan */ - {"kv", {HB_TAG('K','O','M',' ')}}, /* Komi [macrolanguage] */ - {"kvb", {HB_TAG('M','L','Y',' ')}}, /* Kubu -> Malay */ - {"kvr", {HB_TAG('M','L','Y',' ')}}, /* Kerinci -> Malay */ - {"kw", {HB_TAG('C','O','R',' ')}}, /* Cornish */ - {"kwy", {HB_TAG('K','O','N','0')}}, /* San Salvador Kongo -> Kongo */ - {"kxc", {HB_TAG('K','M','S',' ')}}, /* Konso -> Komso */ - {"kxd", {HB_TAG('M','L','Y',' ')}}, /* Brunei -> Malay */ - {"kxu", {HB_TAG('K','U','I',' ')}}, /* Kui (India) */ - {"ky", {HB_TAG('K','I','R',' ')}}, /* Kirghiz (Kyrgyz) */ - {"kyu", {HB_TAG('K','Y','U',' ')}}, /* Western Kayah */ - {"la", {HB_TAG('L','A','T',' ')}}, /* Latin */ - {"lad", {HB_TAG('J','U','D',' ')}}, /* Ladino */ - {"lb", {HB_TAG('L','T','Z',' ')}}, /* Luxembourgish */ - {"lbe", {HB_TAG('L','A','K',' ')}}, /* Lak */ - {"lbj", {HB_TAG('L','D','K',' ')}}, /* Ladakhi */ - {"lbl", {HB_TAG('B','I','K',' ')}}, /* Libon Bikol -> Bikol */ - {"lce", {HB_TAG('M','L','Y',' ')}}, /* Loncong -> Malay */ - {"lcf", {HB_TAG('M','L','Y',' ')}}, /* Lubu -> Malay */ - {"ldi", {HB_TAG('K','O','N','0')}}, /* Laari -> Kongo */ - {"lez", {HB_TAG('L','E','Z',' ')}}, /* Lezghian -> Lezgi */ - {"lg", {HB_TAG('L','U','G',' ')}}, /* Ganda */ - {"li", {HB_TAG('L','I','M',' ')}}, /* Limburgish */ - {"lif", {HB_TAG('L','M','B',' ')}}, /* Limbu */ - {"lij", {HB_TAG('L','I','J',' ')}}, /* Ligurian */ - {"lis", {HB_TAG('L','I','S',' ')}}, /* Lisu */ - {"liw", {HB_TAG('M','L','Y',' ')}}, /* Col -> Malay */ - {"ljp", {HB_TAG('L','J','P',' ')}}, /* Lampung Api -> Lampung */ - {"lkb", {HB_TAG('L','U','H',' ')}}, /* Kabras -> Luyia */ - {"lki", {HB_TAG('L','K','I',' ')}}, /* Laki */ - {"lko", {HB_TAG('L','U','H',' ')}}, /* Khayo -> Luyia */ - {"lks", {HB_TAG('L','U','H',' ')}}, /* Kisa -> Luyia */ - {"lld", {HB_TAG('L','A','D',' ')}}, /* Ladin */ - {"lmn", {HB_TAG('L','A','M',' ')}}, /* Lambadi -> Lambani */ - {"lmo", {HB_TAG('L','M','O',' ')}}, /* Lombard */ - {"ln", {HB_TAG('L','I','N',' ')}}, /* Lingala */ - {"lo", {HB_TAG('L','A','O',' ')}}, /* Lao */ - {"lom", {HB_TAG('L','O','M',' ')}}, /* Loma (Liberia) */ - {"lrc", {HB_TAG('L','R','C',' ')}}, /* Northern Luri -> Luri */ - {"lri", {HB_TAG('L','U','H',' ')}}, /* Marachi -> Luyia */ - {"lrm", {HB_TAG('L','U','H',' ')}}, /* Marama -> Luyia */ - {"lsm", {HB_TAG('L','U','H',' ')}}, /* Saamia -> Luyia */ - {"lt", {HB_TAG('L','T','H',' ')}}, /* Lithuanian */ - {"ltg", {HB_TAG('L','V','I',' ')}}, /* Latgalian -> Latvian */ - {"lto", {HB_TAG('L','U','H',' ')}}, /* Tsotso -> Luyia */ - {"lts", {HB_TAG('L','U','H',' ')}}, /* Tachoni -> Luyia */ - {"lu", {HB_TAG('L','U','B',' ')}}, /* Luba-Katanga */ - {"lua", {HB_TAG('L','U','A',' ')}}, /* Luba-Lulua */ - {"luo", {HB_TAG('L','U','O',' ')}}, /* Luo (Kenya and Tanzania) */ - {"lus", {HB_TAG('M','I','Z',' ')}}, /* Lushai -> Mizo */ - {"luy", {HB_TAG('L','U','H',' ')}}, /* Luyia [macrolanguage] */ - {"luz", {HB_TAG('L','R','C',' ')}}, /* Southern Luri -> Luri */ - {"lv", {HB_TAG('L','V','I',' ')}}, /* Latvian [macrolanguage] */ - {"lvs", {HB_TAG('L','V','I',' ')}}, /* Standard Latvian -> Latvian */ - {"lwg", {HB_TAG('L','U','H',' ')}}, /* Wanga -> Luyia */ - {"lzh", {HB_TAG('Z','H','T',' ')}}, /* Literary Chinese -> Chinese Traditional */ - {"lzz", {HB_TAG('L','A','Z',' ')}}, /* Laz */ - {"mad", {HB_TAG('M','A','D',' ')}}, /* Madurese -> Madura */ - {"mag", {HB_TAG('M','A','G',' ')}}, /* Magahi */ - {"mai", {HB_TAG('M','T','H',' ')}}, /* Maithili */ - {"mak", {HB_TAG('M','K','R',' ')}}, /* Makasar */ - {"mam", {HB_TAG('M','A','M',' ')}}, /* Mam */ - {"man", {HB_TAG('M','N','K',' ')}}, /* Mandingo [macrolanguage] -> Maninka */ - {"max", {HB_TAG('M','L','Y',' ')}}, /* North Moluccan Malay -> Malay */ - {"mbo", {HB_TAG('M','B','O',' ')}}, /* Mbo (Cameroon) */ - {"mct", {HB_TAG('B','T','I',' ')}}, /* Mengisa -> Beti */ - {"mdf", {HB_TAG('M','O','K',' ')}}, /* Moksha */ - {"mdr", {HB_TAG('M','D','R',' ')}}, /* Mandar */ - {"mdy", {HB_TAG('M','L','E',' ')}}, /* Male (Ethiopia) */ - {"men", {HB_TAG('M','D','E',' ')}}, /* Mende (Sierra Leone) */ - {"meo", {HB_TAG('M','L','Y',' ')}}, /* Kedah Malay -> Malay */ - {"mer", {HB_TAG('M','E','R',' ')}}, /* Meru */ - {"mfa", {HB_TAG('M','F','A',' ')}}, /* Pattani Malay */ - {"mfb", {HB_TAG('M','L','Y',' ')}}, /* Bangka -> Malay */ - {"mfe", {HB_TAG('M','F','E',' ')}}, /* Morisyen */ - {"mg", {HB_TAG('M','L','G',' ')}}, /* Malagasy [macrolanguage] */ - {"mh", {HB_TAG('M','A','H',' ')}}, /* Marshallese */ - {"mhr", {HB_TAG('L','M','A',' ')}}, /* Eastern Mari -> Low Mari */ - {"mhv", {HB_TAG('A','R','K',' ')}}, /* Arakanese (retired code) -> Rakhine */ - {"mi", {HB_TAG('M','R','I',' ')}}, /* Maori */ - {"min", {HB_TAG('M','I','N',' ')}}, /* Minangkabau */ - {"mk", {HB_TAG('M','K','D',' ')}}, /* Macedonian */ - {"mku", {HB_TAG('M','N','K',' ')}}, /* Konyanka Maninka -> Maninka */ - {"mkw", {HB_TAG('M','K','W',' ')}}, /* Kituba (Congo) */ - {"ml", {HB_TAG('M','A','L',' '), /* Malayalam -> Malayalam Traditional */ - HB_TAG('M','L','R',' ')}}, /* Malayalam -> Malayalam Reformed */ - {"mlq", {HB_TAG('M','L','N',' '), /* Western Maninkakan -> Malinke */ - HB_TAG('M','N','K',' ')}}, /* Western Maninkakan -> Maninka */ - {"mmr", {HB_TAG('H','M','N',' ')}}, /* Western Xiangxi Miao -> Hmong */ - {"mn", {HB_TAG('M','N','G',' ')}}, /* Mongolian [macrolanguage] */ - {"mnc", {HB_TAG('M','C','H',' ')}}, /* Manchu */ - {"mni", {HB_TAG('M','N','I',' ')}}, /* Manipuri */ - {"mnk", {HB_TAG('M','N','D',' '), /* Mandinka */ - HB_TAG('M','N','K',' ')}}, /* Mandinka -> Maninka */ - {"mnp", {HB_TAG('Z','H','S',' ')}}, /* Min Bei Chinese -> Chinese Simplified */ - {"mns", {HB_TAG('M','A','N',' ')}}, /* Mansi */ - {"mnw", {HB_TAG('M','O','N',' ')}}, /* Mon */ - {"mo", {HB_TAG('M','O','L',' ')}}, /* Moldavian (retired code) */ - {"moh", {HB_TAG('M','O','H',' ')}}, /* Mohawk */ - {"mos", {HB_TAG('M','O','S',' ')}}, /* Mossi */ - {"mpe", {HB_TAG('M','A','J',' ')}}, /* Majang */ - {"mqg", {HB_TAG('M','L','Y',' ')}}, /* Kota Bangun Kutai Malay -> Malay */ - {"mr", {HB_TAG('M','A','R',' ')}}, /* Marathi */ - {"mrh", {HB_TAG('Q','I','N',' ')}}, /* Mara Chin -> Chin */ - {"mrj", {HB_TAG('H','M','A',' ')}}, /* Western Mari -> High Mari */ - {"ms", {HB_TAG('M','L','Y',' ')}}, /* Malay [macrolanguage] */ - {"msc", {HB_TAG('M','N','K',' ')}}, /* Sankaran Maninka -> Maninka */ - {"msh", {HB_TAG('M','L','G',' ')}}, /* Masikoro Malagasy -> Malagasy */ - {"msi", {HB_TAG('M','L','Y',' ')}}, /* Sabah Malay -> Malay */ - {"mt", {HB_TAG('M','T','S',' ')}}, /* Maltese */ - {"mtr", {HB_TAG('M','A','W',' ')}}, /* Mewari -> Marwari */ - {"mui", {HB_TAG('M','L','Y',' ')}}, /* Musi -> Malay */ - {"mup", {HB_TAG('R','A','J',' ')}}, /* Malvi -> Rajasthani */ - {"muq", {HB_TAG('H','M','N',' ')}}, /* Eastern Xiangxi Miao -> Hmong */ - {"mus", {HB_TAG('M','U','S',' ')}}, /* Creek -> Muscogee */ - {"mvb", {HB_TAG('A','T','H',' ')}}, /* Mattole -> Athapaskan */ - {"mve", {HB_TAG('M','A','W',' ')}}, /* Marwari (Pakistan) */ - {"mvf", {HB_TAG('M','N','G',' ')}}, /* Peripheral Mongolian -> Mongolian */ - {"mwk", {HB_TAG('M','N','K',' ')}}, /* Kita Maninkakan -> Maninka */ - {"mwl", {HB_TAG('M','W','L',' ')}}, /* Mirandese */ - {"mwr", {HB_TAG('M','A','W',' ')}}, /* Marwari [macrolanguage] */ - {"mww", {HB_TAG('M','W','W',' ')}}, /* Hmong Daw */ - {"my", {HB_TAG('B','R','M',' ')}}, /* Burmese */ - {"mym", {HB_TAG('M','E','N',' ')}}, /* Me'en */ - {"myn", {HB_TAG('M','Y','N',' ')}}, /* Mayan [family] */ - {"myq", {HB_TAG('M','N','K',' ')}}, /* Forest Maninka (retired code) -> Maninka */ - {"myv", {HB_TAG('E','R','Z',' ')}}, /* Erzya */ - {"mzn", {HB_TAG('M','Z','N',' ')}}, /* Mazanderani */ - {"na", {HB_TAG('N','A','U',' ')}}, /* Nauru -> Nauruan */ - {"nag", {HB_TAG('N','A','G',' ')}}, /* Naga Pidgin -> Naga-Assamese */ - {"nah", {HB_TAG('N','A','H',' ')}}, /* Nahuatl [family] */ - {"nan", {HB_TAG('Z','H','S',' ')}}, /* Min Nan Chinese -> Chinese Simplified */ - {"nap", {HB_TAG('N','A','P',' ')}}, /* Neapolitan */ - {"nb", {HB_TAG('N','O','R',' ')}}, /* Norwegian Bokmål -> Norwegian */ - {"nd", {HB_TAG('N','D','B',' ')}}, /* North Ndebele -> Ndebele */ - {"ndc", {HB_TAG('N','D','C',' ')}}, /* Ndau */ - {"nds", {HB_TAG('N','D','S',' ')}}, /* Low Saxon */ - {"ne", {HB_TAG('N','E','P',' ')}}, /* Nepali [macrolanguage] */ - {"new", {HB_TAG('N','E','W',' ')}}, /* Newari */ - {"ng", {HB_TAG('N','D','G',' ')}}, /* Ndonga */ - {"nga", {HB_TAG('N','G','A',' ')}}, /* Ngbaka */ - {"ngl", {HB_TAG('L','M','W',' ')}}, /* Lomwe */ - {"ngo", {HB_TAG('S','X','T',' ')}}, /* Ngoni -> Sutu */ - {"nhd", {HB_TAG('G','U','A',' ')}}, /* Chiripá -> Guarani */ - {"niq", {HB_TAG('K','A','L',' ')}}, /* Nandi -> Kalenjin */ - {"niu", {HB_TAG('N','I','U',' ')}}, /* Niuean */ - {"niv", {HB_TAG('G','I','L',' ')}}, /* Gilyak */ - {"njz", {HB_TAG('N','I','S',' ')}}, /* Nyishi -> Nisi */ - {"nl", {HB_TAG('N','L','D',' ')}}, /* Dutch */ - {"nle", {HB_TAG('L','U','H',' ')}}, /* East Nyala -> Luyia */ - {"nn", {HB_TAG('N','Y','N',' ')}}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ - {"no", {HB_TAG('N','O','R',' ')}}, /* Norwegian [macrolanguage] */ - {"nod", {HB_TAG('N','T','A',' ')}}, /* Northern Thai -> Northern Tai */ - {"noe", {HB_TAG('N','O','E',' ')}}, /* Nimadi */ - {"nog", {HB_TAG('N','O','G',' ')}}, /* Nogai */ - {"nov", {HB_TAG('N','O','V',' ')}}, /* Novial */ - {"npi", {HB_TAG('N','E','P',' ')}}, /* Nepali */ - {"nqo", {HB_TAG('N','K','O',' ')}}, /* N'Ko */ - {"nr", {HB_TAG('N','D','B',' ')}}, /* South Ndebele -> Ndebele */ - {"nsk", {HB_TAG('N','A','S',' ')}}, /* Naskapi */ - {"nso", {HB_TAG('N','S','O',' ')}}, /* Pedi -> Sotho, Northern */ - {"nv", {HB_TAG('N','A','V',' '), /* Navajo */ - HB_TAG('A','T','H',' ')}}, /* Navajo -> Athapaskan */ - {"ny", {HB_TAG('C','H','I',' ')}}, /* Chichewa (Chewa, Nyanja) */ - {"nyd", {HB_TAG('L','U','H',' ')}}, /* Nyore -> Luyia */ - {"nym", {HB_TAG('N','Y','M',' ')}}, /* Nyamwezi */ - {"nyn", {HB_TAG('N','K','L',' ')}}, /* Nyankole */ - {"nza", {HB_TAG('N','Z','A',' ')}}, /* Tigon Mbembe -> Mbembe Tigon */ - {"oc", {HB_TAG('O','C','I',' ')}}, /* Occitan (post 1500) */ - {"oj", {HB_TAG('O','J','B',' ')}}, /* Ojibwa [macrolanguage] -> Ojibway */ - {"ojb", {HB_TAG('O','J','B',' ')}}, /* Northwestern Ojibwa -> Ojibway */ - {"ojc", {HB_TAG('O','J','B',' ')}}, /* Central Ojibwa -> Ojibway */ - {"ojg", {HB_TAG('O','J','B',' ')}}, /* Eastern Ojibwa -> Ojibway */ - {"ojs", {HB_TAG('O','C','R',' ')}}, /* Severn Ojibwa -> Oji-Cree */ - {"ojw", {HB_TAG('O','J','B',' ')}}, /* Western Ojibwa -> Ojibway */ - {"oki", {HB_TAG('K','A','L',' ')}}, /* Okiek -> Kalenjin */ - {"okm", {HB_TAG('K','O','H',' ')}}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ - {"om", {HB_TAG('O','R','O',' ')}}, /* Oromo [macrolanguage] */ - {"or", {HB_TAG('O','R','I',' ')}}, /* Odia (formerly Oriya) [macrolanguage] */ - {"orc", {HB_TAG('O','R','O',' ')}}, /* Orma -> Oromo */ - {"orn", {HB_TAG('M','L','Y',' ')}}, /* Orang Kanaq -> Malay */ - {"ors", {HB_TAG('M','L','Y',' ')}}, /* Orang Seletar -> Malay */ - {"ory", {HB_TAG('O','R','I',' ')}}, /* Odia (formerly Oriya) */ - {"os", {HB_TAG('O','S','S',' ')}}, /* Ossetian */ - {"otw", {HB_TAG('O','J','B',' ')}}, /* Ottawa -> Ojibway */ - {"pa", {HB_TAG('P','A','N',' ')}}, /* Punjabi */ - {"pag", {HB_TAG('P','A','G',' ')}}, /* Pangasinan */ - {"pam", {HB_TAG('P','A','M',' ')}}, /* Pampanga -> Pampangan */ - {"pap", {HB_TAG('P','A','P','0')}}, /* Papiamento -> Papiamentu */ - {"pau", {HB_TAG('P','A','U',' ')}}, /* Palauan */ - {"pbt", {HB_TAG('P','A','S',' ')}}, /* Southern Pashto -> Pashto */ - {"pbu", {HB_TAG('P','A','S',' ')}}, /* Northern Pashto -> Pashto */ - {"pcc", {HB_TAG('P','C','C',' ')}}, /* Bouyei */ - {"pcd", {HB_TAG('P','C','D',' ')}}, /* Picard */ - {"pce", {HB_TAG('P','L','G',' ')}}, /* Ruching Palaung -> Palaung */ - {"pck", {HB_TAG('Q','I','N',' ')}}, /* Paite Chin -> Chin */ - {"pdc", {HB_TAG('P','D','C',' ')}}, /* Pennsylvania German */ - {"pel", {HB_TAG('M','L','Y',' ')}}, /* Pekal -> Malay */ - {"pes", {HB_TAG('F','A','R',' ')}}, /* Iranian Persian -> Persian */ - {"pga", {HB_TAG('A','R','A',' ')}}, /* Sudanese Creole Arabic -> Arabic */ - {"phk", {HB_TAG('P','H','K',' ')}}, /* Phake */ - {"pi", {HB_TAG('P','A','L',' ')}}, /* Pali */ - {"pih", {HB_TAG('P','I','H',' ')}}, /* Pitcairn-Norfolk -> Norfolk */ - {"pko", {HB_TAG('K','A','L',' ')}}, /* Pökoot -> Kalenjin */ - {"pl", {HB_TAG('P','L','K',' ')}}, /* Polish */ - {"pll", {HB_TAG('P','L','G',' ')}}, /* Shwe Palaung -> Palaung */ - {"plp", {HB_TAG('P','A','P',' ')}}, /* Palpa */ - {"plt", {HB_TAG('M','L','G',' ')}}, /* Plateau Malagasy -> Malagasy */ - {"pms", {HB_TAG('P','M','S',' ')}}, /* Piemontese */ - {"pnb", {HB_TAG('P','N','B',' ')}}, /* Western Panjabi */ - {"poh", {HB_TAG('P','O','H',' ')}}, /* Poqomchi' -> Pocomchi */ - {"pon", {HB_TAG('P','O','N',' ')}}, /* Pohnpeian */ - {"ppa", {HB_TAG('B','A','G',' ')}}, /* Pao (retired code) -> Baghelkhandi */ - {"pro", {HB_TAG('P','R','O',' ')}}, /* Old Provençal (to 1500) -> Provençal / Old Provençal */ - {"prs", {HB_TAG('D','R','I',' ')}}, /* Dari */ - {"ps", {HB_TAG('P','A','S',' ')}}, /* Pashto [macrolanguage] */ - {"pse", {HB_TAG('M','L','Y',' ')}}, /* Central Malay -> Malay */ - {"pst", {HB_TAG('P','A','S',' ')}}, /* Central Pashto -> Pashto */ - {"pt", {HB_TAG('P','T','G',' ')}}, /* Portuguese */ - {"pwo", {HB_TAG('P','W','O',' ')}}, /* Pwo Western Karen -> Western Pwo Karen */ - {"qu", {HB_TAG('Q','U','Z',' ')}}, /* Quechua [macrolanguage] */ - {"qub", {HB_TAG('Q','W','H',' ')}}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ - {"quc", {HB_TAG('Q','U','C',' ')}}, /* K’iche’ */ - {"qud", {HB_TAG('Q','V','I',' ')}}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ - {"quf", {HB_TAG('Q','U','Z',' ')}}, /* Lambayeque Quechua -> Quechua */ - {"qug", {HB_TAG('Q','V','I',' ')}}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ - {"quh", {HB_TAG('Q','U','H',' ')}}, /* South Bolivian Quechua -> Quechua (Bolivia) */ - {"quk", {HB_TAG('Q','U','Z',' ')}}, /* Chachapoyas Quechua -> Quechua */ - {"qul", {HB_TAG('Q','U','Z',' ')}}, /* North Bolivian Quechua -> Quechua */ - {"qup", {HB_TAG('Q','V','I',' ')}}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ - {"qur", {HB_TAG('Q','W','H',' ')}}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ - {"qus", {HB_TAG('Q','U','H',' ')}}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ - {"quw", {HB_TAG('Q','V','I',' ')}}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ - {"qux", {HB_TAG('Q','W','H',' ')}}, /* Yauyos Quechua -> Quechua (Peru) */ - {"quy", {HB_TAG('Q','U','Z',' ')}}, /* Ayacucho Quechua -> Quechua */ - {"quz", {HB_TAG('Q','U','Z',' ')}}, /* Cusco Quechua -> Quechua */ - {"qva", {HB_TAG('Q','W','H',' ')}}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ - {"qvc", {HB_TAG('Q','U','Z',' ')}}, /* Cajamarca Quechua -> Quechua */ - {"qve", {HB_TAG('Q','U','Z',' ')}}, /* Eastern Apurímac Quechua -> Quechua */ - {"qvh", {HB_TAG('Q','W','H',' ')}}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ - {"qvi", {HB_TAG('Q','V','I',' ')}}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ - {"qvj", {HB_TAG('Q','V','I',' ')}}, /* Loja Highland Quichua -> Quechua (Ecuador) */ - {"qvl", {HB_TAG('Q','W','H',' ')}}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ - {"qvm", {HB_TAG('Q','W','H',' ')}}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ - {"qvn", {HB_TAG('Q','W','H',' ')}}, /* North Junín Quechua -> Quechua (Peru) */ - {"qvo", {HB_TAG('Q','V','I',' ')}}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ - {"qvp", {HB_TAG('Q','W','H',' ')}}, /* Pacaraos Quechua -> Quechua (Peru) */ - {"qvs", {HB_TAG('Q','U','Z',' ')}}, /* San Martín Quechua -> Quechua */ - {"qvw", {HB_TAG('Q','W','H',' ')}}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ - {"qvz", {HB_TAG('Q','V','I',' ')}}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ - {"qwa", {HB_TAG('Q','W','H',' ')}}, /* Corongo Ancash Quechua -> Quechua (Peru) */ - {"qwc", {HB_TAG('Q','U','Z',' ')}}, /* Classical Quechua -> Quechua */ - {"qwh", {HB_TAG('Q','W','H',' ')}}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ - {"qws", {HB_TAG('Q','W','H',' ')}}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ - {"qxa", {HB_TAG('Q','W','H',' ')}}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ - {"qxc", {HB_TAG('Q','W','H',' ')}}, /* Chincha Quechua -> Quechua (Peru) */ - {"qxh", {HB_TAG('Q','W','H',' ')}}, /* Panao Huánuco Quechua -> Quechua (Peru) */ - {"qxl", {HB_TAG('Q','V','I',' ')}}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ - {"qxn", {HB_TAG('Q','W','H',' ')}}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ - {"qxo", {HB_TAG('Q','W','H',' ')}}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ - {"qxp", {HB_TAG('Q','U','Z',' ')}}, /* Puno Quechua -> Quechua */ - {"qxr", {HB_TAG('Q','V','I',' ')}}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ - {"qxt", {HB_TAG('Q','W','H',' ')}}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ - {"qxu", {HB_TAG('Q','U','Z',' ')}}, /* Arequipa-La Unión Quechua -> Quechua */ - {"qxw", {HB_TAG('Q','W','H',' ')}}, /* Jauja Wanca Quechua -> Quechua (Peru) */ - {"rag", {HB_TAG('L','U','H',' ')}}, /* Logooli -> Luyia */ - {"raj", {HB_TAG('R','A','J',' ')}}, /* Rajasthani [macrolanguage] */ - {"rar", {HB_TAG('R','A','R',' ')}}, /* Rarotongan */ - {"rbb", {HB_TAG('P','L','G',' ')}}, /* Rumai Palaung -> Palaung */ - {"rbl", {HB_TAG('B','I','K',' ')}}, /* Miraya Bikol -> Bikol */ - {"rej", {HB_TAG('R','E','J',' ')}}, /* Rejang */ - {"ria", {HB_TAG('R','I','A',' ')}}, /* Riang (India) */ - {"rif", {HB_TAG('R','I','F',' ')}}, /* Tarifit */ - {"rit", {HB_TAG('R','I','T',' ')}}, /* Ritarungo */ - {"rki", {HB_TAG('A','R','K',' ')}}, /* Rakhine */ - {"rkw", {HB_TAG('R','K','W',' ')}}, /* Arakwal */ - {"rm", {HB_TAG('R','M','S',' ')}}, /* Romansh */ - {"rmc", {HB_TAG('R','O','Y',' ')}}, /* Carpathian Romani -> Romany */ - {"rmf", {HB_TAG('R','O','Y',' ')}}, /* Kalo Finnish Romani -> Romany */ - {"rml", {HB_TAG('R','O','Y',' ')}}, /* Baltic Romani -> Romany */ - {"rmn", {HB_TAG('R','O','Y',' ')}}, /* Balkan Romani -> Romany */ - {"rmo", {HB_TAG('R','O','Y',' ')}}, /* Sinte Romani -> Romany */ - {"rmw", {HB_TAG('R','O','Y',' ')}}, /* Welsh Romani -> Romany */ - {"rmy", {HB_TAG('R','M','Y',' ')}}, /* Vlax Romani */ - {"rmz", {HB_TAG('A','R','K',' ')}}, /* Marma -> Rakhine */ - {"rn", {HB_TAG('R','U','N',' ')}}, /* Rundi */ - {"rnl", {HB_TAG('H','A','L',' ')}}, /* Ranglong -> Halam (Falam Chin) */ - {"ro", {HB_TAG('R','O','M',' ')}}, /* Romanian */ - {"rom", {HB_TAG('R','O','Y',' ')}}, /* Romany [macrolanguage] */ - {"rtm", {HB_TAG('R','T','M',' ')}}, /* Rotuman */ - {"ru", {HB_TAG('R','U','S',' ')}}, /* Russian */ - {"rue", {HB_TAG('R','S','Y',' ')}}, /* Rusyn */ - {"rup", {HB_TAG('R','U','P',' ')}}, /* Aromanian */ - {"rw", {HB_TAG('R','U','A',' ')}}, /* Kinyarwanda */ - {"rwr", {HB_TAG('M','A','W',' ')}}, /* Marwari (India) */ - {"sa", {HB_TAG('S','A','N',' ')}}, /* Sanskrit */ - {"sah", {HB_TAG('Y','A','K',' ')}}, /* Yakut -> Sakha */ - {"sam", {HB_TAG('P','A','A',' ')}}, /* Samaritan Aramaic -> Palestinian Aramaic */ - {"sas", {HB_TAG('S','A','S',' ')}}, /* Sasak */ - {"sat", {HB_TAG('S','A','T',' ')}}, /* Santali */ - {"sc", {HB_TAG('S','R','D',' ')}}, /* Sardinian [macrolanguage] */ - {"sck", {HB_TAG('S','A','D',' ')}}, /* Sadri */ - {"scn", {HB_TAG('S','C','N',' ')}}, /* Sicilian */ - {"sco", {HB_TAG('S','C','O',' ')}}, /* Scots */ - {"scs", {HB_TAG('S','C','S',' '), /* North Slavey */ - HB_TAG('S','L','A',' '), /* North Slavey -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* North Slavey -> Athapaskan */ - {"sd", {HB_TAG('S','N','D',' ')}}, /* Sindhi */ - {"sdc", {HB_TAG('S','R','D',' ')}}, /* Sassarese Sardinian -> Sardinian */ - {"sdh", {HB_TAG('K','U','R',' ')}}, /* Southern Kurdish -> Kurdish */ - {"sdn", {HB_TAG('S','R','D',' ')}}, /* Gallurese Sardinian -> Sardinian */ - {"se", {HB_TAG('N','S','M',' ')}}, /* Northern Sami */ - {"seh", {HB_TAG('S','N','A',' ')}}, /* Sena */ - {"sek", {HB_TAG('A','T','H',' ')}}, /* Sekani -> Athapaskan */ - {"sel", {HB_TAG('S','E','L',' ')}}, /* Selkup */ - {"sez", {HB_TAG('Q','I','N',' ')}}, /* Senthang Chin -> Chin */ - {"sfm", {HB_TAG('H','M','N',' ')}}, /* Small Flowery Miao -> Hmong */ - {"sg", {HB_TAG('S','G','O',' ')}}, /* Sango */ - {"sga", {HB_TAG('S','G','A',' ')}}, /* Old Irish (to 900) */ - {"sgc", {HB_TAG('K','A','L',' ')}}, /* Kipsigis -> Kalenjin */ - {"sgs", {HB_TAG('S','G','S',' ')}}, /* Samogitian */ - {"sgw", {HB_TAG('C','H','G',' '), /* Sebat Bet Gurage -> Chaha Gurage */ - HB_TAG('S','G','W',' ')}}, /* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */ - {"shi", {HB_TAG('S','H','I',' ')}}, /* Tachelhit */ - {"shn", {HB_TAG('S','H','N',' ')}}, /* Shan */ - {"shu", {HB_TAG('A','R','A',' ')}}, /* Chadian Arabic -> Arabic */ - {"si", {HB_TAG('S','N','H',' ')}}, /* Sinhala (Sinhalese) */ - {"sid", {HB_TAG('S','I','D',' ')}}, /* Sidamo */ - {"sjd", {HB_TAG('K','S','M',' ')}}, /* Kildin Sami */ - {"sjo", {HB_TAG('S','I','B',' ')}}, /* Xibe -> Sibe */ - {"sk", {HB_TAG('S','K','Y',' ')}}, /* Slovak */ - {"skg", {HB_TAG('M','L','G',' ')}}, /* Sakalava Malagasy -> Malagasy */ - {"skr", {HB_TAG('S','R','K',' ')}}, /* Saraiki */ - {"sl", {HB_TAG('S','L','V',' ')}}, /* Slovenian */ - {"sm", {HB_TAG('S','M','O',' ')}}, /* Samoan */ - {"sma", {HB_TAG('S','S','M',' ')}}, /* Southern Sami */ - {"smj", {HB_TAG('L','S','M',' ')}}, /* Lule Sami */ - {"smn", {HB_TAG('I','S','M',' ')}}, /* Inari Sami */ - {"sms", {HB_TAG('S','K','S',' ')}}, /* Skolt Sami */ - {"sn", {HB_TAG('S','N','A','0')}}, /* Shona */ - {"snk", {HB_TAG('S','N','K',' ')}}, /* Soninke */ - {"so", {HB_TAG('S','M','L',' ')}}, /* Somali */ - {"sop", {HB_TAG('S','O','P',' ')}}, /* Songe */ - {"spv", {HB_TAG('O','R','I',' ')}}, /* Sambalpuri -> Odia (formerly Oriya) */ - {"spy", {HB_TAG('K','A','L',' ')}}, /* Sabaot -> Kalenjin */ - {"sq", {HB_TAG('S','Q','I',' ')}}, /* Albanian [macrolanguage] */ - {"sr", {HB_TAG('S','R','B',' ')}}, /* Serbian */ - {"src", {HB_TAG('S','R','D',' ')}}, /* Logudorese Sardinian -> Sardinian */ - {"sro", {HB_TAG('S','R','D',' ')}}, /* Campidanese Sardinian -> Sardinian */ - {"srr", {HB_TAG('S','R','R',' ')}}, /* Serer */ - {"srs", {HB_TAG('A','T','H',' ')}}, /* Sarsi -> Athapaskan */ - {"ss", {HB_TAG('S','W','Z',' ')}}, /* Swati */ - {"ssh", {HB_TAG('A','R','A',' ')}}, /* Shihhi Arabic -> Arabic */ - {"st", {HB_TAG('S','O','T',' ')}}, /* Southern Sotho -> Sotho, Southern */ - {"stq", {HB_TAG('S','T','Q',' ')}}, /* Saterfriesisch -> Saterland Frisian */ - {"stv", {HB_TAG('S','I','G',' ')}}, /* Silt'e -> Silte Gurage */ - {"su", {HB_TAG('S','U','N',' ')}}, /* Sundanese */ - {"suk", {HB_TAG('S','U','K',' ')}}, /* Sukuma */ - {"suq", {HB_TAG('S','U','R',' ')}}, /* Suri */ - {"sv", {HB_TAG('S','V','E',' ')}}, /* Swedish */ - {"sva", {HB_TAG('S','V','A',' ')}}, /* Svan */ - {"sw", {HB_TAG('S','W','K',' ')}}, /* Swahili [macrolanguage] */ - {"swb", {HB_TAG('C','M','R',' ')}}, /* Maore Comorian -> Comorian */ - {"swc", {HB_TAG('S','W','K',' ')}}, /* Congo Swahili -> Swahili */ - {"swh", {HB_TAG('S','W','K',' ')}}, /* Swahili */ - {"swv", {HB_TAG('M','A','W',' ')}}, /* Shekhawati -> Marwari */ - {"sxu", {HB_TAG('S','X','U',' ')}}, /* Upper Saxon */ - {"syc", {HB_TAG('S','Y','R',' ')}}, /* Classical Syriac -> Syriac */ - {"syl", {HB_TAG('S','Y','L',' ')}}, /* Sylheti */ - {"syr", {HB_TAG('S','Y','R',' ')}}, /* Syriac [macrolanguage] */ - {"szl", {HB_TAG('S','Z','L',' ')}}, /* Silesian */ - {"ta", {HB_TAG('T','A','M',' ')}}, /* Tamil */ - {"taa", {HB_TAG('A','T','H',' ')}}, /* Lower Tanana -> Athapaskan */ - {"tab", {HB_TAG('T','A','B',' ')}}, /* Tabassaran -> Tabasaran */ - {"taq", {HB_TAG('T','M','H',' ')}}, /* Tamasheq -> Tamashek */ - {"tau", {HB_TAG('A','T','H',' ')}}, /* Upper Tanana -> Athapaskan */ - {"tcb", {HB_TAG('A','T','H',' ')}}, /* Tanacross -> Athapaskan */ - {"tce", {HB_TAG('A','T','H',' ')}}, /* Southern Tutchone -> Athapaskan */ - {"tcp", {HB_TAG('Q','I','N',' ')}}, /* Tawr Chin -> Chin */ - {"tcy", {HB_TAG('T','U','L',' ')}}, /* Tulu -> Tumbuka */ - {"tcz", {HB_TAG('Q','I','N',' ')}}, /* Thado Chin -> Chin */ - {"tdd", {HB_TAG('T','D','D',' ')}}, /* Tai Nüa -> Dehong Dai */ - {"tdx", {HB_TAG('M','L','G',' ')}}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ - {"te", {HB_TAG('T','E','L',' ')}}, /* Telugu */ - {"tec", {HB_TAG('K','A','L',' ')}}, /* Terik -> Kalenjin */ - {"tem", {HB_TAG('T','M','N',' ')}}, /* Timne -> Temne */ - {"tet", {HB_TAG('T','E','T',' ')}}, /* Tetum */ - {"tfn", {HB_TAG('A','T','H',' ')}}, /* Tanaina -> Athapaskan */ - {"tg", {HB_TAG('T','A','J',' ')}}, /* Tajik -> Tajiki */ - {"tgj", {HB_TAG('N','I','S',' ')}}, /* Tagin -> Nisi */ - {"tgx", {HB_TAG('A','T','H',' ')}}, /* Tagish -> Athapaskan */ - {"th", {HB_TAG('T','H','A',' ')}}, /* Thai */ - {"tht", {HB_TAG('A','T','H',' ')}}, /* Tahltan -> Athapaskan */ - {"thv", {HB_TAG('T','M','H',' ')}}, /* Tahaggart Tamahaq -> Tamashek */ - {"thz", {HB_TAG('T','M','H',' ')}}, /* Tayart Tamajeq -> Tamashek */ - {"ti", {HB_TAG('T','G','Y',' ')}}, /* Tigrinya */ - {"tig", {HB_TAG('T','G','R',' ')}}, /* Tigre */ - {"tiv", {HB_TAG('T','I','V',' ')}}, /* Tiv */ - {"tk", {HB_TAG('T','K','M',' ')}}, /* Turkmen */ - {"tkg", {HB_TAG('M','L','G',' ')}}, /* Tesaka Malagasy -> Malagasy */ - {"tl", {HB_TAG('T','G','L',' ')}}, /* Tagalog */ - {"tmh", {HB_TAG('T','M','H',' ')}}, /* Tamashek [macrolanguage] */ - {"tmw", {HB_TAG('M','L','Y',' ')}}, /* Temuan -> Malay */ - {"tn", {HB_TAG('T','N','A',' ')}}, /* Tswana */ - {"tnf", {HB_TAG('D','R','I',' ')}}, /* Tangshewi (retired code) -> Dari */ - {"to", {HB_TAG('T','G','N',' ')}}, /* Tonga (Tonga Islands) -> Tongan */ - {"tod", {HB_TAG('T','O','D','0')}}, /* Toma */ - {"toi", {HB_TAG('T','N','G',' ')}}, /* Tonga (Zambia) */ - {"tol", {HB_TAG('A','T','H',' ')}}, /* Tolowa -> Athapaskan */ - {"tpi", {HB_TAG('T','P','I',' ')}}, /* Tok Pisin */ - {"tr", {HB_TAG('T','R','K',' ')}}, /* Turkish */ - {"tru", {HB_TAG('T','U','A',' '), /* Turoyo -> Turoyo Aramaic */ - HB_TAG('S','Y','R',' ')}}, /* Turoyo -> Syriac */ - {"ts", {HB_TAG('T','S','G',' ')}}, /* Tsonga */ - {"tsj", {HB_TAG('T','S','J',' ')}}, /* Tshangla */ - {"tt", {HB_TAG('T','A','T',' ')}}, /* Tatar */ - {"ttm", {HB_TAG('A','T','H',' ')}}, /* Northern Tutchone -> Athapaskan */ - {"ttq", {HB_TAG('T','M','H',' ')}}, /* Tawallammat Tamajaq -> Tamashek */ - {"tum", {HB_TAG('T','U','M',' ')}}, /* Tumbuka -> Tulu */ - {"tuu", {HB_TAG('A','T','H',' ')}}, /* Tututni -> Athapaskan */ - {"tuy", {HB_TAG('K','A','L',' ')}}, /* Tugen -> Kalenjin */ - {"tvl", {HB_TAG('T','V','L',' ')}}, /* Tuvalu */ - {"tw", {HB_TAG('T','W','I',' '), /* Twi */ - HB_TAG('A','K','A',' ')}}, /* Twi -> Akan */ - {"txc", {HB_TAG('A','T','H',' ')}}, /* Tsetsaut -> Athapaskan */ - {"txy", {HB_TAG('M','L','G',' ')}}, /* Tanosy Malagasy -> Malagasy */ - {"ty", {HB_TAG('T','H','T',' ')}}, /* Tahitian */ - {"tyv", {HB_TAG('T','U','V',' ')}}, /* Tuvinian -> Tuvin */ - {"tyz", {HB_TAG('T','Y','Z',' ')}}, /* Tày */ - {"tzm", {HB_TAG('T','Z','M',' ')}}, /* Central Atlas Tamazight -> Tamazight */ - {"tzo", {HB_TAG('T','Z','O',' ')}}, /* Tzotzil */ - {"ubl", {HB_TAG('B','I','K',' ')}}, /* Buhi'non Bikol -> Bikol */ - {"udm", {HB_TAG('U','D','M',' ')}}, /* Udmurt */ - {"ug", {HB_TAG('U','Y','G',' ')}}, /* Uyghur */ - {"uk", {HB_TAG('U','K','R',' ')}}, /* Ukrainian */ - {"umb", {HB_TAG('U','M','B',' ')}}, /* Umbundu */ - {"unr", {HB_TAG('M','U','N',' ')}}, /* Mundari */ - {"ur", {HB_TAG('U','R','D',' ')}}, /* Urdu */ - {"urk", {HB_TAG('M','L','Y',' ')}}, /* Urak Lawoi' -> Malay */ - {"uz", {HB_TAG('U','Z','B',' ')}}, /* Uzbek [macrolanguage] */ - {"uzn", {HB_TAG('U','Z','B',' ')}}, /* Northern Uzbek -> Uzbek */ - {"uzs", {HB_TAG('U','Z','B',' ')}}, /* Southern Uzbek -> Uzbek */ - {"ve", {HB_TAG('V','E','N',' ')}}, /* Venda */ - {"vec", {HB_TAG('V','E','C',' ')}}, /* Venetian */ - {"vi", {HB_TAG('V','I','T',' ')}}, /* Vietnamese */ - {"vkk", {HB_TAG('M','L','Y',' ')}}, /* Kaur -> Malay */ - {"vkt", {HB_TAG('M','L','Y',' ')}}, /* Tenggarong Kutai Malay -> Malay */ - {"vls", {HB_TAG('F','L','E',' ')}}, /* Vlaams -> Dutch (Flemish) */ - {"vmw", {HB_TAG('M','A','K',' ')}}, /* Makhuwa */ - {"vo", {HB_TAG('V','O','L',' ')}}, /* Volapük */ - {"vro", {HB_TAG('V','R','O',' ')}}, /* Võro */ - {"wa", {HB_TAG('W','L','N',' ')}}, /* Walloon */ - {"war", {HB_TAG('W','A','R',' ')}}, /* Waray (Philippines) -> Waray-Waray */ - {"wbm", {HB_TAG('W','A',' ',' ')}}, /* Wa */ - {"wbr", {HB_TAG('W','A','G',' ')}}, /* Wagdi */ - {"wlc", {HB_TAG('C','M','R',' ')}}, /* Mwali Comorian -> Comorian */ - {"wle", {HB_TAG('S','I','G',' ')}}, /* Wolane -> Silte Gurage */ - {"wlk", {HB_TAG('A','T','H',' ')}}, /* Wailaki -> Athapaskan */ - {"wni", {HB_TAG('C','M','R',' ')}}, /* Ndzwani Comorian -> Comorian */ - {"wo", {HB_TAG('W','L','F',' ')}}, /* Wolof */ - {"wry", {HB_TAG('M','A','W',' ')}}, /* Merwari -> Marwari */ - {"wsg", {HB_TAG('G','O','N',' ')}}, /* Adilabad Gondi -> Gondi */ - {"wtm", {HB_TAG('W','T','M',' ')}}, /* Mewati */ - {"wuu", {HB_TAG('Z','H','S',' ')}}, /* Wu Chinese -> Chinese Simplified */ - {"xal", {HB_TAG('K','L','M',' '), /* Kalmyk */ - HB_TAG('T','O','D',' ')}}, /* Kalmyk -> Todo */ - {"xan", {HB_TAG('S','E','K',' ')}}, /* Xamtanga -> Sekota */ - {"xh", {HB_TAG('X','H','S',' ')}}, /* Xhosa */ - {"xjb", {HB_TAG('X','J','B',' ')}}, /* Minjungbal -> Minjangbal */ - {"xkf", {HB_TAG('X','K','F',' ')}}, /* Khengkha */ - {"xmm", {HB_TAG('M','L','Y',' ')}}, /* Manado Malay -> Malay */ - {"xmv", {HB_TAG('M','L','G',' ')}}, /* Antankarana Malagasy -> Malagasy */ - {"xmw", {HB_TAG('M','L','G',' ')}}, /* Tsimihety Malagasy -> Malagasy */ - {"xnr", {HB_TAG('D','G','R',' ')}}, /* Kangri -> Dogri */ - {"xog", {HB_TAG('X','O','G',' ')}}, /* Soga */ - {"xpe", {HB_TAG('X','P','E',' ')}}, /* Liberia Kpelle -> Kpelle (Liberia) */ - {"xsl", {HB_TAG('S','S','L',' '), /* South Slavey */ - HB_TAG('S','L','A',' '), /* South Slavey -> Slavey */ - HB_TAG('A','T','H',' ')}}, /* South Slavey -> Athapaskan */ - {"xst", {HB_TAG('S','I','G',' ')}}, /* Silt'e (retired code) -> Silte Gurage */ - {"xwo", {HB_TAG('T','O','D',' ')}}, /* Written Oirat -> Todo */ - {"yao", {HB_TAG('Y','A','O',' ')}}, /* Yao */ - {"yap", {HB_TAG('Y','A','P',' ')}}, /* Yapese */ - {"ybd", {HB_TAG('A','R','K',' ')}}, /* Yangbye (retired code) -> Rakhine */ - {"ydd", {HB_TAG('J','I','I',' ')}}, /* Eastern Yiddish -> Yiddish */ - {"yi", {HB_TAG('J','I','I',' ')}}, /* Yiddish [macrolanguage] */ - {"yih", {HB_TAG('J','I','I',' ')}}, /* Western Yiddish -> Yiddish */ - {"yo", {HB_TAG('Y','B','A',' ')}}, /* Yoruba */ - {"yos", {HB_TAG('Q','I','N',' ')}}, /* Yos (retired code) -> Chin */ - {"yrk", {HB_TAG('T','N','E',' '), /* Nenets -> Tundra Nenets */ - HB_TAG('F','N','E',' ')}}, /* Nenets -> Forest Nenets */ - {"yue", {HB_TAG('Z','H','H',' ')}}, /* Yue Chinese -> Chinese, Hong Kong SAR */ - {"za", {HB_TAG('Z','H','A',' ')}}, /* Zhuang [macrolanguage] */ - {"zch", {HB_TAG('Z','H','A',' ')}}, /* Central Hongshuihe Zhuang -> Zhuang */ - {"zdj", {HB_TAG('C','M','R',' ')}}, /* Ngazidja Comorian -> Comorian */ - {"zea", {HB_TAG('Z','E','A',' ')}}, /* Zeeuws -> Zealandic */ - {"zeh", {HB_TAG('Z','H','A',' ')}}, /* Eastern Hongshuihe Zhuang -> Zhuang */ - {"zgb", {HB_TAG('Z','H','A',' ')}}, /* Guibei Zhuang -> Zhuang */ - {"zgh", {HB_TAG('Z','G','H',' ')}}, /* Standard Moroccan Tamazight */ - {"zgm", {HB_TAG('Z','H','A',' ')}}, /* Minz Zhuang -> Zhuang */ - {"zgn", {HB_TAG('Z','H','A',' ')}}, /* Guibian Zhuang -> Zhuang */ - {"zh", {HB_TAG('Z','H','S',' ')}}, /* Chinese [macrolanguage] -> Chinese Simplified */ - {"zhd", {HB_TAG('Z','H','A',' ')}}, /* Dai Zhuang -> Zhuang */ - {"zhn", {HB_TAG('Z','H','A',' ')}}, /* Nong Zhuang -> Zhuang */ - {"zlj", {HB_TAG('Z','H','A',' ')}}, /* Liujiang Zhuang -> Zhuang */ - {"zlm", {HB_TAG('M','L','Y',' ')}}, /* Malay */ - {"zln", {HB_TAG('Z','H','A',' ')}}, /* Lianshan Zhuang -> Zhuang */ - {"zlq", {HB_TAG('Z','H','A',' ')}}, /* Liuqian Zhuang -> Zhuang */ - {"zmi", {HB_TAG('M','L','Y',' ')}}, /* Negeri Sembilan Malay -> Malay */ - {"zne", {HB_TAG('Z','N','D',' ')}}, /* Zande */ - {"zom", {HB_TAG('Q','I','N',' ')}}, /* Zou -> Chin */ - {"zqe", {HB_TAG('Z','H','A',' ')}}, /* Qiubei Zhuang -> Zhuang */ - {"zsm", {HB_TAG('M','L','Y',' ')}}, /* Standard Malay -> Malay */ - {"zu", {HB_TAG('Z','U','L',' ')}}, /* Zulu */ - {"zum", {HB_TAG('L','R','C',' ')}}, /* Kumzari -> Luri */ - {"zyb", {HB_TAG('Z','H','A',' ')}}, /* Yongbei Zhuang -> Zhuang */ - {"zyg", {HB_TAG('Z','H','A',' ')}}, /* Yang Zhuang -> Zhuang */ - {"zyj", {HB_TAG('Z','H','A',' ')}}, /* Youjiang Zhuang -> Zhuang */ - {"zyn", {HB_TAG('Z','H','A',' ')}}, /* Yongnan Zhuang -> Zhuang */ - {"zza", {HB_TAG('Z','Z','A',' ')}}, /* Zazaki [macrolanguage] */ - {"zzj", {HB_TAG('Z','H','A',' ')}}, /* Zuojiang Zhuang -> Zhuang */ + {"aa", HB_TAG('A','F','R',' ')}, /* Afar */ + {"aae", HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ + {"aao", HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ + {"aat", HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ + {"ab", HB_TAG('A','B','K',' ')}, /* Abkhazian */ + {"abh", HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ + {"abq", HB_TAG('A','B','A',' ')}, /* Abaza */ + {"abv", HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */ + {"acf", HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */ +/*{"ach", HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */ + {"acm", HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */ + {"acq", HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */ +/*{"acr", HB_TAG('A','C','R',' ')},*/ /* Achi */ + {"acw", HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ + {"acx", HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ + {"acy", HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ + {"ada", HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ + {"adf", HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ + {"adp", HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */ +/*{"ady", HB_TAG('A','D','Y',' ')},*/ /* Adyghe */ + {"aeb", HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */ + {"aec", HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */ + {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {"afb", HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */ + {"ahg", HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */ + {"aht", HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */ + {"aii", HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + {"aii", HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */ +/*{"aio", HB_TAG('A','I','O',' ')},*/ /* Aiton */ + {"aiw", HB_TAG('A','R','I',' ')}, /* Aari */ + {"ajp", HB_TAG('A','R','A',' ')}, /* South Levantine Arabic -> Arabic */ + {"ak", HB_TAG('A','K','A',' ')}, /* Akan [macrolanguage] */ + {"ak", HB_TAG('T','W','I',' ')}, /* Akan [macrolanguage] -> Twi */ + {"aln", HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */ + {"als", HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */ +/*{"alt", HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */ + {"am", HB_TAG('A','M','H',' ')}, /* Amharic */ + {"amf", HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */ + {"amw", HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */ + {"an", HB_TAG('A','R','G',' ')}, /* Aragonese */ +/*{"ang", HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */ + {"apc", HB_TAG('A','R','A',' ')}, /* North Levantine Arabic -> Arabic */ + {"apd", HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */ + {"apj", HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */ + {"apk", HB_TAG('A','T','H',' ')}, /* Kiowa Apache -> Athapaskan */ + {"apl", HB_TAG('A','T','H',' ')}, /* Lipan Apache -> Athapaskan */ + {"apm", HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */ + {"apw", HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */ + {"ar", HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */ + {"arb", HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */ + {"arn", HB_TAG('M','A','P',' ')}, /* Mapudungun */ + {"arq", HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */ + {"ars", HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */ + {"ary", HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */ + {"arz", HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */ + {"as", HB_TAG('A','S','M',' ')}, /* Assamese */ +/*{"ast", HB_TAG('A','S','T',' ')},*/ /* Asturian */ +/*{"ath", HB_TAG('A','T','H',' ')},*/ /* Athapascan [family] -> Athapaskan */ + {"atj", HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ + {"atv", HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ + {"auz", HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ + {"av", HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */ + {"avl", HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ +/*{"awa", HB_TAG('A','W','A',' ')},*/ /* Awadhi */ + {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ + {"ayc", HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ + {"ayh", HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */ + {"ayl", HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */ + {"ayn", HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */ + {"ayp", HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */ + {"ayr", HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */ + {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ +/*{"azb", HB_TAG('A','Z','B',' ')},*/ /* South Azerbaijani -> Torki */ + {"azj", HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */ + {"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */ + {"bad", HB_TAG('B','A','D','0')}, /* Banda [family] */ + {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [family] */ + {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */ +/*{"ban", HB_TAG('B','A','N',' ')},*/ /* Balinese */ +/*{"bar", HB_TAG('B','A','R',' ')},*/ /* Bavarian */ +/*{"bbc", HB_TAG('B','B','C',' ')},*/ /* Batak Toba */ + {"bbz", HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */ + {"bcc", HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */ + {"bci", HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */ + {"bcl", HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ + {"bcq", HB_TAG('B','C','H',' ')}, /* Bench */ + {"bcr", HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ +/*{"bdy", HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ + {"be", HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */ + {"bea", HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ + {"beb", HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ +/*{"bem", HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */ + {"ber", HB_TAG('B','B','R',' ')}, /* Berber [family] */ + {"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */ + {"bft", HB_TAG('B','L','T',' ')}, /* Balti */ + {"bfu", HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */ + {"bfy", HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */ + {"bg", HB_TAG('B','G','R',' ')}, /* Bulgarian */ +/*{"bgc", HB_TAG('B','G','C',' ')},*/ /* Haryanvi */ + {"bgn", HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */ + {"bgp", HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */ +/*{"bgq", HB_TAG('B','G','Q',' ')},*/ /* Bagri */ + {"bgr", HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */ + {"bhb", HB_TAG('B','H','I',' ')}, /* Bhili */ +/*{"bhi", HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */ + {"bhk", HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */ +/*{"bho", HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */ + {"bhr", HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */ + {"bi", HB_TAG('B','I','S',' ')}, /* Bislama */ +/*{"bik", HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */ + {"bin", HB_TAG('E','D','O',' ')}, /* Edo */ +/*{"bjj", HB_TAG('B','J','J',' ')},*/ /* Kanauji */ + {"bjn", HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */ + {"bjq", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ + {"bjt", HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */ + {"bla", HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */ + {"ble", HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */ +/*{"blk", HB_TAG('B','L','K',' ')},*/ /* Pa’o Karen */ + {"bln", HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */ + {"bm", HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ + {"bmm", HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */ + {"bn", HB_TAG('B','E','N',' ')}, /* Bengali */ + {"bo", HB_TAG('T','I','B',' ')}, /* Tibetan */ +/*{"bpy", HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */ + {"bqi", HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */ + {"br", HB_TAG('B','R','E',' ')}, /* Breton */ + {"bra", HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */ +/*{"brh", HB_TAG('B','R','H',' ')},*/ /* Brahui */ +/*{"brx", HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */ + {"bs", HB_TAG('B','O','S',' ')}, /* Bosnian */ +/*{"bsk", HB_TAG('B','S','K',' ')},*/ /* Burushaski */ + {"btb", HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */ + {"btj", HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */ + {"bto", HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */ +/*{"bts", HB_TAG('B','T','S',' ')},*/ /* Batak Simalungun */ +/*{"bug", HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */ + {"bum", HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */ + {"bve", HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */ + {"bvu", HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */ + {"bxk", HB_TAG('L','U','H',' ')}, /* Bukusu -> Luyia */ + {"bxp", HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */ + {"bxr", HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */ + {"byn", HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */ +/*{"byv", HB_TAG('B','Y','V',' ')},*/ /* Medumba */ + {"bzc", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */ + {"ca", HB_TAG('C','A','T',' ')}, /* Catalan */ + {"caf", HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */ + {"caf", HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ +/*{"cak", HB_TAG('C','A','K',' ')},*/ /* Kaqchikel */ +/*{"cbk", HB_TAG('C','B','K',' ')},*/ /* Chavacano -> Zamboanga Chavacano */ + {"cbl", HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ + {"cco", HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */ + {"ccq", HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */ + {"cdo", HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese Simplified */ + {"ce", HB_TAG('C','H','E',' ')}, /* Chechen */ +/*{"ceb", HB_TAG('C','E','B',' ')},*/ /* Cebuano */ + {"cfm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */ +/*{"cgg", HB_TAG('C','G','G',' ')},*/ /* Chiga */ + {"ch", HB_TAG('C','H','A',' ')}, /* Chamorro */ + {"chj", HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */ + {"chk", HB_TAG('C','H','K','0')}, /* Chuukese */ +/*{"cho", HB_TAG('C','H','O',' ')},*/ /* Choctaw */ + {"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */ + {"chp", HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */ + {"chp", HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */ + {"chq", HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */ +/*{"chr", HB_TAG('C','H','R',' ')},*/ /* Cherokee */ +/*{"chy", HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */ + {"chz", HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */ + {"ciw", HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */ +/*{"cja", HB_TAG('C','J','A',' ')},*/ /* Western Cham */ +/*{"cjm", HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */ + {"cjy", HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese Simplified */ + {"cka", HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */ + {"ckb", HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */ + {"ckt", HB_TAG('C','H','K',' ')}, /* Chukot -> Chukchi */ + {"clc", HB_TAG('A','T','H',' ')}, /* Chilcotin -> Athapaskan */ + {"cld", HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ + {"cle", HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ + {"cmn", HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese Simplified */ + {"cmr", HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ + {"cnb", HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ + {"cnh", HB_TAG('Q','I','N',' ')}, /* Hakha Chin -> Chin */ + {"cnk", HB_TAG('Q','I','N',' ')}, /* Khumi Chin -> Chin */ + {"cnl", HB_TAG('C','C','H','N')}, /* Lalana Chinantec -> Chinantec */ + {"cnp", HB_TAG('Z','H','S',' ')}, /* Northern Ping Chinese -> Chinese Simplified */ + {"cnt", HB_TAG('C','C','H','N')}, /* Tepetotutla Chinantec -> Chinantec */ + {"cnw", HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ + {"co", HB_TAG('C','O','S',' ')}, /* Corsican */ + {"coa", HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ +/*{"cop", HB_TAG('C','O','P',' ')},*/ /* Coptic */ + {"coq", HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ + {"cpa", HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ + {"cpe", HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [family] -> Creoles */ + {"cpf", HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [family] -> Creoles */ +/*{"cpp", HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [family] -> Creoles */ + {"cpx", HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese Simplified */ + {"cqd", HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */ + {"cqu", HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + {"cr", HB_TAG('C','R','E',' ')}, /* Cree [macrolanguage] */ + {"cr", HB_TAG('Y','C','R',' ')}, /* Cree [macrolanguage] -> Y-Cree */ + {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {"crj", HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */ + {"crk", HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */ + {"crl", HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */ + {"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */ + {"crm", HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */ + {"crp", HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [family] -> Creoles */ + {"crx", HB_TAG('C','R','R',' ')}, /* Carrier */ + {"crx", HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */ + {"cs", HB_TAG('C','S','Y',' ')}, /* Czech */ + {"csa", HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */ +/*{"csb", HB_TAG('C','S','B',' ')},*/ /* Kashubian */ + {"csh", HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */ + {"cso", HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */ + {"csp", HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese Simplified */ + {"csw", HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */ + {"csw", HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */ + {"csy", HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */ + {"ctc", HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */ + {"ctd", HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */ + {"cte", HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */ +/*{"ctg", HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ + {"ctl", HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ + {"cts", HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ + {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavonic */ + {"cuc", HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ +/*{"cuk", HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */ + {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */ + {"cvn", HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */ + {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */ + {"cwd", HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */ + {"cy", HB_TAG('W','E','L',' ')}, /* Welsh */ + {"czh", HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese Simplified */ + {"czo", HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese Simplified */ + {"czt", HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */ + {"da", HB_TAG('D','A','N',' ')}, /* Danish */ + {"dao", HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */ + {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */ +/*{"dar", HB_TAG('D','A','R',' ')},*/ /* Dargwa */ +/*{"dax", HB_TAG('D','A','X',' ')},*/ /* Dayi */ + {"de", HB_TAG('D','E','U',' ')}, /* German */ + {"den", HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ + {"den", HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ +/*{"dgo", HB_TAG('D','G','O',' ')},*/ /* Dogri */ + {"dgr", HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */ + {"dhd", HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ +/*{"dhg", HB_TAG('D','H','G',' ')},*/ /* Dhangu */ + {"dib", HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */ + {"dik", HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */ + {"din", HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {"dip", HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */ +/*{"diq", HB_TAG('D','I','Q',' ')},*/ /* Dimli */ + {"diw", HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */ + {"dje", HB_TAG('D','J','R',' ')}, /* Zarma */ + {"djr", HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ + {"dks", HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */ + {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */ +/*{"dnj", HB_TAG('D','N','J',' ')},*/ /* Dan */ + {"doi", HB_TAG('D','G','R',' ')}, /* Dogri [macrolanguage] */ + {"drh", HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */ + {"drw", HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */ + {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ + {"dty", HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */ +/*{"duj", HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */ + {"dup", HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */ + {"dv", HB_TAG('D','I','V',' ')}, /* Divehi (Dhivehi, Maldivian) */ + {"dv", HB_TAG('D','H','V',' ')}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ + {"dwk", HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */ + {"dwu", HB_TAG('D','U','J',' ')}, /* Dhuwal */ + {"dwy", HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */ + {"dyu", HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */ + {"dz", HB_TAG('D','Z','N',' ')}, /* Dzongkha */ + {"ee", HB_TAG('E','W','E',' ')}, /* Ewe */ +/*{"efi", HB_TAG('E','F','I',' ')},*/ /* Efik */ + {"ekk", HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */ + {"el", HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) -> Greek */ + {"emk", HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ + {"emk", HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ + {"en", HB_TAG('E','N','G',' ')}, /* English */ + {"enb", HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ + {"enf", HB_TAG('F','N','E',' ')}, /* Forest Enets -> Forest Nenets */ + {"enh", HB_TAG('T','N','E',' ')}, /* Tundra Enets -> Tundra Nenets */ + {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */ + {"es", HB_TAG('E','S','P',' ')}, /* Spanish */ + {"esg", HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */ + {"esi", HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */ + {"esk", HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */ +/*{"esu", HB_TAG('E','S','U',' ')},*/ /* Central Yupik */ + {"et", HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */ + {"eto", HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */ + {"eu", HB_TAG('E','U','Q',' ')}, /* Basque */ + {"eve", HB_TAG('E','V','N',' ')}, /* Even */ + {"evn", HB_TAG('E','V','K',' ')}, /* Evenki */ + {"ewo", HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */ + {"eyo", HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */ + {"fa", HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {"fan", HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */ +/*{"fat", HB_TAG('F','A','T',' ')},*/ /* Fanti */ + {"fbl", HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */ + {"ff", HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */ + {"ffm", HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */ + {"fi", HB_TAG('F','I','N',' ')}, /* Finnish */ + {"fil", HB_TAG('P','I','L',' ')}, /* Filipino */ + {"fj", HB_TAG('F','J','I',' ')}, /* Fijian */ + {"flm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */ + {"flm", HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */ +/*{"fmp", HB_TAG('F','M','P',' ')},*/ /* Fe’fe’ */ + {"fo", HB_TAG('F','O','S',' ')}, /* Faroese */ +/*{"fon", HB_TAG('F','O','N',' ')},*/ /* Fon */ + {"fr", HB_TAG('F','R','A',' ')}, /* French */ +/*{"frc", HB_TAG('F','R','C',' ')},*/ /* Cajun French */ +/*{"frp", HB_TAG('F','R','P',' ')},*/ /* Arpitan */ + {"fub", HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */ + {"fuc", HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */ + {"fue", HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */ + {"fuf", HB_TAG('F','T','A',' ')}, /* Pular -> Futa */ + {"fuh", HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */ + {"fui", HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */ + {"fuq", HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */ + {"fur", HB_TAG('F','R','L',' ')}, /* Friulian */ +/*{"fuv", HB_TAG('F','U','V',' ')},*/ /* Nigerian Fulfulde */ + {"fy", HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ + {"ga", HB_TAG('I','R','I',' ')}, /* Irish */ + {"gaa", HB_TAG('G','A','D',' ')}, /* Ga */ +/*{"gag", HB_TAG('G','A','G',' ')},*/ /* Gagauz */ + {"gan", HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese Simplified */ + {"gax", HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */ + {"gaz", HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */ + {"gbm", HB_TAG('G','A','W',' ')}, /* Garhwali */ + {"gce", HB_TAG('A','T','H',' ')}, /* Galice -> Athapaskan */ + {"gd", HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {"gda", HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */ +/*{"gez", HB_TAG('G','E','Z',' ')},*/ /* Geez */ + {"ggo", HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ +/*{"gih", HB_TAG('G','I','H',' ')},*/ /* Githabul */ + {"gil", HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ + {"gju", HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */ +/*{"gkp", HB_TAG('G','K','P',' ')},*/ /* Guinea Kpelle -> Kpelle (Guinea) */ + {"gl", HB_TAG('G','A','L',' ')}, /* Galician */ + {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */ +/*{"glk", HB_TAG('G','L','K',' ')},*/ /* Gilaki */ + {"gn", HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ +/*{"gnn", HB_TAG('G','N','N',' ')},*/ /* Gumatj */ + {"gno", HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */ + {"gnw", HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */ +/*{"gog", HB_TAG('G','O','G',' ')},*/ /* Gogo */ + {"gom", HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */ +/*{"gon", HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */ + {"grt", HB_TAG('G','R','O',' ')}, /* Garo */ + {"gru", HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */ + {"gsw", HB_TAG('A','L','S',' ')}, /* Alsatian */ + {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */ +/*{"guc", HB_TAG('G','U','C',' ')},*/ /* Wayuu */ +/*{"guf", HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */ + {"gug", HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */ + {"gui", HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */ + {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */ + {"guk", HB_TAG('G','U','K',' ')}, /* Gumuz (SIL fonts) */ + {"gun", HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */ +/*{"guz", HB_TAG('G','U','Z',' ')},*/ /* Gusii */ + {"gv", HB_TAG('M','N','X',' ')}, /* Manx */ + {"gwi", HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ + {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */ + {"haa", HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {"hae", HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ + {"hak", HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese Simplified */ + {"har", HB_TAG('H','R','I',' ')}, /* Harari */ +/*{"haw", HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ +/*{"hay", HB_TAG('H','A','Y',' ')},*/ /* Haya */ +/*{"haz", HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ + {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */ + {"hea", HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ + {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */ +/*{"hil", HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ + {"hji", HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ + {"hlt", HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ + {"hma", HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ + {"hmc", HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ + {"hmd", HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ + {"hme", HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ + {"hmg", HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ + {"hmh", HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */ + {"hmi", HB_TAG('H','M','N',' ')}, /* Northern Huishui Hmong -> Hmong */ + {"hmj", HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */ + {"hml", HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */ + {"hmm", HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */ +/*{"hmn", HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */ + {"hmp", HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */ + {"hmq", HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */ + {"hms", HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ + {"hmw", HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ + {"hmy", HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ + {"hmz", HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ +/*{"hnd", HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ + {"hne", HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ + {"hnj", HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ + {"hno", HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ + {"ho", HB_TAG('H','M','O',' ')}, /* Hiri Motu */ + {"hoc", HB_TAG('H','O',' ',' ')}, /* Ho */ + {"hoi", HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ + {"hoj", HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */ + {"hr", HB_TAG('H','R','V',' ')}, /* Croatian */ + {"hrm", HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */ + {"hsb", HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ + {"hsn", HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese Simplified */ + {"ht", HB_TAG('H','A','I',' ')}, /* Haitian (Haitian Creole) */ + {"hu", HB_TAG('H','U','N',' ')}, /* Hungarian */ + {"huj", HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ + {"hup", HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ + {"hy", HB_TAG('H','Y','E','0')}, /* Armenian -> Armenian East */ + {"hy", HB_TAG('H','Y','E',' ')}, /* Armenian */ + {"hyw", HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ + {"hz", HB_TAG('H','E','R',' ')}, /* Herero */ + {"ia", HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */ +/*{"iba", HB_TAG('I','B','A',' ')},*/ /* Iban */ +/*{"ibb", HB_TAG('I','B','B',' ')},*/ /* Ibibio */ + {"id", HB_TAG('I','N','D',' ')}, /* Indonesian */ + {"ida", HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */ + {"ie", HB_TAG('I','L','E',' ')}, /* Interlingue */ + {"ig", HB_TAG('I','B','O',' ')}, /* Igbo */ + {"igb", HB_TAG('E','B','I',' ')}, /* Ebira */ + {"ii", HB_TAG('Y','I','M',' ')}, /* Sichuan Yi -> Yi Modern */ + {"ijc", HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */ +/*{"ijo", HB_TAG('I','J','O',' ')},*/ /* Ijo [family] */ + {"ik", HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */ + {"ike", HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */ + {"ikt", HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */ +/*{"ilo", HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */ + {"in", HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */ + {"ing", HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */ + {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */ + {"io", HB_TAG('I','D','O',' ')}, /* Ido */ + {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */ + {"it", HB_TAG('I','T','A',' ')}, /* Italian */ + {"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */ + {"iw", HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */ + {"ja", HB_TAG('J','A','N',' ')}, /* Japanese */ + {"jak", HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */ +/*{"jam", HB_TAG('J','A','M',' ')},*/ /* Jamaican Creole English -> Jamaican Creole */ + {"jax", HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */ +/*{"jbo", HB_TAG('J','B','O',' ')},*/ /* Lojban */ +/*{"jct", HB_TAG('J','C','T',' ')},*/ /* Krymchak */ + {"ji", HB_TAG('J','I','I',' ')}, /* Yiddish (retired code) */ + {"jv", HB_TAG('J','A','V',' ')}, /* Javanese */ + {"jw", HB_TAG('J','A','V',' ')}, /* Javanese (retired code) */ + {"ka", HB_TAG('K','A','T',' ')}, /* Georgian */ + {"kaa", HB_TAG('K','R','K',' ')}, /* Karakalpak */ + {"kab", HB_TAG('K','A','B','0')}, /* Kabyle */ + {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */ + {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */ + {"kby", HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ + {"kca", HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ + {"kca", HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */ + {"kca", HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */ +/*{"kde", HB_TAG('K','D','E',' ')},*/ /* Makonde */ + {"kdr", HB_TAG('K','R','M',' ')}, /* Karaim */ + {"kdt", HB_TAG('K','U','Y',' ')}, /* Kuy */ +/*{"kea", HB_TAG('K','E','A',' ')},*/ /* Kabuverdianu (Crioulo) */ +/*{"kek", HB_TAG('K','E','K',' ')},*/ /* Kekchi */ + {"kex", HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */ + {"kfa", HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */ + {"kfr", HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */ + {"kfx", HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ + {"kfy", HB_TAG('K','M','N',' ')}, /* Kumaoni */ + {"kg", HB_TAG('K','O','N','0')}, /* Kongo [macrolanguage] */ + {"kha", HB_TAG('K','S','I',' ')}, /* Khasi */ + {"khb", HB_TAG('X','B','D',' ')}, /* Lü */ + {"khk", HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ + {"kht", HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */ + {"kht", HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */ +/*{"khw", HB_TAG('K','H','W',' ')},*/ /* Khowar */ + {"ki", HB_TAG('K','I','K',' ')}, /* Kikuyu (Gikuyu) */ +/*{"kiu", HB_TAG('K','I','U',' ')},*/ /* Kirmanjki */ + {"kj", HB_TAG('K','U','A',' ')}, /* Kuanyama */ +/*{"kjd", HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ + {"kjh", HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ +/*{"kjp", HB_TAG('K','J','P',' ')},*/ /* Pwo Eastern Karen -> Eastern Pwo Karen */ +/*{"kjz", HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */ + {"kk", HB_TAG('K','A','Z',' ')}, /* Kazakh */ + {"kkz", HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */ + {"kl", HB_TAG('G','R','N',' ')}, /* Greenlandic */ + {"kln", HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ + {"km", HB_TAG('K','H','M',' ')}, /* Khmer */ + {"kmb", HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ + {"kmr", HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ + {"kmw", HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ +/*{"kmz", HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */ + {"kn", HB_TAG('K','A','N',' ')}, /* Kannada */ + {"knc", HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */ + {"kng", HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */ + {"knn", HB_TAG('K','O','K',' ')}, /* Konkani */ + {"ko", HB_TAG('K','O','R',' ')}, /* Korean */ + {"koi", HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ +/*{"kok", HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */ +/*{"kos", HB_TAG('K','O','S',' ')},*/ /* Kosraean */ + {"koy", HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */ + {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ + {"kpv", HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ + {"kpy", HB_TAG('K','Y','K',' ')}, /* Koryak */ + {"kqs", HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */ + {"kqy", HB_TAG('K','R','T',' ')}, /* Koorete */ + {"kr", HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */ + {"krc", HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */ + {"krc", HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */ +/*{"kri", HB_TAG('K','R','I',' ')},*/ /* Krio */ +/*{"krl", HB_TAG('K','R','L',' ')},*/ /* Karelian */ + {"krt", HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */ + {"kru", HB_TAG('K','U','U',' ')}, /* Kurukh */ + {"ks", HB_TAG('K','S','H',' ')}, /* Kashmiri */ + {"ksh", HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */ + {"kss", HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ +/*{"ksw", HB_TAG('K','S','W',' ')},*/ /* S’gaw Karen */ + {"ktb", HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ + {"ktu", HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ + {"ktw", HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */ + {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */ +/*{"kum", HB_TAG('K','U','M',' ')},*/ /* Kumyk */ + {"kuu", HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */ + {"kv", HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */ + {"kvb", HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ + {"kvr", HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ + {"kw", HB_TAG('C','O','R',' ')}, /* Cornish */ + {"kwy", HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ + {"kxc", HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ + {"kxd", HB_TAG('M','L','Y',' ')}, /* Brunei -> Malay */ + {"kxl", HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */ + {"kxu", HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */ + {"ky", HB_TAG('K','I','R',' ')}, /* Kirghiz (Kyrgyz) */ +/*{"kyu", HB_TAG('K','Y','U',' ')},*/ /* Western Kayah */ + {"la", HB_TAG('L','A','T',' ')}, /* Latin */ + {"lad", HB_TAG('J','U','D',' ')}, /* Ladino */ + {"lb", HB_TAG('L','T','Z',' ')}, /* Luxembourgish */ + {"lbe", HB_TAG('L','A','K',' ')}, /* Lak */ + {"lbj", HB_TAG('L','D','K',' ')}, /* Ladakhi */ + {"lbl", HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */ + {"lce", HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */ + {"lcf", HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ + {"ldi", HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ +/*{"lez", HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ + {"lg", HB_TAG('L','U','G',' ')}, /* Ganda */ + {"li", HB_TAG('L','I','M',' ')}, /* Limburgish */ + {"lif", HB_TAG('L','M','B',' ')}, /* Limbu */ +/*{"lij", HB_TAG('L','I','J',' ')},*/ /* Ligurian */ +/*{"lis", HB_TAG('L','I','S',' ')},*/ /* Lisu */ + {"liw", HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ +/*{"ljp", HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ + {"lkb", HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */ +/*{"lki", HB_TAG('L','K','I',' ')},*/ /* Laki */ + {"lko", HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */ + {"lks", HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */ + {"lld", HB_TAG('L','A','D',' ')}, /* Ladin */ + {"lmn", HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */ +/*{"lmo", HB_TAG('L','M','O',' ')},*/ /* Lombard */ + {"ln", HB_TAG('L','I','N',' ')}, /* Lingala */ + {"lo", HB_TAG('L','A','O',' ')}, /* Lao */ +/*{"lom", HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ +/*{"lrc", HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ + {"lri", HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ + {"lrm", HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ + {"lsm", HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */ + {"lt", HB_TAG('L','T','H',' ')}, /* Lithuanian */ + {"ltg", HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */ + {"lto", HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ + {"lts", HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ + {"lu", HB_TAG('L','U','B',' ')}, /* Luba-Katanga */ +/*{"lua", HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ +/*{"luo", HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ + {"lus", HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ + {"luy", HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ + {"luz", HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ + {"lv", HB_TAG('L','V','I',' ')}, /* Latvian [macrolanguage] */ + {"lvs", HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */ + {"lwg", HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */ + {"lzh", HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese Traditional */ + {"lzz", HB_TAG('L','A','Z',' ')}, /* Laz */ +/*{"mad", HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */ +/*{"mag", HB_TAG('M','A','G',' ')},*/ /* Magahi */ + {"mai", HB_TAG('M','T','H',' ')}, /* Maithili */ + {"mak", HB_TAG('M','K','R',' ')}, /* Makasar */ +/*{"mam", HB_TAG('M','A','M',' ')},*/ /* Mam */ + {"man", HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */ + {"max", HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */ +/*{"mbo", HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */ + {"mct", HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */ + {"mdf", HB_TAG('M','O','K',' ')}, /* Moksha */ +/*{"mdr", HB_TAG('M','D','R',' ')},*/ /* Mandar */ + {"mdy", HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ + {"men", HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ + {"meo", HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ +/*{"mer", HB_TAG('M','E','R',' ')},*/ /* Meru */ +/*{"mfa", HB_TAG('M','F','A',' ')},*/ /* Pattani Malay */ + {"mfb", HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ +/*{"mfe", HB_TAG('M','F','E',' ')},*/ /* Morisyen */ + {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */ + {"mh", HB_TAG('M','A','H',' ')}, /* Marshallese */ + {"mhr", HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ + {"mhv", HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ + {"mi", HB_TAG('M','R','I',' ')}, /* Maori */ +/*{"min", HB_TAG('M','I','N',' ')},*/ /* Minangkabau */ + {"mk", HB_TAG('M','K','D',' ')}, /* Macedonian */ + {"mku", HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */ +/*{"mkw", HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */ + {"ml", HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ + {"ml", HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ + {"mlq", HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */ + {"mlq", HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */ + {"mmr", HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */ + {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ + {"mnc", HB_TAG('M','C','H',' ')}, /* Manchu */ +/*{"mni", HB_TAG('M','N','I',' ')},*/ /* Manipuri */ + {"mnk", HB_TAG('M','N','D',' ')}, /* Mandinka */ + {"mnk", HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */ + {"mnp", HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese Simplified */ + {"mns", HB_TAG('M','A','N',' ')}, /* Mansi */ + {"mnw", HB_TAG('M','O','N',' ')}, /* Mon */ + {"mo", HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ +/*{"moh", HB_TAG('M','O','H',' ')},*/ /* Mohawk */ +/*{"mos", HB_TAG('M','O','S',' ')},*/ /* Mossi */ + {"mpe", HB_TAG('M','A','J',' ')}, /* Majang */ + {"mqg", HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */ + {"mr", HB_TAG('M','A','R',' ')}, /* Marathi */ + {"mrh", HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */ + {"mrj", HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */ + {"ms", HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ + {"msc", HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */ + {"msh", HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */ + {"msi", HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */ + {"mt", HB_TAG('M','T','S',' ')}, /* Maltese */ + {"mtr", HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */ + {"mui", HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */ + {"mup", HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */ + {"muq", HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */ +/*{"mus", HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */ + {"mvb", HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */ + {"mve", HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ + {"mvf", HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */ + {"mwk", HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */ +/*{"mwl", HB_TAG('M','W','L',' ')},*/ /* Mirandese */ + {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ +/*{"mww", HB_TAG('M','W','W',' ')},*/ /* Hmong Daw */ + {"my", HB_TAG('B','R','M',' ')}, /* Burmese */ + {"mym", HB_TAG('M','E','N',' ')}, /* Me’en */ +/*{"myn", HB_TAG('M','Y','N',' ')},*/ /* Mayan [family] */ + {"myq", HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */ + {"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */ +/*{"mzn", HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */ + {"na", HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */ +/*{"nag", HB_TAG('N','A','G',' ')},*/ /* Naga Pidgin -> Naga-Assamese */ +/*{"nah", HB_TAG('N','A','H',' ')},*/ /* Nahuatl [family] */ + {"nan", HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese Simplified */ +/*{"nap", HB_TAG('N','A','P',' ')},*/ /* Neapolitan */ + {"nb", HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål -> Norwegian */ + {"nd", HB_TAG('N','D','B',' ')}, /* North Ndebele -> Ndebele */ +/*{"ndc", HB_TAG('N','D','C',' ')},*/ /* Ndau */ +/*{"nds", HB_TAG('N','D','S',' ')},*/ /* Low Saxon */ + {"ne", HB_TAG('N','E','P',' ')}, /* Nepali [macrolanguage] */ +/*{"new", HB_TAG('N','E','W',' ')},*/ /* Newari */ + {"ng", HB_TAG('N','D','G',' ')}, /* Ndonga */ +/*{"nga", HB_TAG('N','G','A',' ')},*/ /* Ngbaka */ + {"ngl", HB_TAG('L','M','W',' ')}, /* Lomwe */ + {"ngo", HB_TAG('S','X','T',' ')}, /* Ngoni -> Sutu */ + {"nhd", HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */ + {"niq", HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */ +/*{"niu", HB_TAG('N','I','U',' ')},*/ /* Niuean */ + {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */ + {"njz", HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */ + {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */ + {"nle", HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */ + {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + {"no", HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */ + {"nod", HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ +/*{"noe", HB_TAG('N','O','E',' ')},*/ /* Nimadi */ +/*{"nog", HB_TAG('N','O','G',' ')},*/ /* Nogai */ +/*{"nov", HB_TAG('N','O','V',' ')},*/ /* Novial */ + {"npi", HB_TAG('N','E','P',' ')}, /* Nepali */ + {"nqo", HB_TAG('N','K','O',' ')}, /* N’Ko */ + {"nr", HB_TAG('N','D','B',' ')}, /* South Ndebele -> Ndebele */ + {"nsk", HB_TAG('N','A','S',' ')}, /* Naskapi */ +/*{"nso", HB_TAG('N','S','O',' ')},*/ /* Pedi -> Sotho, Northern */ + {"nv", HB_TAG('N','A','V',' ')}, /* Navajo */ + {"nv", HB_TAG('A','T','H',' ')}, /* Navajo -> Athapaskan */ + {"ny", HB_TAG('C','H','I',' ')}, /* Chichewa (Chewa, Nyanja) */ + {"nyd", HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */ +/*{"nym", HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */ + {"nyn", HB_TAG('N','K','L',' ')}, /* Nyankole */ +/*{"nza", HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */ + {"oc", HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ + {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ +/*{"ojb", HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */ + {"ojc", HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */ + {"ojg", HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */ + {"ojs", HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */ + {"ojw", HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */ + {"oki", HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ + {"okm", HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ + {"om", HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ + {"or", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {"orc", HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ + {"orn", HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */ + {"ors", HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ + {"ory", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {"os", HB_TAG('O','S','S',' ')}, /* Ossetian */ + {"otw", HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ + {"pa", HB_TAG('P','A','N',' ')}, /* Punjabi */ +/*{"pag", HB_TAG('P','A','G',' ')},*/ /* Pangasinan */ +/*{"pam", HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */ + {"pap", HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */ +/*{"pau", HB_TAG('P','A','U',' ')},*/ /* Palauan */ + {"pbt", HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */ + {"pbu", HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */ +/*{"pcc", HB_TAG('P','C','C',' ')},*/ /* Bouyei */ +/*{"pcd", HB_TAG('P','C','D',' ')},*/ /* Picard */ + {"pce", HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */ + {"pck", HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */ +/*{"pdc", HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */ + {"pel", HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */ + {"pes", HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */ + {"pga", HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */ +/*{"phk", HB_TAG('P','H','K',' ')},*/ /* Phake */ + {"pi", HB_TAG('P','A','L',' ')}, /* Pali */ +/*{"pih", HB_TAG('P','I','H',' ')},*/ /* Pitcairn-Norfolk -> Norfolk */ + {"pko", HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ + {"pl", HB_TAG('P','L','K',' ')}, /* Polish */ + {"pll", HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ + {"plp", HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */ + {"plt", HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */ +/*{"pms", HB_TAG('P','M','S',' ')},*/ /* Piemontese */ +/*{"pnb", HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */ +/*{"poh", HB_TAG('P','O','H',' ')},*/ /* Poqomchi' -> Pocomchi */ +/*{"pon", HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */ + {"ppa", HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */ +/*{"pro", HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */ + {"prs", HB_TAG('D','R','I',' ')}, /* Dari */ + {"ps", HB_TAG('P','A','S',' ')}, /* Pashto [macrolanguage] */ + {"pse", HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */ + {"pst", HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */ + {"pt", HB_TAG('P','T','G',' ')}, /* Portuguese */ +/*{"pwo", HB_TAG('P','W','O',' ')},*/ /* Pwo Western Karen -> Western Pwo Karen */ + {"qu", HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */ + {"qub", HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ +/*{"quc", HB_TAG('Q','U','C',' ')},*/ /* K’iche’ */ + {"qud", HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ + {"quf", HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */ + {"qug", HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ +/*{"quh", HB_TAG('Q','U','H',' ')},*/ /* South Bolivian Quechua -> Quechua (Bolivia) */ + {"quk", HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */ + {"qul", HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */ + {"qup", HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + {"qur", HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + {"qus", HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + {"quw", HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ + {"qux", HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */ + {"quy", HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */ +/*{"quz", HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */ + {"qva", HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ + {"qvc", HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */ + {"qve", HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */ + {"qvh", HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ +/*{"qvi", HB_TAG('Q','V','I',' ')},*/ /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + {"qvj", HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */ + {"qvl", HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + {"qvm", HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + {"qvn", HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */ + {"qvo", HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ + {"qvp", HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */ + {"qvs", HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */ + {"qvw", HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ + {"qvz", HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + {"qwa", HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */ + {"qwc", HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */ +/*{"qwh", HB_TAG('Q','W','H',' ')},*/ /* Huaylas Ancash Quechua -> Quechua (Peru) */ + {"qws", HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ + {"qxa", HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ + {"qxc", HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */ + {"qxh", HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */ + {"qxl", HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + {"qxn", HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + {"qxo", HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + {"qxp", HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */ + {"qxr", HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ + {"qxt", HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + {"qxu", HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */ + {"qxw", HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */ + {"rag", HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */ +/*{"raj", HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */ +/*{"rar", HB_TAG('R','A','R',' ')},*/ /* Rarotongan */ + {"rbb", HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */ + {"rbl", HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ +/*{"rej", HB_TAG('R','E','J',' ')},*/ /* Rejang */ +/*{"ria", HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ +/*{"rif", HB_TAG('R','I','F',' ')},*/ /* Tarifit */ +/*{"rit", HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */ + {"rki", HB_TAG('A','R','K',' ')}, /* Rakhine */ +/*{"rkw", HB_TAG('R','K','W',' ')},*/ /* Arakwal */ + {"rm", HB_TAG('R','M','S',' ')}, /* Romansh */ + {"rmc", HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */ + {"rmf", HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */ + {"rml", HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */ + {"rmn", HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */ + {"rmo", HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */ + {"rmw", HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */ +/*{"rmy", HB_TAG('R','M','Y',' ')},*/ /* Vlax Romani */ + {"rmz", HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */ + {"rn", HB_TAG('R','U','N',' ')}, /* Rundi */ + {"rnl", HB_TAG('H','A','L',' ')}, /* Ranglong -> Halam (Falam Chin) */ + {"ro", HB_TAG('R','O','M',' ')}, /* Romanian */ + {"rom", HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ +/*{"rtm", HB_TAG('R','T','M',' ')},*/ /* Rotuman */ + {"ru", HB_TAG('R','U','S',' ')}, /* Russian */ + {"rue", HB_TAG('R','S','Y',' ')}, /* Rusyn */ +/*{"rup", HB_TAG('R','U','P',' ')},*/ /* Aromanian */ + {"rw", HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ + {"rwr", HB_TAG('M','A','W',' ')}, /* Marwari (India) */ + {"sa", HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {"sah", HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */ + {"sam", HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */ +/*{"sas", HB_TAG('S','A','S',' ')},*/ /* Sasak */ +/*{"sat", HB_TAG('S','A','T',' ')},*/ /* Santali */ + {"sc", HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */ +/*{"scn", HB_TAG('S','C','N',' ')},*/ /* Sicilian */ +/*{"sco", HB_TAG('S','C','O',' ')},*/ /* Scots */ + {"scs", HB_TAG('S','C','S',' ')}, /* North Slavey */ + {"scs", HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */ + {"scs", HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */ + {"sd", HB_TAG('S','N','D',' ')}, /* Sindhi */ + {"sdc", HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */ + {"sdh", HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ + {"sdn", HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ + {"se", HB_TAG('N','S','M',' ')}, /* Northern Sami */ + {"seh", HB_TAG('S','N','A',' ')}, /* Sena */ + {"sek", HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ +/*{"sel", HB_TAG('S','E','L',' ')},*/ /* Selkup */ + {"sez", HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ + {"sfm", HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ + {"sg", HB_TAG('S','G','O',' ')}, /* Sango */ +/*{"sga", HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ + {"sgc", HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */ +/*{"sgs", HB_TAG('S','G','S',' ')},*/ /* Samogitian */ + {"sgw", HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */ + {"sgw", HB_TAG('S','G','W',' ')}, /* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */ +/*{"shi", HB_TAG('S','H','I',' ')},*/ /* Tachelhit */ +/*{"shn", HB_TAG('S','H','N',' ')},*/ /* Shan */ + {"shu", HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */ + {"si", HB_TAG('S','N','H',' ')}, /* Sinhala (Sinhalese) */ +/*{"sid", HB_TAG('S','I','D',' ')},*/ /* Sidamo */ + {"sjd", HB_TAG('K','S','M',' ')}, /* Kildin Sami */ + {"sjo", HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ + {"sk", HB_TAG('S','K','Y',' ')}, /* Slovak */ + {"skg", HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ + {"skr", HB_TAG('S','R','K',' ')}, /* Saraiki */ + {"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */ + {"sm", HB_TAG('S','M','O',' ')}, /* Samoan */ + {"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */ + {"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */ + {"smn", HB_TAG('I','S','M',' ')}, /* Inari Sami */ + {"sms", HB_TAG('S','K','S',' ')}, /* Skolt Sami */ + {"sn", HB_TAG('S','N','A','0')}, /* Shona */ +/*{"snk", HB_TAG('S','N','K',' ')},*/ /* Soninke */ + {"so", HB_TAG('S','M','L',' ')}, /* Somali */ +/*{"sop", HB_TAG('S','O','P',' ')},*/ /* Songe */ + {"spv", HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {"spy", HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ + {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */ + {"sr", HB_TAG('S','R','B',' ')}, /* Serbian */ + {"src", HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ + {"sro", HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */ +/*{"srr", HB_TAG('S','R','R',' ')},*/ /* Serer */ + {"srs", HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */ + {"ss", HB_TAG('S','W','Z',' ')}, /* Swati */ + {"ssh", HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */ + {"st", HB_TAG('S','O','T',' ')}, /* Southern Sotho -> Sotho, Southern */ +/*{"stq", HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ + {"stv", HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ + {"su", HB_TAG('S','U','N',' ')}, /* Sundanese */ +/*{"suk", HB_TAG('S','U','K',' ')},*/ /* Sukuma */ + {"suq", HB_TAG('S','U','R',' ')}, /* Suri */ + {"sv", HB_TAG('S','V','E',' ')}, /* Swedish */ +/*{"sva", HB_TAG('S','V','A',' ')},*/ /* Svan */ + {"sw", HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ + {"swb", HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */ + {"swc", HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */ + {"swh", HB_TAG('S','W','K',' ')}, /* Swahili */ + {"swv", HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */ +/*{"sxu", HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */ + {"syc", HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */ +/*{"syl", HB_TAG('S','Y','L',' ')},*/ /* Sylheti */ +/*{"syr", HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */ +/*{"szl", HB_TAG('S','Z','L',' ')},*/ /* Silesian */ + {"ta", HB_TAG('T','A','M',' ')}, /* Tamil */ + {"taa", HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ +/*{"tab", HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ + {"taq", HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ + {"tau", HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ + {"tcb", HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ + {"tce", HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ + {"tcp", HB_TAG('Q','I','N',' ')}, /* Tawr Chin -> Chin */ + {"tcy", HB_TAG('T','U','L',' ')}, /* Tulu -> Tumbuka */ + {"tcz", HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ +/*{"tdd", HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ + {"tdx", HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ + {"te", HB_TAG('T','E','L',' ')}, /* Telugu */ + {"tec", HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ + {"tem", HB_TAG('T','M','N',' ')}, /* Timne -> Temne */ +/*{"tet", HB_TAG('T','E','T',' ')},*/ /* Tetum */ + {"tfn", HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */ + {"tg", HB_TAG('T','A','J',' ')}, /* Tajik -> Tajiki */ + {"tgj", HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */ + {"tgx", HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ + {"th", HB_TAG('T','H','A',' ')}, /* Thai */ + {"tht", HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ + {"thv", HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ + {"thz", HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ + {"ti", HB_TAG('T','G','Y',' ')}, /* Tigrinya */ + {"tig", HB_TAG('T','G','R',' ')}, /* Tigre */ +/*{"tiv", HB_TAG('T','I','V',' ')},*/ /* Tiv */ + {"tk", HB_TAG('T','K','M',' ')}, /* Turkmen */ + {"tkg", HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ + {"tl", HB_TAG('T','G','L',' ')}, /* Tagalog */ +/*{"tmh", HB_TAG('T','M','H',' ')},*/ /* Tamashek [macrolanguage] */ + {"tmw", HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */ + {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */ + {"tnf", HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */ + {"to", HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) -> Tongan */ + {"tod", HB_TAG('T','O','D','0')}, /* Toma */ + {"toi", HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ + {"tol", HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */ +/*{"tpi", HB_TAG('T','P','I',' ')},*/ /* Tok Pisin */ + {"tr", HB_TAG('T','R','K',' ')}, /* Turkish */ + {"tru", HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */ + {"tru", HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */ + {"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */ +/*{"tsj", HB_TAG('T','S','J',' ')},*/ /* Tshangla */ + {"tt", HB_TAG('T','A','T',' ')}, /* Tatar */ + {"ttm", HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ + {"ttq", HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ +/*{"tum", HB_TAG('T','U','M',' ')},*/ /* Tumbuka -> Tulu */ + {"tuu", HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ + {"tuy", HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ +/*{"tvl", HB_TAG('T','V','L',' ')},*/ /* Tuvalu */ + {"tw", HB_TAG('T','W','I',' ')}, /* Twi */ + {"tw", HB_TAG('A','K','A',' ')}, /* Twi -> Akan */ + {"txc", HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */ + {"txy", HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */ + {"ty", HB_TAG('T','H','T',' ')}, /* Tahitian */ + {"tyv", HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */ +/*{"tyz", HB_TAG('T','Y','Z',' ')},*/ /* Tày */ +/*{"tzm", HB_TAG('T','Z','M',' ')},*/ /* Central Atlas Tamazight -> Tamazight */ +/*{"tzo", HB_TAG('T','Z','O',' ')},*/ /* Tzotzil */ + {"ubl", HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ +/*{"udm", HB_TAG('U','D','M',' ')},*/ /* Udmurt */ + {"ug", HB_TAG('U','Y','G',' ')}, /* Uyghur */ + {"uk", HB_TAG('U','K','R',' ')}, /* Ukrainian */ + {"uki", HB_TAG('K','U','I',' ')}, /* Kui (India) */ +/*{"umb", HB_TAG('U','M','B',' ')},*/ /* Umbundu */ + {"unr", HB_TAG('M','U','N',' ')}, /* Mundari */ + {"ur", HB_TAG('U','R','D',' ')}, /* Urdu */ + {"urk", HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */ + {"uz", HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */ + {"uzn", HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */ + {"uzs", HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */ + {"ve", HB_TAG('V','E','N',' ')}, /* Venda */ +/*{"vec", HB_TAG('V','E','C',' ')},*/ /* Venetian */ + {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {"vkk", HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */ + {"vkt", HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ + {"vls", HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ + {"vmw", HB_TAG('M','A','K',' ')}, /* Makhuwa */ + {"vo", HB_TAG('V','O','L',' ')}, /* Volapük */ +/*{"vro", HB_TAG('V','R','O',' ')},*/ /* Võro */ + {"wa", HB_TAG('W','L','N',' ')}, /* Walloon */ +/*{"war", HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ + {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */ + {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */ + {"wlc", HB_TAG('C','M','R',' ')}, /* Mwali Comorian -> Comorian */ + {"wle", HB_TAG('S','I','G',' ')}, /* Wolane -> Silte Gurage */ + {"wlk", HB_TAG('A','T','H',' ')}, /* Wailaki -> Athapaskan */ + {"wni", HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */ + {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */ + {"wry", HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */ + {"wsg", HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ +/*{"wtm", HB_TAG('W','T','M',' ')},*/ /* Mewati */ + {"wuu", HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese Simplified */ + {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */ + {"xal", HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ + {"xan", HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ + {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */ +/*{"xjb", HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */ +/*{"xkf", HB_TAG('X','K','F',' ')},*/ /* Khengkha */ + {"xmm", HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */ + {"xmv", HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */ + {"xmw", HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */ + {"xnr", HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri */ +/*{"xog", HB_TAG('X','O','G',' ')},*/ /* Soga */ +/*{"xpe", HB_TAG('X','P','E',' ')},*/ /* Liberia Kpelle -> Kpelle (Liberia) */ + {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */ + {"xsl", HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ + {"xsl", HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ + {"xst", HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ + {"xwo", HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ +/*{"yao", HB_TAG('Y','A','O',' ')},*/ /* Yao */ +/*{"yap", HB_TAG('Y','A','P',' ')},*/ /* Yapese */ + {"ybd", HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ + {"ydd", HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ + {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ + {"yih", HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ + {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */ + {"yos", HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ + {"yrk", HB_TAG('T','N','E',' ')}, /* Nenets -> Tundra Nenets */ + {"yrk", HB_TAG('F','N','E',' ')}, /* Nenets -> Forest Nenets */ + {"yue", HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Hong Kong SAR */ + {"za", HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */ + {"zch", HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ + {"zdj", HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ +/*{"zea", HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */ + {"zeh", HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */ + {"zgb", HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */ +/*{"zgh", HB_TAG('Z','G','H',' ')},*/ /* Standard Moroccan Tamazight */ + {"zgm", HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */ + {"zgn", HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */ + {"zh", HB_TAG('Z','H','S',' ')}, /* Chinese [macrolanguage] -> Chinese Simplified */ + {"zhd", HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */ + {"zhn", HB_TAG('Z','H','A',' ')}, /* Nong Zhuang -> Zhuang */ + {"zlj", HB_TAG('Z','H','A',' ')}, /* Liujiang Zhuang -> Zhuang */ + {"zlm", HB_TAG('M','L','Y',' ')}, /* Malay */ + {"zln", HB_TAG('Z','H','A',' ')}, /* Lianshan Zhuang -> Zhuang */ + {"zlq", HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */ + {"zmi", HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */ + {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */ + {"zom", HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */ + {"zqe", HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */ + {"zsm", HB_TAG('M','L','Y',' ')}, /* Standard Malay -> Malay */ + {"zu", HB_TAG('Z','U','L',' ')}, /* Zulu */ + {"zum", HB_TAG('L','R','C',' ')}, /* Kumzari -> Luri */ + {"zyb", HB_TAG('Z','H','A',' ')}, /* Yongbei Zhuang -> Zhuang */ + {"zyg", HB_TAG('Z','H','A',' ')}, /* Yang Zhuang -> Zhuang */ + {"zyj", HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */ + {"zyn", HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */ +/*{"zza", HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */ + {"zzj", HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */ }; -static_assert (HB_OT_MAX_TAGS_PER_LANGUAGE == 3u, ""); - /** * hb_ot_tags_from_complex_language: * @lang_str: a BCP 47 language tag to convert. @@ -1185,6 +1188,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "np-hant-hk")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "np-hant-mo")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "px-hant-hk")) { /* Pu-Xian Chinese */ @@ -1199,6 +1216,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "sp-hant-hk")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "sp-hant-mo")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "zh-hant-hk")) { /* Huizhou Chinese */ @@ -1269,6 +1300,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "np-hans")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "np-hant")) + { + /* Northern Ping Chinese */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "px-hans")) { /* Pu-Xian Chinese */ @@ -1283,6 +1328,20 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (lang_matches (&lang_str[1], "sp-hans")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], "sp-hant")) + { + /* Southern Ping Chinese */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (lang_matches (&lang_str[1], "zh-hans")) { /* Huizhou Chinese */ @@ -1383,6 +1442,30 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk")) + { + /* Northern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo")) + { + /* Northern Ping Chinese; Macao */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw")) + { + /* Northern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (0 == strncmp (&lang_str[1], "px-", 3) && subtag_matches (lang_str, limit, "-hk")) { @@ -1407,6 +1490,30 @@ hb_ot_tags_from_complex_language (const char *lang_str, *count = 1; return true; } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-hk")) + { + /* Southern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-mo")) + { + /* Southern Ping Chinese; Macao */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-tw")) + { + /* Southern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */ + *count = 1; + return true; + } if (0 == strncmp (&lang_str[1], "zh-", 3) && subtag_matches (lang_str, limit, "-hk")) { @@ -1934,7 +2041,8 @@ hb_ot_tags_from_complex_language (const char *lang_str, * * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to * many language tags) and the best tag is not the alphabetically first, or if - * the best tag consists of multiple subtags. + * the best tag consists of multiple subtags, or if the best tag does not appear + * in #ot_languages. * * Return value: The #hb_language_t corresponding to the BCP 47 language tag, * or #HB_LANGUAGE_INVALID if @tag is not ambiguous. @@ -1944,6 +2052,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) { switch (tag) { + case HB_TAG('A','L','T',' '): /* Altai */ + return hb_language_from_string ("alt", -1); /* Southern Altai */ case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ return hb_language_from_string ("und-fonnapa", -1); /* Undetermined; North American Phonetic Alphabet */ case HB_TAG('A','R','A',' '): /* Arabic */ @@ -1962,8 +2072,6 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("din", -1); /* Dinka */ case HB_TAG('D','R','I',' '): /* Dari */ return hb_language_from_string ("prs", -1); /* Dari */ - case HB_TAG('D','U','J',' '): /* Dhuwal */ - return hb_language_from_string ("dwu", -1); /* Dhuwal */ case HB_TAG('D','Z','N',' '): /* Dzongkha */ return hb_language_from_string ("dz", -1); /* Dzongkha */ case HB_TAG('E','T','I',' '): /* Estonian */ @@ -1972,6 +2080,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("gon", -1); /* Gondi */ case HB_TAG('H','M','N',' '): /* Hmong */ return hb_language_from_string ("hmn", -1); /* Hmong */ + case HB_TAG('H','N','D',' '): /* Hindko */ + return hb_language_from_string ("hnd", -1); /* Southern Hindko */ case HB_TAG('I','J','O',' '): /* Ijo */ return hb_language_from_string ("ijo", -1); /* Ijo */ case HB_TAG('I','N','U',' '): /* Inuktitut */ @@ -1992,6 +2102,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) return hb_language_from_string ("kr", -1); /* Kanuri */ case HB_TAG('K','O','K',' '): /* Konkani */ return hb_language_from_string ("kok", -1); /* Konkani */ + case HB_TAG('K','U','I',' '): /* Kui */ + return hb_language_from_string ("uki", -1); /* Kui (India) */ case HB_TAG('K','U','R',' '): /* Kurdish */ return hb_language_from_string ("ku", -1); /* Kurdish */ case HB_TAG('L','U','H',' '): /* Luyia */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc index 18b5686b5c863..36ff854e3edda 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-tag.cc @@ -28,6 +28,8 @@ #include "hb.hh" +#ifndef HB_NO_OT_TAG + /* hb_script_t */ @@ -113,6 +115,7 @@ hb_ot_new_tag_to_script (hb_tag_t tag) return HB_SCRIPT_UNKNOWN; } +#ifndef HB_DISABLE_DEPRECATED void hb_ot_tags_from_script (hb_script_t script, hb_tag_t *script_tag_1, @@ -124,6 +127,7 @@ hb_ot_tags_from_script (hb_script_t script, *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT; *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT; } +#endif /* * Complete list at: @@ -143,7 +147,9 @@ hb_ot_all_tags_from_script (hb_script_t script, hb_tag_t new_tag = hb_ot_new_tag_from_script (script); if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) { - tags[i++] = new_tag | '3'; + /* HB_SCRIPT_MYANMAR maps to 'mym2', but there is no 'mym3'. */ + if (new_tag != HB_TAG('m','y','m','2')) + tags[i++] = new_tag | '3'; if (*count > i) tags[i++] = new_tag; } @@ -171,24 +177,6 @@ hb_ot_tag_to_script (hb_tag_t tag) /* hb_language_t */ -static int -lang_compare_first_component (const void *pa, - const void *pb) -{ - const char *a = (const char *) pa; - const char *b = (const char *) pb; - unsigned int da, db; - const char *p; - - p = strchr (a, '-'); - da = p ? (unsigned int) (p - a) : strlen (a); - - p = strchr (b, '-'); - db = p ? (unsigned int) (p - b) : strlen (b); - - return strncmp (a, b, MAX (da, db)); -} - static bool subtag_matches (const char *lang_str, const char *limit, @@ -213,10 +201,28 @@ lang_matches (const char *lang_str, const char *spec) (lang_str[len] == '\0' || lang_str[len] == '-'); } -typedef struct { +struct LangTag +{ char language[4]; - hb_tag_t tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; -} LangTag; + hb_tag_t tag; + + int cmp (const char *a) const + { + const char *b = this->language; + unsigned int da, db; + const char *p; + + p = strchr (a, '-'); + da = p ? (unsigned int) (p - a) : strlen (a); + + p = strchr (b, '-'); + db = p ? (unsigned int) (p - b) : strlen (b); + + return strncmp (a, b, hb_max (da, db)); + } + int cmp (const LangTag *that) const + { return cmp (that->language); } +}; #include "hb-ot-tag-table.hh" @@ -230,6 +236,7 @@ typedef struct { /*{"??", {HB_TAG('Y','I','C',' ')}},*/ /* Yi Classic */ /*{"zh?", {HB_TAG('Z','H','P',' ')}},*/ /* Chinese Phonetic */ +#ifndef HB_DISABLE_DEPRECATED hb_tag_t hb_ot_tag_from_language (hb_language_t language) { @@ -238,6 +245,7 @@ hb_ot_tag_from_language (hb_language_t language) hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE; } +#endif static void hb_ot_tags_from_language (const char *lang_str, @@ -246,6 +254,7 @@ hb_ot_tags_from_language (const char *lang_str, hb_tag_t *tags) { const char *s; + unsigned int tag_idx; /* Check for matches of multiple subtags. */ if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags)) @@ -254,7 +263,6 @@ hb_ot_tags_from_language (const char *lang_str, /* Find a language matching in the first component. */ s = strchr (lang_str, '-'); { - const LangTag *lang_tag; if (s && limit - lang_str >= 6) { const char *extlang_end = strchr (s + 1, '-'); @@ -263,14 +271,18 @@ hb_ot_tags_from_language (const char *lang_str, ISALPHA (s[1])) lang_str = s + 1; } - lang_tag = (LangTag *) bsearch (lang_str, ot_languages, - ARRAY_LENGTH (ot_languages), sizeof (LangTag), - lang_compare_first_component); - if (lang_tag) + if (hb_sorted_array (ot_languages).bfind (lang_str, &tag_idx)) { unsigned int i; - for (i = 0; i < *count && lang_tag->tags[i] != HB_TAG_NONE; i++) - tags[i] = lang_tag->tags[i]; + while (tag_idx != 0 && + 0 == strcmp (ot_languages[tag_idx].language, ot_languages[tag_idx - 1].language)) + tag_idx--; + for (i = 0; + i < *count && + tag_idx + i < ARRAY_LENGTH (ot_languages) && + 0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language); + i++) + tags[i] = ot_languages[tag_idx + i].tag; *count = i; return; } @@ -295,28 +307,42 @@ parse_private_use_subtag (const char *private_use_subtag, const char *prefix, unsigned char (*normalize) (unsigned char)) { - if (private_use_subtag && count && tags && *count) - { - const char *s = strstr (private_use_subtag, prefix); - if (s) +#ifdef HB_NO_LANGUAGE_PRIVATE_SUBTAG + return false; +#endif + + if (!(private_use_subtag && count && tags && *count)) return false; + + const char *s = strstr (private_use_subtag, prefix); + if (!s) return false; + + char tag[4]; + int i; + s += strlen (prefix); + if (s[0] == '-') { + s += 1; + char c; + for (i = 0; i < 8 && ISHEX (s[i]); i++) { - char tag[4]; - int i; - s += strlen (prefix); - for (i = 0; i < 4 && ISALNUM (s[i]); i++) - tag[i] = normalize (s[i]); - if (i) - { - for (; i < 4; i++) - tag[i] = ' '; - tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); - if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) - tags[0] ^= ~0xDFDFDFDF; - *count = 1; - return false; - } + c = FROMHEX (s[i]); + if (i % 2 == 0) + tag[i / 2] = c << 4; + else + tag[i / 2] += c; } + if (i != 8) return false; + } else { + for (i = 0; i < 4 && ISALNUM (s[i]); i++) + tag[i] = normalize (s[i]); + if (!i) return false; + + for (; i < 4; i++) + tag[i] = ' '; } + tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); + if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) + tags[0] ^= ~0xDFDFDFDF; + *count = 1; return true; } @@ -384,8 +410,8 @@ hb_ot_tags_from_script_and_language (hb_script_t script, limit = s; } - needs_script = parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); - needs_language = parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); + needs_script = !parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); + needs_language = !parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); if (needs_language && language_count && language_tags && *language_count) hb_ot_tags_from_language (lang_str, limit, language_count, language_tags); @@ -419,20 +445,31 @@ hb_ot_tag_to_language (hb_tag_t tag) } for (i = 0; i < ARRAY_LENGTH (ot_languages); i++) - if (ot_languages[i].tags[0] == tag) + if (ot_languages[i].tag == tag) return hb_language_from_string (ot_languages[i].language, -1); - /* Else return a custom language in the form of "x-hbotABCD" */ + /* Return a custom language in the form of "x-hbot-AABBCCDD". + * If it's three letters long, also guess it's ISO 639-3 and lower-case and + * prepend it (if it's not a registered tag, the private use subtags will + * ensure that calling hb_ot_tag_from_language on the result will still return + * the same tag as the original tag). + */ { - unsigned char buf[11] = "x-hbot"; - buf[6] = tag >> 24; - buf[7] = (tag >> 16) & 0xFF; - buf[8] = (tag >> 8) & 0xFF; - buf[9] = tag & 0xFF; - if (buf[9] == 0x20) - buf[9] = '\0'; - buf[10] = '\0'; - return hb_language_from_string ((char *) buf, -1); + char buf[20]; + char *str = buf; + if (ISALPHA (tag >> 24) + && ISALPHA ((tag >> 16) & 0xFF) + && ISALPHA ((tag >> 8) & 0xFF) + && (tag & 0xFF) == ' ') + { + buf[0] = TOLOWER (tag >> 24); + buf[1] = TOLOWER ((tag >> 16) & 0xFF); + buf[2] = TOLOWER ((tag >> 8) & 0xFF); + buf[3] = '-'; + str += 4; + } + snprintf (str, 16, "x-hbot-%08x", tag); + return hb_language_from_string (&*buf, -1); } } @@ -473,13 +510,14 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, unsigned char *buf; const char *lang_str = hb_language_to_string (*language); size_t len = strlen (lang_str); - buf = (unsigned char *) malloc (len + 11); + buf = (unsigned char *) malloc (len + 16); if (unlikely (!buf)) { *language = nullptr; } else { + int shift; memcpy (buf, lang_str, len); if (lang_str[0] != 'x' || lang_str[1] != '-') { buf[len++] = '-'; @@ -490,10 +528,9 @@ hb_ot_tags_to_script_and_language (hb_tag_t script_tag, buf[len++] = 'b'; buf[len++] = 's'; buf[len++] = 'c'; - buf[len++] = script_tag >> 24; - buf[len++] = (script_tag >> 16) & 0xFF; - buf[len++] = (script_tag >> 8) & 0xFF; - buf[len++] = script_tag & 0xFF; + buf[len++] = '-'; + for (shift = 28; shift >= 0; shift -= 4) + buf[len++] = TOHEX (script_tag >> shift); *language = hb_language_from_string ((char *) buf, len); free (buf); } @@ -507,8 +544,8 @@ test_langs_sorted () { for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++) { - int c = lang_compare_first_component (ot_languages[i-1].language, ot_languages[i].language); - if (c >= 0) + int c = ot_languages[i].cmp (&ot_languages[i - 1]); + if (c > 0) { fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n", i, ot_languages[i-1].language, c, ot_languages[i].language); @@ -525,3 +562,6 @@ main () } #endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh index 5ae0359e5b80d..2c2934dd6fa36 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-avar-table.hh @@ -49,9 +49,10 @@ struct AxisValueMap } public: - F2DOT14 fromCoord; /* A normalized coordinate value obtained using - * default normalization. */ - F2DOT14 toCoord; /* The modified, normalized coordinate value. */ + F2DOT14 coords[2]; +// F2DOT14 fromCoord; /* A normalized coordinate value obtained using +// * default normalization. */ +// F2DOT14 toCoord; /* The modified, normalized coordinate value. */ public: DEFINE_SIZE_STATIC (4); @@ -59,12 +60,13 @@ struct AxisValueMap struct SegmentMaps : ArrayOf { - int map (int value) const + int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const { +#define fromCoord coords[from_offset] +#define toCoord coords[to_offset] /* The following special-cases are not part of OpenType, which requires * that at least -1, 0, and +1 must be mapped. But we include these as * part of a better error recovery scheme. */ - if (len < 2) { if (!len) @@ -77,7 +79,7 @@ struct SegmentMaps : ArrayOf return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; unsigned int i; - unsigned int count = len; + unsigned int count = len - 1; for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) ; @@ -88,11 +90,14 @@ struct SegmentMaps : ArrayOf return arrayZ[i-1].toCoord; int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; - return arrayZ[i-1].toCoord + - ((arrayZ[i].toCoord - arrayZ[i-1].toCoord) * - (value - arrayZ[i-1].fromCoord) + denom/2) / denom; + return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * + (value - arrayZ[i-1].fromCoord)) / denom); +#undef toCoord +#undef fromCoord } + int unmap (int value) const { return map (value, 1, 0); } + public: DEFINE_SIZE_ARRAY (2, *this); }; @@ -123,7 +128,7 @@ struct avar void map_coords (int *coords, unsigned int coords_length) const { - unsigned int count = MIN (coords_length, axisCount); + unsigned int count = hb_min (coords_length, axisCount); const SegmentMaps *map = &firstAxisSegmentMaps; for (unsigned int i = 0; i < count; i++) @@ -133,6 +138,18 @@ struct avar } } + void unmap_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->unmap (coords[i]); + map = &StructAfter (*map); + } + } + protected: FixedVersion<>version; /* Version of the avar table * initially set to 0x00010000u */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh index 3785d099b5cfc..e20cfa4df0f24 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-fvar-table.hh @@ -44,7 +44,7 @@ struct InstanceRecord { friend struct fvar; - hb_array_t get_coordinates (unsigned int axis_count) const + hb_array_t get_coordinates (unsigned int axis_count) const { return coordinatesZ.as_array (axis_count); } bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const @@ -58,7 +58,7 @@ struct InstanceRecord NameID subfamilyNameID;/* The name ID for entries in the 'name' table * that provide subfamily names for this instance. */ HBUINT16 flags; /* Reserved for future use — set to 0. */ - UnsizedArrayOf + UnsizedArrayOf coordinatesZ; /* The coordinates array for this instance. */ //NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name' // * table that provide PostScript names for this @@ -70,22 +70,83 @@ struct InstanceRecord struct AxisRecord { + int cmp (hb_tag_t key) const { return axisTag.cmp (key); } + enum { AXIS_FLAG_HIDDEN = 0x0001, }; +#ifndef HB_DISABLE_DEPRECATED + void get_axis_deprecated (hb_ot_var_axis_t *info) const + { + info->tag = axisTag; + info->name_id = axisNameID; + get_coordinates (info->min_value, info->default_value, info->max_value); + } +#endif + + void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const + { + info->axis_index = axis_index; + info->tag = axisTag; + info->name_id = axisNameID; + info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags; + get_coordinates (info->min_value, info->default_value, info->max_value); + info->reserved = 0; + } + + int normalize_axis_value (float v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + v = hb_clamp (v, min_value, max_value); + + if (v == default_value) + return 0; + else if (v < default_value) + v = (v - default_value) / (default_value - min_value); + else + v = (v - default_value) / (max_value - default_value); + return roundf (v * 16384.f); + } + + float unnormalize_axis_value (int v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + if (v == 0) + return default_value; + else if (v < 0) + return v * (default_value - min_value) / 16384.f + default_value; + else + return v * (max_value - default_value) / 16384.f + default_value; + } + + hb_ot_name_id_t get_name_id () const { return axisNameID; } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } - public: + protected: + void get_coordinates (float &min, float &default_, float &max) const + { + default_ = defaultValue / 65536.f; + /* Ensure order, to simplify client math. */ + min = hb_min (default_, minValue / 65536.f); + max = hb_max (default_, maxValue / 65536.f); + } + + protected: Tag axisTag; /* Tag identifying the design variation for the axis. */ - Fixed minValue; /* The minimum coordinate value for the axis. */ - Fixed defaultValue; /* The default coordinate value for the axis. */ - Fixed maxValue; /* The maximum coordinate value for the axis. */ + HBFixed minValue; /* The minimum coordinate value for the axis. */ + HBFixed defaultValue; /* The default coordinate value for the axis. */ + HBFixed maxValue; /* The maximum coordinate value for the axis. */ HBUINT16 flags; /* Axis flags. */ NameID axisNameID; /* The name ID for entries in the 'name' table that * provide a display name for this axis. */ @@ -114,54 +175,20 @@ struct fvar unsigned int get_axis_count () const { return axisCount; } - void get_axis_deprecated (unsigned int axis_index, - hb_ot_var_axis_t *info) const - { - const AxisRecord &axis = get_axes ()[axis_index]; - info->tag = axis.axisTag; - info->name_id = axis.axisNameID; - info->default_value = axis.defaultValue / 65536.; - /* Ensure order, to simplify client math. */ - info->min_value = MIN (info->default_value, axis.minValue / 65536.); - info->max_value = MAX (info->default_value, axis.maxValue / 65536.); - } - - void get_axis_info (unsigned int axis_index, - hb_ot_var_axis_info_t *info) const - { - const AxisRecord &axis = get_axes ()[axis_index]; - info->axis_index = axis_index; - info->tag = axis.axisTag; - info->name_id = axis.axisNameID; - info->flags = (hb_ot_var_axis_flags_t) (unsigned int) axis.flags; - info->default_value = axis.defaultValue / 65536.; - /* Ensure order, to simplify client math. */ - info->min_value = MIN (info->default_value, axis.minValue / 65536.); - info->max_value = MAX (info->default_value, axis.maxValue / 65536.); - info->reserved = 0; - } - +#ifndef HB_DISABLE_DEPRECATED unsigned int get_axes_deprecated (unsigned int start_offset, unsigned int *axes_count /* IN/OUT */, hb_ot_var_axis_t *axes_array /* OUT */) const { if (axes_count) { - /* TODO Rewrite as hb_array_t<>::sub-array() */ - unsigned int count = axisCount; - start_offset = MIN (start_offset, count); - - count -= start_offset; - axes_array += start_offset; - - count = MIN (count, *axes_count); - *axes_count = count; - - for (unsigned int i = 0; i < count; i++) - get_axis_deprecated (start_offset + i, axes_array + i); + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_deprecated (&axes_array[i]); } return axisCount; } +#endif unsigned int get_axis_infos (unsigned int start_offset, unsigned int *axes_count /* IN/OUT */, @@ -169,70 +196,38 @@ struct fvar { if (axes_count) { - /* TODO Rewrite as hb_array_t<>::sub-array() */ - unsigned int count = axisCount; - start_offset = MIN (start_offset, count); - - count -= start_offset; - axes_array += start_offset; - - count = MIN (count, *axes_count); - *axes_count = count; - - for (unsigned int i = 0; i < count; i++) - get_axis_info (start_offset + i, axes_array + i); + hb_array_t arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_info (start_offset + i, &axes_array[i]); } return axisCount; } - bool find_axis_deprecated (hb_tag_t tag, - unsigned int *axis_index, - hb_ot_var_axis_t *info) const +#ifndef HB_DISABLE_DEPRECATED + bool + find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const { - const AxisRecord *axes = get_axes (); - unsigned int count = get_axis_count (); - for (unsigned int i = 0; i < count; i++) - if (axes[i].axisTag == tag) - { - if (axis_index) - *axis_index = i; - get_axis_deprecated (i, info); - return true; - } - if (axis_index) - *axis_index = HB_OT_VAR_NO_AXIS_INDEX; - return false; + unsigned i; + if (!axis_index) axis_index = &i; + *axis_index = HB_OT_VAR_NO_AXIS_INDEX; + auto axes = get_axes (); + return axes.lfind (tag, axis_index) && (axes[*axis_index].get_axis_deprecated (info), true); } +#endif - bool find_axis_info (hb_tag_t tag, - hb_ot_var_axis_info_t *info) const + bool + find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const { - const AxisRecord *axes = get_axes (); - unsigned int count = get_axis_count (); - for (unsigned int i = 0; i < count; i++) - if (axes[i].axisTag == tag) - { - get_axis_info (i, info); - return true; - } - return false; + unsigned i; + auto axes = get_axes (); + return axes.lfind (tag, &i) && (axes[i].get_axis_info (i, info), true); } int normalize_axis_value (unsigned int axis_index, float v) const - { - hb_ot_var_axis_info_t axis; - get_axis_info (axis_index, &axis); + { return get_axes ()[axis_index].normalize_axis_value (v); } - v = MAX (MIN (v, axis.max_value), axis.min_value); /* Clamp. */ - - if (v == axis.default_value) - return 0; - else if (v < axis.default_value) - v = (v - axis.default_value) / (axis.default_value - axis.min_value); - else - v = (v - axis.default_value) / (axis.max_value - axis.default_value); - return (int) (v * 16384.f + (v >= 0.f ? .5f : -.5f)); - } + float unnormalize_axis_value (unsigned int axis_index, int v) const + { return get_axes ()[axis_index].unnormalize_axis_value (v); } unsigned int get_instance_count () const { return instanceCount; } @@ -253,8 +248,8 @@ struct fvar } unsigned int get_instance_coords (unsigned int instance_index, - unsigned int *coords_length, /* IN/OUT */ - float *coords /* OUT */) const + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) const { const InstanceRecord *instance = get_instance (instance_index); if (unlikely (!instance)) @@ -266,7 +261,7 @@ struct fvar if (coords_length && *coords_length) { - hb_array_t instanceCoords = instance->get_coordinates (axisCount) + hb_array_t instanceCoords = instance->get_coordinates (axisCount) .sub_array (0, *coords_length); for (unsigned int i = 0; i < instanceCoords.length; i++) coords[i] = instanceCoords.arrayZ[i].to_float (); @@ -274,6 +269,26 @@ struct fvar return axisCount; } + void collect_name_ids (hb_set_t *nameids) const + { + if (!has_data ()) return; + + + get_axes () + | hb_map (&AxisRecord::get_name_id) + | hb_sink (nameids) + ; + + + hb_range ((unsigned) instanceCount) + | hb_map ([this] (const unsigned _) { return get_instance_subfamily_name_id (_); }) + | hb_sink (nameids) + ; + + + hb_range ((unsigned) instanceCount) + | hb_map ([this] (const unsigned _) { return get_instance_postscript_name_id (_); }) + | hb_sink (nameids) + ; + } + protected: hb_array_t get_axes () const { return hb_array (&(this+firstAxis), axisCount); } @@ -299,8 +314,8 @@ struct fvar HBUINT16 instanceCount; /* The number of named instances defined in the font * (the number of records in the instances array). */ HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set - * to either axisCount * sizeof(Fixed) + 4, or to - * axisCount * sizeof(Fixed) + 6. */ + * to either axisCount * sizeof(HBFixed) + 4, or to + * axisCount * sizeof(HBFixed) + 6. */ public: DEFINE_SIZE_STATIC (16); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh new file mode 100644 index 0000000000000..7c99b5de06149 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-gvar-table.hh @@ -0,0 +1,701 @@ +/* + * Copyright © 2019 Adobe Inc. + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VAR_GVAR_TABLE_HH +#define HB_OT_VAR_GVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct contour_point_t +{ + void init (float x_ = 0.f, float y_ = 0.f, bool is_end_point_ = false) + { flag = 0; x = x_; y = y_; is_end_point = is_end_point_; } + + void translate (const contour_point_t &p) { x += p.x; y += p.y; } + + uint8_t flag; + float x, y; + bool is_end_point; +}; + +struct contour_point_vector_t : hb_vector_t +{ + void extend (const hb_array_t &a) + { + unsigned int old_len = length; + resize (old_len + a.length); + for (unsigned int i = 0; i < a.length; i++) + (*this)[old_len + i] = a[i]; + } + + void transform (const float (&matrix)[4]) + { + for (unsigned int i = 0; i < length; i++) + { + contour_point_t &p = (*this)[i]; + float x_ = p.x * matrix[0] + p.y * matrix[2]; + p.y = p.x * matrix[1] + p.y * matrix[3]; + p.x = x_; + } + } + + void translate (const contour_point_t& delta) + { + for (unsigned int i = 0; i < length; i++) + (*this)[i].translate (delta); + } +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ +struct TupleVariationHeader +{ + unsigned get_size (unsigned axis_count) const + { return min_size + get_all_tuples (axis_count).get_size (); } + + unsigned get_data_size () const { return varDataSize; } + + const TupleVariationHeader &get_next (unsigned axis_count) const + { return StructAtOffset (this, get_size (axis_count)); } + + float calculate_scalar (const int *coords, unsigned int coord_count, + const hb_array_t shared_tuples) const + { + hb_array_t peak_tuple; + + if (has_peak ()) + peak_tuple = get_peak_tuple (coord_count); + else + { + unsigned int index = get_index (); + if (unlikely (index * coord_count >= shared_tuples.length)) + return 0.f; + peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count); + } + + hb_array_t start_tuple; + hb_array_t end_tuple; + if (has_intermediate ()) + { + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); + } + + float scalar = 1.f; + for (unsigned int i = 0; i < coord_count; i++) + { + int v = coords[i]; + int peak = peak_tuple[i]; + if (!peak || v == peak) continue; + + if (has_intermediate ()) + { + int start = start_tuple[i]; + int end = end_tuple[i]; + if (unlikely (start > peak || peak > end || + (start < 0 && end > 0 && peak))) continue; + if (v < start || v > end) return 0.f; + if (v < peak) + { if (peak != start) scalar *= (float) (v - start) / (peak - start); } + else + { if (peak != end) scalar *= (float) (end - v) / (end - peak); } + } + else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.f; + else + scalar *= (float) v / peak; + } + return scalar; + } + + bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + + protected: + struct TuppleIndex : HBUINT16 + { + enum Flags { + EmbeddedPeakTuple = 0x8000u, + IntermediateRegion = 0x4000u, + PrivatePointNumbers = 0x2000u, + TupleIndexMask = 0x0FFFu + }; + + DEFINE_SIZE_STATIC (2); + }; + + hb_array_t get_all_tuples (unsigned axis_count) const + { return StructAfter> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } + hb_array_t get_peak_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (0, axis_count); } + hb_array_t get_start_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } + hb_array_t get_end_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + + HBUINT16 varDataSize; /* The size in bytes of the serialized + * data for this tuple variation table. */ + TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + The low 12 bits are an index into a shared tuple + records array. */ + /* UnsizedArrayOf peakTuple - optional */ + /* Peak tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. + * + * Note that this must always be included in the 'cvar' table. */ + /* UnsizedArrayOf intermediateStartTuple - optional */ + /* Intermediate start tuple record for this tuple variation table — optional, + determined by flags in the tupleIndex value. */ + /* UnsizedArrayOf intermediateEndTuple - optional */ + /* Intermediate end tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct GlyphVariationData +{ + const TupleVariationHeader &get_tuple_var_header (void) const + { return StructAfter (data); } + + struct tuple_iterator_t + { + void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_) + { + var_data_bytes = var_data_bytes_; + var_data = var_data_bytes_.as (); + index = 0; + axis_count = axis_count_; + current_tuple = &var_data->get_tuple_var_header (); + data_offset = 0; + } + + bool get_shared_indices (hb_vector_t &shared_indices /* OUT */) + { + if (var_data->has_shared_point_numbers ()) + { + const HBUINT8 *base = &(var_data+var_data->data); + const HBUINT8 *p = base; + if (!unpack_points (p, shared_indices, var_data_bytes)) return false; + data_offset = p - base; + } + return true; + } + + bool is_valid () const + { + return (index < var_data->tupleVarCount.get_count ()) && + var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && + var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), current_tuple->get_size (axis_count))) && + current_tuple->get_size (axis_count); + } + + bool move_to_next () + { + data_offset += current_tuple->get_data_size (); + current_tuple = ¤t_tuple->get_next (axis_count); + index++; + return is_valid (); + } + + const HBUINT8 *get_serialized_data () const + { return &(var_data+var_data->data) + data_offset; } + + private: + const GlyphVariationData *var_data; + unsigned int index; + unsigned int axis_count; + unsigned int data_offset; + + public: + hb_bytes_t var_data_bytes; + const TupleVariationHeader *current_tuple; + }; + + static bool get_tuple_iterator (hb_bytes_t var_data_bytes, unsigned axis_count, + hb_vector_t &shared_indices /* OUT */, + tuple_iterator_t *iterator /* OUT */) + { + iterator->init (var_data_bytes, axis_count); + if (!iterator->get_shared_indices (shared_indices)) + return false; + return iterator->is_valid (); + } + + bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + + static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &points /* OUT */, + const hb_bytes_t &bytes) + { + enum packed_point_flag_t + { + POINTS_ARE_WORDS = 0x80, + POINT_RUN_COUNT_MASK = 0x7F + }; + + if (unlikely (!bytes.check_range (p))) return false; + + uint16_t count = *p++; + if (count & POINTS_ARE_WORDS) + { + if (unlikely (!bytes.check_range (p))) return false; + count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; + } + points.resize (count); + + unsigned int n = 0; + uint16_t i = 0; + while (i < count) + { + if (unlikely (!bytes.check_range (p))) return false; + uint16_t j; + uint8_t control = *p++; + uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; + if (control & POINTS_ARE_WORDS) + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) + return false; + n += *(const HBUINT16 *)p; + points[i] = n; + p += HBUINT16::static_size; + } + } + else + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range (p))) return false; + n += *p++; + points[i] = n; + } + } + if (j < run_count) return false; + } + return true; + } + + static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &deltas /* IN/OUT */, + const hb_bytes_t &bytes) + { + enum packed_delta_flag_t + { + DELTAS_ARE_ZERO = 0x80, + DELTAS_ARE_WORDS = 0x40, + DELTA_RUN_COUNT_MASK = 0x3F + }; + + unsigned int i = 0; + unsigned int count = deltas.length; + while (i < count) + { + if (unlikely (!bytes.check_range (p))) return false; + uint8_t control = *p++; + unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + unsigned int j; + if (control & DELTAS_ARE_ZERO) + for (j = 0; j < run_count && i < count; j++, i++) + deltas[i] = 0; + else if (control & DELTAS_ARE_WORDS) + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) + return false; + deltas[i] = *(const HBINT16 *) p; + p += HBUINT16::static_size; + } + else + for (j = 0; j < run_count && i < count; j++, i++) + { + if (unlikely (!bytes.check_range (p))) + return false; + deltas[i] = *(const HBINT8 *) p++; + } + if (j < run_count) + return false; + } + return true; + } + + bool has_data () const { return tupleVarCount; } + + protected: + struct TupleVarCount : HBUINT16 + { + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } + unsigned int get_count () const { return (*this) & CountMask; } + + protected: + enum Flags + { + SharedPointNumbers= 0x8000u, + CountMask = 0x0FFFu + }; + public: + DEFINE_SIZE_STATIC (2); + }; + + TupleVarCount tupleVarCount; /* A packed field. The high 4 bits are flags, and the + * low 12 bits are the number of tuple variation tables + * for this glyph. The number of tuple variation tables + * can be any number between 1 and 4095. */ + OffsetTo + data; /* Offset from the start of the GlyphVariationData table + * to the serialized data. */ + /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ + public: + DEFINE_SIZE_MIN (4); +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (version.major == 1) && + (glyphCount == c->get_num_glyphs ()) && + sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && + (is_long_offset () ? + c->check_array (get_long_offset_array (), glyphCount+1) : + c->check_array (get_short_offset_array (), glyphCount+1)) && + c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0), + get_offset (glyphCount) - get_offset (0))); + } + + /* GlyphVariationData not sanitized here; must be checked while accessing each glyph varation data */ + bool sanitize (hb_sanitize_context_t *c) const + { return sanitize_shallow (c); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + gvar *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + out->axisCount = axisCount; + out->sharedTupleCount = sharedTupleCount; + + unsigned int num_glyphs = c->plan->num_output_glyphs (); + out->glyphCount = num_glyphs; + + unsigned int subset_data_size = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t old_gid; + if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; + subset_data_size += get_glyph_var_data_bytes (c->source_blob, old_gid).length; + } + + bool long_offset = subset_data_size & ~0xFFFFu; + out->flags = long_offset ? 1 : 0; + + HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset ? 4 : 2) * (num_glyphs + 1)); + if (!subset_offsets) return_trace (false); + + /* shared tuples */ + if (!sharedTupleCount || !sharedTuples) + out->sharedTuples = 0; + else + { + unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; + F2DOT14 *tuples = c->serializer->allocate_size (shared_tuple_size); + if (!tuples) return_trace (false); + out->sharedTuples = (char *) tuples - (char *) out; + memcpy (tuples, this+sharedTuples, shared_tuple_size); + } + + char *subset_data = c->serializer->allocate_size (subset_data_size); + if (!subset_data) return_trace (false); + out->dataZ = subset_data - (char *) out; + + unsigned int glyph_offset = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + hb_codepoint_t old_gid; + hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid) + ? get_glyph_var_data_bytes (c->source_blob, old_gid) + : hb_bytes_t (); + + if (long_offset) + ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; + + if (var_data_bytes.length > 0) + memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); + subset_data += var_data_bytes.length; + glyph_offset += var_data_bytes.length; + } + if (long_offset) + ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2; + + return_trace (true); + } + + protected: + const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const + { + unsigned start_offset = get_offset (glyph); + unsigned length = get_offset (glyph+1) - start_offset; + hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length); + return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t (); + } + + bool is_long_offset () const { return flags & 1; } + + unsigned get_offset (unsigned i) const + { return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; } + + const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; } + const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; } + + public: + struct accelerator_t + { + void init (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table (face); } + void fini () { table.destroy (); } + + private: + struct x_getter { static float get (const contour_point_t &p) { return p.x; } }; + struct y_getter { static float get (const contour_point_t &p) { return p.y; } }; + + template + static float infer_delta (const hb_array_t points, + const hb_array_t deltas, + unsigned int target, unsigned int prev, unsigned int next) + { + float target_val = T::get (points[target]); + float prev_val = T::get (points[prev]); + float next_val = T::get (points[next]); + float prev_delta = T::get (deltas[prev]); + float next_delta = T::get (deltas[next]); + + if (prev_val == next_val) + return (prev_delta == next_delta) ? prev_delta : 0.f; + else if (target_val <= hb_min (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta : next_delta; + else if (target_val >= hb_max (prev_val, next_val)) + return (prev_val > next_val) ? prev_delta : next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return (1.f - r) * prev_delta + r * next_delta; + } + + static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) + { return (i >= end) ? start : (i + 1); } + + public: + bool apply_deltas_to_points (hb_codepoint_t glyph, hb_font_t *font, + const hb_array_t points) const + { + /* num_coords should exactly match gvar's axisCount due to how GlyphVariationData tuples are aligned */ + if (!font->num_coords || font->num_coords != table->axisCount) return true; + + if (unlikely (glyph >= table->glyphCount)) return true; + + hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyph); + if (!var_data_bytes.as ()->has_data ()) return true; + hb_vector_t shared_indices; + GlyphVariationData::tuple_iterator_t iterator; + if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount, + shared_indices, &iterator)) + return true; /* so isn't applied at all */ + + /* Save original points for inferred delta calculation */ + contour_point_vector_t orig_points; + orig_points.resize (points.length); + for (unsigned int i = 0; i < orig_points.length; i++) + orig_points[i] = points[i]; + + contour_point_vector_t deltas; /* flag is used to indicate referenced point */ + deltas.resize (points.length); + + hb_vector_t end_points; + for (unsigned i = 0; i < points.length; ++i) + if (points[i].is_end_point) + end_points.push (i); + + int *coords = font->coords; + unsigned num_coords = font->num_coords; + hb_array_t shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); + do + { + float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + return false; + + hb_bytes_t bytes ((const char *) p, length); + hb_vector_t private_indices; + if (iterator.current_tuple->has_private_points () && + !GlyphVariationData::unpack_points (p, private_indices, bytes)) + return false; + const hb_array_t &indices = private_indices.length ? private_indices : shared_indices; + + bool apply_to_all = (indices.length == 0); + unsigned int num_deltas = apply_to_all ? points.length : indices.length; + hb_vector_t x_deltas; + x_deltas.resize (num_deltas); + if (!GlyphVariationData::unpack_deltas (p, x_deltas, bytes)) + return false; + hb_vector_t y_deltas; + y_deltas.resize (num_deltas); + if (!GlyphVariationData::unpack_deltas (p, y_deltas, bytes)) + return false; + + for (unsigned int i = 0; i < deltas.length; i++) + deltas[i].init (); + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all ? i : indices[i]; + deltas[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + deltas[pt_index].x += x_deltas[i] * scalar; + deltas[pt_index].y += y_deltas[i] * scalar; + } + + /* infer deltas for unreferenced points */ + unsigned start_point = 0; + for (unsigned c = 0; c < end_points.length; c++) + { + unsigned end_point = end_points[c]; + + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i <= end_point; i++) + if (!deltas[i].flag) unref_count++; + + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; + + for (;;) + { + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (deltas[i].flag && !deltas[j].flag) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!deltas[i].flag && deltas[j].flag) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); + deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); + if (--unref_count == 0) goto no_more_gaps; + } + } +no_more_gaps: + start_point = end_point + 1; + } + + /* apply specified / inferred deltas to points */ + for (unsigned int i = 0; i < points.length; i++) + { + points[i].x += roundf (deltas[i].x); + points[i].y += roundf (deltas[i].y); + } + } while (iterator.move_to_next ()); + + return true; + } + + unsigned int get_axis_count () const { return table->axisCount; } + + private: + hb_blob_ptr_t table; + }; + + protected: + FixedVersion<>version; /* Version number of the glyph variations table + * Set to 0x00010000u. */ + HBUINT16 axisCount; /* The number of variation axes for this font. This must be + * the same number as axisCount in the 'fvar' table. */ + HBUINT16 sharedTupleCount; + /* The number of shared tuple records. Shared tuple records + * can be referenced within glyph variation data tables for + * multiple glyphs, as opposed to other tuple records stored + * directly within a glyph variation data table. */ + LNNOffsetTo> + sharedTuples; /* Offset from the start of this table to the shared tuple records. + * Array of tuple records shared across all glyph variation data tables. */ + HBUINT16 glyphCount; /* The number of glyphs in this font. This must match the number of + * glyphs stored elsewhere in the font. */ + HBUINT16 flags; /* Bit-field that gives the format of the offset array that follows. + * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the + * offsets are uint32. */ + LOffsetTo + dataZ; /* Offset from the start of this table to the array of + * GlyphVariationData tables. */ + UnsizedArrayOf + offsetZ; /* Offsets from the start of the GlyphVariationData array + * to each GlyphVariationData table. */ + public: + DEFINE_SIZE_MIN (20); +}; + +struct gvar_accelerator_t : gvar::accelerator_t {}; + +} /* namespace OT */ + +#endif /* HB_OT_VAR_GVAR_TABLE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh index 4d030f3d7cfc1..d024147183fa3 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-hvar-table.hh @@ -44,6 +44,38 @@ struct DeltaSetIndexMap get_width ())); } + template + bool serialize (hb_serialize_context_t *c, const T &plan) + { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + const hb_array_t output_map = plan.get_output_map (); + + TRACE_SERIALIZE (this); + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (*this))) return_trace (false); + + format = ((width-1)<<4)|(inner_bit_count-1); + mapCount = output_map.length; + HBUINT8 *p = c->allocate_size (width * output_map.length); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = output_map[i]; + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count) | inner; + for (unsigned int w = width; w > 0;) + { + p[--w] = u; + u >>= 8; + } + p += width; + } + return_trace (true); + } + unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */ { /* If count is zero, pass value unchanged. This takes @@ -63,7 +95,7 @@ struct DeltaSetIndexMap } { /* Repack it. */ - unsigned int n = get_inner_bitcount (); + unsigned int n = get_inner_bit_count (); unsigned int outer = u >> n; unsigned int inner = u & ((1 << n) - 1); u = (outer<<16) | inner; @@ -72,10 +104,9 @@ struct DeltaSetIndexMap return u; } - protected: - unsigned int get_width () const { return ((format >> 4) & 3) + 1; } - - unsigned int get_inner_bitcount () const { return (format & 0xF) + 1; } + unsigned int get_map_count () const { return mapCount; } + unsigned int get_width () const { return ((format >> 4) & 3) + 1; } + unsigned int get_inner_bit_count () const { return (format & 0xF) + 1; } protected: HBUINT16 format; /* A packed field that describes the compressed @@ -88,6 +119,215 @@ struct DeltaSetIndexMap DEFINE_SIZE_ARRAY (4, mapDataZ); }; +struct index_map_subset_plan_t +{ + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, /* dual as TSB */ + RSB_INDEX, /* dual as BSB */ + VORG_INDEX + }; + + void init (const DeltaSetIndexMap &index_map, + hb_inc_bimap_t &outer_map, + hb_vector_t &inner_sets, + const hb_subset_plan_t *plan) + { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 1; + max_inners.init (); + output_map.init (); + + if (&index_map == &Null (DeltaSetIndexMap)) return; + + unsigned int last_val = (unsigned int)-1; + hb_codepoint_t last_gid = (hb_codepoint_t)-1; + hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ()); + + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); + max_inners.resize (inner_sets.length); + for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; + + /* Search backwards for a map value different from the last map value */ + for (; gid > 0; gid--) + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (gid - 1, &old_gid)) + { + if (last_gid == (hb_codepoint_t) -1) + continue; + else + break; + } + + unsigned int v = index_map.map (old_gid); + if (last_gid == (hb_codepoint_t) -1) + { + last_val = v; + last_gid = gid; + continue; + } + if (v != last_val) break; + + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid; + for (gid = 0; gid < map_count; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + unsigned int v = index_map.map (old_gid); + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + outer_map.add (outer); + if (inner > max_inners[outer]) max_inners[outer] = inner; + if (outer >= inner_sets.length) return; + inner_sets[outer]->add (inner); + } + } + } + + void fini () + { + max_inners.fini (); + output_map.fini (); + } + + void remap (const DeltaSetIndexMap *input_map, + const hb_inc_bimap_t &outer_map, + const hb_vector_t &inner_maps, + const hb_subset_plan_t *plan) + { + if (input_map == &Null (DeltaSetIndexMap)) return; + + for (unsigned int i = 0; i < max_inners.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_maps[i][max_inners[i]]); + if (bit_count > inner_bit_count) inner_bit_count = bit_count; + } + + output_map.resize (map_count); + for (hb_codepoint_t gid = 0; gid < output_map.length; gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + { + unsigned int v = input_map->map (old_gid); + unsigned int outer = v >> 16; + output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + } + else + output_map[gid] = 0; /* Map unused glyph to outer/inner=0/0 */ + } + } + + unsigned int get_inner_bit_count () const { return inner_bit_count; } + unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count () const { return map_count; } + + unsigned int get_size () const + { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + + bool is_identity () const { return get_output_map ().length == 0; } + hb_array_t get_output_map () const { return output_map.as_array (); } + + protected: + unsigned int map_count; + hb_vector_t max_inners; + unsigned int outer_bit_count; + unsigned int inner_bit_count; + hb_vector_t output_map; +}; + +struct hvarvvar_subset_plan_t +{ + hvarvvar_subset_plan_t() : inner_maps (), index_map_plans () {} + ~hvarvvar_subset_plan_t() { fini (); } + + void init (const hb_array_t &index_maps, + const VariationStore &_var_store, + const hb_subset_plan_t *plan) + { + index_map_plans.resize (index_maps.length); + + var_store = &_var_store; + inner_sets.resize (var_store->get_sub_table_count ()); + for (unsigned int i = 0; i < inner_sets.length; i++) + inner_sets[i] = hb_set_create (); + adv_set = hb_set_create (); + + inner_maps.resize (var_store->get_sub_table_count ()); + + for (unsigned int i = 0; i < inner_maps.length; i++) + inner_maps[i].init (); + + if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; + + bool retain_adv_map = false; + index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan); + if (index_maps[0] == &Null (DeltaSetIndexMap)) + { + retain_adv_map = plan->retain_gids; + outer_map.add (0); + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + { + hb_codepoint_t old_gid; + if (plan->old_gid_for_new_gid (gid, &old_gid)) + inner_sets[0]->add (old_gid); + } + hb_set_union (adv_set, inner_sets[0]); + } + + for (unsigned int i = 1; i < index_maps.length; i++) + index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + + outer_map.sort (); + + if (retain_adv_map) + { + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + if (inner_sets[0]->has (gid)) + inner_maps[0].add (gid); + else + inner_maps[0].skip (); + } + else + { + inner_maps[0].add_set (adv_set); + hb_set_subtract (inner_sets[0], adv_set); + inner_maps[0].add_set (inner_sets[0]); + } + + for (unsigned int i = 1; i < inner_maps.length; i++) + inner_maps[i].add_set (inner_sets[i]); + + for (unsigned int i = 0; i < index_maps.length; i++) + index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan); + } + + void fini () + { + for (unsigned int i = 0; i < inner_sets.length; i++) + hb_set_destroy (inner_sets[i]); + hb_set_destroy (adv_set); + inner_maps.fini_deep (); + index_map_plans.fini_deep (); + } + + hb_inc_bimap_t outer_map; + hb_vector_t inner_maps; + hb_vector_t index_map_plans; + const VariationStore *var_store; + + protected: + hb_vector_t inner_sets; + hb_set_t *adv_set; +}; /* * HVAR -- Horizontal Metrics Variations @@ -114,14 +354,73 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } - float get_advance_var (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count) const + void listup_index_maps (hb_vector_t &index_maps) const + { + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) + advMap = 0; + else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) + lsbMap = 0; + else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) + rsbMap = 0; + else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) + return_trace (false); + + return_trace (true); + } + + template + bool _subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + hvarvvar_subset_plan_t hvar_plan; + hb_vector_t + index_maps; + + ((T*)this)->listup_index_maps (index_maps); + hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); + + T *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + + if (unlikely (!out->varStore.serialize (c->serializer, out) + .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_maps.as_array ()))) + return_trace (false); + + return_trace (out->T::serialize_index_maps (c->serializer, + hvar_plan.index_map_plans.as_array ())); + } + + float get_advance_var (hb_codepoint_t glyph, hb_font_t *font) const { unsigned int varidx = (this+advMap).map (glyph); + return (this+varStore).get_delta (varidx, font->coords, font->num_coords); + } + + float get_side_bearing_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count) const + { + if (!has_side_bearing_deltas ()) return 0.f; + unsigned int varidx = (this+lsbMap).map (glyph); return (this+varStore).get_delta (varidx, coords, coord_count); } - bool has_sidebearing_deltas () const { return lsbMap && rsbMap; } + bool has_side_bearing_deltas () const { return lsbMap && rsbMap; } protected: FixedVersion<>version; /* Version of the metrics variation table @@ -141,6 +440,7 @@ struct HVARVVAR struct HVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR; + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } }; struct VVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR; @@ -152,6 +452,28 @@ struct VVAR : HVARVVAR { vorgMap.sanitize (c, this)); } + void listup_index_maps (hb_vector_t &index_maps) const + { + HVARVVAR::listup_index_maps (index_maps); + index_maps.push (&(this+vorgMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SERIALIZE (this); + if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) + return_trace (false); + if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) + vorgMap = 0; + else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) + return_trace (false); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } + protected: LOffsetTo vorgMap; /* Offset to vertical-origin var-idx mapping. */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh index 374ce665f76d5..37a47ff1d4130 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var-mvar-table.hh @@ -77,9 +77,11 @@ struct MVAR const int *coords, unsigned int coord_count) const { const VariationValueRecord *record; - record = (VariationValueRecord *) bsearch (&tag, valuesZ.arrayZ, - valueRecordCount, valueRecordSize, - tag_compare); + record = (VariationValueRecord *) hb_bsearch (tag, + (const VariationValueRecord *) + (const HBUINT8 *) valuesZ, + valueRecordCount, valueRecordSize, + tag_compare); if (!record) return 0.; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc index ed36012514888..1c12d56658229 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.cc @@ -24,13 +24,15 @@ * Google Author(s): Behdad Esfahbod */ -#include "hb-open-type.hh" +#include "hb.hh" + +#ifndef HB_NO_VAR + +#include "hb-ot-var.h" -#include "hb-ot-face.hh" #include "hb-ot-var-avar-table.hh" #include "hb-ot-var-fvar-table.hh" #include "hb-ot-var-mvar-table.hh" -#include "hb-ot-var.h" /** @@ -75,6 +77,7 @@ hb_ot_var_get_axis_count (hb_face_t *face) return face->table.fvar->get_axis_count (); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_ot_var_get_axes: * @@ -104,6 +107,7 @@ hb_ot_var_find_axis (hb_face_t *face, { return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info); } +#endif /** * hb_ot_var_get_axis_infos: @@ -211,3 +215,6 @@ hb_ot_var_normalize_coords (hb_face_t *face, face->table.avar->map_coords (normalized_coords, coords_length); } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h index 5b028240d301e..92494c24a0f0b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-var.h @@ -68,7 +68,7 @@ hb_ot_var_get_axis_count (hb_face_t *face); typedef enum { /*< flags >*/ HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u, - _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= 0x7FFFFFFFu /*< skip >*/ + _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/ } hb_ot_var_axis_flags_t; /** diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh index c2dc77b62bb15..4dd8dfdb02afd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot-vorg-table.hh @@ -48,7 +48,7 @@ struct VertOriginMetric } public: - GlyphID glyph; + HBGlyphID glyph; FWORD vertOriginY; public: @@ -69,94 +69,48 @@ struct VORG return vertYOrigins[i].vertOriginY; } - bool _subset (const hb_subset_plan_t *plan HB_UNUSED, - const VORG *vorg_table, - const hb_vector_t &subset_metrics, - unsigned int dest_sz, - void *dest) const + template + void serialize (hb_serialize_context_t *c, + Iterator it, + FWORD defaultVertOriginY) { - hb_serialize_context_t c (dest, dest_sz); - - VORG *subset_table = c.start_serialize (); - if (unlikely (!c.extend_min (*subset_table))) - return false; - - subset_table->version.major.set (1); - subset_table->version.minor.set (0); - - subset_table->defaultVertOriginY.set (vorg_table->defaultVertOriginY); - subset_table->vertYOrigins.len.set (subset_metrics.length); - - bool success = true; - if (subset_metrics.length > 0) - { - unsigned int size = VertOriginMetric::static_size * subset_metrics.length; - VertOriginMetric *metrics = c.allocate_size (size); - if (likely (metrics != nullptr)) - memcpy (metrics, &subset_metrics[0], size); - else - success = false; - } - c.end_serialize (); - - return success; + + if (unlikely (!c->extend_min ((*this)))) return; + + this->version.major = 1; + this->version.minor = 0; + + this->defaultVertOriginY = defaultVertOriginY; + this->vertYOrigins.len = it.len (); + + c->copy_all (it); } - bool subset (hb_subset_plan_t *plan) const + bool subset (hb_subset_context_t *c) const { - hb_blob_t *vorg_blob = hb_sanitize_context_t().reference_table (plan->source); - const VORG *vorg_table = vorg_blob->as (); - - /* count the number of glyphs to be included in the subset table */ - hb_vector_t subset_metrics; - subset_metrics.init (); - unsigned int glyph = 0; - unsigned int i = 0; - while ((glyph < plan->glyphs.length) && (i < vertYOrigins.len)) - { - if (plan->glyphs[glyph] > vertYOrigins[i].glyph) - i++; - else if (plan->glyphs[glyph] < vertYOrigins[i].glyph) - glyph++; - else - { - VertOriginMetric *metrics = subset_metrics.push (); - metrics->glyph.set (glyph); - metrics->vertOriginY.set (vertYOrigins[i].vertOriginY); - glyph++; - i++; - } - } - - /* alloc the new table */ - unsigned int dest_sz = VORG::min_size + VertOriginMetric::static_size * subset_metrics.length; - void *dest = (void *) malloc (dest_sz); - if (unlikely (!dest)) - { - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return false; - } + TRACE_SUBSET (this); + VORG *vorg_prime = c->serializer->start_embed (); + if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false); + + auto it = + + vertYOrigins.as_array () + | hb_filter (c->plan->glyphset (), &VertOriginMetric::glyph) + | hb_map ([&] (const VertOriginMetric& _) + { + hb_codepoint_t new_glyph = HB_SET_VALUE_INVALID; + c->plan->new_gid_for_old_gid (_.glyph, &new_glyph); + + VertOriginMetric metric; + metric.glyph = new_glyph; + metric.vertOriginY = _.vertOriginY; + return metric; + }) + ; /* serialize the new table */ - if (!_subset (plan, vorg_table, subset_metrics, dest_sz, dest)) - { - subset_metrics.fini (); - free (dest); - hb_blob_destroy (vorg_blob); - return false; - } - - hb_blob_t *result = hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool success = plan->add_table (HB_OT_TAG_VORG, result); - hb_blob_destroy (result); - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return success; + vorg_prime->serialize (c->serializer, it, defaultVertOriginY); + return_trace (true); } bool sanitize (hb_sanitize_context_t *c) const @@ -168,10 +122,11 @@ struct VORG } protected: - FixedVersion<> version; /* Version of VORG table. Set to 0x00010000u. */ - FWORD defaultVertOriginY; /* The default vertical origin. */ + FixedVersion<>version; /* Version of VORG table. Set to 0x00010000u. */ + FWORD defaultVertOriginY; + /* The default vertical origin. */ SortedArrayOf - vertYOrigins; /* The array of vertical origins. */ + vertYOrigins; /* The array of vertical origins. */ public: DEFINE_SIZE_ARRAY(8, vertYOrigins); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ot.h b/src/java.desktop/share/native/libharfbuzz/hb-ot.h index db784694c6ab3..f2dbaa1b317e7 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-ot.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-ot.h @@ -35,6 +35,8 @@ #include "hb-ot-font.h" #include "hb-ot-layout.h" #include "hb-ot-math.h" +#include "hb-ot-meta.h" +#include "hb-ot-metrics.h" #include "hb-ot-name.h" #include "hb-ot-shape.h" #include "hb-ot-var.h" diff --git a/src/java.desktop/share/native/libharfbuzz/hb-pool.hh b/src/java.desktop/share/native/libharfbuzz/hb-pool.hh new file mode 100644 index 0000000000000..a2266a3846184 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-pool.hh @@ -0,0 +1,100 @@ +/* + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_POOL_HH +#define HB_POOL_HH + +#include "hb.hh" + +/* Memory pool for persistent allocation of small objects. */ + +template +struct hb_pool_t +{ + hb_pool_t () : next (nullptr) {} + ~hb_pool_t () { fini (); } + + void fini () + { + next = nullptr; + + for (chunk_t *_ : chunks) ::free (_); + + chunks.fini (); + } + + T* alloc () + { + if (unlikely (!next)) + { + if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr; + chunk_t *chunk = (chunk_t *) calloc (1, sizeof (chunk_t)); + if (unlikely (!chunk)) return nullptr; + chunks.push (chunk); + next = chunk->thread (); + } + + T* obj = next; + next = * ((T**) next); + + memset (obj, 0, sizeof (T)); + + return obj; + } + + void free (T* obj) + { + * (T**) obj = next; + next = obj; + } + + private: + + static_assert (ChunkLen > 1, ""); + static_assert (sizeof (T) >= sizeof (void *), ""); + static_assert (alignof (T) % alignof (void *) == 0, ""); + + struct chunk_t + { + T* thread () + { + for (unsigned i = 0; i < ARRAY_LENGTH (arrayZ) - 1; i++) + * (T**) &arrayZ[i] = &arrayZ[i + 1]; + + * (T**) &arrayZ[ARRAY_LENGTH (arrayZ) - 1] = nullptr; + + return arrayZ; + } + + T arrayZ[ChunkLen]; + }; + + T* next; + hb_vector_t chunks; +}; + + +#endif /* HB_POOL_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh new file mode 100644 index 0000000000000..0e293e9eb1b41 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-sanitize.hh @@ -0,0 +1,412 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SANITIZE_HH +#define HB_SANITIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-dispatch.hh" + + +/* + * Sanitize + * + * + * === Introduction === + * + * The sanitize machinery is at the core of our zero-cost font loading. We + * mmap() font file into memory and create a blob out of it. Font subtables + * are returned as a readonly sub-blob of the main font blob. These table + * blobs are then sanitized before use, to ensure invalid memory access does + * not happen. The toplevel sanitize API use is like, eg. to load the 'head' + * table: + * + * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table (face); + * + * The blob then can be converted to a head table struct with: + * + * const head *head_table = head_blob->as (); + * + * What the reference_table does is, to call hb_face_reference_table() to load + * the table blob, sanitize it and return either the sanitized blob, or empty + * blob if sanitization failed. The blob->as() function returns the null + * object of its template type argument if the blob is empty. Otherwise, it + * just casts the blob contents to the desired type. + * + * Sanitizing a blob of data with a type T works as follows (with minor + * simplification): + * + * - Cast blob content to T*, call sanitize() method of it, + * - If sanitize succeeded, return blob. + * - Otherwise, if blob is not writable, try making it writable, + * or copy if cannot be made writable in-place, + * - Call sanitize() again. Return blob if sanitize succeeded. + * - Return empty blob otherwise. + * + * + * === The sanitize() contract === + * + * The sanitize() method of each object type shall return true if it's safe to + * call other methods of the object, and false otherwise. + * + * Note that what sanitize() checks for might align with what the specification + * describes as valid table data, but does not have to be. In particular, we + * do NOT want to be pedantic and concern ourselves with validity checks that + * are irrelevant to our use of the table. On the contrary, we want to be + * lenient with error handling and accept invalid data to the extent that it + * does not impose extra burden on us. + * + * Based on the sanitize contract, one can see that what we check for depends + * on how we use the data in other table methods. Ie. if other table methods + * assume that offsets do NOT point out of the table data block, then that's + * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On + * the other hand, if other methods do such checks themselves, then sanitize() + * does not have to bother with them (glyf/local work this way). The choice + * depends on the table structure and sanitize() performance. For example, to + * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard + * to avoid such costs during font loading. By postponing such checks to the + * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime + * cost to O(used-glyphs). As such, this is preferred. + * + * The same argument can be made re GSUB/GPOS/GDEF, but there, the table + * structure is so complicated that by checking all offsets at sanitize() time, + * we make the code much simpler in other methods, as offsets and referenced + * objects do not need to be validated at each use site. + */ + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif +#ifndef HB_SANITIZE_MAX_OPS_FACTOR +#define HB_SANITIZE_MAX_OPS_FACTOR 8 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MAX +#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF +#endif +#ifndef HB_SANITIZE_MAX_SUTABLES +#define HB_SANITIZE_MAX_SUTABLES 0x4000 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t +{ + hb_sanitize_context_t () : + start (nullptr), end (nullptr), + max_ops (0), max_subtables (0), + writable (false), edit_count (0), + blob (nullptr), + num_glyphs (65536), + num_glyphs_set (false) {} + + const char *get_name () { return "SANITIZE"; } + template + bool may_dispatch (const T *obj HB_UNUSED, const F *format) + { return format->sanitize (this); } + static return_t default_return_value () { return true; } + static return_t no_dispatch_return_value () { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + bool visit_subtables (unsigned count) + { + max_subtables += count; + return max_subtables < HB_SANITIZE_MAX_SUTABLES; + } + + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.sanitize (this, hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, hb_forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) + + + void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + void set_num_glyphs (unsigned int num_glyphs_) + { + num_glyphs = num_glyphs_; + num_glyphs_set = true; + } + unsigned int get_num_glyphs () { return num_glyphs; } + + void set_max_ops (int max_ops_) { max_ops = max_ops_; } + + template + void set_object (const T *obj) + { + reset_object (); + + if (!obj) return; + + const char *obj_start = (const char *) obj; + if (unlikely (obj_start < this->start || this->end <= obj_start)) + this->start = this->end = nullptr; + else + { + this->start = obj_start; + this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ()); + } + } + + void reset_object () + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + assert (this->start <= this->end); /* Must not overflow. */ + } + + void start_processing () + { + reset_object (); + if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR))) + this->max_ops = HB_SANITIZE_MAX_OPS_MAX; + else + this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + (unsigned) HB_SANITIZE_MAX_OPS_MIN, + (unsigned) HB_SANITIZE_MAX_OPS_MAX); + this->edit_count = 0; + this->debug_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + void end_processing () + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = nullptr; + this->start = this->end = nullptr; + } + + unsigned get_edit_count () { return edit_count; } + + bool check_range (const void *base, + unsigned int len) const + { + const char *p = (const char *) base; + bool ok = !len || + (this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len && + this->max_ops-- > 0); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p]" + " (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b); + } + + template + bool check_range (const T *base, + unsigned int a, + unsigned int b, + unsigned int c) const + { + return !hb_unsigned_mul_overflows (a, b) && + this->check_range (base, a * b, c); + } + + template + bool check_array (const T *base, unsigned int len) const + { + return this->check_range (base, len, hb_static_size (T)); + } + + template + bool check_array (const T *base, + unsigned int a, + unsigned int b) const + { + return this->check_range (base, a, b, hb_static_size (T)); + } + + template + bool check_struct (const Type *obj) const + { return likely (this->check_range (obj, obj->min_size)); } + + bool may_edit (const void *base, unsigned int len) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template + bool try_set (const Type *obj, const ValueType &v) + { + if (this->may_edit (obj, hb_static_size (Type))) + { + * const_cast (obj) = v; + return true; + } + return false; + } + + template + hb_blob_t *sanitize_blob (hb_blob_t *blob) + { + bool sane; + + init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, start, "start"); + + start_processing (); + + if (unlikely (!start)) + { + end_processing (); + return blob; + } + + Type *t = reinterpret_cast (const_cast (start)); + + sane = t->sanitize (this); + if (sane) + { + if (edit_count) + { + DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); + + /* sanitize again to ensure no toe-stepping */ + edit_count = 0; + sane = t->sanitize (this); + if (edit_count) { + DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); + sane = false; + } + } + } + else + { + if (edit_count && !writable) { + start = hb_blob_get_data_writable (blob, nullptr); + end = start + blob->length; + + if (start) + { + writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, start, "retry"); + goto retry; + } + } + } + + end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); + if (sane) + { + hb_blob_make_immutable (blob); + return blob; + } + else + { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + template + hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) + { + if (!num_glyphs_set) + set_num_glyphs (hb_face_get_glyph_count (face)); + return sanitize_blob (hb_face_reference_table (face, tableTag)); + } + + const char *start, *end; + mutable int max_ops, max_subtables; + private: + bool writable; + unsigned int edit_count; + hb_blob_t *blob; + unsigned int num_glyphs; + bool num_glyphs_set; +}; + +struct hb_sanitize_with_object_t +{ + template + hb_sanitize_with_object_t (hb_sanitize_context_t *c, const T& obj) : c (c) + { c->set_object (obj); } + ~hb_sanitize_with_object_t () + { c->reset_object (); } + + private: + hb_sanitize_context_t *c; +}; + + +#endif /* HB_SANITIZE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh new file mode 100644 index 0000000000000..e0ceae2c4a18a --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-serialize.hh @@ -0,0 +1,553 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * Copyright © 2019 Facebook, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_SERIALIZE_HH +#define HB_SERIALIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-map.hh" +#include "hb-pool.hh" + + +/* + * Serialize + */ + +struct hb_serialize_context_t +{ + typedef unsigned objidx_t; + + enum whence_t { + Head, /* Relative to the current object head (default). */ + Tail, /* Relative to the current object tail after packed. */ + Absolute /* Absolute: from the start of the serialize buffer. */ + }; + + struct object_t + { + void fini () { links.fini (); } + + bool operator == (const object_t &o) const + { + return (tail - head == o.tail - o.head) + && (links.length == o.links.length) + && 0 == hb_memcmp (head, o.head, tail - head) + && links.as_bytes () == o.links.as_bytes (); + } + uint32_t hash () const + { + return hb_bytes_t (head, tail - head).hash () ^ + links.as_bytes ().hash (); + } + + struct link_t + { + bool is_wide: 1; + bool is_signed: 1; + unsigned whence: 2; + unsigned position: 28; + unsigned bias; + objidx_t objidx; + }; + + char *head; + char *tail; + hb_vector_t links; + object_t *next; + }; + + struct snapshot_t + { + char *head; + char *tail; + object_t *current; // Just for sanity check + unsigned num_links; + }; + + snapshot_t snapshot () + { return snapshot_t { head, tail, current, current->links.length }; } + + hb_serialize_context_t (void *start_, unsigned int size) : + start ((char *) start_), + end (start + size), + current (nullptr) + { reset (); } + ~hb_serialize_context_t () { fini (); } + + void fini () + { + for (object_t *_ : ++hb_iter (packed)) _->fini (); + packed.fini (); + this->packed_map.fini (); + + while (current) + { + auto *_ = current; + current = current->next; + _->fini (); + } + object_pool.fini (); + } + + bool in_error () const { return !this->successful; } + + void reset () + { + this->successful = true; + this->ran_out_of_room = false; + this->head = this->start; + this->tail = this->end; + this->debug_depth = 0; + + fini (); + this->packed.push (nullptr); + } + + bool check_success (bool success) + { return this->successful && (success || (err_other_error (), false)); } + + template + bool check_equal (T1 &&v1, T2 &&v2) + { return check_success ((long long) v1 == (long long) v2); } + + template + bool check_assign (T1 &v1, T2 &&v2) + { return check_equal (v1 = v2, v2); } + + template bool propagate_error (T &&obj) + { return check_success (!hb_deref (obj).in_error ()); } + + template bool propagate_error (T1 &&o1, Ts&&... os) + { return propagate_error (hb_forward (o1)) && + propagate_error (hb_forward (os)...); } + + /* To be called around main operation. */ + template + Type *start_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + assert (!current); + return push (); + } + void end_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %u bytes; %s", + this->start, this->end, + (unsigned) (this->head - this->start), + this->successful ? "successful" : "UNSUCCESSFUL"); + + propagate_error (packed, packed_map); + + if (unlikely (!current)) return; + if (unlikely (in_error())) return; + + assert (!current->next); + + /* Only "pack" if there exist other objects... Otherwise, don't bother. + * Saves a move. */ + if (packed.length <= 1) + return; + + pop_pack (false); + + resolve_links (); + } + + template + Type *push () + { + if (unlikely (in_error ())) return start_embed (); + + object_t *obj = object_pool.alloc (); + if (unlikely (!obj)) + check_success (false); + else + { + obj->head = head; + obj->tail = tail; + obj->next = current; + current = obj; + } + return start_embed (); + } + void pop_discard () + { + object_t *obj = current; + if (unlikely (!obj)) return; + if (unlikely (in_error())) return; + + current = current->next; + revert (obj->head, obj->tail); + obj->fini (); + object_pool.free (obj); + } + + /* Set share to false when an object is unlikely sharable with others + * so not worth an attempt, or a contiguous table is serialized as + * multiple consecutive objects in the reverse order so can't be shared. + */ + objidx_t pop_pack (bool share=true) + { + object_t *obj = current; + if (unlikely (!obj)) return 0; + if (unlikely (in_error())) return 0; + + current = current->next; + obj->tail = head; + obj->next = nullptr; + unsigned len = obj->tail - obj->head; + head = obj->head; /* Rewind head. */ + + if (!len) + { + assert (!obj->links.length); + return 0; + } + + objidx_t objidx; + if (share) + { + objidx = packed_map.get (obj); + if (objidx) + { + obj->fini (); + return objidx; + } + } + + tail -= len; + memmove (tail, obj->head, len); + + obj->head = tail; + obj->tail = tail + len; + + packed.push (obj); + + if (unlikely (packed.in_error ())) { + // obj wasn't successfully added to packed, so clean it up otherwise it's + // links will be leaked. + propagate_error (packed); + obj->fini (); + return 0; + } + + objidx = packed.length - 1; + + if (share) packed_map.set (obj, objidx); + propagate_error (packed_map); + + return objidx; + } + + void revert (snapshot_t snap) + { + if (unlikely (in_error ())) return; + assert (snap.current == current); + current->links.shrink (snap.num_links); + revert (snap.head, snap.tail); + } + + void revert (char *snap_head, + char *snap_tail) + { + if (unlikely (in_error ())) return; + assert (snap_head <= head); + assert (tail <= snap_tail); + head = snap_head; + tail = snap_tail; + discard_stale_objects (); + } + + void discard_stale_objects () + { + if (unlikely (in_error ())) return; + while (packed.length > 1 && + packed.tail ()->head < tail) + { + packed_map.del (packed.tail ()); + assert (!packed.tail ()->next); + packed.tail ()->fini (); + packed.pop (); + } + if (packed.length > 1) + assert (packed.tail ()->head == tail); + } + + template + void add_link (T &ofs, objidx_t objidx, + whence_t whence = Head, + unsigned bias = 0) + { + static_assert (sizeof (T) == 2 || sizeof (T) == 4, ""); + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + assert (current->head <= (const char *) &ofs); + + auto& link = *current->links.push (); + + link.is_wide = sizeof (T) == 4; + link.is_signed = hb_is_signed (hb_unwrap_type (T)); + link.whence = (unsigned) whence; + link.position = (const char *) &ofs - current->head; + link.bias = bias; + link.objidx = objidx; + } + + unsigned to_bias (const void *base) const + { + if (unlikely (in_error ())) return 0; + if (!base) return 0; + assert (current); + assert (current->head <= (const char *) base); + return (const char *) base - current->head; + } + + void resolve_links () + { + if (unlikely (in_error ())) return; + + assert (!current); + assert (packed.length > 1); + + for (const object_t* parent : ++hb_iter (packed)) + for (const object_t::link_t &link : parent->links) + { + const object_t* child = packed[link.objidx]; + if (unlikely (!child)) { err_other_error(); return; } + unsigned offset = 0; + switch ((whence_t) link.whence) { + case Head: offset = child->head - parent->head; break; + case Tail: offset = child->head - parent->tail; break; + case Absolute: offset = (head - start) + (child->head - tail); break; + } + + assert (offset >= link.bias); + offset -= link.bias; + if (link.is_signed) + { + if (link.is_wide) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + else + { + if (link.is_wide) + assign_offset (parent, link, offset); + else + assign_offset (parent, link, offset); + } + } + } + + unsigned int length () const + { + if (unlikely (!current)) return 0; + return this->head - current->head; + } + + void align (unsigned int alignment) + { + unsigned int l = length () % alignment; + if (l) + allocate_size (alignment - l); + } + + template + Type *start_embed (const Type *obj HB_UNUSED = nullptr) const + { return reinterpret_cast (this->head); } + template + Type *start_embed (const Type &obj) const + { return start_embed (hb_addressof (obj)); } + + /* Following two functions exist to allow setting breakpoint on. */ + void err_ran_out_of_room () { this->ran_out_of_room = true; } + void err_other_error () { this->successful = false; } + + template + Type *allocate_size (unsigned int size) + { + if (unlikely (!this->successful)) return nullptr; + + if (this->tail - this->head < ptrdiff_t (size)) + { + err_ran_out_of_room (); + this->successful = false; + return nullptr; + } + memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast (ret); + } + + template + Type *allocate_min () + { return this->allocate_size (Type::min_size); } + + template + Type *embed (const Type *obj) + { + unsigned int size = obj->get_size (); + Type *ret = this->allocate_size (size); + if (unlikely (!ret)) return nullptr; + memcpy (ret, obj, size); + return ret; + } + template + Type *embed (const Type &obj) + { return embed (hb_addressof (obj)); } + + template auto + _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN + (Type *, src.copy (this, hb_forward (ds)...)) + + template auto + _copy (const Type &src, hb_priority<0>) -> decltype (&(hb_declval () = src)) + { + Type *ret = this->allocate_size (sizeof (Type)); + if (unlikely (!ret)) return nullptr; + *ret = src; + return ret; + } + + /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data + * instead of memcpy(). */ + template + Type *copy (const Type &src, Ts&&... ds) + { return _copy (src, hb_prioritize, hb_forward (ds)...); } + template + Type *copy (const Type *src, Ts&&... ds) + { return copy (*src, hb_forward (ds)...); } + + template + void copy_all (Iterator it, Ts&&... ds) + { for (decltype (*it) _ : it) copy (_, hb_forward (ds)...); } + + template + hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } + + template + Type *extend_size (Type *obj, unsigned int size) + { + if (unlikely (in_error ())) return nullptr; + + assert (this->start <= (char *) obj); + assert ((char *) obj <= this->head); + assert ((char *) obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) obj) + size - this->head))) return nullptr; + return reinterpret_cast (obj); + } + template + Type *extend_size (Type &obj, unsigned int size) + { return extend_size (hb_addressof (obj), size); } + + template + Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); } + template + Type *extend_min (Type &obj) { return extend_min (hb_addressof (obj)); } + + template + Type *extend (Type *obj, Ts&&... ds) + { return extend_size (obj, obj->get_size (hb_forward (ds)...)); } + template + Type *extend (Type &obj, Ts&&... ds) + { return extend (hb_addressof (obj), hb_forward (ds)...); } + + /* Output routines. */ + hb_bytes_t copy_bytes () const + { + assert (this->successful); + /* Copy both items from head side and tail side... */ + unsigned int len = (this->head - this->start) + + (this->end - this->tail); + + char *p = (char *) malloc (len); + if (unlikely (!p)) return hb_bytes_t (); + + memcpy (p, this->start, this->head - this->start); + memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + return hb_bytes_t (p, len); + } + template + Type *copy () const + { return reinterpret_cast ((char *) copy_bytes ().arrayZ); } + hb_blob_t *copy_blob () const + { + hb_bytes_t b = copy_bytes (); + return hb_blob_create (b.arrayZ, b.length, + HB_MEMORY_MODE_WRITABLE, + (char *) b.arrayZ, free); + } + + private: + template + void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) + { + auto &off = * ((BEInt *) (parent->head + link.position)); + assert (0 == off); + check_assign (off, offset); + } + + public: /* TODO Make private. */ + char *start, *head, *tail, *end; + unsigned int debug_depth; + bool successful; + bool ran_out_of_room; + + private: + + /* Object memory pool. */ + hb_pool_t object_pool; + + /* Stack of currently under construction objects. */ + object_t *current; + + /* Stack of packed objects. Object 0 is always nil object. */ + hb_vector_t packed; + + /* Map view of packed objects. */ + hb_hashmap_t packed_map; +}; + + +#endif /* HB_SERIALIZE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.cc b/src/java.desktop/share/native/libharfbuzz/hb-set.cc index 7dff3319963ca..fb40368965eec 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.cc @@ -69,7 +69,7 @@ hb_set_create () hb_set_t * hb_set_get_empty () { - return const_cast (&Null(hb_set_t)); + return const_cast (&Null (hb_set_t)); } /** @@ -389,6 +389,7 @@ hb_set_symmetric_difference (hb_set_t *set, set->symmetric_difference (other); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_set_invert: * @set: a set. @@ -403,6 +404,7 @@ void hb_set_invert (hb_set_t *set HB_UNUSED) { } +#endif /** * hb_set_get_population: @@ -477,7 +479,7 @@ hb_set_next (const hb_set_t *set, * @set: a set. * @codepoint: (inout): * - * Gets the previous number in @set that is slower than current value of @codepoint. + * Gets the previous number in @set that is lower than current value of @codepoint. * * Set @codepoint to %HB_SET_VALUE_INVALID to get started. * @@ -522,7 +524,7 @@ hb_set_next_range (const hb_set_t *set, * @last: (out): output last codepoint in the range. * * Gets the previous consecutive range of numbers in @set that - * are greater than current value of @last. + * are less than current value of @first. * * Set @first to %HB_SET_VALUE_INVALID to get started. * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-set.hh b/src/java.desktop/share/native/libharfbuzz/hb-set.hh index 6b426949eb1cf..93ba0d0aa7449 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-set.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-set.hh @@ -28,6 +28,7 @@ #define HB_SET_HH #include "hb.hh" +#include "hb-machinery.hh" /* @@ -39,7 +40,7 @@ struct hb_set_t { - HB_NO_COPY_ASSIGN (hb_set_t); + HB_DELETE_COPY_ASSIGN (hb_set_t); hb_set_t () { init (); } ~hb_set_t () { fini (); } @@ -69,7 +70,7 @@ struct hb_set_t void add (hb_codepoint_t g) { elt (g) |= mask (g); } void del (hb_codepoint_t g) { elt (g) &= ~mask (g); } - bool has (hb_codepoint_t g) const { return !!(elt (g) & mask (g)); } + bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } void add_range (hb_codepoint_t a, hb_codepoint_t b) { @@ -88,6 +89,23 @@ struct hb_set_t } } + void del_range (hb_codepoint_t a, hb_codepoint_t b) + { + elt_t *la = &elt (a); + elt_t *lb = &elt (b); + if (la == lb) + *la &= ~((mask (b) << 1) - mask(a)); + else + { + *la &= mask (a) - 1; + la++; + + memset (la, 0, (char *) lb - (char *) la); + + *lb &= ~((mask (b) << 1) - 1); + } + } + bool is_equal (const page_t *other) const { return 0 == hb_memcmp (&v, &other->v, sizeof (v)); @@ -134,13 +152,22 @@ struct hb_set_t unsigned int i = m / ELT_BITS; unsigned int j = m & ELT_MASK; - const elt_t vv = v[i] & ((elt_t (1) << (j + 1)) - 1); - for (const elt_t *p = &vv; (int) i >= 0; p = &v[--i]) + /* Fancy mask to avoid shifting by elt_t bitsize, which is undefined. */ + const elt_t mask = j < 8 * sizeof (elt_t) - 1 ? + ((elt_t (1) << (j + 1)) - 1) : + (elt_t) -1; + const elt_t vv = v[i] & mask; + const elt_t *p = &vv; + while (true) + { if (*p) { *codepoint = i * ELT_BITS + elt_get_max (*p); return true; } + if ((int) i <= 0) break; + p = &v[--i]; + } *codepoint = INVALID; return false; @@ -186,7 +213,7 @@ struct hb_set_t hb_object_header_t header; bool successful; /* Allocations successful */ mutable unsigned int population; - hb_vector_t page_map; + hb_sorted_vector_t page_map; hb_vector_t pages; void init_shallow () @@ -227,11 +254,18 @@ struct hb_set_t return true; } - void clear () + void reset () { if (unlikely (hb_object_is_immutable (this))) return; + clear (); successful = true; + } + + void clear () + { + if (unlikely (hb_object_is_immutable (this))) + return; population = 0; page_map.resize (0); pages.resize (0); @@ -245,7 +279,7 @@ struct hb_set_t return true; } - void dirty () { population = (unsigned int) -1; } + void dirty () { population = UINT_MAX; } void add (hb_codepoint_t g) { @@ -301,7 +335,7 @@ struct hb_set_t { page->add (g); - array = (const T *) ((const char *) array + stride); + array = &StructAtOffsetUnaligned (array, stride); count--; } while (count && (g = *array, start <= g && g < end)); @@ -349,23 +383,79 @@ struct hb_set_t dirty (); page->del (g); } + + private: + void del_pages (int ds, int de) + { + if (ds <= de) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < page_map.length; i++) + { + int m = (int) page_map[i].major; + if (m < ds || de < m) + page_map[write_index++] = page_map[i]; + } + compact (write_index); + resize (write_index); + } + } + + public: void del_range (hb_codepoint_t a, hb_codepoint_t b) { /* TODO perform op even if !successful. */ - /* TODO Optimize, like add_range(). */ if (unlikely (!successful)) return; - for (unsigned int i = a; i < b + 1; i++) - del (i); + if (unlikely (a > b || a == INVALID || b == INVALID)) return; + dirty (); + unsigned int ma = get_major (a); + unsigned int mb = get_major (b); + /* Delete pages from ds through de if ds <= de. */ + int ds = (a == major_start (ma))? (int) ma: (int) (ma + 1); + int de = (b + 1 == major_start (mb + 1))? (int) mb: ((int) mb - 1); + if (ds > de || (int) ma < ds) + { + page_t *page = page_for (a); + if (page) + { + if (ma == mb) + page->del_range (a, b); + else + page->del_range (a, major_start (ma + 1) - 1); + } + } + if (de < (int) mb && ma != mb) + { + page_t *page = page_for (b); + if (page) + page->del_range (major_start (mb), b); + } + del_pages (ds, de); } - bool has (hb_codepoint_t g) const + + bool get (hb_codepoint_t g) const { const page_t *page = page_for (g); if (!page) return false; - return page->has (g); + return page->get (g); } - bool intersects (hb_codepoint_t first, - hb_codepoint_t last) const + + /* Has interface. */ + static constexpr bool SENTINEL = false; + typedef bool value_t; + value_t operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; } + /* Predicate. */ + bool operator () (hb_codepoint_t k) const { return has (k); } + + /* Sink interface. */ + hb_set_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_set_t& operator << (const hb_pair_t& range) + { add_range (range.first, range.second); return *this; } + + bool intersects (hb_codepoint_t first, hb_codepoint_t last) const { hb_codepoint_t c = first - 1; return next (&c) && c <= last; @@ -422,8 +512,36 @@ struct hb_set_t return true; } - template - void process (const hb_set_t *other) + void compact (unsigned int length) + { + hb_vector_t old_index_to_page_map_index; + old_index_to_page_map_index.resize(pages.length); + for (uint32_t i = 0; i < old_index_to_page_map_index.length; i++) + old_index_to_page_map_index[i] = 0xFFFFFFFF; + + for (uint32_t i = 0; i < length; i++) + old_index_to_page_map_index[page_map[i].index] = i; + + compact_pages (old_index_to_page_map_index); + } + + void compact_pages (const hb_vector_t& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + + template + void process (const Op& op, const hb_set_t *other) { if (unlikely (!successful)) return; @@ -435,10 +553,22 @@ struct hb_set_t unsigned int count = 0, newCount = 0; unsigned int a = 0, b = 0; + unsigned int write_index = 0; for (; a < na && b < nb; ) { if (page_map[a].major == other->page_map[b].major) { + if (!Op::passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + count++; a++; b++; @@ -461,9 +591,16 @@ struct hb_set_t if (Op::passthru_right) count += nb - b; - if (count > pages.length) - if (!resize (count)) - return; + if (!Op::passthru_left) + { + na = write_index; + next_page = write_index; + compact (write_index); + } + + if (!resize (count)) + return; + newCount = count; /* Process in-place backward. */ @@ -477,7 +614,7 @@ struct hb_set_t b--; count--; page_map[count] = page_map[a]; - Op::process (page_at (count).v, page_at (a).v, other->page_at (b).v); + page_at (count).v = op (page_at (a).v, other->page_at (b).v); } else if (page_map[a - 1].major > other->page_map[b - 1].major) { @@ -523,19 +660,19 @@ struct hb_set_t void union_ (const hb_set_t *other) { - process (other); + process (hb_bitwise_or, other); } void intersect (const hb_set_t *other) { - process (other); + process (hb_bitwise_and, other); } void subtract (const hb_set_t *other) { - process (other); + process (hb_bitwise_sub, other); } void symmetric_difference (const hb_set_t *other) { - process (other); + process (hb_bitwise_xor, other); } bool next (hb_codepoint_t *codepoint) const { @@ -638,7 +775,7 @@ struct hb_set_t unsigned int get_population () const { - if (population != (unsigned int) -1) + if (population != UINT_MAX) return population; unsigned int pop = 0; @@ -671,27 +808,36 @@ struct hb_set_t /* * Iterator implementation. */ - struct const_iter_t : hb_sorted_iter_t + struct iter_t : hb_iter_with_fallback_t { - const_iter_t (const hb_set_t &s_) : - s (s_), v (INVALID), l (s.get_population () + 1) { __next__ (); } + static constexpr bool is_sorted_iterator = true; + iter_t (const hb_set_t &s_ = Null (hb_set_t), + bool init = true) : s (&s_), v (INVALID), l(0) + { + if (init) + { + l = s->get_population () + 1; + __next__ (); + } + } - typedef hb_codepoint_t __item_type__; + typedef hb_codepoint_t __item_t__; hb_codepoint_t __item__ () const { return v; } bool __more__ () const { return v != INVALID; } - void __next__ () { s.next (&v); if (l) l--; } - void __prev__ () { s.previous (&v); } - unsigned __len__ () { return l; } + void __next__ () { s->next (&v); if (l) l--; } + void __prev__ () { s->previous (&v); } + unsigned __len__ () const { return l; } + iter_t end () const { return iter_t (*s, false); } + bool operator != (const iter_t& o) const + { return s != o.s || v != o.v; } protected: - const hb_set_t &s; + const hb_set_t *s; hb_codepoint_t v; unsigned l; }; - const_iter_t const_iter () const { return const_iter_t (*this); } - operator const_iter_t () const { return const_iter (); } - typedef const_iter_t iter_t; - iter_t iter () const { return const_iter (); } + iter_t iter () const { return iter_t (*this); } + operator iter_t () const { return iter (); } protected: diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc index 800cf0a12f5b2..3243effb3a5af 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.cc @@ -79,7 +79,9 @@ hb_shape_plan_key_t::init (bool copy, } this->shaper_func = nullptr; this->shaper_name = nullptr; +#ifndef HB_NO_OT_SHAPE this->ot.init (face, coords, num_coords); +#endif /* * Choose shaper. @@ -148,7 +150,9 @@ hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other) { return hb_segment_properties_equal (&this->props, &other->props) && this->user_features_match (other) && +#ifndef HB_NO_OT_SHAPE this->ot.equal (&other->ot) && +#endif this->shaper_func == other->shaper_func; } @@ -224,12 +228,16 @@ hb_shape_plan_create2 (hb_face_t *face, num_coords, shaper_list))) goto bail2; +#ifndef HB_NO_OT_SHAPE if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key))) goto bail3; +#endif return shape_plan; +#ifndef HB_NO_OT_SHAPE bail3: +#endif shape_plan->key.free (); bail2: free (shape_plan); @@ -249,7 +257,7 @@ hb_shape_plan_create2 (hb_face_t *face, hb_shape_plan_t * hb_shape_plan_get_empty () { - return const_cast (&Null(hb_shape_plan_t)); + return const_cast (&Null (hb_shape_plan_t)); } /** @@ -281,7 +289,9 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) { if (!hb_object_destroy (shape_plan)) return; +#ifndef HB_NO_OT_SHAPE shape_plan->ot.fini (); +#endif shape_plan->key.free (); free (shape_plan); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh index a99cfc3eea796..debe6596330b0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape-plan.hh @@ -39,21 +39,23 @@ struct hb_shape_plan_key_t const hb_feature_t *user_features; unsigned int num_user_features; +#ifndef HB_NO_OT_SHAPE hb_ot_shape_plan_key_t ot; +#endif hb_shape_func_t *shaper_func; const char *shaper_name; - HB_INTERNAL inline bool init (bool copy, - hb_face_t *face, - const hb_segment_properties_t *props, - const hb_feature_t *user_features, - unsigned int num_user_features, - const int *coords, - unsigned int num_coords, - const char * const *shaper_list); + HB_INTERNAL bool init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); - HB_INTERNAL inline void free () { ::free ((void *) user_features); } + HB_INTERNAL void free () { ::free ((void *) user_features); } HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other); @@ -65,7 +67,9 @@ struct hb_shape_plan_t hb_object_header_t header; hb_face_t *face_unsafe; /* We don't carry a reference to face. */ hb_shape_plan_key_t key; +#ifndef HB_NO_OT_SHAPE hb_ot_shape_plan_t ot; +#endif }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc index 90876d658bd3c..5dc55febb02b6 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shape.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shape.cc @@ -132,6 +132,8 @@ hb_shape_full (hb_font_t *font, unsigned int num_features, const char * const *shaper_list) { + if (unlikely (hb_object_is_immutable (buffer))) return false; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props, features, num_features, font->coords, font->num_coords, @@ -154,7 +156,9 @@ hb_shape_full (hb_font_t *font, * * Shapes @buffer using @font turning its Unicode characters content to * positioned glyphs. If @features is not %NULL, it will be used to control the - * features applied during shaping. + * features applied during shaping. If two @features have the same tag but + * overlapping ranges the value of the feature with the higher index takes + * precedence. * * Since: 0.9.2 **/ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh index 36d8fc7f6608a..0d63933a7667e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper-list.hh @@ -28,6 +28,9 @@ #define HB_SHAPER_LIST_HH #endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */ +#ifndef HB_NO_SHAPER + + /* v--- Add new shapers in the right place here. */ #ifdef HAVE_GRAPHITE2 @@ -35,7 +38,9 @@ HB_SHAPER_IMPLEMENT (graphite2) #endif +#ifndef HB_NO_OT_SHAPE HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */ +#endif #ifdef HAVE_UNISCRIBE HB_SHAPER_IMPLEMENT (uniscribe) @@ -45,12 +50,11 @@ HB_SHAPER_IMPLEMENT (directwrite) #endif #ifdef HAVE_CORETEXT HB_SHAPER_IMPLEMENT (coretext) - -/* Only picks up fonts that have a "mort" or "morx" table. - Probably going to be removed https://github.com/harfbuzz/harfbuzz/issues/1478 */ -HB_SHAPER_IMPLEMENT (coretext_aat) #endif -#ifdef HAVE_FALLBACK +#ifndef HB_NO_FALLBACK_SHAPE HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ #endif + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc b/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc index 158b454bf1d79..b1d76dcc4eedc 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper.cc @@ -34,6 +34,9 @@ static const hb_shaper_entry_t all_shapers[] = { #include "hb-shaper-list.hh" #undef HB_SHAPER_IMPLEMENT }; +#ifndef HB_NO_SHAPER +static_assert (0 != ARRAY_LENGTH_CONST (all_shapers), "No shaper enabled."); +#endif #if HB_USE_ATEXIT static void free_static_shapers (); diff --git a/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh b/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh index 37ca9ffb66538..0c9b681519da1 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-shaper.hh @@ -102,7 +102,7 @@ template struct hb_shaper_object static void destroy (Type *p) { HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (p); } \ }; \ \ - static_assert (true, "") /* Require semicolon. */ + static_assert (true, "") /* Require semicolon after. */ template diff --git a/src/java.desktop/share/native/libharfbuzz/hb-static.cc b/src/java.desktop/share/native/libharfbuzz/hb-static.cc index 4c515886024be..b213c57c7e57f 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-static.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-static.cc @@ -37,9 +37,10 @@ #include "hb-ot-maxp-table.hh" #ifndef HB_NO_VISIBILITY +#include "hb-ot-name-language-static.hh" -hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; -/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; @@ -50,6 +51,9 @@ DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF}; const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF}; + +/* hb_face_t */ + unsigned int hb_face_t::load_num_glyphs () const { @@ -72,4 +76,37 @@ hb_face_t::load_upem () const return ret; } + +/* hb_user_data_array_t */ + +bool +hb_user_data_array_t::set (hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + if (!key) + return false; + + if (replace) { + if (!data && !destroy) { + items.remove (key, lock); + return true; + } + } + hb_user_data_item_t item = {key, data, destroy}; + bool ret = !!items.replace_or_insert (item, lock, (bool) replace); + + return ret; +} + +void * +hb_user_data_array_t::get (hb_user_data_key_t *key) +{ + hb_user_data_item_t item = {nullptr, nullptr, nullptr}; + + return items.find (key, &item, lock) ? item.data : nullptr; +} + + #endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh index dffa9f3beba4d..9d00cae6c3247 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-string-array.hh @@ -37,6 +37,7 @@ #define HB_STRING_ARRAY_TYPE_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t) #define HB_STRING_ARRAY_POOL_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr) #define HB_STRING_ARRAY_OFFS_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx) +#define HB_STRING_ARRAY_LENG_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _length) static const union HB_STRING_ARRAY_TYPE_NAME { struct { @@ -48,7 +49,7 @@ static const union HB_STRING_ARRAY_TYPE_NAME { #include HB_STRING_ARRAY_LIST #undef _S } st; - char str[VAR]; + char str[HB_VAR_ARRAY]; } HB_STRING_ARRAY_POOL_NAME = { @@ -66,6 +67,8 @@ static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = sizeof (HB_STRING_ARRAY_TYPE_NAME) }; +static const unsigned int HB_STRING_ARRAY_LENG_NAME = ARRAY_LENGTH_CONST (HB_STRING_ARRAY_OFFS_NAME) - 1; + static inline hb_bytes_t HB_STRING_ARRAY_NAME (unsigned int i) { @@ -77,5 +80,6 @@ HB_STRING_ARRAY_NAME (unsigned int i) #undef HB_STRING_ARRAY_TYPE_NAME #undef HB_STRING_ARRAY_POOL_NAME #undef HB_STRING_ARRAY_OFFS_NAME +#undef HB_STRING_ARRAY_LENG_NAME #endif /* HB_STRING_ARRAY_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-style.cc b/src/java.desktop/share/native/libharfbuzz/hb-style.cc new file mode 100644 index 0000000000000..50f9df9a2827e --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-style.cc @@ -0,0 +1,135 @@ +/* + * Copyright © 2019 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.hh" + +#ifndef HB_NO_STYLE +#ifdef HB_EXPERIMENTAL_API + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-face.hh" + +/** + * hb_style_tag_t: + * @HB_STYLE_TAG_ITALIC: Used to vary between non-italic and italic. + * A value of 0 can be interpreted as "Roman" (non-italic); a value of 1 can + * be interpreted as (fully) italic. + * @HB_STYLE_TAG_OPTICAL_SIZE: Used to vary design to suit different text sizes. + * Non-zero. Values can be interpreted as text size, in points. + * @HB_STYLE_TAG_SLANT: Used to vary between upright and slanted text. Values + * must be greater than -90 and less than +90. Values can be interpreted as + * the angle, in counter-clockwise degrees, of oblique slant from whatever the + * designer considers to be upright for that font design. + * @HB_STYLE_TAG_WIDTH: Used to vary width of text from narrower to wider. + * Non-zero. Values can be interpreted as a percentage of whatever the font + * designer considers “normal width” for that font design. + * @HB_STYLE_TAG_WEIGHT: Used to vary stroke thicknesses or other design details + * to give variation from lighter to blacker. Values can be interpreted in direct + * comparison to values for usWeightClass in the OS/2 table, + * or the CSS font-weight property. + * + * Defined by https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg + * + * Since: EXPERIMENTAL + **/ +typedef enum { + HB_STYLE_TAG_ITALIC = HB_TAG ('i','t','a','l'), + HB_STYLE_TAG_OPTICAL_SIZE = HB_TAG ('o','p','s','z'), + HB_STYLE_TAG_SLANT = HB_TAG ('s','l','n','t'), + HB_STYLE_TAG_WIDTH = HB_TAG ('w','d','t','h'), + HB_STYLE_TAG_WEIGHT = HB_TAG ('w','g','h','t'), + + _HB_STYLE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_style_tag_t; + +/** + * hb_style_get_value: + * @font: a #hb_font_t object. + * @style_tag: a style tag. + * + * Searches variation axes of a hb_font_t object for a specific axis first, + * if not set, then tries to get default style values from different + * tables of the font. + * + * Returns: Corresponding axis or default value to a style tag. + * + * Since: EXPERIMENTAL + **/ +float +hb_style_get_value (hb_font_t *font, hb_tag_t tag) +{ + hb_style_tag_t style_tag = (hb_style_tag_t) tag; + hb_face_t *face = font->face; + +#ifndef HB_NO_VAR + hb_ot_var_axis_info_t axis; + if (hb_ot_var_find_axis_info (face, style_tag, &axis)) + { + if (axis.axis_index < font->num_coords) return font->design_coords[axis.axis_index]; + /* If a face is variable, fvar's default_value is better than STAT records */ + return axis.default_value; + } +#endif + + if (style_tag == HB_STYLE_TAG_OPTICAL_SIZE && font->ptem) + return font->ptem; + + /* STAT */ + float value; + if (face->table.STAT->get_value (style_tag, &value)) + return value; + + switch ((unsigned) style_tag) + { + case HB_STYLE_TAG_ITALIC: + return face->table.OS2->is_italic () || face->table.head->is_italic () ? 1 : 0; + case HB_STYLE_TAG_OPTICAL_SIZE: + { + unsigned int lower, upper; + return face->table.OS2->v5 ().get_optical_size (&lower, &upper) + ? (float) (lower + upper) / 2.f + : 12.f; + } + case HB_STYLE_TAG_SLANT: + return face->table.post->table->italicAngle.to_float (); + case HB_STYLE_TAG_WIDTH: + return face->table.OS2->has_data () + ? face->table.OS2->get_width () + : (face->table.head->is_condensed () ? 75 : 100); + case HB_STYLE_TAG_WEIGHT: + return face->table.OS2->has_data () + ? face->table.OS2->usWeightClass + : (face->table.head->is_bold () ? 700 : 400); + default: + return 0; + } +} + +#endif +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-warning.cc b/src/java.desktop/share/native/libharfbuzz/hb-style.h similarity index 70% rename from src/java.desktop/share/native/libharfbuzz/hb-warning.cc rename to src/java.desktop/share/native/libharfbuzz/hb-style.h index 9fb4100380853..1209c79e9451e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-warning.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-style.h @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Google, Inc. + * Copyright © 2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -20,18 +20,24 @@ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Behdad Esfahbod */ -#include "hb.hh" - -#if defined(HB_ATOMIC_INT_NIL) -#error "Could not find any system to define atomic_int macros, library WILL NOT be thread-safe" -#error "Check hb-atomic.hh for possible resolutions." +#ifndef HB_H_IN +#error "Include instead." #endif -#if defined(HB_MUTEX_IMPL_NIL) -#error "Could not find any system to define mutex macros, library WILL NOT be thread-safe" -#error "Check hb-mutex.hh for possible resolutions." +#ifndef HB_STYLE_H +#define HB_STYLE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN float +hb_style_get_value (hb_font_t *font, hb_tag_t style_tag); #endif + +HB_END_DECLS + +#endif /* HB_STYLE_H */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc index 75004fe77ff10..e17433152b079 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.cc @@ -24,6 +24,10 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-ot-cff-common.hh" #include "hb-ot-cff2-table.hh" #include "hb-subset-cff-common.hh" @@ -43,33 +47,39 @@ using namespace CFF; **/ bool -hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, - unsigned int fdCount, - const FDSelect &src, /* IN */ - unsigned int &subset_fd_count /* OUT */, - unsigned int &subset_fdselect_size /* OUT */, - unsigned int &subset_fdselect_format /* OUT */, - hb_vector_t &fdselect_ranges /* OUT */, - remap_t &fdmap /* OUT */) +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */) { subset_fd_count = 0; subset_fdselect_size = 0; subset_fdselect_format = 0; - unsigned int num_ranges = 0; + unsigned int num_ranges = 0; - unsigned int subset_num_glyphs = glyphs.length; + unsigned int subset_num_glyphs = plan->num_output_glyphs (); if (subset_num_glyphs == 0) return true; { /* use hb_set to determine the subset of font dicts */ - hb_set_t *set = hb_set_create (); - if (set == &Null (hb_set_t)) - return false; - hb_codepoint_t prev_fd = CFF_UNDEF_CODE; + hb_set_t *set = hb_set_create (); + if (unlikely (set == &Null (hb_set_t))) return false; + hb_codepoint_t prev_fd = CFF_UNDEF_CODE; for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++) { - hb_codepoint_t fd = src.get_fd (glyphs[i]); + hb_codepoint_t glyph; + hb_codepoint_t fd; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ + glyph = i; + } + fd = src.get_fd (glyph); set->add (fd); if (fd != prev_fd) @@ -91,17 +101,13 @@ hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, else { /* create a fdmap */ - if (!fdmap.reset (fdCount)) - { - hb_set_destroy (set); - return false; - } + fdmap.reset (); - hb_codepoint_t fd = CFF_UNDEF_CODE; + hb_codepoint_t fd = CFF_UNDEF_CODE; while (set->next (&fd)) fdmap.add (fd); hb_set_destroy (set); - if (unlikely (fdmap.get_count () != subset_fd_count)) + if (unlikely (fdmap.get_population () != subset_fd_count)) return false; } @@ -145,21 +151,21 @@ hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, template static inline bool serialize_fdselect_3_4 (hb_serialize_context_t *c, - const unsigned int num_glyphs, - const FDSelect &src, - unsigned int size, - const hb_vector_t &fdselect_ranges) + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int size, + const hb_vector_t &fdselect_ranges) { TRACE_SERIALIZE (this); FDSELECT3_4 *p = c->allocate_size (size); - if (unlikely (p == nullptr)) return_trace (false); - p->nRanges ().set (fdselect_ranges.length); + if (unlikely (!p)) return_trace (false); + p->nRanges () = fdselect_ranges.length; for (unsigned int i = 0; i < fdselect_ranges.length; i++) { - p->ranges[i].first.set (fdselect_ranges[i].glyph); - p->ranges[i].fd.set (fdselect_ranges[i].code); + p->ranges[i].first = fdselect_ranges[i].glyph; + p->ranges[i].fd = fdselect_ranges[i].code; } - p->sentinel().set (num_glyphs); + p->sentinel () = num_glyphs; return_trace (true); } @@ -169,58 +175,53 @@ serialize_fdselect_3_4 (hb_serialize_context_t *c, **/ bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, - const unsigned int num_glyphs, - const FDSelect &src, - unsigned int fd_count, - unsigned int fdselect_format, - unsigned int size, - const hb_vector_t &fdselect_ranges) + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t &fdselect_ranges) { TRACE_SERIALIZE (this); - FDSelect *p = c->allocate_min (); - if (unlikely (p == nullptr)) return_trace (false); - p->format.set (fdselect_format); + FDSelect *p = c->allocate_min (); + if (unlikely (!p)) return_trace (false); + p->format = fdselect_format; size -= FDSelect::min_size; switch (fdselect_format) { #if CFF_SERIALIZE_FDSELECT_0 - case 0: + case 0: + { + FDSelect0 *p = c->allocate_size (size); + if (unlikely (!p)) return_trace (false); + unsigned int range_index = 0; + unsigned int fd = fdselect_ranges[range_index++].code; + for (unsigned int i = 0; i < num_glyphs; i++) { - FDSelect0 *p = c->allocate_size (size); - if (unlikely (p == nullptr)) return_trace (false); - unsigned int range_index = 0; - unsigned int fd = fdselect_ranges[range_index++].code; - for (unsigned int i = 0; i < num_glyphs; i++) + if ((range_index < fdselect_ranges.len) && + (i >= fdselect_ranges[range_index].glyph)) { - if ((range_index < fdselect_ranges.len) && - (i >= fdselect_ranges[range_index].glyph)) - { - fd = fdselect_ranges[range_index++].code; - } - p->fds[i].set (fd); + fd = fdselect_ranges[range_index++].code; } - break; + p->fds[i] = fd; } + return_trace (true); + } #endif /* CFF_SERIALIZE_FDSELECT_0 */ - case 3: - return serialize_fdselect_3_4 (c, - num_glyphs, - src, - size, - fdselect_ranges); - - case 4: - return serialize_fdselect_3_4 (c, - num_glyphs, - src, - size, - fdselect_ranges); - - default: - assert(false); - } + case 3: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); - return_trace (true); + case 4: + return serialize_fdselect_3_4 (c, num_glyphs, src, + size, fdselect_ranges); + + default: + return_trace (false); + } } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh index a3492545ec964..b81864164a948 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff-common.hh @@ -44,7 +44,7 @@ struct str_encoder_t void encode_byte (unsigned char b) { - if (unlikely (buff.push (b) == &Crap(unsigned char))) + if (unlikely (buff.push (b) == &Crap (unsigned char))) set_error (); } @@ -110,7 +110,11 @@ struct str_encoder_t void copy_str (const byte_str_t &str) { unsigned int offset = buff.length; - buff.resize (offset + str.length); + if (unlikely (!buff.resize (offset + str.length))) + { + set_error (); + return; + } if (unlikely (buff.length < offset + str.length)) { set_error (); @@ -128,26 +132,17 @@ struct str_encoder_t bool error; }; -struct cff_sub_table_offsets_t { - cff_sub_table_offsets_t () : privateDictsOffset (0) +struct cff_sub_table_info_t { + cff_sub_table_info_t () + : fd_array_link (0), + char_strings_link (0) { - topDictInfo.init (); - FDSelectInfo.init (); - FDArrayInfo.init (); - charStringsInfo.init (); - globalSubrsInfo.init (); - localSubrsInfos.init (); + fd_select.init (); } - ~cff_sub_table_offsets_t () { localSubrsInfos.fini (); } - - table_info_t topDictInfo; - table_info_t FDSelectInfo; - table_info_t FDArrayInfo; - table_info_t charStringsInfo; - unsigned int privateDictsOffset; - table_info_t globalSubrsInfo; - hb_vector_t localSubrsInfos; + table_info_t fd_select; + objidx_t fd_array_link; + objidx_t char_strings_link; }; template @@ -155,40 +150,26 @@ struct cff_top_dict_op_serializer_t : op_serializer_t { bool serialize (hb_serialize_context_t *c, const OPSTR &opstr, - const cff_sub_table_offsets_t &offsets) const + const cff_sub_table_info_t &info) const { TRACE_SERIALIZE (this); switch (opstr.op) { case OpCode_CharStrings: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); case OpCode_FDArray: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); case OpCode_FDSelect: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); default: return_trace (copy_opstr (c, opstr)); } return_trace (true); } - - unsigned int calculate_serialized_size (const OPSTR &opstr) const - { - switch (opstr.op) - { - case OpCode_CharStrings: - case OpCode_FDArray: - case OpCode_FDSelect: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return opstr.str.length; - } - } }; struct cff_font_dict_op_serializer_t : op_serializer_t @@ -202,33 +183,17 @@ struct cff_font_dict_op_serializer_t : op_serializer_t if (opstr.op == OpCode_Private) { /* serialize the private dict size & offset as 2-byte & 4-byte integers */ - if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) || - !UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset))) - return_trace (false); - - /* serialize the opcode */ - HBUINT8 *p = c->allocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (OpCode_Private); - - return_trace (true); + return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) && + Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute)); } else { HBUINT8 *d = c->allocate_size (opstr.str.length); - if (unlikely (d == nullptr)) return_trace (false); + if (unlikely (!d)) return_trace (false); memcpy (d, &opstr.str[0], opstr.str.length); } return_trace (true); } - - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - if (opstr.op == OpCode_Private) - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); - else - return opstr.str.length; - } }; struct cff_private_dict_op_serializer_t : op_serializer_t @@ -238,7 +203,7 @@ struct cff_private_dict_op_serializer_t : op_serializer_t bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const unsigned int subrsOffset) const + objidx_t subrs_link) const { TRACE_SERIALIZE (this); @@ -246,31 +211,15 @@ struct cff_private_dict_op_serializer_t : op_serializer_t return true; if (opstr.op == OpCode_Subrs) { - if (desubroutinize || (subrsOffset == 0)) + if (desubroutinize || !subrs_link) return_trace (true); else - return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset)); + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); } else return_trace (copy_opstr (c, opstr)); } - unsigned int calculate_serialized_size (const op_str_t &opstr, - bool has_localsubr=true) const - { - if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) - return 0; - if (opstr.op == OpCode_Subrs) - { - if (desubroutinize || !has_localsubr) - return 0; - else - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op); - } - else - return opstr.str.length; - } - protected: const bool desubroutinize; const bool drop_hints; @@ -282,30 +231,35 @@ struct flatten_param_t bool drop_hints; }; -template +template struct subr_flattener_t { subr_flattener_t (const ACC &acc_, - const hb_vector_t &glyphs_, - bool drop_hints_) : acc (acc_), glyphs (glyphs_), - drop_hints (drop_hints_) {} + const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) {} bool flatten (str_buff_vec_t &flat_charstrings) { - if (!flat_charstrings.resize (glyphs.length)) + if (!flat_charstrings.resize (plan->num_output_glyphs ())) return false; - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) flat_charstrings[i].init (); - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - hb_codepoint_t glyph = glyphs[i]; + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); + continue; + } const byte_str_t str = (*acc.charStrings)[glyph]; unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; cs_interpreter_t interp; interp.env.init (str, acc, fd); - flatten_param_t param = { flat_charstrings[i], drop_hints }; + flatten_param_t param = { flat_charstrings[i], plan->drop_hints }; if (unlikely (!interp.interpret (param))) return false; } @@ -313,8 +267,7 @@ struct subr_flattener_t } const ACC &acc; - const hb_vector_t &glyphs; - bool drop_hints; + const hb_subset_plan_t *plan; }; struct subr_closures_t @@ -463,7 +416,8 @@ struct parsed_cs_str_vec_t : hb_vector_t void init (unsigned int len_ = 0) { SUPER::init (); - resize (len_); + if (unlikely (!resize (len_))) + return; for (unsigned int i = 0; i < length; i++) (*this)[i].init (); } @@ -512,19 +466,19 @@ struct subr_subset_param_t template void set_current_str (ENV &env, bool calling) { - parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); - if (likely (parsed_str != nullptr)) + parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); + if (unlikely (!parsed_str)) { - /* If the called subroutine is parsed partially but not completely yet, - * it must be because we are calling it recursively. - * Handle it as an error. */ - if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) - env.set_error (); - else - current_parsed_str = parsed_str; + env.set_error (); + return; } - else + /* If the called subroutine is parsed partially but not completely yet, + * it must be because we are calling it recursively. + * Handle it as an error. */ + if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) env.set_error (); + else + current_parsed_str = parsed_str; } parsed_cs_str_t *current_parsed_str; @@ -537,39 +491,29 @@ struct subr_subset_param_t bool drop_hints; }; -struct subr_remap_t : remap_t +struct subr_remap_t : hb_inc_bimap_t { void create (hb_set_t *closure) { /* create a remapping of subroutine numbers from old to new. * no optimization based on usage counts. fonttools doesn't appear doing that either. */ - reset (closure->get_max () + 1); - for (hb_codepoint_t old_num = 0; old_num < length; old_num++) - { - if (hb_set_has (closure, old_num)) - add (old_num); - } - if (get_count () < 1240) + hb_codepoint_t old_num = HB_SET_VALUE_INVALID; + while (hb_set_next (closure, &old_num)) + add (old_num); + + if (get_population () < 1240) bias = 107; - else if (get_count () < 33900) + else if (get_population () < 33900) bias = 1131; else bias = 32768; } - hb_codepoint_t operator[] (unsigned int old_num) const - { - if (old_num >= length) - return CFF_UNDEF_CODE; - else - return remap_t::operator[] (old_num); - } - int biased_num (unsigned int old_num) const { - hb_codepoint_t new_num = (*this)[old_num]; + hb_codepoint_t new_num = get (old_num); return (int)new_num - bias; } @@ -577,23 +521,28 @@ struct subr_remap_t : remap_t int bias; }; -struct subr_remap_ts +struct subr_remaps_t { - subr_remap_ts () + subr_remaps_t () { global_remap.init (); local_remaps.init (); } - ~subr_remap_ts () { fini (); } + ~subr_remaps_t () { fini (); } void init (unsigned int fdCount) { - local_remaps.resize (fdCount); + if (unlikely (!local_remaps.resize (fdCount))) return; for (unsigned int i = 0; i < fdCount; i++) local_remaps[i].init (); } + bool in_error() + { + return local_remaps.in_error (); + } + void create (subr_closures_t& closures) { global_remap.create (closures.global_closure); @@ -611,10 +560,11 @@ struct subr_remap_ts hb_vector_t local_remaps; }; -template +template struct subr_subsetter_t { - subr_subsetter_t () + subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) { parsed_charstrings.init (); parsed_global_subrs.init (); @@ -644,25 +594,36 @@ struct subr_subsetter_t * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. */ - bool subset (ACC &acc, const hb_vector_t &glyphs, bool drop_hints) + bool subset (void) { closures.init (acc.fdCount); remaps.init (acc.fdCount); - parsed_charstrings.init (glyphs.length); + parsed_charstrings.init (plan->num_output_glyphs ()); parsed_global_subrs.init (acc.globalSubrs->count); - parsed_local_subrs.resize (acc.fdCount); + + if (unlikely (remaps.in_error() + || parsed_charstrings.in_error () + || parsed_global_subrs.in_error ())) { + return false; + } + + if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false; + for (unsigned int i = 0; i < acc.fdCount; i++) { parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count); + if (unlikely (parsed_local_subrs[i].in_error ())) return false; } if (unlikely (!closures.valid)) return false; /* phase 1 & 2 */ - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - hb_codepoint_t glyph = glyphs[i]; + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; const byte_str_t str = (*acc.charStrings)[glyph]; unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) @@ -675,28 +636,31 @@ struct subr_subsetter_t param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); if (unlikely (!interp.interpret (param))) return false; - /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ - SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]); + /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); } - if (drop_hints) + if (plan->drop_hints) { /* mark hint ops and arguments for drop */ - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; subr_subset_param_t param; param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); drop_hints_param_t drop; if (drop_hints_in_str (parsed_charstrings[i], param, drop)) @@ -709,16 +673,19 @@ struct subr_subsetter_t /* after dropping hints recreate closures of actually used subrs */ closures.reset (); - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + continue; + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; subr_subset_param_t param; param.init (&parsed_charstrings[i], &parsed_global_subrs, &parsed_local_subrs[fd], closures.global_closure, closures.local_closures[fd], - drop_hints); + plan->drop_hints); collect_subr_refs_in_str (parsed_charstrings[i], param); } } @@ -728,13 +695,20 @@ struct subr_subsetter_t return true; } - bool encode_charstrings (ACC &acc, const hb_vector_t &glyphs, str_buff_vec_t &buffArray) const + bool encode_charstrings (str_buff_vec_t &buffArray) const { - if (unlikely (!buffArray.resize (glyphs.length))) + if (unlikely (!buffArray.resize (plan->num_output_glyphs ()))) return false; - for (unsigned int i = 0; i < glyphs.length; i++) + for (unsigned int i = 0; i < plan->num_output_glyphs (); i++) { - unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) buffArray[i].push (endchar_op); + continue; + } + unsigned int fd = acc.fdSelect->get_fd (glyph); if (unlikely (fd >= acc.fdCount)) return false; if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) @@ -745,7 +719,7 @@ struct subr_subsetter_t bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const { - unsigned int count = remap.get_count (); + unsigned int count = remap.get_population (); if (unlikely (!buffArray.resize (count))) return false; @@ -777,10 +751,12 @@ struct subr_subsetter_t drop_hints_param_t () : seen_moveto (false), ends_in_hint (false), + all_dropped (false), vsindex_dropped (false) {} bool seen_moveto; bool ends_in_hint; + bool all_dropped; bool vsindex_dropped; }; @@ -791,7 +767,7 @@ struct subr_subsetter_t drop.ends_in_hint = false; bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); - /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto), + /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), * then this entire subroutine must be a hint. drop its call. */ if (drop.ends_in_hint) { @@ -801,6 +777,10 @@ struct subr_subsetter_t if (!str.at_end (pos)) drop.ends_in_hint = false; } + else if (drop.all_dropped) + { + str.values[pos].set_drop (); + } return has_hint; } @@ -819,7 +799,6 @@ struct subr_subsetter_t has_hint = drop_hints_in_subr (str, pos, *param.parsed_local_subrs, str.values[pos].subr_num, param, drop); - break; case OpCode_callgsubr: @@ -876,6 +855,23 @@ struct subr_subsetter_t } } + /* Raise all_dropped flag if all operators except return are dropped from a subr. + * It may happen even after seeing the first moveto if a subr contains + * only (usually one) hintmask operator, then calls to this subr can be dropped. + */ + drop.all_dropped = true; + for (unsigned int pos = 0; pos < str.values.length; pos++) + { + parsed_cs_op_t &csop = str.values[pos]; + if (csop.op == OpCode_return) + break; + if (!csop.for_drop ()) + { + drop.all_dropped = false; + break; + } + } + return seen_hint; } @@ -884,7 +880,7 @@ struct subr_subsetter_t hb_set_t *closure, const subr_subset_param_t ¶m) { - hb_set_add (closure, subr_num); + closure->add (subr_num); collect_subr_refs_in_str (subrs[subr_num], param); } @@ -954,13 +950,16 @@ struct subr_subsetter_t } protected: - subr_closures_t closures; + const ACC &acc; + const hb_subset_plan_t *plan; + + subr_closures_t closures; - parsed_cs_str_vec_t parsed_charstrings; - parsed_cs_str_vec_t parsed_global_subrs; + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs; hb_vector_t parsed_local_subrs; - subr_remap_ts remaps; + subr_remaps_t remaps; private: typedef typename SUBRS::count_type subr_count_type; @@ -969,14 +968,14 @@ struct subr_subsetter_t } /* namespace CFF */ HB_INTERNAL bool -hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int fdCount, const CFF::FDSelect &src, /* IN */ unsigned int &subset_fd_count /* OUT */, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - CFF::remap_t &fdmap /* OUT */); + hb_inc_bimap_t &fdmap /* OUT */); HB_INTERNAL bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc index 7f35e5ecfeff2..650c3aeec9b4d 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.cc @@ -24,9 +24,14 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-open-type.hh" #include "hb-ot-cff1-table.hh" #include "hb-set.h" +#include "hb-bimap.hh" #include "hb-subset-cff1.hh" #include "hb-subset-plan.hh" #include "hb-subset-cff-common.hh" @@ -34,12 +39,12 @@ using namespace CFF; -struct remap_sid_t : remap_t +struct remap_sid_t : hb_inc_bimap_t { unsigned int add (unsigned int sid) { if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) - return offset_sid (remap_t::add (unoffset_sid (sid))); + return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid))); else return sid; } @@ -49,7 +54,7 @@ struct remap_sid_t : remap_t if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) return sid; else - return offset_sid (remap_t::operator [] (unoffset_sid (sid))); + return offset_sid (get (unoffset_sid (sid))); } static const unsigned int num_std_strings = 391; @@ -59,29 +64,25 @@ struct remap_sid_t : remap_t static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; } }; -struct cff1_sub_table_offsets_t : cff_sub_table_offsets_t +struct cff1_sub_table_info_t : cff_sub_table_info_t { - cff1_sub_table_offsets_t () - : cff_sub_table_offsets_t (), - nameIndexOffset (0), - encodingOffset (0) - { - stringIndexInfo.init (); - charsetInfo.init (); + cff1_sub_table_info_t () + : cff_sub_table_info_t (), + encoding_link (0), + charset_link (0) + { privateDictInfo.init (); } - unsigned int nameIndexOffset; - table_info_t stringIndexInfo; - unsigned int encodingOffset; - table_info_t charsetInfo; + objidx_t encoding_link; + objidx_t charset_link; table_info_t privateDictInfo; }; /* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */ struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t { - void init (const cff1_top_dict_values_t *base_= &Null(cff1_top_dict_values_t)) + void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t)) { SUPER::init (); base = base_; @@ -112,13 +113,13 @@ struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t struct top_dict_modifiers_t { - top_dict_modifiers_t (const cff1_sub_table_offsets_t &offsets_, - const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) - : offsets (offsets_), + top_dict_modifiers_t (const cff1_sub_table_info_t &info_, + const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) + : info (info_), nameSIDs (nameSIDs_) {} - const cff1_sub_table_offsets_t &offsets; + const cff1_sub_table_info_t &info; const unsigned int (&nameSIDs)[name_dict_values_t::ValCount]; }; @@ -134,22 +135,20 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_tallocate_size (1); - if (unlikely (p == nullptr)) return_trace (false); - p->set (OpCode_Private); - } - break; + return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) && + Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute)); case OpCode_version: case OpCode_Notice: @@ -160,7 +159,7 @@ struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t::serialize (c, opstr, mod.offsets)); + return_trace (cff_top_dict_op_serializer_t::serialize (c, opstr, mod.info)); } return_trace (true); } - unsigned int calculate_serialized_size (const cff1_top_dict_val_t &opstr) const - { - op_code_t op = opstr.op; - switch (op) - { - case OpCode_charset: - case OpCode_Encoding: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); - - case OpCode_Private: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); - - case OpCode_version: - case OpCode_Notice: - case OpCode_Copyright: - case OpCode_FullName: - case OpCode_FamilyName: - case OpCode_Weight: - case OpCode_PostScript: - case OpCode_BaseFontName: - case OpCode_FontName: - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (op); - - case OpCode_ROS: - return ((OpCode_Size (OpCode_shortint) + 2) * 2) + (opstr.str.length - opstr.last_arg_offset)/* supplement + op */; - - default: - return cff_top_dict_op_serializer_t::calculate_serialized_size (opstr); - } - } -}; - -struct font_dict_values_mod_t -{ - void init (const cff1_font_dict_values_t *base_, - unsigned int fontName_, - const table_info_t &privateDictInfo_) - { - base = base_; - fontName = fontName_; - privateDictInfo = privateDictInfo_; - } - - unsigned get_count () const { return base->get_count (); } - - const op_str_t &operator [] (unsigned int i) const { return (*base)[i]; } - - const cff1_font_dict_values_t *base; - table_info_t privateDictInfo; - unsigned int fontName; }; struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t { bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const font_dict_values_mod_t &mod) const + const cff1_font_dict_values_mod_t &mod) const { TRACE_SERIALIZE (this); if (opstr.op == OpCode_FontName) - return_trace (FontDict::serialize_uint2_op (c, opstr.op, mod.fontName)); + return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName)); else return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo)); } - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - if (opstr.op == OpCode_FontName) - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_FontName); - else - return SUPER::calculate_serialized_size (opstr); - } - private: typedef cff_font_dict_op_serializer_t SUPER; }; @@ -326,7 +268,7 @@ struct cff1_cs_opset_flatten_t : cff1_cs_opset_t { /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ - bool finalize (unsigned int last_glyph) + bool complete (unsigned int last_glyph) { bool two_byte = false; for (unsigned int i = (*this).length; i > 0; i--) @@ -351,7 +293,7 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_tadd_op (op, env.str_ref); param.current_parsed_str->set_parsed (); - env.returnFromSubr (); + env.return_from_subr (); param.set_current_str (env, false); break; @@ -382,9 +324,9 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_tadd_call_op (op, str_ref, env.context.subr_num); - hb_set_add (closure, env.context.subr_num); + closure->add (env.context.subr_num); param.set_current_str (env, true); } @@ -392,9 +334,12 @@ struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t SUPER; }; -struct cff1_subr_subsetter_t : subr_subsetter_t +struct cff1_subr_subsetter_t : subr_subsetter_t { - static void finalize_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) { /* insert width at the beginning of the charstring as necessary */ if (env.has_width) @@ -406,8 +351,8 @@ struct cff1_subr_subsetter_t : subr_subsetter_tset_parsed (); for (unsigned int i = 0; i < env.callStack.get_count (); i++) { - parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); - if (likely (parsed_str != nullptr)) + parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); + if (likely (parsed_str)) parsed_str->set_parsed (); else env.set_error (); @@ -417,16 +362,13 @@ struct cff1_subr_subsetter_t : subr_subsetter_t supp_codes; - subset_enc_code_ranges.resize (0); + if (unlikely (!subset_enc_code_ranges.resize (0))) + { + plan->check_success (false); + return; + } + supp_size = 0; supp_codes.init (); - subset_enc_num_codes = plan->glyphs.length - 1; + subset_enc_num_codes = plan->num_output_glyphs () - 1; unsigned int glyph; - for (glyph = 1; glyph < plan->glyphs.length; glyph++) + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) { - hb_codepoint_t orig_glyph = plan->glyphs[glyph]; - code = acc.glyph_to_code (orig_glyph); + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the code for the old missing glyph ID */ + old_glyph = glyph; + } + code = acc.glyph_to_code (old_glyph); if (code == CFF_UNDEF_CODE) { subset_enc_num_codes = glyph - 1; break; } - if (code != last_code + 1) + if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1)) { code_pair_t pair = { code, glyph }; subset_enc_code_ranges.push (pair); } last_code = code; - if (encoding != &Null(Encoding)) + if (encoding != &Null (Encoding)) { - hb_codepoint_t sid = acc.glyph_to_sid (orig_glyph); + hb_codepoint_t sid = acc.glyph_to_sid (old_glyph); encoding->get_supplement_codes (sid, supp_codes); for (unsigned int i = 0; i < supp_codes.length; i++) { @@ -502,7 +453,7 @@ struct cff_subset_plan { } supp_codes.fini (); - subset_enc_code_ranges.finalize (glyph); + subset_enc_code_ranges.complete (glyph); assert (subset_enc_num_codes <= 0xFF); size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes; @@ -512,29 +463,34 @@ struct cff_subset_plan { subset_enc_format = 0; else subset_enc_format = 1; - - return Encoding::calculate_serialized_size ( - subset_enc_format, - subset_enc_format? subset_enc_code_ranges.length: subset_enc_num_codes, - subset_enc_supp_codes.length); } - unsigned int plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) { unsigned int size0, size_ranges; hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE; - subset_charset_ranges.resize (0); + if (unlikely (!subset_charset_ranges.resize (0))) + { + plan->check_success (false); + return; + } + unsigned int glyph; - for (glyph = 1; glyph < plan->glyphs.length; glyph++) + for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++) { - hb_codepoint_t orig_glyph = plan->glyphs[glyph]; - sid = acc.glyph_to_sid (orig_glyph); + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (glyph, &old_glyph)) + { + /* Retain the SID for the old missing glyph ID */ + old_glyph = glyph; + } + sid = acc.glyph_to_sid (old_glyph); if (!acc.is_CID ()) sid = sidmap.add (sid); - if (sid != last_sid + 1) + if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1)) { code_pair_t pair = { sid, glyph }; subset_charset_ranges.push (pair); @@ -542,9 +498,9 @@ struct cff_subset_plan { last_sid = sid; } - bool two_byte = subset_charset_ranges.finalize (glyph); + bool two_byte = subset_charset_ranges.complete (glyph); - size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.length - 1); + size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1); if (!two_byte) size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length; else @@ -556,16 +512,11 @@ struct cff_subset_plan { subset_charset_format = 1; else subset_charset_format = 2; - - return Charset::calculate_serialized_size ( - subset_charset_format, - subset_charset_format? subset_charset_ranges.length: plan->glyphs.length); } bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) { - if (unlikely (!sidmap.reset (acc.stringIndex->count))) - return false; + sidmap.reset (); for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) { @@ -577,31 +528,33 @@ struct cff_subset_plan { } } - if (acc.fdArray != &Null(CFF1FDArray)) + if (acc.fdArray != &Null (CFF1FDArray)) for (unsigned int i = 0; i < orig_fdcount; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) (void)sidmap.add (acc.fontDicts[i].fontName); return true; } bool create (const OT::cff1::accelerator_subset_t &acc, - hb_subset_plan_t *plan) + hb_subset_plan_t *plan) { - /* make sure notdef is first */ - if ((plan->glyphs.length == 0) || (plan->glyphs[0] != 0)) return false; + /* make sure notdef is first */ + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; - final_size = 0; - num_glyphs = plan->glyphs.length; + num_glyphs = plan->num_output_glyphs (); orig_fdcount = acc.fdCount; drop_hints = plan->drop_hints; desubroutinize = plan->desubroutinize; /* check whether the subset renumbers any glyph IDs */ gid_renum = false; - for (unsigned int glyph = 0; glyph < plan->glyphs.length; glyph++) + for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++) { - if (plan->glyphs[glyph] != glyph) { + if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph)) + continue; + if (new_glyph != old_glyph) { gid_renum = true; break; } @@ -610,13 +563,6 @@ struct cff_subset_plan { subset_charset = gid_renum || !acc.is_predef_charset (); subset_encoding = !acc.is_CID() && !acc.is_predef_encoding (); - /* CFF header */ - final_size += OT::cff1::static_size; - - /* Name INDEX */ - offsets.nameIndexOffset = final_size; - final_size += acc.nameIndex->get_size (); - /* top dict INDEX */ { /* Add encoding/charset to a (copy of) top dict as necessary */ @@ -630,25 +576,16 @@ struct cff_subset_plan { if (need_to_add_set) topdict_mod.add_op (OpCode_charset); } - offsets.topDictInfo.offset = final_size; - cff1_top_dict_op_serializer_t topSzr; - unsigned int topDictSize = TopDict::calculate_serialized_size (topdict_mod, topSzr); - offsets.topDictInfo.offSize = calcOffSize(topDictSize); - if (unlikely (offsets.topDictInfo.offSize > 4)) - return false; - final_size += CFF1IndexOf::calculate_serialized_size - (offsets.topDictInfo.offSize, - &topdict_mod, 1, topdict_sizes, topSzr); } /* Determine re-mapping of font index as fdmap among other info */ - if (acc.fdSelect != &Null(CFF1FDSelect)) + if (acc.fdSelect != &Null (CFF1FDSelect)) { - if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, + if (unlikely (!hb_plan_subset_cff_fdselect (plan, orig_fdcount, *acc.fdSelect, subset_fdcount, - offsets.FDSelectInfo.size, + info.fd_select.size, subset_fdselect_format, subset_fdselect_ranges, fdmap))) @@ -662,170 +599,79 @@ struct cff_subset_plan { /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */ if (unlikely (!collect_sids_in_dicts (acc))) return false; - if (unlikely (sidmap.get_count () > 0x8000)) /* assumption: a dict won't reference that many strings */ + if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */ return false; - if (subset_charset) - offsets.charsetInfo.size = plan_subset_charset (acc, plan); - topdict_mod.reassignSIDs (sidmap); - } + if (subset_charset) plan_subset_charset (acc, plan); - /* String INDEX */ - { - offsets.stringIndexInfo.offset = final_size; - offsets.stringIndexInfo.size = acc.stringIndex->calculate_serialized_size (offsets.stringIndexInfo.offSize, sidmap); - final_size += offsets.stringIndexInfo.size; + topdict_mod.reassignSIDs (sidmap); } if (desubroutinize) { /* Flatten global & local subrs */ - subr_flattener_t - flattener(acc, plan->glyphs, plan->drop_hints); + subr_flattener_t + flattener(acc, plan); if (!flattener.flatten (subset_charstrings)) return false; - - /* no global/local subroutines */ - offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (1, 0, 0); } else { + cff1_subr_subsetter_t subr_subsetter (acc, plan); + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ - if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints)) + if (!subr_subsetter.subset ()) return false; /* encode charstrings, global subrs, local subrs with new subroutine numbers */ - if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings)) + if (!subr_subsetter.encode_charstrings (subset_charstrings)) return false; if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) return false; - /* global subrs */ - unsigned int dataSize = subset_globalsubrs.total_size (); - offsets.globalSubrsInfo.offSize = calcOffSize (dataSize); - if (unlikely (offsets.globalSubrsInfo.offSize > 4)) - return false; - offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.length, dataSize); - /* local subrs */ - if (!offsets.localSubrsInfos.resize (orig_fdcount)) - return false; if (!subset_localsubrs.resize (orig_fdcount)) return false; for (unsigned int fd = 0; fd < orig_fdcount; fd++) { subset_localsubrs[fd].init (); - offsets.localSubrsInfos[fd].init (); - if (fdmap.includes (fd)) + if (fdmap.has (fd)) { if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) return false; - - unsigned int dataSize = subset_localsubrs[fd].total_size (); - if (dataSize > 0) - { - offsets.localSubrsInfos[fd].offset = final_size; - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - if (unlikely (offsets.localSubrsInfos[fd].offSize > 4)) - return false; - offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); - } } } } - /* global subrs */ - offsets.globalSubrsInfo.offset = final_size; - final_size += offsets.globalSubrsInfo.size; - /* Encoding */ - if (!subset_encoding) - offsets.encodingOffset = acc.topDict.EncodingOffset; - else - { - offsets.encodingOffset = final_size; - final_size += plan_subset_encoding (acc, plan); - } - - /* Charset */ - if (!subset_charset && acc.is_predef_charset ()) - offsets.charsetInfo.offset = acc.topDict.CharsetOffset; - else - offsets.charsetInfo.offset = final_size; - final_size += offsets.charsetInfo.size; - - /* FDSelect */ - if (acc.fdSelect != &Null(CFF1FDSelect)) - { - offsets.FDSelectInfo.offset = final_size; - final_size += offsets.FDSelectInfo.size; - } - - /* FDArray (FDIndex) */ - if (acc.fdArray != &Null(CFF1FDArray)) { - offsets.FDArrayInfo.offset = final_size; - cff1_font_dict_op_serializer_t fontSzr; - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < acc.fontDicts.length; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); - - offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); - if (unlikely (offsets.FDArrayInfo.offSize > 4)) - return false; - final_size += CFF1Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize); - } - - /* CharStrings */ - { - offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = subset_charstrings.total_size (); - offsets.charStringsInfo.offSize = calcOffSize (dataSize); - if (unlikely (offsets.charStringsInfo.offSize > 4)) - return false; - final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize); - } + if (subset_encoding) + plan_subset_encoding (acc, plan); /* private dicts & local subrs */ - offsets.privateDictInfo.offset = final_size; - for (unsigned int i = 0; i < orig_fdcount; i++) + if (!acc.is_CID ()) + fontdicts_mod.push (cff1_font_dict_values_mod_t ()); + else { - if (fdmap.includes (i)) - { - bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; - cff_private_dict_op_serializer_t privSzr (desubroutinize, plan->drop_hints); - unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs); - table_info_t privInfo = { final_size, priv_size, 0 }; - font_dict_values_mod_t fontdict_mod; - if (!acc.is_CID ()) - fontdict_mod.init ( &Null(cff1_font_dict_values_t), CFF_UNDEF_SID, privInfo ); - else - fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo ); - fontdicts_mod.push (fontdict_mod); - final_size += privInfo.size; - - if (!plan->desubroutinize && has_localsubrs) + + hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff1_font_dict_values_t &_) + { return fdmap.has (&_ - &acc.fontDicts[0]); } ) + | hb_map ([&] (const cff1_font_dict_values_t &_) { - offsets.localSubrsInfos[i].offset = final_size; - final_size += offsets.localSubrsInfos[i].size; - } - } + cff1_font_dict_values_mod_t mod; + mod.init (&_, sidmap[_.fontName]); + return mod; + }) + | hb_sink (fontdicts_mod) + ; } - if (!acc.is_CID ()) - offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo; - - return ((subset_charstrings.length == plan->glyphs.length) + return ((subset_charstrings.length == plan->num_output_glyphs ()) && (fontdicts_mod.length == subset_fdcount)); } - unsigned int get_final_size () const { return final_size; } - - unsigned int final_size; - hb_vector_t topdict_sizes; cff1_top_dict_values_mod_t topdict_mod; - cff1_sub_table_offsets_t offsets; + cff1_sub_table_info_t info; unsigned int num_glyphs; unsigned int orig_fdcount; @@ -835,12 +681,12 @@ struct cff_subset_plan { /* font dict index remap table from fullset FDArray to subset FDArray. * set to CFF_UNDEF_CODE if excluded from subset */ - remap_t fdmap; + hb_inc_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; hb_vector_t subset_localsubrs; - hb_vector_t fontdicts_mod; + hb_vector_t fontdicts_mod; bool drop_hints; @@ -859,94 +705,97 @@ struct cff_subset_plan { unsigned int topDictModSIDs[name_dict_values_t::ValCount]; bool desubroutinize; - cff1_subr_subsetter_t subr_subsetter; }; -static inline bool _write_cff1 (const cff_subset_plan &plan, - const OT::cff1::accelerator_subset_t &acc, - const hb_vector_t& glyphs, - unsigned int dest_sz, - void *dest) +static bool _serialize_cff1 (hb_serialize_context_t *c, + cff_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc, + unsigned int num_glyphs) { - hb_serialize_context_t c (dest, dest_sz); - - OT::cff1 *cff = c.start_serialize (); - if (unlikely (!c.extend_min (*cff))) - return false; - - /* header */ - cff->version.major.set (0x01); - cff->version.minor.set (0x00); - cff->nameIndex.set (cff->min_size); - cff->offSize.set (4); /* unused? */ - - /* name INDEX */ + /* private dicts & local subrs */ + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) { - assert (cff->nameIndex == (unsigned) (c.head - c.start)); - CFF1NameIndex *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, *acc.nameIndex))) + if (plan.fdmap.has (i)) { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX"); - return false; - } - } + objidx_t subrs_link = 0; + if (plan.subset_localsubrs[i].length > 0) + { + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } - /* top dict INDEX */ - { - assert (plan.offsets.topDictInfo.offset == (unsigned) (c.head - c.start)); - CFF1IndexOf *dest = c.start_embed< CFF1IndexOf > (); - if (dest == nullptr) return false; - cff1_top_dict_op_serializer_t topSzr; - top_dict_modifiers_t modifier (plan.offsets, plan.topDictModSIDs); - if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize, - &plan.topdict_mod, 1, - plan.topdict_sizes, topSzr, modifier))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict"); - return false; + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + plan.fontdicts_mod[fd].privateDictInfo.size = c->length (); + plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } } } - /* String INDEX */ + if (!acc.is_CID ()) + plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; + + /* CharStrings */ { - assert (plan.offsets.stringIndexInfo.offset == (unsigned) (c.head - c.start)); - CFF1StringIndex *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, *acc.stringIndex, plan.offsets.stringIndexInfo.offSize, plan.sidmap))) + CFF1CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX"); + c->pop_discard (); return false; } } - /* global subrs */ + /* FDArray (FD Index) */ + if (acc.fdArray != &Null (CFF1FDArray)) { - assert (plan.offsets.globalSubrsInfo.offset != 0); - assert (plan.offsets.globalSubrsInfo.offset == (unsigned) (c.head - c.start)); - - CFF1Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) + CFF1FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + c->push (); + cff1_font_dict_op_serializer_t fontSzr; + auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod)); + if (likely (fda->serialize (c, it, fontSzr))) + plan.info.fd_array_link = c->pop_pack (false); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); + c->pop_discard (); return false; } } - /* Encoding */ - if (plan.subset_encoding) + /* FDSelect */ + if (acc.fdSelect != &Null (CFF1FDSelect)) { - assert (plan.offsets.encodingOffset == (unsigned) (c.head - c.start)); - Encoding *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, - plan.subset_enc_format, - plan.subset_enc_num_codes, - plan.subset_enc_code_ranges, - plan.subset_enc_supp_codes))) + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.fdCount, + plan.subset_fdselect_format, plan.info.fd_select.size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding"); + c->pop_discard (); return false; } } @@ -954,129 +803,120 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, /* Charset */ if (plan.subset_charset) { - assert (plan.offsets.charsetInfo.offset == (unsigned) (c.head - c.start)); - Charset *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, - plan.subset_charset_format, - plan.num_glyphs, - plan.subset_charset_ranges))) + Charset *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_charset_format, + plan.num_glyphs, + plan.subset_charset_ranges))) + plan.info.charset_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset"); + c->pop_discard (); return false; } } - /* FDSelect */ - if (acc.fdSelect != &Null(CFF1FDSelect)) + /* Encoding */ + if (plan.subset_encoding) { - assert (plan.offsets.FDSelectInfo.offset == (unsigned) (c.head - c.start)); - - if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.length, *acc.fdSelect, acc.fdCount, - plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size, - plan.subset_fdselect_ranges))) + Encoding *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, + plan.subset_enc_format, + plan.subset_enc_num_codes, + plan.subset_enc_code_ranges, + plan.subset_enc_supp_codes))) + plan.info.encoding_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect"); + c->pop_discard (); return false; } } - /* FDArray (FD Index) */ - if (acc.fdArray != &Null(CFF1FDArray)) + /* global subrs */ { - assert (plan.offsets.FDArrayInfo.offset == (unsigned) (c.head - c.start)); - CFF1FDArray *fda = c.start_embed (); - if (unlikely (fda == nullptr)) return false; - cff1_font_dict_op_serializer_t fontSzr; - if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize, - plan.fontdicts_mod, - fontSzr))) + c->push (); + CFF1Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + if (likely (dest->serialize (c, plan.subset_globalsubrs))) + c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray"); + c->pop_discard (); return false; } } - /* CharStrings */ + /* String INDEX */ { - assert (plan.offsets.charStringsInfo.offset == (unsigned) (c.head - c.start)); - CFF1CharStrings *cs = c.start_embed (); - if (unlikely (cs == nullptr)) return false; - if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings))) + CFF1StringIndex *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap))) + c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings"); + c->pop_discard (); return false; } } - /* private dicts & local subrs */ - assert (plan.offsets.privateDictInfo.offset == (unsigned) (c.head - c.start)); - for (unsigned int i = 0; i < acc.privateDicts.length; i++) + OT::cff1 *cff = c->allocate_min (); + if (unlikely (!cff)) + return false; + + /* header */ + cff->version.major = 0x01; + cff->version.minor = 0x00; + cff->nameIndex = cff->min_size; + cff->offSize = 4; /* unused? */ + + /* name INDEX */ + if (unlikely (!(*acc.nameIndex).copy (c))) return false; + + /* top dict INDEX */ { - if (plan.fdmap.includes (i)) + /* serialize singleton TopDict */ + TopDict *top = c->start_embed (); + if (!top) return false; + c->push (); + cff1_top_dict_op_serializer_t topSzr; + unsigned top_size = 0; + top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs); + if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier))) { - PrivateDict *pd = c.start_embed (); - if (unlikely (pd == nullptr)) return false; - unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size; - bool result; - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); - /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0; - result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset); - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); - return false; - } - if (plan.offsets.localSubrsInfos[i].size > 0) - { - CFF1Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i]))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines"); - return false; - } - } + top_size = c->length (); + c->pop_pack (false); } + else + { + c->pop_discard (); + return false; + } + /* serialize INDEX header for above */ + CFF1Index *dest = c->start_embed (); + if (!dest) return false; + return dest->serialize_header (c, hb_iter (hb_array_t (&top_size, 1))); } - - assert (c.head == c.end); - c.end_serialize (); - - return true; } static bool _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, - const char *data, - hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) + hb_subset_context_t *c) { cff_subset_plan cff_plan; - if (unlikely (!cff_plan.create (acc, plan))) + if (unlikely (!cff_plan.create (acc, c->plan))) { DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan."); return false; } - unsigned int cff_prime_size = cff_plan.get_final_size (); - char *cff_prime_data = (char *) calloc (1, cff_prime_size); - - if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs, - cff_prime_size, cff_prime_data))) { - DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff."); - free (cff_prime_data); - return false; - } - - *prime = hb_blob_create (cff_prime_data, - cff_prime_size, - HB_MEMORY_MODE_READONLY, - cff_prime_data, - free); - return true; + return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ()); } /** @@ -1086,18 +926,15 @@ _hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc, * Return value: subsetted cff table. **/ bool -hb_subset_cff1 (hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) +hb_subset_cff1 (hb_subset_context_t *c) { - hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table (plan->source); - const char *data = hb_blob_get_data(cff_blob, nullptr); - OT::cff1::accelerator_subset_t acc; - acc.init(plan->source); - bool result = likely (acc.is_valid ()) && - _hb_subset_cff1 (acc, data, plan, prime); - hb_blob_destroy (cff_blob); + acc.init (c->plan->source); + bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c); acc.fini (); return result; } + + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh index 33a66380a3989..aaf5def1ed5d0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff1.hh @@ -32,7 +32,6 @@ #include "hb-subset-plan.hh" HB_INTERNAL bool -hb_subset_cff1 (hb_subset_plan_t *plan, - hb_blob_t **cff_prime /* OUT */); +hb_subset_cff1 (hb_subset_context_t *c); #endif /* HB_SUBSET_CFF1_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc index ec69ed64894b8..6f1b4a7f6d92c 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.cc @@ -24,6 +24,10 @@ * Adobe Author(s): Michiharu Ariza */ +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + #include "hb-open-type.hh" #include "hb-ot-cff2-table.hh" #include "hb-set.h" @@ -34,43 +38,31 @@ using namespace CFF; -struct cff2_sub_table_offsets_t : cff_sub_table_offsets_t +struct cff2_sub_table_info_t : cff_sub_table_info_t { - cff2_sub_table_offsets_t () - : cff_sub_table_offsets_t (), - varStoreOffset (0) + cff2_sub_table_info_t () + : cff_sub_table_info_t (), + var_store_link (0) {} - unsigned int varStoreOffset; + objidx_t var_store_link; }; struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> { bool serialize (hb_serialize_context_t *c, const op_str_t &opstr, - const cff2_sub_table_offsets_t &offsets) const + const cff2_sub_table_info_t &info) const { TRACE_SERIALIZE (this); switch (opstr.op) { case OpCode_vstore: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset)); + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); default: - return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, offsets)); - } - } - - unsigned int calculate_serialized_size (const op_str_t &opstr) const - { - switch (opstr.op) - { - case OpCode_vstore: - return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); - - default: - return cff_top_dict_op_serializer_t<>::calculate_serialized_size (opstr); + return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); } } }; @@ -183,7 +175,7 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_tset_parsed (); - env.returnFromSubr (); + env.return_from_subr (); param.set_current_str (env, false); break; @@ -213,9 +205,9 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_tadd_call_op (op, str_ref, env.context.subr_num); - hb_set_add (closure, env.context.subr_num); + closure->add (env.context.subr_num); param.set_current_str (env, true); } @@ -225,7 +217,10 @@ struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t { - static void finalize_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) { /* vsindex is inserted at the beginning of the charstring as necessary */ if (env.seen_vsindex ()) @@ -239,9 +234,9 @@ struct cff2_subr_subsetter_t : subr_subsetter_tcount; drop_hints = plan->drop_hints; desubroutinize = plan->desubroutinize; - /* CFF2 header */ - final_size += OT::cff2::static_size; - - /* top dict */ - { - cff2_top_dict_op_serializer_t topSzr; - offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr); - final_size += offsets.topDictInfo.size; - } - if (desubroutinize) { /* Flatten global & local subrs */ subr_flattener_t - flattener(acc, plan->glyphs, plan->drop_hints); + flattener(acc, plan); if (!flattener.flatten (subset_charstrings)) return false; - - /* no global/local subroutines */ - offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0); } else { + cff2_subr_subsetter_t subr_subsetter (acc, plan); + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ - if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints)) + if (!subr_subsetter.subset ()) return false; /* encode charstrings, global subrs, local subrs with new subroutine numbers */ - if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings)) + if (!subr_subsetter.encode_charstrings (subset_charstrings)) return false; if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) return false; - /* global subrs */ - unsigned int dataSize = subset_globalsubrs.total_size (); - offsets.globalSubrsInfo.offSize = calcOffSize (dataSize); - offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.length, dataSize); - /* local subrs */ - if (!offsets.localSubrsInfos.resize (orig_fdcount)) - return false; if (!subset_localsubrs.resize (orig_fdcount)) return false; for (unsigned int fd = 0; fd < orig_fdcount; fd++) { subset_localsubrs[fd].init (); - offsets.localSubrsInfos[fd].init (); - if (fdmap.includes (fd)) - { - if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) - return false; - - unsigned int dataSize = subset_localsubrs[fd].total_size (); - if (dataSize > 0) - { - offsets.localSubrsInfos[fd].offset = final_size; - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); - } - } + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; } } - /* global subrs */ - offsets.globalSubrsInfo.offset = final_size; - final_size += offsets.globalSubrsInfo.size; - - /* variation store */ - if (acc.varStore != &Null(CFF2VariationStore)) - { - offsets.varStoreOffset = final_size; - final_size += acc.varStore->get_size (); - } - /* FDSelect */ - if (acc.fdSelect != &Null(CFF2FDSelect)) + if (acc.fdSelect != &Null (CFF2FDSelect)) { - offsets.FDSelectInfo.offset = final_size; - if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, - orig_fdcount, - *(const FDSelect *)acc.fdSelect, - subset_fdcount, - offsets.FDSelectInfo.size, - subset_fdselect_format, - subset_fdselect_ranges, - fdmap))) + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *(const FDSelect *)acc.fdSelect, + subset_fdcount, + subset_fdselect_size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) return false; - - final_size += offsets.FDSelectInfo.size; } else fdmap.identity (1); - /* FDArray (FDIndex) */ - { - offsets.FDArrayInfo.offset = final_size; - cff_font_dict_op_serializer_t fontSzr; - unsigned int dictsSize = 0; - for (unsigned int i = 0; i < acc.fontDicts.length; i++) - if (fdmap.includes (i)) - dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); - - offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); - final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize); - } - - /* CharStrings */ - { - offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = subset_charstrings.total_size (); - offsets.charStringsInfo.offSize = calcOffSize (dataSize); - final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.length, dataSize); - } - - /* private dicts & local subrs */ - offsets.privateDictsOffset = final_size; - for (unsigned int i = 0; i < orig_fdcount; i++) - { - if (fdmap.includes (i)) - { - bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; - cff_private_dict_op_serializer_t privSzr (desubroutinize, drop_hints); - unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs); - table_info_t privInfo = { final_size, priv_size, 0 }; - privateDictInfos.push (privInfo); - final_size += privInfo.size; - - if (!plan->desubroutinize && has_localsubrs) - { - offsets.localSubrsInfos[i].offset = final_size; - final_size += offsets.localSubrsInfos[i].size; - } - } - } - return true; } - unsigned int get_final_size () const { return final_size; } - - unsigned int final_size; - cff2_sub_table_offsets_t offsets; + cff2_sub_table_info_t info; unsigned int orig_fdcount; unsigned int subset_fdcount; + unsigned int subset_fdselect_size; unsigned int subset_fdselect_format; hb_vector_t subset_fdselect_ranges; - remap_t fdmap; + hb_inc_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; hb_vector_t subset_localsubrs; - hb_vector_t privateDictInfos; bool drop_hints; bool desubroutinize; - cff2_subr_subsetter_t subr_subsetter; }; -static inline bool _write_cff2 (const cff2_subset_plan &plan, - const OT::cff2::accelerator_subset_t &acc, - const hb_vector_t& glyphs, - unsigned int dest_sz, - void *dest) +static bool _serialize_cff2 (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc, + unsigned int num_glyphs) { - hb_serialize_context_t c (dest, dest_sz); - - OT::cff2 *cff2 = c.start_serialize (); - if (unlikely (!c.extend_min (*cff2))) - return false; - - /* header */ - cff2->version.major.set (0x02); - cff2->version.minor.set (0x00); - cff2->topDict.set (OT::cff2::static_size); + /* private dicts & local subrs */ + hb_vector_t private_dict_infos; + if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; - /* top dict */ + for (int i = (int)acc.privateDicts.length; --i >= 0 ;) { - assert (cff2->topDict == (unsigned) (c.head - c.start)); - cff2->topDictSize.set (plan.offsets.topDictInfo.size); - TopDict &dict = cff2 + cff2->topDict; - cff2_top_dict_op_serializer_t topSzr; - if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets))) + if (plan.fdmap.has (i)) { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict"); - return false; - } - } + objidx_t subrs_link = 0; - /* global subrs */ - { - assert (cff2->topDict + plan.offsets.topDictInfo.size == (unsigned) (c.head - c.start)); - CFF2Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); - return false; + if (plan.subset_localsubrs[i].length > 0) + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + c->push (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + PrivateDict *pd = c->start_embed (); + if (unlikely (!pd)) return false; + c->push (); + cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + private_dict_infos[fd].size = c->length (); + private_dict_infos[fd].link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } } } - /* variation store */ - if (acc.varStore != &Null(CFF2VariationStore)) + /* CharStrings */ { - assert (plan.offsets.varStoreOffset == (unsigned) (c.head - c.start)); - CFF2VariationStore *dest = c.start_embed (); - if (unlikely (!dest->serialize (&c, acc.varStore))) + CFF2CharStrings *cs = c->start_embed (); + if (unlikely (!cs)) return false; + c->push (); + if (likely (cs->serialize (c, plan.subset_charstrings))) + plan.info.char_strings_link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store"); + c->pop_discard (); return false; } } /* FDSelect */ - if (acc.fdSelect != &Null(CFF2FDSelect)) + if (acc.fdSelect != &Null (CFF2FDSelect)) { - assert (plan.offsets.FDSelectInfo.offset == (unsigned) (c.head - c.start)); - - if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs.length, *(const FDSelect *)acc.fdSelect, acc.fdArray->count, - plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size, - plan.subset_fdselect_ranges))) + c->push (); + if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, plan.orig_fdcount, + plan.subset_fdselect_format, plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect"); + c->pop_discard (); return false; } } /* FDArray (FD Index) */ { - assert (plan.offsets.FDArrayInfo.offset == (unsigned) (c.head - c.start)); - CFF2FDArray *fda = c.start_embed (); - if (unlikely (fda == nullptr)) return false; - cff_font_dict_op_serializer_t fontSzr; - if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize, - acc.fontDicts, plan.subset_fdcount, plan.fdmap, - fontSzr, plan.privateDictInfos))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray"); - return false; - } + c->push (); + CFF2FDArray *fda = c->start_embed (); + if (unlikely (!fda)) return false; + cff_font_dict_op_serializer_t fontSzr; + auto it = + + hb_zip (+ hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff2_font_dict_values_t &_) + { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), + hb_iter (private_dict_infos)) + ; + if (unlikely (!fda->serialize (c, it, fontSzr))) return false; + plan.info.fd_array_link = c->pop_pack (); } - /* CharStrings */ + /* variation store */ + if (acc.varStore != &Null (CFF2VariationStore)) { - assert (plan.offsets.charStringsInfo.offset == (unsigned) (c.head - c.start)); - CFF2CharStrings *cs = c.start_embed (); - if (unlikely (cs == nullptr)) return false; - if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings"); - return false; - } + c->push (); + CFF2VariationStore *dest = c->start_embed (); + if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false; + plan.info.var_store_link = c->pop_pack (); } - /* private dicts & local subrs */ - assert (plan.offsets.privateDictsOffset == (unsigned) (c.head - c.start)); - for (unsigned int i = 0; i < acc.privateDicts.length; i++) + OT::cff2 *cff2 = c->allocate_min (); + if (unlikely (!cff2)) return false; + + /* header */ + cff2->version.major = 0x02; + cff2->version.minor = 0x00; + cff2->topDict = OT::cff2::static_size; + + /* top dict */ { - if (plan.fdmap.includes (i)) - { - PrivateDict *pd = c.start_embed (); - if (unlikely (pd == nullptr)) return false; - unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; - bool result; - cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); - /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0; - result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset); - if (unlikely (!result)) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); - return false; - } - if (plan.offsets.localSubrsInfos[i].size > 0) - { - CFF2Subrs *dest = c.start_embed (); - if (unlikely (dest == nullptr)) return false; - if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i]))) - { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines"); - return false; - } - } - } + TopDict &dict = cff2 + cff2->topDict; + cff2_top_dict_op_serializer_t topSzr; + if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false; + cff2->topDictSize = c->head - (const char *)&dict; } - assert (c.head == c.end); - c.end_serialize (); - - return true; + /* global subrs */ + { + CFF2Subrs *dest = c->start_embed (); + if (unlikely (!dest)) return false; + return dest->serialize (c, plan.subset_globalsubrs); + } } static bool _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, - const char *data, - hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) + hb_subset_context_t *c) { cff2_subset_plan cff2_plan; - if (unlikely (!cff2_plan.create (acc, plan))) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan."); - return false; - } - - unsigned int cff2_prime_size = cff2_plan.get_final_size (); - char *cff2_prime_data = (char *) calloc (1, cff2_prime_size); - - if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs, - cff2_prime_size, cff2_prime_data))) { - DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2."); - free (cff2_prime_data); - return false; - } - - *prime = hb_blob_create (cff2_prime_data, - cff2_prime_size, - HB_MEMORY_MODE_READONLY, - cff2_prime_data, - free); - return true; + if (unlikely (!cff2_plan.create (acc, c->plan))) return false; + return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ()); } /** * hb_subset_cff2: - * Subsets the CFF2 table according to a provided plan. - * - * Return value: subsetted cff2 table. + * Subsets the CFF2 table according to a provided subset context. **/ bool -hb_subset_cff2 (hb_subset_plan_t *plan, - hb_blob_t **prime /* OUT */) +hb_subset_cff2 (hb_subset_context_t *c) { - hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table (plan->source); - const char *data = hb_blob_get_data(cff2_blob, nullptr); - OT::cff2::accelerator_subset_t acc; - acc.init(plan->source); - bool result = likely (acc.is_valid ()) && - _hb_subset_cff2 (acc, data, plan, prime); - - hb_blob_destroy (cff2_blob); + acc.init (c->plan->source); + bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c); acc.fini (); return result; } + +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh index baac1a9be2fa3..f10556ddd74bb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-cff2.hh @@ -32,7 +32,6 @@ #include "hb-subset-plan.hh" HB_INTERNAL bool -hb_subset_cff2 (hb_subset_plan_t *plan, - hb_blob_t **cff2_prime /* OUT */); +hb_subset_cff2 (hb_subset_context_t *c); #endif /* HB_SUBSET_CFF2_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc deleted file mode 100644 index 03c39a6dbbe1e..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-glyf.cc +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright © 2018 Google, Inc. - * - * This is part of HarfBuzz, a text shaping library. - * - * Permission is hereby granted, without written agreement and without - * license or royalty fees, to use, copy, modify, and distribute this - * software and its documentation for any purpose, provided that the - * above copyright notice and the following two paragraphs appear in - * all copies of this software. - * - * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES - * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN - * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - * Google Author(s): Garret Rieger, Roderick Sheeter - */ - -#include "hb-open-type.hh" -#include "hb-ot-glyf-table.hh" -#include "hb-set.h" -#include "hb-subset-glyf.hh" - -static bool -_calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, - hb_vector_t &glyph_ids, - hb_bool_t drop_hints, - bool *use_short_loca /* OUT */, - unsigned int *glyf_size /* OUT */, - unsigned int *loca_size /* OUT */, - hb_vector_t *instruction_ranges /* OUT */) -{ - unsigned int total = 0; - for (unsigned int i = 0; i < glyph_ids.length; i++) - { - hb_codepoint_t next_glyph = glyph_ids[i]; - if (!instruction_ranges->resize (instruction_ranges->length + 2)) - { - DEBUG_MSG(SUBSET, nullptr, "Failed to resize instruction_ranges."); - return false; - } - unsigned int *instruction_start = &(*instruction_ranges)[instruction_ranges->length - 2]; - *instruction_start = 0; - unsigned int *instruction_end = &(*instruction_ranges)[instruction_ranges->length - 1]; - *instruction_end = 0; - - unsigned int start_offset, end_offset; - if (unlikely (!(glyf.get_offsets (next_glyph, &start_offset, &end_offset) && - glyf.remove_padding (start_offset, &end_offset)))) - { - DEBUG_MSG(SUBSET, nullptr, "Invalid gid %d", next_glyph); - continue; - } - if (end_offset - start_offset < OT::glyf::GlyphHeader::static_size) - continue; /* 0-length glyph */ - - if (drop_hints) - { - if (unlikely (!glyf.get_instruction_offsets (start_offset, end_offset, - instruction_start, instruction_end))) - { - DEBUG_MSG(SUBSET, nullptr, "Unable to get instruction offsets for %d", next_glyph); - return false; - } - } - - total += end_offset - start_offset - (*instruction_end - *instruction_start); - /* round2 so short loca will work */ - total += total % 2; - } - - *glyf_size = total; - *use_short_loca = (total <= 131070); - *loca_size = (glyph_ids.length + 1) - * (*use_short_loca ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32)); - - DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca", - total, - *loca_size, - *use_short_loca ? "short" : "long"); - return true; -} - -static bool -_write_loca_entry (unsigned int id, - unsigned int offset, - bool is_short, - void *loca_prime, - unsigned int loca_size) -{ - unsigned int entry_size = is_short ? sizeof (OT::HBUINT16) : sizeof (OT::HBUINT32); - if ((id + 1) * entry_size <= loca_size) - { - if (is_short) { - ((OT::HBUINT16*) loca_prime) [id].set (offset / 2); - } else { - ((OT::HBUINT32*) loca_prime) [id].set (offset); - } - return true; - } - - // Offset was not written because the write is out of bounds. - DEBUG_MSG(SUBSET, - nullptr, - "WARNING: Attempted to write an out of bounds loca entry at index %d. Loca size is %d.", - id, - loca_size); - return false; -} - -static void -_update_components (hb_subset_plan_t * plan, - char * glyph_start, - unsigned int length) -{ - OT::glyf::CompositeGlyphHeader::Iterator iterator; - if (OT::glyf::CompositeGlyphHeader::get_iterator (glyph_start, - length, - &iterator)) - { - do - { - hb_codepoint_t new_gid; - if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex, - &new_gid)) - continue; - - ((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex.set (new_gid); - } while (iterator.move_to_next ()); - } -} - -static bool _remove_composite_instruction_flag (char *glyf_prime, unsigned int length) -{ - /* remove WE_HAVE_INSTRUCTIONS from flags in dest */ - OT::glyf::CompositeGlyphHeader::Iterator composite_it; - if (unlikely (!OT::glyf::CompositeGlyphHeader::get_iterator (glyf_prime, length, &composite_it))) return false; - const OT::glyf::CompositeGlyphHeader *glyph; - do { - glyph = composite_it.current; - OT::HBUINT16 *flags = const_cast (&glyph->flags); - flags->set ( (uint16_t) *flags & ~OT::glyf::CompositeGlyphHeader::WE_HAVE_INSTRUCTIONS); - } while (composite_it.move_to_next ()); - return true; -} - -static bool -_write_glyf_and_loca_prime (hb_subset_plan_t *plan, - const OT::glyf::accelerator_t &glyf, - const char *glyf_data, - bool use_short_loca, - hb_vector_t &instruction_ranges, - unsigned int glyf_prime_size, - char *glyf_prime_data /* OUT */, - unsigned int loca_prime_size, - char *loca_prime_data /* OUT */) -{ - hb_vector_t &glyph_ids = plan->glyphs; - char *glyf_prime_data_next = glyf_prime_data; - - bool success = true; - for (unsigned int i = 0; i < glyph_ids.length; i++) - { - unsigned int start_offset, end_offset; - if (unlikely (!(glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset) && - glyf.remove_padding (start_offset, &end_offset)))) - end_offset = start_offset = 0; - - unsigned int instruction_start = instruction_ranges[i * 2]; - unsigned int instruction_end = instruction_ranges[i * 2 + 1]; - - int length = end_offset - start_offset - (instruction_end - instruction_start); - - if (glyf_prime_data_next + length > glyf_prime_data + glyf_prime_size) - { - DEBUG_MSG(SUBSET, - nullptr, - "WARNING: Attempted to write an out of bounds glyph entry for gid %d (length %d)", - i, length); - return false; - } - - if (instruction_start == instruction_end) - memcpy (glyf_prime_data_next, glyf_data + start_offset, length); - else - { - memcpy (glyf_prime_data_next, glyf_data + start_offset, instruction_start - start_offset); - memcpy (glyf_prime_data_next + instruction_start - start_offset, glyf_data + instruction_end, end_offset - instruction_end); - /* if the instructions end at the end this was a composite glyph, else simple */ - if (instruction_end == end_offset) - { - if (unlikely (!_remove_composite_instruction_flag (glyf_prime_data_next, length))) return false; - } - else - /* zero instruction length, which is just before instruction_start */ - memset (glyf_prime_data_next + instruction_start - start_offset - 2, 0, 2); - } - - success = success && _write_loca_entry (i, - glyf_prime_data_next - glyf_prime_data, - use_short_loca, - loca_prime_data, - loca_prime_size); - _update_components (plan, glyf_prime_data_next, length); - - // TODO: don't align to two bytes if using long loca. - glyf_prime_data_next += length + (length % 2); // Align to 2 bytes for short loca. - } - - success = success && _write_loca_entry (glyph_ids.length, - glyf_prime_data_next - glyf_prime_data, - use_short_loca, - loca_prime_data, - loca_prime_size); - return success; -} - -static bool -_hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, - const char *glyf_data, - hb_subset_plan_t *plan, - bool *use_short_loca, - hb_blob_t **glyf_prime /* OUT */, - hb_blob_t **loca_prime /* OUT */) -{ - // TODO(grieger): Sanity check allocation size for the new table. - hb_vector_t &glyphs_to_retain = plan->glyphs; - - unsigned int glyf_prime_size; - unsigned int loca_prime_size; - hb_vector_t instruction_ranges; - instruction_ranges.init (); - - if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf, - glyphs_to_retain, - plan->drop_hints, - use_short_loca, - &glyf_prime_size, - &loca_prime_size, - &instruction_ranges))) { - instruction_ranges.fini (); - return false; - } - - char *glyf_prime_data = (char *) calloc (1, glyf_prime_size); - char *loca_prime_data = (char *) calloc (1, loca_prime_size); - if (unlikely (!_write_glyf_and_loca_prime (plan, glyf, glyf_data, - *use_short_loca, - instruction_ranges, - glyf_prime_size, glyf_prime_data, - loca_prime_size, loca_prime_data))) { - free (glyf_prime_data); - free (loca_prime_data); - instruction_ranges.fini (); - return false; - } - instruction_ranges.fini (); - - *glyf_prime = hb_blob_create (glyf_prime_data, - glyf_prime_size, - HB_MEMORY_MODE_READONLY, - glyf_prime_data, - free); - *loca_prime = hb_blob_create (loca_prime_data, - loca_prime_size, - HB_MEMORY_MODE_READONLY, - loca_prime_data, - free); - return true; -} - -/** - * hb_subset_glyf: - * Subsets the glyph table according to a provided plan. - * - * Return value: subsetted glyf table. - * - * Since: 1.7.5 - **/ -bool -hb_subset_glyf_and_loca (hb_subset_plan_t *plan, - bool *use_short_loca, /* OUT */ - hb_blob_t **glyf_prime, /* OUT */ - hb_blob_t **loca_prime /* OUT */) -{ - hb_blob_t *glyf_blob = hb_sanitize_context_t ().reference_table (plan->source); - const char *glyf_data = hb_blob_get_data (glyf_blob, nullptr); - - OT::glyf::accelerator_t glyf; - glyf.init (plan->source); - bool result = _hb_subset_glyf_and_loca (glyf, - glyf_data, - plan, - use_short_loca, - glyf_prime, - loca_prime); - - hb_blob_destroy (glyf_blob); - glyf.fini (); - - return result; -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc index 3feeb5c75922b..087981b52439b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.cc @@ -44,7 +44,45 @@ hb_subset_input_create_or_fail () input->unicodes = hb_set_create (); input->glyphs = hb_set_create (); - input->drop_layout = true; + input->name_ids = hb_set_create (); + hb_set_add_range (input->name_ids, 0, 6); + input->name_languages = hb_set_create (); + hb_set_add (input->name_languages, 0x0409); + input->drop_tables = hb_set_create (); + input->drop_hints = false; + input->desubroutinize = false; + input->retain_gids = false; + input->name_legacy = false; + + hb_tag_t default_drop_tables[] = { + // Layout disabled by default + HB_TAG ('G', 'S', 'U', 'B'), + HB_TAG ('G', 'P', 'O', 'S'), + HB_TAG ('G', 'D', 'E', 'F'), + HB_TAG ('m', 'o', 'r', 'x'), + HB_TAG ('m', 'o', 'r', 't'), + HB_TAG ('k', 'e', 'r', 'x'), + HB_TAG ('k', 'e', 'r', 'n'), + + // Copied from fontTools: + HB_TAG ('B', 'A', 'S', 'E'), + HB_TAG ('J', 'S', 'T', 'F'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('P', 'C', 'L', 'T'), + HB_TAG ('L', 'T', 'S', 'H'), + // Graphite tables + HB_TAG ('F', 'e', 'a', 't'), + HB_TAG ('G', 'l', 'a', 't'), + HB_TAG ('G', 'l', 'o', 'c'), + HB_TAG ('S', 'i', 'l', 'f'), + HB_TAG ('S', 'i', 'l', 'l'), + }; + + input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); return input; } @@ -78,6 +116,9 @@ hb_subset_input_destroy (hb_subset_input_t *subset_input) hb_set_destroy (subset_input->unicodes); hb_set_destroy (subset_input->glyphs); + hb_set_destroy (subset_input->name_ids); + hb_set_destroy (subset_input->name_languages); + hb_set_destroy (subset_input->drop_tables); free (subset_input); } @@ -106,6 +147,24 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input) return subset_input->glyphs; } +HB_EXTERN hb_set_t * +hb_subset_input_nameid_set (hb_subset_input_t *subset_input) +{ + return subset_input->name_ids; +} + +HB_EXTERN hb_set_t * +hb_subset_input_namelangid_set (hb_subset_input_t *subset_input) +{ + return subset_input->name_languages; +} + +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input) +{ + return subset_input->drop_tables; +} + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints) @@ -120,27 +179,51 @@ hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input) } HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout) +hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, + hb_bool_t desubroutinize) { - subset_input->drop_layout = drop_layout; + subset_input->desubroutinize = desubroutinize; } HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input) +hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input) { - return subset_input->drop_layout; + return subset_input->desubroutinize; } +/** + * hb_subset_input_set_retain_gids: + * @subset_input: a subset_input. + * @retain_gids: If true the subsetter will not renumber glyph ids. + * Since: 2.4.0 + **/ HB_EXTERN void -hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, - hb_bool_t desubroutinize) +hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input, + hb_bool_t retain_gids) { - subset_input->desubroutinize = desubroutinize; + subset_input->retain_gids = retain_gids; } +/** + * hb_subset_input_get_retain_gids: + * Returns: value of retain_gids. + * Since: 2.4.0 + **/ HB_EXTERN hb_bool_t -hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input) +hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input) { - return subset_input->desubroutinize; + return subset_input->retain_gids; +} + +HB_EXTERN void +hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, + hb_bool_t name_legacy) +{ + subset_input->name_legacy = name_legacy; +} + +HB_EXTERN hb_bool_t +hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input) +{ + return subset_input->name_legacy; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh index 8dad94f584ee8..0aeb96695b77b 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-input.hh @@ -40,15 +40,19 @@ struct hb_subset_input_t hb_set_t *unicodes; hb_set_t *glyphs; - - bool drop_hints : 1; - bool drop_layout : 1; - bool desubroutinize : 1; + hb_set_t *name_ids; + hb_set_t *name_languages; + hb_set_t *drop_tables; + + bool drop_hints; + bool desubroutinize; + bool retain_gids; + bool name_legacy; /* TODO * * features * lookups - * nameIDs + * name_ids * ... */ }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc index 69b432872da50..2f2f343ae2bc0 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.cc @@ -30,44 +30,47 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-cff1-table.hh" +#include "hb-ot-color-colr-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" -static void -_add_gid_and_children (const OT::glyf::accelerator_t &glyf, - hb_codepoint_t gid, - hb_set_t *gids_to_retain) -{ - if (hb_set_has (gids_to_retain, gid)) - // Already visited this gid, ignore. - return; - - hb_set_add (gids_to_retain, gid); - OT::glyf::CompositeGlyphHeader::Iterator composite; - if (glyf.get_composite (gid, &composite)) - { - do - { - _add_gid_and_children (glyf, (hb_codepoint_t) composite.current->glyphIndex, gids_to_retain); - } while (composite.move_to_next()); - } -} - -static void +#ifndef HB_NO_SUBSET_CFF +static inline void _add_cff_seac_components (const OT::cff1::accelerator_t &cff, - hb_codepoint_t gid, - hb_set_t *gids_to_retain) + hb_codepoint_t gid, + hb_set_t *gids_to_retain) { hb_codepoint_t base_gid, accent_gid; if (cff.get_seac_components (gid, &base_gid, &accent_gid)) { - hb_set_add (gids_to_retain, base_gid); - hb_set_add (gids_to_retain, accent_gid); + gids_to_retain->add (base_gid); + gids_to_retain->add (accent_gid); } } +#endif +#ifndef HB_NO_SUBSET_LAYOUT static void -_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain) +_remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned count = indexes->get_population (); + + for (auto _ : + hb_zip (indexes->iter (), hb_range (count))) + mapping->set (_.first, _.second); + +} + +static inline void +_gsub_closure_glyphs_lookups_features (hb_face_t *face, + hb_set_t *gids_to_retain, + hb_map_t *gsub_lookups, + hb_map_t *gsub_features) { hb_set_t lookup_indices; hb_ot_layout_collect_lookups (face, @@ -79,9 +82,88 @@ _gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain) hb_ot_layout_lookups_substitute_closure (face, &lookup_indices, gids_to_retain); + hb_blob_ptr_t gsub = hb_sanitize_context_t ().reference_table (face); + gsub->closure_lookups (face, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, gsub_lookups); + + //closure features + hb_set_t feature_indices; + gsub->closure_features (gsub_lookups, &feature_indices); + _remap_indexes (&feature_indices, gsub_features); + gsub.destroy (); } -static void +static inline void +_gpos_closure_lookups_features (hb_face_t *face, + const hb_set_t *gids_to_retain, + hb_map_t *gpos_lookups, + hb_map_t *gpos_features) +{ + hb_set_t lookup_indices; + hb_ot_layout_collect_lookups (face, + HB_OT_TAG_GPOS, + nullptr, + nullptr, + nullptr, + &lookup_indices); + hb_blob_ptr_t gpos = hb_sanitize_context_t ().reference_table (face); + gpos->closure_lookups (face, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, gpos_lookups); + + //closure features + hb_set_t feature_indices; + gpos->closure_features (gpos_lookups, &feature_indices); + _remap_indexes (&feature_indices, gpos_features); + gpos.destroy (); +} +#endif + +#ifndef HB_NO_VAR +static inline void + _collect_layout_variation_indices (hb_face_t *face, + const hb_set_t *glyphset, + const hb_map_t *gpos_lookups, + hb_set_t *layout_variation_indices, + hb_map_t *layout_variation_idx_map) +{ + hb_blob_ptr_t gdef = hb_sanitize_context_t ().reference_table (face); + hb_blob_ptr_t gpos = hb_sanitize_context_t ().reference_table (face); + + if (!gdef->has_data ()) + { + gdef.destroy (); + gpos.destroy (); + return; + } + OT::hb_collect_variation_indices_context_t c (layout_variation_indices, glyphset, gpos_lookups); + gdef->collect_variation_indices (&c); + + if (hb_ot_layout_has_positioning (face)) + gpos->collect_variation_indices (&c); + + gdef->remap_layout_variation_indices (layout_variation_indices, layout_variation_idx_map); + + gdef.destroy (); + gpos.destroy (); +} +#endif + +static inline void +_cmap_closure (hb_face_t *face, + const hb_set_t *unicodes, + hb_set_t *glyphset) +{ + OT::cmap::accelerator_t cmap; + cmap.init (face); + cmap.table->closure_glyphs (unicodes, glyphset); + cmap.fini (); +} + +static inline void _remove_invalid_gids (hb_set_t *glyphs, unsigned int num_glyphs) { @@ -93,23 +175,29 @@ _remove_invalid_gids (hb_set_t *glyphs, } } -static hb_set_t * -_populate_gids_to_retain (hb_face_t *face, +static void +_populate_gids_to_retain (hb_subset_plan_t* plan, const hb_set_t *unicodes, + const hb_set_t *input_glyphs_to_retain, bool close_over_gsub, - hb_set_t *unicodes_to_retain, - hb_map_t *codepoint_to_glyph, - hb_vector_t *glyphs) + bool close_over_gpos, + bool close_over_gdef) { OT::cmap::accelerator_t cmap; OT::glyf::accelerator_t glyf; +#ifndef HB_NO_SUBSET_CFF OT::cff1::accelerator_t cff; - cmap.init (face); - glyf.init (face); - cff.init (face); +#endif + OT::COLR::accelerator_t colr; + cmap.init (plan->source); + glyf.init (plan->source); +#ifndef HB_NO_SUBSET_CFF + cff.init (plan->source); +#endif + colr.init (plan->source); - hb_set_t *initial_gids_to_retain = hb_set_create (); - initial_gids_to_retain->add (0); // Not-def + plan->_glyphset_gsub->add (0); // Not-def + hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain); hb_codepoint_t cp = HB_SET_VALUE_INVALID; while (unicodes->next (&cp)) @@ -120,48 +208,96 @@ _populate_gids_to_retain (hb_face_t *face, DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); continue; } - unicodes_to_retain->add (cp); - codepoint_to_glyph->set (cp, gid); - initial_gids_to_retain->add (gid); + plan->unicodes->add (cp); + plan->codepoint_to_glyph->set (cp, gid); + plan->_glyphset_gsub->add (gid); } + _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub); + +#ifndef HB_NO_SUBSET_LAYOUT if (close_over_gsub) - // Add all glyphs needed for GSUB substitutions. - _gsub_closure (face, initial_gids_to_retain); + // closure all glyphs/lookups/features needed for GSUB substitutions. + _gsub_closure_glyphs_lookups_features (plan->source, plan->_glyphset_gsub, plan->gsub_lookups, plan->gsub_features); + + if (close_over_gpos) + _gpos_closure_lookups_features (plan->source, plan->_glyphset_gsub, plan->gpos_lookups, plan->gpos_features); +#endif + _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ()); // Populate a full set of glyphs to retain by adding all referenced // composite glyphs. hb_codepoint_t gid = HB_SET_VALUE_INVALID; - hb_set_t *all_gids_to_retain = hb_set_create (); - while (initial_gids_to_retain->next (&gid)) + while (plan->_glyphset_gsub->next (&gid)) { - _add_gid_and_children (glyf, gid, all_gids_to_retain); + glyf.add_gid_and_children (gid, plan->_glyphset); +#ifndef HB_NO_SUBSET_CFF if (cff.is_valid ()) - _add_cff_seac_components (cff, gid, all_gids_to_retain); + _add_cff_seac_components (cff, gid, plan->_glyphset); +#endif + if (colr.is_valid ()) + colr.closure_glyphs (gid, plan->_glyphset); } - hb_set_destroy (initial_gids_to_retain); - _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ()); + _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ()); - glyphs->alloc (all_gids_to_retain->get_population ()); - gid = HB_SET_VALUE_INVALID; - while (all_gids_to_retain->next (&gid)) - glyphs->push (gid); +#ifndef HB_NO_VAR + if (close_over_gdef) + _collect_layout_variation_indices (plan->source, plan->_glyphset, plan->gpos_lookups, plan->layout_variation_indices, plan->layout_variation_idx_map); +#endif +#ifndef HB_NO_SUBSET_CFF cff.fini (); +#endif glyf.fini (); cmap.fini (); - - return all_gids_to_retain; } static void -_create_old_gid_to_new_gid_map (const hb_vector_t &glyphs, - hb_map_t *glyph_map) +_create_old_gid_to_new_gid_map (const hb_face_t *face, + bool retain_gids, + const hb_set_t *all_gids_to_retain, + hb_map_t *glyph_map, /* OUT */ + hb_map_t *reverse_glyph_map, /* OUT */ + unsigned int *num_glyphs /* OUT */) { - for (unsigned int i = 0; i < glyphs.length; i++) { - glyph_map->set (glyphs[i], i); + if (!retain_gids) + { + + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0) + | hb_sink (reverse_glyph_map) + ; + *num_glyphs = reverse_glyph_map->get_population (); + } else { + + hb_iter (all_gids_to_retain) + | hb_map ([] (hb_codepoint_t _) { + return hb_pair_t (_, _); + }) + | hb_sink (reverse_glyph_map) + ; + + unsigned max_glyph = + + hb_iter (all_gids_to_retain) + | hb_reduce (hb_max, 0u) + ; + *num_glyphs = max_glyph + 1; } + + + reverse_glyph_map->iter () + | hb_map (&hb_pair_t::reverse) + | hb_sink (glyph_map) + ; +} + +static void +_nameid_closure (hb_face_t *face, + hb_set_t *nameids) +{ +#ifndef HB_NO_STYLE + face->table.STAT->collect_name_ids (nameids); +#endif +#ifndef HB_NO_VAR + face->table.fvar->collect_name_ids (nameids); +#endif } /** @@ -175,28 +311,52 @@ _create_old_gid_to_new_gid_map (const hb_vector_t &glyphs, * Since: 1.7.5 **/ hb_subset_plan_t * -hb_subset_plan_create (hb_face_t *face, - hb_subset_input_t *input) +hb_subset_plan_create (hb_face_t *face, + hb_subset_input_t *input) { - hb_subset_plan_t *plan = hb_object_create (); + hb_subset_plan_t *plan; + if (unlikely (!(plan = hb_object_create ()))) + return const_cast (&Null (hb_subset_plan_t)); + plan->successful = true; plan->drop_hints = input->drop_hints; - plan->drop_layout = input->drop_layout; plan->desubroutinize = input->desubroutinize; - plan->unicodes = hb_set_create(); - plan->glyphs.init(); + plan->retain_gids = input->retain_gids; + plan->name_legacy = input->name_legacy; + plan->unicodes = hb_set_create (); + plan->name_ids = hb_set_reference (input->name_ids); + _nameid_closure (face, plan->name_ids); + plan->name_languages = hb_set_reference (input->name_languages); + plan->glyphs_requested = hb_set_reference (input->glyphs); + plan->drop_tables = hb_set_reference (input->drop_tables); plan->source = hb_face_reference (face); plan->dest = hb_face_builder_create (); - plan->codepoint_to_glyph = hb_map_create(); - plan->glyph_map = hb_map_create(); - plan->glyphset = _populate_gids_to_retain (face, - input->unicodes, - !plan->drop_layout, - plan->unicodes, - plan->codepoint_to_glyph, - &plan->glyphs); - _create_old_gid_to_new_gid_map (plan->glyphs, - plan->glyph_map); + + plan->_glyphset = hb_set_create (); + plan->_glyphset_gsub = hb_set_create (); + plan->codepoint_to_glyph = hb_map_create (); + plan->glyph_map = hb_map_create (); + plan->reverse_glyph_map = hb_map_create (); + plan->gsub_lookups = hb_map_create (); + plan->gpos_lookups = hb_map_create (); + plan->gsub_features = hb_map_create (); + plan->gpos_features = hb_map_create (); + plan->layout_variation_indices = hb_set_create (); + plan->layout_variation_idx_map = hb_map_create (); + + _populate_gids_to_retain (plan, + input->unicodes, + input->glyphs, + !input->drop_tables->has (HB_OT_TAG_GSUB), + !input->drop_tables->has (HB_OT_TAG_GPOS), + !input->drop_tables->has (HB_OT_TAG_GDEF)); + + _create_old_gid_to_new_gid_map (face, + input->retain_gids, + plan->_glyphset, + plan->glyph_map, + plan->reverse_glyph_map, + &plan->_num_output_glyphs); return plan; } @@ -212,12 +372,24 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) if (!hb_object_destroy (plan)) return; hb_set_destroy (plan->unicodes); - plan->glyphs.fini (); + hb_set_destroy (plan->name_ids); + hb_set_destroy (plan->name_languages); + hb_set_destroy (plan->glyphs_requested); + hb_set_destroy (plan->drop_tables); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); hb_map_destroy (plan->glyph_map); - hb_set_destroy (plan->glyphset); + hb_map_destroy (plan->reverse_glyph_map); + hb_set_destroy (plan->_glyphset); + hb_set_destroy (plan->_glyphset_gsub); + hb_map_destroy (plan->gsub_lookups); + hb_map_destroy (plan->gpos_lookups); + hb_map_destroy (plan->gsub_features); + hb_map_destroy (plan->gpos_features); + hb_set_destroy (plan->layout_variation_indices); + hb_map_destroy (plan->layout_variation_idx_map); + free (plan); } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh index 83dd370f6a732..b029548e0dd46 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset-plan.hh @@ -33,30 +33,111 @@ #include "hb-subset-input.hh" #include "hb-map.hh" +#include "hb-set.hh" struct hb_subset_plan_t { hb_object_header_t header; + bool successful : 1; bool drop_hints : 1; - bool drop_layout : 1; bool desubroutinize : 1; + bool retain_gids : 1; + bool name_legacy : 1; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; - hb_vector_t glyphs; - hb_set_t *glyphset; + // name_ids we would like to retain + hb_set_t *name_ids; + // name_languages we would like to retain + hb_set_t *name_languages; + + //glyph ids requested to retain + hb_set_t *glyphs_requested; + + // Tables which should be dropped. + hb_set_t *drop_tables; + + // The glyph subset hb_map_t *codepoint_to_glyph; + + // Old -> New glyph id mapping hb_map_t *glyph_map; + hb_map_t *reverse_glyph_map; // Plan is only good for a specific source/dest so keep them with it hb_face_t *source; hb_face_t *dest; - bool new_gid_for_codepoint (hb_codepoint_t codepoint, - hb_codepoint_t *new_gid) const + unsigned int _num_output_glyphs; + hb_set_t *_glyphset; + hb_set_t *_glyphset_gsub; + + //active lookups we'd like to retain + hb_map_t *gsub_lookups; + hb_map_t *gpos_lookups; + + //active features we'd like to retain + hb_map_t *gsub_features; + hb_map_t *gpos_features; + + //The set of layout item variation store delta set indices to be retained + hb_set_t *layout_variation_indices; + //Old -> New layout item variation store delta set index mapping + hb_map_t *layout_variation_idx_map; + + public: + + bool in_error () const { return !successful; } + + bool check_success(bool success) + { + successful = (successful && success); + return successful; + } + + /* + * The set of input glyph ids which will be retained in the subset. + * Does NOT include ids kept due to retain_gids. You probably want to use + * glyph_map/reverse_glyph_map. + */ + inline const hb_set_t * + glyphset () const + { + return _glyphset; + } + + /* + * The set of input glyph ids which will be retained in the subset. + */ + inline const hb_set_t * + glyphset_gsub () const + { + return _glyphset_gsub; + } + + /* + * The total number of output glyphs in the final subset. + */ + inline unsigned int + num_output_glyphs () const + { + return _num_output_glyphs; + } + + /* + * Given an output gid , returns true if that glyph id is an empty + * glyph (ie. it's a gid that we are dropping all data for). + */ + inline bool is_empty_glyph (hb_codepoint_t gid) const + { + return !_glyphset->has (gid); + } + + inline bool new_gid_for_codepoint (hb_codepoint_t codepoint, + hb_codepoint_t *new_gid) const { hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint); if (old_gid == HB_MAP_VALUE_INVALID) @@ -65,8 +146,8 @@ struct hb_subset_plan_t return new_gid_for_old_gid (old_gid, new_gid); } - bool new_gid_for_old_gid (hb_codepoint_t old_gid, - hb_codepoint_t *new_gid) const + inline bool new_gid_for_old_gid (hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) const { hb_codepoint_t gid = glyph_map->get (old_gid); if (gid == HB_MAP_VALUE_INVALID) @@ -76,7 +157,18 @@ struct hb_subset_plan_t return true; } - bool + inline bool old_gid_for_new_gid (hb_codepoint_t new_gid, + hb_codepoint_t *old_gid) const + { + hb_codepoint_t gid = reverse_glyph_map->get (new_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *old_gid = gid; + return true; + } + + inline bool add_table (hb_tag_t tag, hb_blob_t *contents) { diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc index 65841672ad61e..43afc30b9acdd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.cc @@ -28,7 +28,6 @@ #include "hb-open-type.hh" #include "hb-subset.hh" -#include "hb-subset-glyf.hh" #include "hb-open-file.hh" #include "hb-ot-cmap-table.hh" @@ -38,218 +37,194 @@ #include "hb-ot-hhea-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-maxp-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-colr-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" #include "hb-ot-cff1-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-vorg-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-color-cbdt-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-ot-var-hvar-table.hh" -static unsigned int -_plan_estimate_subset_table_size (hb_subset_plan_t *plan, - unsigned int table_len) +static unsigned +_plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len) { - unsigned int src_glyphs = plan->source->get_num_glyphs (); - unsigned int dst_glyphs = plan->glyphset->get_population (); + unsigned src_glyphs = plan->source->get_num_glyphs (); + unsigned dst_glyphs = plan->glyphset ()->get_population (); if (unlikely (!src_glyphs)) return 512 + table_len; - return 512 + (unsigned int) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); + return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); } template static bool -_subset2 (hb_subset_plan_t *plan) +_subset (hb_subset_plan_t *plan) { + bool result = false; hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table (plan->source); const TableType *table = source_blob->as (); hb_tag_t tag = TableType::tableTag; - hb_bool_t result = false; if (source_blob->data) { hb_vector_t buf; - unsigned int buf_size = _plan_estimate_subset_table_size (plan, source_blob->length); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); + /* TODO Not all tables are glyph-related. 'name' table size for example should not be + * affected by number of glyphs. Accommodate that. */ + unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); if (unlikely (!buf.alloc (buf_size))) { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + hb_blob_destroy (source_blob); return false; } retry: hb_serialize_context_t serializer ((void *) buf, buf_size); - hb_subset_context_t c (plan, &serializer); - result = table->subset (&c); - if (serializer.in_error ()) + serializer.start_serialize (); + hb_subset_context_t c (source_blob, plan, &serializer, tag); + bool needed = table->subset (&c); + if (serializer.ran_out_of_room) { buf_size += (buf_size >> 1) + 32; - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size); if (unlikely (!buf.alloc (buf_size))) { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size); + hb_blob_destroy (source_blob); return false; } goto retry; } + serializer.end_serialize (); + + result = !serializer.in_error (); + if (result) { - hb_blob_t *dest_blob = serializer.copy_blob (); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length); - result = c.plan->add_table (tag, dest_blob); - hb_blob_destroy (dest_blob); - } - else - { - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); - result = true; + if (needed) + { + hb_blob_t *dest_blob = serializer.copy_blob (); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length); + result = c.plan->add_table (tag, dest_blob); + hb_blob_destroy (dest_blob); + } + else + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); + } } } else - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); hb_blob_destroy (source_blob); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); return result; } -template static bool -_subset (hb_subset_plan_t *plan) +_is_table_present (hb_face_t *source, hb_tag_t tag) { - hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table (plan->source); - const TableType *table = source_blob->as (); - - hb_tag_t tag = TableType::tableTag; - hb_bool_t result = false; - if (source_blob->data) - result = table->subset (plan); - else - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); - - hb_blob_destroy (source_blob); - DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG (tag), result ? "success" : "FAILED!"); - return result; + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + if (table_tags[i] == tag) + return true; + offset += num_tables; + } + return false; } - static bool -_subset_table (hb_subset_plan_t *plan, - hb_tag_t tag) +_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) { - DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG (tag)); - bool result = true; - switch (tag) { - case HB_OT_TAG_glyf: - result = _subset (plan); - break; - case HB_OT_TAG_hdmx: - result = _subset (plan); - break; - case HB_OT_TAG_head: - // TODO that won't work well if there is no glyf - DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf"); - result = true; - break; - case HB_OT_TAG_hhea: - DEBUG_MSG(SUBSET, nullptr, "skip hhea handled by hmtx"); - return true; - case HB_OT_TAG_hmtx: - result = _subset (plan); - break; - case HB_OT_TAG_vhea: - DEBUG_MSG(SUBSET, nullptr, "skip vhea handled by vmtx"); - return true; - case HB_OT_TAG_vmtx: - result = _subset (plan); - break; - case HB_OT_TAG_maxp: - result = _subset (plan); - break; - case HB_OT_TAG_loca: - DEBUG_MSG(SUBSET, nullptr, "skip loca handled by glyf"); - return true; - case HB_OT_TAG_cmap: - result = _subset (plan); - break; - case HB_OT_TAG_OS2: - result = _subset (plan); - break; - case HB_OT_TAG_post: - result = _subset (plan); - break; - case HB_OT_TAG_cff1: - result = _subset (plan); - break; - case HB_OT_TAG_cff2: - result = _subset (plan); - break; - case HB_OT_TAG_VORG: - result = _subset (plan); - break; - case HB_OT_TAG_GDEF: - result = _subset2 (plan); - break; - case HB_OT_TAG_GSUB: - result = _subset2 (plan); - break; - case HB_OT_TAG_GPOS: - result = _subset2 (plan); - break; + if (plan->drop_tables->has (tag)) + return true; - default: - hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); - if (likely (source_table)) - result = plan->add_table (tag, source_table); - else - result = false; - hb_blob_destroy (source_table); - break; + switch (tag) + { + case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + return plan->drop_hints; + +#ifdef HB_NO_SUBSET_LAYOUT + // Drop Layout Tables if requested. + case HB_OT_TAG_GDEF: + case HB_OT_TAG_GPOS: + case HB_OT_TAG_GSUB: + case HB_TAG ('m','o','r','x'): + case HB_TAG ('m','o','r','t'): + case HB_TAG ('k','e','r','x'): + case HB_TAG ('k','e','r','n'): + return true; +#endif + + default: + return false; } - DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG (tag), result ? "ok" : "FAILED"); - return result; } static bool -_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) +_subset_table (hb_subset_plan_t *plan, hb_tag_t tag) { - switch (tag) { - case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */ - case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */ - case HB_TAG ('f', 'p', 'g', 'm'): /* hint table, fallthrough */ - case HB_TAG ('p', 'r', 'e', 'p'): /* hint table, fallthrough */ - case HB_TAG ('h', 'd', 'm', 'x'): /* hint table, fallthrough */ - case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */ - return plan->drop_hints; - // Drop Layout Tables if requested. - case HB_OT_TAG_GDEF: - case HB_OT_TAG_GPOS: - case HB_OT_TAG_GSUB: - return plan->drop_layout; - // Drop these tables below by default, list pulled - // from fontTools: - case HB_TAG ('B', 'A', 'S', 'E'): - case HB_TAG ('J', 'S', 'T', 'F'): - case HB_TAG ('D', 'S', 'I', 'G'): - case HB_TAG ('E', 'B', 'D', 'T'): - case HB_TAG ('E', 'B', 'L', 'C'): - case HB_TAG ('E', 'B', 'S', 'C'): - case HB_TAG ('S', 'V', 'G', ' '): - case HB_TAG ('P', 'C', 'L', 'T'): - case HB_TAG ('L', 'T', 'S', 'H'): - // Graphite tables: - case HB_TAG ('F', 'e', 'a', 't'): - case HB_TAG ('G', 'l', 'a', 't'): - case HB_TAG ('G', 'l', 'o', 'c'): - case HB_TAG ('S', 'i', 'l', 'f'): - case HB_TAG ('S', 'i', 'l', 'l'): - // Colour - case HB_TAG ('s', 'b', 'i', 'x'): - return true; - default: - return false; + DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + switch (tag) + { + case HB_OT_TAG_glyf: return _subset (plan); + case HB_OT_TAG_hdmx: return _subset (plan); + case HB_OT_TAG_name: return _subset (plan); + case HB_OT_TAG_head: + if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + return true; /* skip head, handled by glyf */ + return _subset (plan); + case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ + case HB_OT_TAG_hmtx: return _subset (plan); + case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ + case HB_OT_TAG_vmtx: return _subset (plan); + case HB_OT_TAG_maxp: return _subset (plan); + case HB_OT_TAG_sbix: return _subset (plan); + case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ + case HB_OT_TAG_cmap: return _subset (plan); + case HB_OT_TAG_OS2 : return _subset (plan); + case HB_OT_TAG_post: return _subset (plan); + case HB_OT_TAG_COLR: return _subset (plan); + case HB_OT_TAG_CBLC: return _subset (plan); + case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ + +#ifndef HB_NO_SUBSET_CFF + case HB_OT_TAG_cff1: return _subset (plan); + case HB_OT_TAG_cff2: return _subset (plan); + case HB_OT_TAG_VORG: return _subset (plan); +#endif + +#ifndef HB_NO_SUBSET_LAYOUT + case HB_OT_TAG_GDEF: return _subset (plan); + case HB_OT_TAG_GSUB: return _subset (plan); + case HB_OT_TAG_GPOS: return _subset (plan); + case HB_OT_TAG_gvar: return _subset (plan); + case HB_OT_TAG_HVAR: return _subset (plan); + case HB_OT_TAG_VVAR: return _subset (plan); +#endif + + default: + hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); + bool result = plan->add_table (tag, source_table); + hb_blob_destroy (source_table); + return result; } } @@ -261,33 +236,34 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) * Subsets a font according to provided input. **/ hb_face_t * -hb_subset (hb_face_t *source, - hb_subset_input_t *input) +hb_subset (hb_face_t *source, hb_subset_input_t *input) { if (unlikely (!input || !source)) return hb_face_get_empty (); hb_subset_plan_t *plan = hb_subset_plan_create (source, input); + if (unlikely (plan->in_error ())) + return hb_face_get_empty (); - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; + hb_set_t tags_set; bool success = true; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) { hb_tag_t tag = table_tags[i]; - if (_should_drop_table (plan, tag)) - { - DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG (tag)); - continue; - } - success = success && _subset_table (plan, tag); + if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue; + tags_set.add (tag); + success = _subset_table (plan, tag); + if (unlikely (!success)) goto end; } - offset += count; - } while (success && count == ARRAY_LENGTH (table_tags)); + offset += num_tables; + } +end: hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty (); + hb_subset_plan_destroy (plan); return result; } diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.h b/src/java.desktop/share/native/libharfbuzz/hb-subset.h index 75cc00aa39df8..3deab75e2e2dd 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.h @@ -54,6 +54,15 @@ hb_subset_input_unicode_set (hb_subset_input_t *subset_input); HB_EXTERN hb_set_t * hb_subset_input_glyph_set (hb_subset_input_t *subset_input); +HB_EXTERN hb_set_t * +hb_subset_input_nameid_set (hb_subset_input_t *subset_input); + +HB_EXTERN hb_set_t * +hb_subset_input_namelangid_set (hb_subset_input_t *subset_input); + +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input); + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints); @@ -61,16 +70,22 @@ HB_EXTERN hb_bool_t hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input); HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout); +hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, + hb_bool_t desubroutinize); HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input); +hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input); HB_EXTERN void -hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, - hb_bool_t desubroutinize); +hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input, + hb_bool_t retain_gids); HB_EXTERN hb_bool_t -hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input); +hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input); + +HB_EXTERN void +hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input, + hb_bool_t name_legacy); +HB_EXTERN hb_bool_t +hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input); /* hb_subset () */ HB_EXTERN hb_face_t * diff --git a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh index d4d720922575d..dbbe45131d290 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-subset.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-subset.hh @@ -40,19 +40,33 @@ struct hb_subset_context_t : hb_dispatch_context_t { const char *get_name () { return "SUBSET"; } - template - bool dispatch (const T &obj) { return obj.subset (this); } - static bool default_return_value () { return true; } + static return_t default_return_value () { return true; } + private: + template auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.subset (this, hb_forward (ds)...) ) + template auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, hb_forward (ds)...) ) + public: + template auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, hb_forward (ds)...) ) + + hb_blob_t *source_blob; hb_subset_plan_t *plan; hb_serialize_context_t *serializer; - unsigned int debug_depth; + hb_tag_t table_tag; - hb_subset_context_t (hb_subset_plan_t *plan_, - hb_serialize_context_t *serializer_) : + hb_subset_context_t (hb_blob_t *source_blob_, + hb_subset_plan_t *plan_, + hb_serialize_context_t *serializer_, + hb_tag_t table_tag_) : + source_blob (source_blob_), plan (plan_), serializer (serializer_), - debug_depth (0) {} + table_tag (table_tag_) {} }; diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh new file mode 100644 index 0000000000000..88623db338760 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd-table.hh @@ -0,0 +1,6780 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * + * on file with this description: Unicode 13.0.0 + */ + +#ifndef HB_UCD_TABLE_HH +#define HB_UCD_TABLE_HH + +#include "hb.hh" + +static const hb_script_t +_hb_ucd_sc_map[157] = +{ + HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, + HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, + HB_SCRIPT_CYRILLIC, HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HANGUL, HB_SCRIPT_HAN, + HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA, + HB_SCRIPT_LAO, HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, + HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, + HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, + HB_SCRIPT_CANADIAN_SYLLABICS, HB_SCRIPT_CHEROKEE, + HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, + HB_SCRIPT_MONGOLIAN, HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, HB_SCRIPT_SYRIAC, + HB_SCRIPT_THAANA, HB_SCRIPT_YI, + HB_SCRIPT_DESERET, HB_SCRIPT_GOTHIC, + HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, + HB_SCRIPT_HANUNOO, HB_SCRIPT_TAGALOG, + HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, HB_SCRIPT_LINEAR_B, + HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, + HB_SCRIPT_TAI_LE, HB_SCRIPT_UGARITIC, + HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC, + HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, + HB_SCRIPT_NEW_TAI_LUE, HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH, + HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, + HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI, + HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI, + HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_OLD_SOUTH_ARABIAN, HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI, HB_SCRIPT_BASSA_VAH, + HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, + HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, + HB_SCRIPT_MANICHAEAN, HB_SCRIPT_MENDE_KIKAKUI, + HB_SCRIPT_MODI, HB_SCRIPT_MRO, + HB_SCRIPT_NABATAEAN, HB_SCRIPT_OLD_NORTH_ARABIAN, + HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, + HB_SCRIPT_PSALTER_PAHLAVI, HB_SCRIPT_SIDDHAM, + HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, + HB_SCRIPT_AHOM, HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, + HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, + HB_SCRIPT_ADLAM, HB_SCRIPT_BHAIKSUKI, + HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, + HB_SCRIPT_TANGUT, HB_SCRIPT_NEWA, + HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, + HB_SCRIPT_SOYOMBO, HB_SCRIPT_ZANABAZAR_SQUARE, + HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI, + HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, + HB_SCRIPT_MEDEFAIDRIN, HB_SCRIPT_OLD_SOGDIAN, + HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, + HB_SCRIPT_WANCHO, HB_SCRIPT_CHORASMIAN, + HB_SCRIPT_DIVES_AKURU, HB_SCRIPT_KHITAN_SMALL_SCRIPT, + HB_SCRIPT_YEZIDI, +}; +static const uint16_t +_hb_ucd_dm1_p0_map[825] = +{ + 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, + 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, + 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, + 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, + 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, + 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, + 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, + 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, + 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, + 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, + 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, + 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, + 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, + 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, + 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, + 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, + 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, + 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, + 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, + 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, + 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, + 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, + 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, + 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, + 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, + 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, + 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, + 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, + 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, + 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, + 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, + 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, + 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, + 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, + 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, + 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, + 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, + 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, + 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, + 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, + 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, + 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, + 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, + 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, + 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, + 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, + 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, + 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, + 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, + 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, + 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, + 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, + 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, + 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, + 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, + 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, + 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, + 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, + 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, + 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, + 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, + 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, + 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, + 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, + 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, + 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, + 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, + 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, + 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, + 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, + 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, + 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, + 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, + 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, + 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, + 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, + 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, + 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, + 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, + 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, + 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, + 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, + 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, + 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, + 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, + 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, + 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, + 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, + 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, + 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, + 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, + 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, + 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, + 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, + 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, + 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, + 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, + 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, + 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, + 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, + 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, + 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, + 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, + 0x9F9Cu, +}; +static const uint16_t +_hb_ucd_dm1_p2_map[110] = +{ + 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, + 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, + 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, + 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, + 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, + 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, + 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, + 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, + 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, + 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, + 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, + 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, + 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, + 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, +}; +static const uint32_t +_hb_ucd_dm2_u32_map[638] = +{ + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), +}; +static const uint64_t +_hb_ucd_dm2_u64_map[388] = +{ + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), + HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), + HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), + HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), + HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), + HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), + HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), + HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), + HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), + HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), + HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), + HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), + HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), + HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), + HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), + HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), + HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), + HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), + HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), + HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), + HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), + HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), + HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), + HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), + HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), + HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), + HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), + HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), + HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), + HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), + HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), + HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), + HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), + HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), + HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), + HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), + HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), + HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), + HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), + HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), + HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), + HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), + HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), + HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), + HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), + HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), + HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), + HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), + HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), + HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), + HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), + HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), + HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), + HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), + HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), + HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), + HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), + HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), + HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), + HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), + HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), + HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), + HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), + HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), + HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), + HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), + HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), + HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), + HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), + HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), + HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), + HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), + HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), + HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), + HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), + HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), + HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), + HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), + HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), + HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), + HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), + HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), + HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), + HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), + HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), + HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), + HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), + HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), + HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), + HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), + HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), + HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), + HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), + HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), + HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), + HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), + HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), + HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), + HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), + HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), + HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), + HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), + HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), + HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), + HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), + HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), + HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), + HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), + HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), + HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), + HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), + HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), + HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu), + HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu),HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu), + HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), +}; + +#ifndef HB_OPTIMIZE_SIZE + +static const uint8_t +_hb_ucd_u8[32480] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 28, + 29, 26, 30, 31, 32, 33, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 34, 35, 35, 35, 35, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 26, 57, 58, 59, 59, 59, 59, 59, 26, 26, 60, 59, 59, 59, 59, 59, + 59, 59, 26, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 26, 62, 59, 63, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 64, 26, 26, 65, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 66, 67, 59, 59, 59, 59, 68, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 69, 70, 71, 72, 73, 74, 59, 59, + 75, 76, 59, 59, 77, 59, 78, 79, 80, 81, 73, 82, 83, 84, 59, 59, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 85, 26, 26, 26, 26, 26, 26, 26, 86, 87, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 88, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 89, 59, 59, 59, 59, 59, 59, 26, 90, 59, 59, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 91, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 92, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 94, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 25, 25, 25, 21, + 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 21, 18, 24, 16, + 24, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 29, 21, 23, 23, 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, + 26, 25, 15, 15, 24, 5, 21, 21, 24, 15, 7, 19, 15, 15, 15, 21, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, + 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 9, 5, 5, + 5, 9, 9, 5, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 9, 9, + 9, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 5, 9, 9, 5, 9, + 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 5, 9, 5, 9, 9, + 5, 9, 9, 9, 5, 9, 5, 9, 9, 5, 5, 7, 9, 5, 5, 5, + 7, 7, 7, 7, 9, 8, 5, 9, 8, 5, 9, 8, 5, 9, 5, 9, + 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, + 5, 9, 8, 5, 9, 5, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 9, 9, 5, 9, 9, 5, + 5, 9, 5, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 6, 6, 6, 6, 6, 24, 24, 24, 24, 24, 24, 24, 6, 24, 6, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 9, 5, 9, 5, 6, 24, 9, 5, 2, 2, 6, 5, 5, 5, 21, 9, + 2, 2, 2, 2, 24, 24, 9, 21, 9, 9, 9, 2, 9, 2, 9, 9, + 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, + 5, 5, 9, 9, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 5, 5, 9, 5, 25, 9, 5, 9, 9, 5, 5, 9, 9, 9, + 9, 5, 26, 12, 12, 12, 12, 12, 11, 11, 9, 5, 9, 5, 9, 5, + 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, + 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 2, 2, 6, 21, 21, 21, 21, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 17, 2, 2, 26, 26, 23, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 17, 12, + 21, 12, 12, 21, 12, 12, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, + 7, 7, 7, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 25, 25, 25, 21, 21, 23, 21, 21, 26, 26, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 1, 2, 21, 21, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 7, 7, + 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 21, 7, 12, 12, 12, 12, 12, 12, 12, 1, 26, 12, + 12, 12, 12, 12, 12, 6, 6, 12, 12, 26, 12, 12, 12, 12, 7, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 26, 26, 7, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 1, + 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 6, 6, 26, 21, 21, 21, 6, 2, 2, 12, 23, 23, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 6, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 6, 12, 12, 12, 6, 12, 12, 12, 12, 12, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 2, 2, 21, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10, 10, 12, 10, 10, + 7, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 12, 12, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 2, 7, 2, 2, 2, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 7, 2, + 2, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 7, 7, 2, 7, + 7, 7, 12, 12, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 23, 23, 15, 15, 15, 15, 15, 15, 26, 23, 7, 21, 12, 2, + 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 2, 7, 7, 2, 2, 12, 2, 10, 10, + 10, 12, 12, 2, 2, 2, 2, 12, 12, 2, 2, 12, 12, 12, 2, 2, + 2, 12, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 2, 7, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 12, 7, 7, 7, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, + 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 12, 2, 12, 12, 10, 2, 10, 10, 12, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 23, 2, 2, 2, 2, 2, 2, 2, 7, 12, 12, 12, 12, 12, 12, + 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12, + 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 2, 2, + 2, 2, 2, 2, 2, 12, 12, 10, 2, 2, 2, 2, 7, 7, 2, 7, + 26, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 12, 7, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7, + 7, 2, 7, 7, 7, 7, 2, 2, 2, 7, 7, 2, 7, 2, 7, 7, + 2, 2, 2, 7, 7, 2, 2, 2, 7, 7, 7, 2, 2, 2, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 10, 10, + 12, 10, 10, 2, 2, 2, 10, 10, 10, 2, 10, 10, 10, 12, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 26, 26, 26, 26, 26, 26, 23, 26, 2, 2, 2, 2, 2, + 12, 10, 10, 10, 12, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 12, 12, + 12, 10, 10, 10, 10, 2, 12, 12, 12, 2, 12, 12, 12, 12, 2, 2, + 2, 2, 2, 2, 2, 12, 12, 2, 7, 7, 7, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 21, 15, 15, 15, 15, 15, 15, 15, 26, + 7, 12, 10, 10, 21, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12, + 10, 10, 10, 10, 10, 2, 12, 10, 10, 2, 10, 10, 12, 12, 2, 2, + 2, 2, 2, 2, 2, 10, 10, 2, 2, 2, 2, 2, 2, 2, 7, 2, + 2, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 7, 10, 10, + 10, 12, 12, 12, 12, 2, 10, 10, 10, 2, 10, 10, 10, 12, 7, 26, + 2, 2, 2, 2, 7, 7, 7, 10, 15, 15, 15, 15, 15, 15, 15, 7, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 7, 7, 7, 7, 7, 7, + 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 12, 2, 2, 2, 2, 10, + 10, 10, 12, 12, 12, 2, 12, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 23, + 7, 7, 7, 7, 7, 7, 6, 12, 12, 12, 12, 12, 12, 12, 12, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 2, 2, 2, + 2, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, + 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 2, 2, + 7, 7, 7, 7, 7, 2, 6, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 7, 7, 7, 7, + 7, 26, 26, 26, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 26, 21, 26, 26, 26, 12, 12, 26, 26, 26, 26, 26, 26, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 26, 12, 26, 12, 26, 12, 22, 18, 22, 18, 10, 10, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, + 12, 12, 12, 12, 12, 21, 12, 12, 7, 7, 7, 7, 7, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 26, 26, + 26, 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 2, 26, 26, + 21, 21, 21, 21, 21, 26, 26, 26, 26, 21, 21, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 12, + 12, 10, 12, 12, 12, 12, 12, 12, 10, 12, 12, 10, 10, 12, 12, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21, + 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 7, 7, 7, 7, 12, 12, + 12, 7, 10, 10, 10, 7, 7, 10, 10, 10, 10, 10, 10, 10, 7, 7, + 7, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 12, 10, 10, 12, 12, 10, 10, 10, 10, 10, 10, 12, 7, 10, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 10, 10, 12, 26, 26, + 9, 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 2, 2, 9, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 6, 5, 5, 5, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 2, + 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 2, + 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 2, 2, 5, 5, 5, 5, 5, 5, 2, 2, + 17, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 26, 21, 7, + 29, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22, 18, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 21, 21, 14, 14, + 14, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, + 7, 7, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 12, 12, 12, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 2, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 12, 12, 10, 12, 12, 12, 12, 12, 12, 12, 10, 10, + 10, 10, 10, 10, 10, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 21, 21, 21, 6, 21, 21, 21, 23, 7, 12, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 17, 21, 21, 21, 21, 12, 12, 12, 1, 2, + 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 7, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, + 12, 12, 12, 10, 10, 10, 10, 12, 12, 10, 10, 10, 2, 2, 2, 2, + 10, 10, 12, 10, 10, 10, 10, 10, 10, 12, 12, 12, 2, 2, 2, 2, + 26, 2, 2, 2, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, + 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 2, 2, 2, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 10, 10, 12, 2, 2, 21, 21, + 7, 7, 7, 7, 7, 10, 12, 10, 12, 12, 12, 12, 12, 12, 12, 2, + 12, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12, + 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 12, + 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 12, 10, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10, + 10, 10, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, + 21, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, + 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 12, 12, 12, 7, 7, + 7, 7, 7, 7, 7, 7, 12, 10, 12, 12, 10, 10, 10, 12, 10, 12, + 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21, + 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, + 12, 12, 12, 12, 10, 10, 12, 12, 2, 2, 2, 21, 21, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, + 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 10, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 12, 7, 7, + 7, 7, 7, 7, 12, 7, 7, 10, 12, 12, 7, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, + 9, 5, 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, + 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 2, 9, 2, 9, 2, 9, 2, 9, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 8, + 5, 5, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 5, 24, + 24, 24, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 24, + 5, 5, 5, 5, 2, 2, 5, 5, 9, 9, 9, 9, 2, 24, 24, 24, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 24, 24, 24, + 2, 2, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 2, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 1, 1, 1, 1, 1, + 17, 17, 17, 17, 17, 17, 21, 21, 20, 19, 22, 20, 20, 19, 22, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 27, 28, 1, 1, 1, 1, 1, 29, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 19, 21, 21, 21, 21, 16, + 16, 21, 21, 21, 25, 22, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 25, 21, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 29, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 15, 6, 2, 2, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 6, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 2, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, + 11, 12, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 26, 26, 9, 26, 26, 26, 26, 9, 26, 26, 5, 9, 9, 9, 5, 5, + 9, 9, 9, 5, 26, 9, 26, 26, 25, 9, 9, 9, 9, 9, 26, 26, + 26, 26, 26, 26, 9, 26, 9, 26, 9, 26, 9, 9, 9, 9, 26, 5, + 9, 9, 9, 9, 5, 7, 7, 7, 7, 5, 26, 26, 5, 5, 9, 9, + 25, 25, 25, 25, 25, 9, 5, 5, 5, 5, 26, 25, 26, 26, 5, 26, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 9, 5, 14, 14, 14, 14, 15, 26, 26, 2, 2, 2, 2, + 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 25, 25, 26, 26, 26, 26, + 25, 26, 26, 25, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 25, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, + 26, 26, 25, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 26, 26, 26, 26, + 25, 25, 26, 26, 26, 26, 26, 26, 26, 22, 18, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, + 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15, + 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18, + 22, 18, 22, 18, 22, 18, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 25, 25, 25, 25, 25, 22, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, + 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, + 18, 22, 18, 22, 18, 22, 18, 22, 18, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 25, 25, + 25, 25, 25, 25, 25, 26, 26, 25, 25, 25, 25, 25, 25, 26, 26, 26, + 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 9, 5, 9, 9, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9, + 9, 5, 9, 5, 5, 9, 5, 5, 5, 5, 5, 5, 6, 6, 9, 9, + 9, 5, 9, 5, 5, 26, 26, 26, 26, 26, 26, 9, 5, 9, 5, 12, + 12, 12, 9, 5, 2, 2, 2, 2, 2, 21, 21, 21, 21, 15, 21, 21, + 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 6, + 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 2, + 21, 21, 20, 19, 20, 19, 21, 21, 21, 20, 19, 21, 20, 19, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 17, 21, 21, 17, 21, 20, 19, 21, 21, + 20, 19, 22, 18, 22, 18, 22, 18, 22, 18, 21, 21, 21, 21, 21, 6, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 17, 17, 21, 21, 21, 21, + 17, 21, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 26, 26, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, + 29, 21, 21, 21, 26, 6, 7, 14, 22, 18, 22, 18, 22, 18, 22, 18, + 22, 18, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18, 17, 22, 18, 18, + 26, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 10, 10, + 17, 6, 6, 6, 6, 6, 26, 26, 14, 14, 14, 6, 7, 21, 26, 26, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 24, 24, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 6, 6, 6, 7, + 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 26, 26, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15, 15, 15, + 26, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 2, 2, 2, 2, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 7, 12, + 11, 11, 11, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 6, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 6, 6, 12, 12, + 7, 7, 7, 7, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 12, 12, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 24, 24, 24, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 24, 24, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, 9, 5, 9, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 6, 24, 24, 9, 5, 9, 5, 7, + 9, 5, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9, 9, 9, 5, + 9, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, + 2, 2, 9, 5, 9, 9, 9, 9, 5, 9, 5, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 9, 5, 7, 6, 6, 5, 7, 7, 7, 7, 7, + 7, 7, 12, 7, 7, 7, 12, 7, 7, 7, 7, 12, 7, 7, 7, 7, + 7, 7, 7, 10, 10, 12, 12, 10, 26, 26, 26, 26, 12, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 26, 26, 23, 26, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21, + 12, 12, 7, 7, 7, 7, 7, 7, 21, 21, 21, 7, 21, 7, 7, 12, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 21, 21, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 12, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 10, + 10, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 6, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 21, 21, + 7, 7, 7, 7, 7, 12, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 10, + 10, 12, 12, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 21, 21, 21, 21, + 6, 7, 7, 7, 7, 7, 7, 26, 26, 26, 7, 10, 12, 10, 7, 7, + 12, 7, 12, 12, 12, 7, 7, 12, 12, 7, 7, 7, 7, 7, 12, 12, + 7, 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 6, 21, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 12, 12, 10, 10, + 21, 21, 7, 6, 6, 10, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 2, + 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 24, 24, 2, 2, 2, 2, + 7, 7, 7, 10, 10, 12, 10, 10, 12, 10, 10, 21, 10, 12, 2, 2, + 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 7, 12, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 25, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 2, + 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 18, 22, + 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23, 26, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 22, 18, 21, 2, 2, 2, 2, 2, 2, + 21, 17, 17, 16, 16, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, + 18, 22, 18, 22, 18, 21, 21, 22, 18, 21, 21, 21, 21, 16, 16, 16, + 21, 21, 21, 2, 21, 21, 21, 21, 17, 22, 18, 22, 18, 22, 18, 21, + 21, 21, 25, 17, 25, 25, 25, 2, 21, 23, 21, 21, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 1, + 2, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 22, + 18, 21, 22, 18, 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, + 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 2, 2, 2, + 23, 23, 25, 24, 26, 23, 23, 2, 26, 25, 25, 25, 25, 26, 26, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 26, 26, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, + 21, 21, 21, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 26, 26, 26, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, + 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 2, 2, + 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, + 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7, + 7, 14, 7, 7, 7, 7, 7, 7, 7, 7, 14, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 21, + 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 21, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 9, 9, 9, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, + 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 2, 2, 7, 2, 2, 7, + 7, 7, 7, 7, 7, 7, 2, 21, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 7, 26, 26, 15, 15, 15, 15, 15, 15, 15, + 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 2, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 15, 15, 7, 7, + 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 12, 12, 12, 2, 12, 12, 2, 2, 2, 2, 2, 12, 12, 12, 12, + 7, 7, 7, 7, 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12, 2, 2, 2, 2, 12, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 21, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, + 7, 7, 7, 7, 7, 7, 7, 7, 26, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 12, 12, 2, 2, 2, 2, 15, 15, 15, 15, 15, + 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 7, 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, + 7, 7, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, + 9, 9, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, + 7, 7, 7, 7, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 12, 12, 17, 2, 2, + 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 15, 15, 15, 15, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, + 10, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 21, 21, 2, 2, + 15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, + 10, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 21, 21, 1, 21, 21, + 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, + 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 10, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 7, 10, 10, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, + 10, 7, 7, 7, 7, 21, 21, 21, 21, 12, 12, 12, 12, 21, 10, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 21, 7, 21, 21, 21, + 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 12, + 12, 12, 10, 10, 12, 10, 12, 12, 21, 21, 21, 21, 21, 21, 12, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 12, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, + 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 12, 12, 7, 10, 10, + 12, 10, 10, 10, 10, 2, 2, 10, 10, 2, 2, 10, 10, 10, 2, 2, + 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 7, 7, 7, + 7, 7, 10, 10, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, + 10, 10, 12, 12, 12, 10, 12, 7, 7, 7, 7, 21, 21, 21, 21, 21, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 21, 12, 7, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10, 10, 12, + 12, 10, 12, 12, 7, 7, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, + 10, 10, 12, 12, 12, 12, 2, 2, 10, 10, 10, 10, 12, 12, 10, 12, + 12, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 7, 7, 7, 7, 12, 12, 2, 2, + 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 12, 10, 12, + 12, 21, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 10, 10, + 12, 12, 12, 12, 12, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 12, 12, 12, 12, 10, 12, 12, 12, 12, 12, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 21, 21, 21, 26, + 12, 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 2, 2, 2, 2, + 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, + 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 2, 7, 7, 7, 7, + 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, + 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 2, 12, 12, 10, 12, 7, + 10, 7, 10, 12, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, + 7, 10, 10, 10, 12, 12, 12, 12, 2, 2, 12, 12, 10, 10, 10, 10, + 12, 7, 21, 7, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7, + 7, 7, 7, 12, 12, 12, 12, 12, 12, 10, 7, 12, 12, 12, 12, 21, + 21, 21, 21, 21, 21, 21, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 12, 12, 12, 12, 12, 12, 10, 10, 12, 12, 12, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 21, 21, 7, 21, 21, + 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 10, 12, + 7, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 2, 10, 12, 12, 12, 12, 12, 12, + 12, 10, 12, 12, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, + 7, 12, 12, 12, 12, 12, 12, 2, 2, 2, 12, 2, 12, 12, 2, 12, + 12, 12, 12, 12, 12, 12, 7, 12, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10, 2, + 12, 12, 2, 10, 10, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 12, 12, 10, 10, 21, 21, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 23, 23, 23, + 23, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, + 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 26, 26, 26, 26, + 6, 6, 6, 6, 21, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 15, 15, 15, 15, 15, + 15, 15, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 7, 7, 7, + 15, 15, 15, 15, 15, 15, 15, 21, 21, 21, 21, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 12, + 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 12, + 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 21, 6, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 26, 12, 12, 21, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 10, 10, 12, 12, 12, 26, 26, 26, 10, 10, 10, + 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12, + 12, 12, 12, 26, 26, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 12, 12, 12, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, + 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 2, 9, 9, + 2, 2, 9, 2, 2, 9, 9, 2, 2, 9, 9, 9, 9, 2, 9, 9, + 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 2, 5, 2, 5, 5, 5, + 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2, 2, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 2, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2, + 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 9, 9, 9, 9, 9, 9, + 9, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, + 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, + 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 5, 2, 2, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, + 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 12, 26, 26, 21, 21, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12, + 12, 12, 2, 12, 12, 2, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, + 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 2, 2, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 7, 26, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 23, + 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 5, 5, 5, 5, 12, 12, 12, 12, 12, 12, 12, 6, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15, 15, 15, + 23, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, + 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2, 7, 2, 2, 2, 2, + 2, 2, 7, 2, 2, 2, 2, 7, 2, 7, 2, 7, 2, 7, 7, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 2, 7, 2, 7, 2, 7, + 2, 7, 7, 2, 7, 2, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7, + 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, + 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, + 25, 25, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, + 2, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 24, 24, 24, 24, 24, + 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, + 26, 26, 26, 26, 26, 2, 2, 2, 26, 26, 26, 2, 2, 2, 2, 2, + 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 12, 12, 13, 14, 12, 15, 16, 17, 18, 19, 20, + 21, 22, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 24, 25, + 0, 26, 27, 0, 28, 29, 30, 31, 32, 33, 0, 34, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, + 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 43, 44, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 0, 0, 51, 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 54, 55, 0, 0, 0, 0, 56, 0, 0, 57, 58, 0, + 59, 60, 61, 62, 63, 64, 65, 0, 66, 67, 0, 68, 69, 70, 71, 0, + 60, 0, 72, 73, 74, 75, 0, 0, 69, 0, 76, 77, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 85, 0, 79, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 87, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 1, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 0, 22, 23, 24, + 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 27, + 28, 29, 0, 0, 0, 0, 30, 0, 0, 0, 31, 32, 33, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 13, 35, 36, 0, 0, 26, 37, 38, 39, 0, 0, 0, 0, 0, 40, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 1, + 42, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 48, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 0, 0, + 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 50, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 47, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 54, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 56, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 58, 59, 0, 0, 0, 0, + 0, 0, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, + 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 68, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 71, 72, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 66, 74, 0, 0, 0, 0, 0, 0, 75, 76, 72, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 67, 0, 0, 0, + 0, 77, 78, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, + 80, 0, 79, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 82, + 83, 84, 85, 86, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 89, 1, + 1, 1, 90, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 93, + 94, 95, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 71,100,101, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 86, 0,102, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, + 1, 1, 86, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0,104, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 73, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106,107,108, 0, 0, 0, + 0, 0,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 47, 0, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,110,111, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26,112, 0,113, 0, 0, 0, 0, 0,114, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 115, 0, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117,118, 72, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, + 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, + 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 0,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73,120, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0,123, 0, 47, 0, 0, + 26,124,124, 0, 0, 0, 0, 0, 0, 0, 0, 0,125, 0, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,127, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 97, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,130, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,132, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,134, 0, 0, 0, 0,135, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 136,137,138,139,140,141, 0, 0, 0,142, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,143, 0, 0, 0, + 0, 0, 0, 0,133, 1, 1,144,145,112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,146, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,147, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230, + 230,230,230,230,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 220,220,220,220,220,220,220,220, 1, 1, 1, 1, 1,220,220,220, + 220,230,230,230,230,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230,230,230,230,230, 0, 0, 0,230, + 230,230,230,230, 0,220,230,230,230,230,220,230,230,230,222,220, + 230,230,230,230,230,230,220,220,220,220,220,220,230,230,220,230, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, + 230,220,220,230,230,230,230,230,220,230,230,220, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230, + 230, 0, 0,230,230,230,230,220,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0,230,220,230,230, + 220,230,230,220,220,220,230,220,220,230,220,230,230,230,220,230, + 220,230,220,230,220,230,230, 0, 0, 0, 0, 0,230,230,220,230, + 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0, 0,230,230, 0,230, + 230,230,230,230,230,230,230,230, 0,230,230,230, 0,230,230,230, + 230,230, 0, 0, 0,220,220,220, 0, 0, 0, 0, 0, 0, 0,220, + 230,230,230,230,230,230, 0,220,230,230,220,230,230,220,230,230, + 230,220,220,220, 27, 28, 29,230,230,230,220,230,230,220,220,230, + 230,230,230,230, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0, 0, 0, + 0, 0,230, 0, 0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0,103,103, 9, 0, + 0, 0, 0, 0,107,107,107,107, 0, 0, 0, 0,118,118, 9, 0, + 0, 0, 0, 0,122,122,122,122, 0, 0, 0, 0,220,220, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0, 0, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130, + 130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0, 0, 0, + 0, 0,220, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0,230, 0, 0, 0,228, 0, 0, + 0, 0, 0, 0, 0,222,230,220, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,230,220, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0, 0, 0,230,230,230,230,230, 0, 0,220,230,230,230,230, + 230,220,220,220,220,220,220,230,230,220, 0,220, 0, 0, 0,230, + 220,230,230,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,230,230,230, 0, + 1,220,220,220,220,220,230,230,220,220,220,220,230, 0, 1, 1, + 1, 1, 1, 1, 1, 0, 0, 0, 0,220, 0, 0, 0, 0, 0, 0, + 230, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0,230,230,220,230, + 230,230,230,230,230,230,220,230,230,234,214,220,202,230,230,230, + 230,230,230,230,230,230,230,230,230,230,232,228,228,220, 0,230, + 233,220,230,220,230,230, 1, 1,230,230,230,230, 1, 1, 1,230, + 230, 0, 0, 0, 0,230, 0, 0, 0, 1, 1,230,220,230, 1, 1, + 220,220,220,220,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230,230,230, + 230,230, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,220, + 220,220, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 7, + 0, 0, 0, 0,230, 0,230,230,220, 0, 0,230,230, 0, 0, 0, + 0, 0,230,230, 0,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 26, 0,230,230,230,230,230,230,230,220,220,220,220,220, + 220,220,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,220, 0,230,230, 1,220, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0,230,220, 0, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0, 0, + 0, 0,220,220,230,230,230,220,230,220,220,220, 0, 9, 7, 0, + 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 7, 0, 0, 0,230,230,230,230,230, 0, 0, 0, 0, 0, 9, 0, + 0, 0, 7, 0, 0, 0, 9, 7, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, + 0, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, + 9, 9, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,230,230,230,230, + 230,230,230, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0, 0, 0, 0, 0, 0, 0, 0,220, + 220,220,220,220,220,220,220, 0, 0,230,230,230,230,230,220,220, + 0, 0, 0, 0, 0, 0,230,230,230,230, 0, 0, 0, 0,230,230, + 230, 0, 0, 0,230, 0, 0,230,230,230,230,230,230,230, 0,230, + 230, 0,230,230,220,220,220,220,220,220,220, 0,230,230, 7, 0, + 0, 0, 0, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, + 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, + 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, + 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, + 0, 40, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, + 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 17, 19, 20, 21, 22, 23, 24, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 27, 28, 28, 29, 30, 31, 32, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 35, 35, 35, 35, 35, 59, 59, 60, 35, + 35, 35, 35, 35, 35, 35, 61, 62, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 63, 64, 35, 65, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 67, 66, 68, 69, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 70, 71, 35, 35, + 35, 35, 72, 35, 35, 35, 35, 35, 35, 35, 35, 35, 73, 74, 75, 76, + 77, 78, 35, 35, 79, 80, 35, 35, 81, 35, 82, 83, 84, 85, 17, 86, + 87, 88, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 89, 25, 25, 25, 25, 25, 25, 25, 90, + 91, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 92, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 93, 35, 35, 35, 35, 35, 35, + 25, 94, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 95, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 2, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, + 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, 11, + 11, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, + 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 2, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 2, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, 2, + 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2, + 22, 22, 22, 22, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23, + 23, 2, 2, 2, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 2, 16, 16, 16, 2, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 2, 2, + 2, 2, 2, 2, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 2, 16, 16, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 36, 36, + 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, + 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, + 2, 2, 2, 8, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 2, + 30, 30, 30, 30, 2, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 29, 29, + 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 2, + 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 2, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 2, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 2, 2, 2, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, + 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, + 2, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 2, 2, 8, 8, 8, 76, 76, 76, 76, 76, 76, 76, 76, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, + 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, + 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 19, 19, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 2, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 61, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, + 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 2, 2, + 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 30, + 30, 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 2, 2, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118,118,118,118,118,118,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,106,106, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106, 2, 2, + 2, 2, 2, 2, 2, 2,104,104,104,104,104,104,104,104,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,110,110,110,110,110,110,110,110,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2, 2, 2, + 2, 2, 2, 2, 2, 2,110,110,110,110,110,110, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,110,110,110,110,110,110,110,110, 2, 2, + 2, 2, 2, 2, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, + 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81, 81, 81, + 81, 81, 81, 81, 81, 81,120,120,120,120,120,120,120,120,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,116,116, + 116,116,116,116,116,116,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, + 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, + 57, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 83, 83, + 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 82,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,130,130,130, 2, 2, 2, 2, 2, 2, 2, + 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, + 144,144,144,144,144,144,144,144,144,144,144,144,144,144, 2, 2, + 2, 2, 2, 2, 2, 2,144,144,144,144,144,144,144,144,144,144, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2,156,156,156,156,156,156,156,156,156,156, + 156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,147,147,147,147,147,147,147,147,147,147, + 147,147,147,147,147,147,147,147,147,147,147,147,147,147, 2, 2, + 2, 2, 2, 2, 2, 2,148,148,148,148,148,148,148,148,148,148, + 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101,101, + 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, 2, + 2, 2, 2, 2, 2, 2,101,101,101,101,101,101,101,101,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 2, + 2, 2, 2, 2, 2, 2,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111, 2, 2, 2, + 2, 2, 2, 2, 2, 2,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 108,108,108,108,108, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109, + 109, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, + 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, + 2, 1,107,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,107,107,107,107,107, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 124,124,124,124,124,124,124,124,124,124,124,124,124,124, 2, 2, + 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124, + 2, 2, 2, 2, 2, 2,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123, 2, 2,114,114,114,114,114,114,114,114,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,114,114,114,114,114,114,114,114,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, 2, + 2, 2, 2, 2, 2, 2,102,102,102,102,102,102,102,102,102,102, + 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126,126,126,126,126,126,126, + 126,126, 2, 2, 2, 2,142,142,142,142,142,142,142,142,142,142, + 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142, + 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154,154,154,154,154,154, 2, 2, 2, + 2, 2, 2, 2, 2, 2,154,154,154,154,154,154,154,154,154,154, + 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, + 150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,141,141,141,141,141,141,141,141,141,141, + 141,141,141,141,141,141,141,141,141,141,141,141,141,141, 2, 2, + 2, 2, 2, 2, 2, 2,140,140,140,140,140,140,140,140,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121,121, + 121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, 2, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133,133,133,133,133,133,133,133,133, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134,134,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2, 2, 2, + 2, 2, 2, 2, 2, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138, + 138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, 2, + 138, 2,138,138, 2,138,138,138,138,138,138,138,138,138, 2, 2, + 2, 2, 2, 2, 2, 2,138,138,138,138,138,138,138,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143,143,143,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145,145, + 145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, + 2, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115,115,115,115,115,115,115,115,115, + 2, 2, 2, 2,115,115,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103, 2, 2,103,103,103,103,103,103, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119, + 2,119,119,119,119,119,119,119, 2,119,119,119,119,119,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119,119,119, 2, 2, + 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146, + 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 0, 0,155, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136,136, + 136,136,136,136,136,136,136,136,136,136,136,136,136,136, 2, 2, + 2, 2, 2, 2, 2, 2,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136, 2, + 2, 2, 2, 2, 2, 2, 17, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 2, + 2, 2, 2, 2, 2, 2,139,139,139,139,139,139,139,139,139,139, + 139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105,105,105, 2, 2, 2,105,105,105,105,105,105,105,105,105, 2, + 2, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 2, 2,105,105,105,105, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2,131,131,131,131,131, 2,131,131,131,131,131,131,131,131,131, + 131,131,131,131,131,131, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 2, + 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, + 56, 2, 2, 2, 2, 2,151,151,151,151,151,151,151,151,151,151, + 151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151, + 151,151,151, 2, 2, 2,151,151,151,151,151,151,151,151,151,151, + 151,151,151,151, 2, 2,151,151,151,151,151,151,151,151,151,151, + 2, 2, 2, 2,151,151,152,152,152,152,152,152,152,152,152,152, + 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,113,113,113,113,113,113,113,113,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113,113,113,113,113,113,113,113,113, 2, 2, 2, + 2, 2, 2, 2, 2, 2,132,132,132,132,132,132,132,132,132,132, + 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132,132,132,132,132,132,132,132,132, + 2, 2, 2, 2,132,132, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 2, 2, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0, + 0, 0, 0, 7, 8, 9, 10, 11, 0, 12, 0, 0, 0, 0, 13, 0, + 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 0, 17, 18, 19, + 0, 0, 0, 20, 21, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 0, + 0, 0, 0, 27, 28, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 41, 0, 42, 43, 44, 45, + 46, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 50, 51, 52, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 71, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 78, 79, 80, 0, +}; +static const uint16_t +_hb_ucd_u16[11328] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 72, 72, 72, 48, 73, 74, 75, 76, 32, + 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102, + 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114, + 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125, + 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136, + 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, + 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, + 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 184, 185, 48, 186, 48, 187, 184, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 72, 72, 72, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 240, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216, + 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 209, 280, 209, 209, 209, 209, 281, 209, 282, 278, 283, 209, 284, 285, 209, + 209, 209, 286, 72, 287, 72, 270, 270, 270, 288, 209, 209, 209, 209, 289, 270, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292, + 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 278, 278, 278, 278, 278, 278, 278, 278, 299, 300, 278, 278, 278, 301, 278, 302, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 209, 209, 209, 278, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209, + 9, 9, 306, 11, 11, 307, 308, 309, 13, 13, 13, 13, 13, 13, 310, 311, + 11, 11, 312, 48, 48, 48, 313, 314, 48, 315, 316, 316, 316, 316, 32, 32, + 317, 318, 319, 320, 321, 322, 72, 72, 209, 323, 209, 209, 209, 209, 209, 324, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325, 72, 326, + 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332, + 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48, + 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 151, + 48, 339, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346, + 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 77, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 316, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 72, 72, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 400, 72, 48, 48, 48, 48, 401, 48, 48, 74, 72, 72, 402, + 32, 403, 32, 404, 405, 406, 407, 73, 48, 48, 48, 48, 48, 48, 48, 408, + 409, 2, 3, 4, 5, 410, 411, 412, 48, 413, 48, 200, 414, 415, 416, 417, + 418, 48, 172, 419, 204, 204, 72, 72, 48, 48, 48, 48, 48, 48, 48, 71, + 420, 270, 270, 421, 271, 271, 271, 422, 423, 424, 425, 72, 72, 209, 209, 426, + 72, 72, 72, 72, 72, 72, 72, 72, 48, 151, 48, 48, 48, 101, 427, 428, + 48, 48, 429, 48, 430, 48, 48, 431, 48, 432, 48, 48, 433, 434, 72, 72, + 9, 9, 435, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 436, 11, 437, + 48, 48, 74, 48, 48, 48, 438, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 315, 48, 199, 74, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 439, 48, 48, 440, 48, 441, 48, 442, 48, 200, 443, 72, 72, 72, 48, 444, + 48, 445, 48, 446, 72, 72, 72, 72, 48, 48, 48, 447, 270, 448, 270, 270, + 449, 450, 48, 451, 452, 453, 48, 454, 48, 455, 72, 72, 456, 48, 457, 458, + 48, 48, 48, 459, 48, 460, 48, 461, 48, 462, 463, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 196, 72, 72, 72, 9, 9, 9, 464, 11, 11, 11, 465, + 48, 48, 466, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72, + 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315, + 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192, + 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489, + 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192, + 497, 84, 85, 498, 499, 500, 501, 502, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 503, 504, 505, 469, 72, 48, 48, 48, 506, 507, 192, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72, + 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72, + 48, 173, 517, 518, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 490, 519, 72, 72, 72, 72, 72, 72, 9, 9, 11, 11, 148, 520, + 521, 522, 48, 523, 524, 192, 72, 72, 72, 72, 525, 48, 48, 526, 527, 72, + 528, 48, 48, 529, 530, 531, 48, 48, 532, 533, 534, 72, 48, 48, 48, 196, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 85, 48, 508, 535, 536, 148, 175, 537, 48, 538, 539, 540, 72, 72, 72, 72, + 541, 48, 48, 542, 543, 192, 544, 48, 545, 546, 192, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 101, 270, 548, 549, 550, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 72, 72, 72, 72, 72, 72, + 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 388, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 200, 553, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 315, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554, + 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 74, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 72, 72, + 196, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 200, 72, 72, 72, 568, 569, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 570, 571, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325, + 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72, + 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579, + 209, 209, 209, 209, 209, 286, 270, 452, 72, 72, 72, 72, 72, 72, 72, 72, + 9, 580, 11, 581, 582, 583, 242, 9, 584, 585, 586, 587, 588, 9, 580, 11, + 589, 590, 11, 591, 592, 593, 594, 9, 595, 11, 9, 580, 11, 581, 582, 11, + 242, 9, 584, 594, 9, 595, 11, 9, 580, 11, 596, 9, 597, 598, 599, 600, + 11, 601, 9, 602, 603, 604, 605, 11, 606, 9, 607, 11, 608, 609, 609, 609, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, + 32, 32, 32, 610, 32, 32, 611, 612, 613, 614, 45, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 615, 616, 617, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 622, 623, 72, 72, + 9, 9, 584, 11, 624, 370, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72, + 488, 270, 627, 628, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 629, 48, 630, 631, 632, 633, 634, 635, 636, 206, 637, 206, 72, 72, 72, 638, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 326, 209, 209, 209, 209, 209, 209, 324, 335, 639, 639, 639, 209, 325, + 640, 209, 209, 209, 209, 209, 209, 209, 209, 209, 641, 72, 72, 72, 642, 209, + 643, 209, 209, 326, 577, 644, 325, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 645, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 646, 424, 424, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72, + 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209, + 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 209, 209, 650, 209, 209, 287, 72, 72, 192, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 205, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 469, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 101, 72, + 48, 204, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 651, 72, 652, 652, 652, 652, 652, 652, 72, 72, 72, 72, 72, 72, 72, 72, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 72, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 653, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 654, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 45, 45, 45, 27, 46, 47, 48, 49, 27, + 50, 50, 50, 50, 50, 51, 52, 50, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 111, 112, 113, 114, 111, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 124, 125, 124, 126, 45, 45, 127, 128, 129, 130, 131, 132, 45, 45, + 133, 133, 133, 133, 134, 133, 135, 136, 133, 134, 133, 137, 137, 138, 45, 45, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 141, 140, 140, 142, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 144, 144, 144, 144, 145, 146, 144, 144, 145, 144, 144, 147, 148, 149, 144, 144, + 144, 148, 144, 144, 144, 150, 144, 151, 144, 152, 153, 153, 153, 153, 153, 154, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 156, 157, 158, 158, 158, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 169, 169, 169, 170, 171, 171, + 172, 173, 174, 174, 174, 174, 174, 175, 174, 174, 176, 155, 155, 155, 155, 177, + 178, 179, 180, 180, 181, 182, 183, 184, 185, 185, 186, 185, 187, 188, 169, 169, + 189, 190, 191, 191, 191, 192, 191, 193, 194, 194, 195, 8, 196, 45, 45, 45, + 197, 197, 197, 197, 198, 197, 197, 199, 200, 200, 200, 200, 201, 201, 201, 202, + 203, 203, 203, 204, 205, 206, 206, 206, 207, 140, 140, 208, 209, 210, 211, 212, + 4, 4, 213, 4, 4, 214, 215, 216, 4, 4, 4, 217, 8, 8, 8, 218, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 11, 219, 11, 11, 219, 220, 11, 221, 11, 11, 11, 222, 222, 223, 11, 224, + 225, 0, 0, 0, 0, 0, 226, 227, 228, 229, 0, 0, 45, 8, 8, 196, + 0, 0, 230, 231, 232, 0, 4, 4, 233, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 234, 45, 235, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 237, 0, 238, 0, 0, 0, 0, 0, 0, + 239, 239, 240, 239, 239, 240, 4, 4, 241, 241, 241, 241, 241, 241, 241, 242, + 140, 140, 141, 243, 243, 243, 244, 245, 144, 246, 247, 247, 247, 247, 14, 14, + 0, 0, 0, 0, 0, 248, 45, 45, 249, 250, 249, 249, 249, 249, 249, 251, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 252, 45, 253, + 254, 0, 255, 256, 257, 258, 258, 258, 258, 259, 260, 261, 261, 261, 261, 262, + 263, 264, 264, 265, 143, 143, 143, 143, 266, 0, 264, 264, 0, 0, 267, 261, + 143, 266, 0, 0, 0, 0, 143, 268, 0, 0, 0, 0, 0, 261, 261, 269, + 261, 261, 261, 261, 261, 270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 0, 0, 0, 0, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 271, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, + 272, 272, 272, 272, 272, 272, 272, 272, 273, 272, 272, 272, 274, 275, 275, 275, + 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, + 276, 276, 277, 45, 14, 14, 14, 14, 14, 14, 278, 278, 278, 278, 278, 279, + 0, 0, 280, 4, 4, 4, 4, 4, 281, 4, 4, 4, 282, 45, 45, 283, + 284, 284, 285, 286, 287, 287, 287, 288, 289, 289, 289, 289, 290, 291, 50, 50, + 292, 292, 293, 294, 294, 295, 143, 296, 297, 297, 297, 297, 298, 299, 139, 300, + 301, 301, 301, 302, 303, 304, 139, 139, 305, 305, 305, 305, 306, 307, 308, 309, + 310, 311, 247, 4, 4, 312, 313, 153, 153, 153, 153, 153, 308, 308, 314, 315, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 316, 143, 317, 143, 143, 318, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 319, 249, 249, 249, 249, 249, 249, 320, 45, 45, + 321, 322, 21, 323, 324, 27, 27, 27, 27, 27, 27, 27, 325, 48, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 326, 45, 27, 27, 27, 27, 327, 27, 27, 47, 45, 45, 328, + 8, 286, 329, 0, 0, 330, 331, 46, 27, 27, 27, 27, 27, 27, 27, 332, + 333, 0, 1, 2, 1, 2, 334, 260, 261, 335, 143, 266, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 344, 45, 45, 341, 341, 341, 341, 341, 341, 341, 345, + 346, 0, 0, 347, 11, 11, 11, 11, 348, 349, 350, 45, 45, 0, 0, 351, + 45, 45, 45, 45, 45, 45, 45, 45, 352, 353, 354, 354, 354, 355, 356, 253, + 357, 357, 358, 359, 360, 361, 361, 362, 363, 364, 365, 365, 366, 367, 45, 45, + 368, 368, 368, 368, 368, 369, 369, 369, 370, 371, 372, 373, 373, 374, 373, 375, + 376, 376, 377, 378, 378, 378, 379, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, + 380, 380, 380, 381, 380, 382, 383, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 45, 45, 45, 393, 394, + 395, 396, 397, 398, 45, 45, 45, 45, 399, 399, 400, 401, 400, 402, 400, 400, + 403, 404, 405, 406, 407, 407, 408, 408, 409, 409, 45, 45, 410, 410, 411, 412, + 413, 413, 413, 414, 415, 416, 417, 418, 419, 420, 421, 45, 45, 45, 45, 45, + 422, 422, 422, 422, 423, 45, 45, 45, 424, 424, 424, 425, 424, 424, 424, 426, + 427, 427, 428, 429, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 27, 430, 431, 431, 432, 433, 45, 45, 45, 45, + 434, 434, 435, 436, 436, 437, 45, 45, 45, 45, 45, 438, 439, 45, 440, 441, + 442, 442, 442, 442, 443, 444, 442, 445, 446, 446, 446, 446, 447, 448, 449, 450, + 451, 451, 451, 452, 453, 454, 454, 455, 456, 456, 456, 456, 456, 456, 457, 458, + 459, 460, 459, 461, 45, 45, 45, 45, 462, 463, 464, 465, 465, 465, 466, 467, + 468, 469, 470, 471, 472, 473, 474, 475, 45, 45, 45, 45, 45, 45, 45, 45, + 476, 476, 476, 476, 476, 477, 478, 45, 479, 479, 479, 479, 480, 481, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 482, 482, 482, 483, 482, 484, 45, 45, + 485, 485, 485, 485, 486, 487, 488, 45, 489, 489, 489, 490, 491, 45, 45, 45, + 492, 493, 494, 492, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 495, 495, 495, 496, 45, 45, 45, 45, 45, 45, 497, 497, 497, 497, 497, 498, + 499, 500, 501, 502, 503, 504, 45, 45, 45, 45, 505, 506, 506, 505, 507, 45, + 508, 508, 508, 508, 509, 510, 510, 510, 510, 510, 511, 45, 512, 512, 512, 513, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 514, 515, 515, 516, 517, 515, 518, 519, 519, 520, 521, 522, 45, 45, 45, 45, + 523, 524, 524, 525, 526, 527, 528, 529, 530, 531, 532, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 533, 534, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 535, 536, 536, 536, 537, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 45, 45, 45, 45, 45, 45, + 538, 538, 538, 538, 538, 538, 540, 541, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 538, 538, 542, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 544, 545, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, + 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, + 546, 546, 546, 546, 547, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 548, 549, 550, 551, 45, 45, 45, 45, 45, 45, 552, 553, 554, + 555, 555, 555, 555, 556, 557, 558, 559, 555, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 560, 560, 560, 560, 560, 561, 45, 45, 45, 45, 45, 45, + 562, 562, 562, 562, 563, 562, 562, 562, 564, 562, 45, 45, 45, 45, 565, 566, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 568, + 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, + 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, + 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 570, 45, 45, + 571, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 572, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, + 258, 573, 45, 45, 45, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 578, 578, 578, 578, 578, 578, 579, 580, 581, 582, 267, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 583, + 0, 0, 584, 0, 0, 0, 585, 586, 587, 0, 588, 0, 0, 0, 589, 45, + 11, 11, 11, 11, 590, 45, 45, 45, 45, 45, 45, 45, 45, 45, 0, 267, + 0, 0, 0, 0, 0, 234, 0, 589, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 226, 0, 0, 0, 591, 592, 593, 594, 0, 0, 0, + 595, 596, 0, 597, 598, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 600, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 601, 0, 0, 0, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 603, 604, 605, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 606, 607, 608, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 609, 609, 610, 611, 612, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 613, 613, 613, 614, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 617, 45, 45, + 618, 618, 618, 618, 619, 620, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 333, 0, 0, 0, 621, 45, 45, 45, 45, + 333, 0, 0, 622, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 623, 27, 624, 625, 626, 627, 628, 629, 630, 631, 632, 631, 45, 45, 45, 325, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 253, 0, 0, 0, 0, 0, 0, 267, 228, 333, 333, 333, 0, 583, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622, 45, 45, 45, 633, 0, + 634, 0, 0, 253, 589, 635, 583, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 636, 349, 349, + 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 589, 253, 45, + 253, 0, 0, 0, 636, 286, 0, 0, 636, 0, 622, 635, 45, 45, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 637, 0, 0, 0, 0, 638, 0, 0, 0, + 0, 0, 0, 0, 0, 267, 622, 639, 234, 0, 589, 234, 248, 234, 45, 45, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 235, 45, 45, 286, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 319, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 640, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 319, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 566, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 641, 45, + 249, 319, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, + 249, 249, 249, 249, 642, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, + 643, 45, 0, 0, 0, 0, 0, 0, 45, 45, 45, 45, 45, 45, 45, 45, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, + 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, + 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[2176+(((_hb_ucd_u16[((_hb_ucd_u8[u>>4>>5])<<5)+((u>>4)&31u)])<<4)+((u)&15u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[15060+(((_hb_ucd_u8[13636+(((_hb_ucd_u8[12656+(u>>3>>4)])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[16372+(((_hb_ucd_b4(16244+_hb_ucd_u8,u>>2>>6))<<6)+((u>>2)&63u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[19126+(((_hb_ucd_u16[3040+(((_hb_ucd_u8[17332+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6144+(((_hb_ucd_u8[29430+(u>>6)])<<6)+((u)&63u))]:0; +} + + +#elif !defined(HB_NO_UCD_UNASSIGNED) + +static const uint8_t +_hb_ucd_u8[17508] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 14, 14, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 21, 23, 21, 21, 21, 21, 24, 7, 7, + 25, 26, 21, 21, 21, 21, 27, 28, 21, 21, 29, 30, 31, 32, 33, 34, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 21, 40, + 7, 7, 41, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 42, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 43, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 44, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 35, 36, 37, 38, 39, 34, 34, 34, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 91, + 92, 34, 34, 34, 34, 34, 34, 34, 34, 93, 34, 34, 94, 95, 96, 97, + 98, 99,100,101,102,103,104,105, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108, + 108,108, 34, 34,109,110,111,112, 34, 34,113,114,115,116,117,118, + 119,120,121,122,123,124,125,126,127,128,129,123, 34, 34,130,123, + 131,132,133,134,135,136,137,138,139,140,141,123,142,143,144,145, + 146,147,148,149,150,151,152,123,153,154,123,155,156,157,158,123, + 159,160,161,162,163,164,123,123,165,166,167,168,123,169,123,170, + 34, 34, 34, 34, 34, 34, 34,171,172, 34,173,123,123,123,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34, 34, 34,174,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123, 34, 34, 34, 34,175,123,123,123, + 34, 34, 34, 34,176,177,178,179,123,123,123,123,180,181,182,183, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,184, + 34, 34, 34, 34, 34, 34, 34, 34, 34,185,186,123,123,123,123,123, + 34, 34,187, 34, 34,188,123,123,123,123,123,123,123,123,123,123, + 123,123,123,123,123,123,123,123,189,190,123,123,123,123,123,123, + 69,191,192,193,194,195,196,123,197,198,199,200,201,202,203,204, + 69, 69, 69, 69,205,206,123,123,123,123,123,123,123,123,123,123, + 207,123,208,123,123,209,123,123,123,123,123,123,123,123,123,123, + 34,210,211,123,123,123,123,123,212,213,214,123,215,216,123,123, + 217,218,219,220,221,123, 69,222, 69, 69, 69, 69, 69,223,224,225, + 226,227,228,229,230,231, 69,232,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,233, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,234, 34, + 235, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,236, 34, 34, + 34, 34, 34, 34, 34, 34, 34,237,123,123,123,123,123,123,123,123, + 34, 34, 34, 34,238,123,123,123,123,123,123,123,123,123,123,123, + 34, 34, 34, 34, 34, 34,239,123,123,123,123,123,123,123,123,123, + 240,123,241,242,123,123,123,123,123,123,123,123,123,123,123,123, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,243, + 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,244, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 44, 44, 57, 43, 43, 43, 43, 43, 43, + 43, 82, 43, 43, 43, 43, 43, 43, 43, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 83, 71, 84, 85, 43, 43, 43, 83, 84, 85, 84, + 70, 43, 43, 43, 36, 36, 36, 36, 36, 43, 2, 7, 7, 7, 7, 7, + 86, 36, 36, 36, 36, 36, 36, 36, 70, 84, 62, 36, 36, 36, 61, 62, + 61, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, + 61, 61, 44, 36, 36, 44, 71, 84, 85, 43, 80, 87, 88, 87, 85, 61, + 44, 44, 44, 87, 44, 44, 36, 62, 36, 43, 44, 7, 7, 7, 7, 7, + 36, 20, 27, 27, 27, 56, 63, 80, 57, 83, 62, 36, 36, 61, 44, 62, + 61, 36, 62, 61, 36, 44, 80, 84, 85, 80, 44, 57, 80, 57, 43, 44, + 57, 44, 44, 44, 62, 36, 61, 61, 44, 44, 44, 7, 7, 7, 7, 7, + 43, 36, 70, 64, 44, 44, 44, 44, 57, 83, 62, 36, 36, 36, 36, 62, + 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, 36, 36, 44, 71, 84, + 85, 43, 43, 57, 83, 87, 85, 44, 61, 44, 44, 44, 44, 44, 44, 44, + 66, 44, 44, 44, 62, 43, 43, 43, 57, 84, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 44, 71, 85, 85, 43, 80, 87, 88, 87, 85, 44, + 44, 44, 57, 83, 44, 44, 36, 62, 78, 27, 27, 27, 44, 44, 44, 44, + 44, 71, 62, 36, 36, 61, 44, 36, 61, 36, 36, 44, 62, 61, 61, 36, + 44, 62, 61, 44, 36, 61, 44, 36, 36, 36, 36, 36, 36, 44, 44, 84, + 83, 88, 44, 84, 88, 84, 85, 44, 61, 44, 44, 87, 44, 44, 44, 44, + 27, 89, 67, 67, 56, 90, 44, 44, 83, 84, 71, 36, 36, 36, 61, 36, + 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 62, 43, + 83, 84, 88, 43, 80, 43, 43, 44, 44, 44, 57, 80, 36, 61, 44, 44, + 44, 44, 44, 91, 27, 27, 27, 89, 70, 84, 72, 36, 36, 36, 61, 36, + 36, 36, 62, 36, 36, 44, 71, 85, 84, 84, 88, 83, 88, 84, 43, 44, + 44, 44, 87, 88, 44, 44, 44, 61, 62, 61, 44, 44, 44, 44, 44, 44, + 43, 84, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 70, 71, 84, + 85, 43, 80, 84, 88, 84, 85, 77, 44, 44, 36, 92, 27, 27, 27, 93, + 27, 27, 27, 27, 89, 36, 36, 36, 57, 84, 62, 36, 36, 36, 36, 36, + 36, 36, 36, 61, 44, 36, 36, 36, 36, 62, 36, 36, 36, 36, 62, 44, + 36, 36, 36, 61, 44, 80, 44, 87, 84, 43, 80, 80, 84, 84, 84, 84, + 44, 84, 64, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, 36, + 70, 36, 43, 43, 43, 80, 44, 94, 36, 36, 36, 75, 43, 43, 43, 60, + 7, 7, 7, 7, 7, 2, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 44, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 95, 95, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 84, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 83, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 90, 67, 2, 2, 95, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 92, 85, 43, 83, 43, 43, 43, 85, 83, 85, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 84, 43, 36, 36, 43, + 71, 84, 96, 92, 84, 84, 84, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 83, 85, 83, 84, 84, 85, 92, 7, 7, 7, 7, 7, 84, 85, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 97, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 99, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,101,102, 44, + 36, 36, 36, 36, 36, 63, 2,103,104, 36, 36, 36, 61, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 61, 36, 36, 43, 80, 44, 44, 44, 44, 44, + 36, 43, 60, 64, 44, 44, 44, 44, 36, 43, 44, 44, 44, 44, 44, 44, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 85, 43, 43, 43, 84, + 84, 84, 84, 83, 85, 43, 43, 43, 43, 43, 2, 86, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,105, 2, 59, 43, 68, 36,106, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 83, 84, 85, 83, 84, 44, 44, + 84, 83, 84, 84, 85, 43, 44, 44, 90, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7, 98, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 83, 85, 44, 2, 36, 36, 92, 83, 43, 43, 43, 80, + 83, 83, 85, 43, 43, 43, 83, 84, 84, 85, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 86, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,107, + 80, 44, 44, 44, 44, 44, 44, 44, 43, 43, 96, 36, 36, 36, 36, 36, + 36, 36, 83, 43, 43, 83, 83, 84, 84, 83, 96, 36, 36, 36, 44, 44, + 95, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 90, 44, + 43, 96, 36, 36, 36, 36, 36, 36, 92, 43, 43, 84, 43, 85, 43, 36, + 36, 36, 36, 83, 43, 84, 85, 85, 43, 84, 44, 44, 44, 44, 2, 2, + 36, 36, 84, 84, 84, 84, 43, 43, 43, 43, 84, 43, 44, 91, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,108, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 83, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 92, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,109, 40, 40, + 43, 43, 43, 43, 43, 57, 43, 43, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 44, 11, 11, 11, 44, + 16, 16, 16, 16, 48, 48, 48, 48, 16, 16, 16, 16, 16, 16, 16, 44, + 16, 16, 16, 16,110,110,110,110, 16, 16,108, 16, 11, 11,111,112, + 41, 16,108, 16, 11, 11,111, 41, 16, 16, 44, 16, 11, 11,113, 41, + 16, 16, 16, 16, 11, 11,114, 41, 44, 16,108, 16, 11, 11,111,115, + 116,116,116,116,116,117, 65, 65,118,118,118, 2,119,120,119,120, + 2, 2, 2, 2,121, 65, 65,122, 2, 2, 2, 2,123,124, 2,125, + 126, 2,127,128, 2, 2, 2, 2, 2, 9,126, 2, 2, 2, 2,129, + 65, 65, 68, 65, 65, 65, 65, 65,130, 44, 27, 27, 27, 8,127,131, + 27, 27, 27, 27, 27, 8,127,102, 40, 40, 40, 40, 40, 40, 81, 44, + 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,132, 51, + 107, 51,107, 43, 43, 43, 43, 43, 67,133, 67,134, 67, 34, 11, 16, + 11, 32,134, 67, 49, 11, 11, 67, 67, 67,133,133,133, 11, 11,135, + 11, 11, 35, 36, 39, 67, 16, 11, 8, 8, 49, 16, 16, 26, 67,136, + 27, 27, 27, 27, 27, 27, 27, 27,103,103,103,103,103,103,103,103, + 103,137,138,103,139, 67, 44, 44, 8, 8,140, 67, 67, 8, 67, 67, + 140, 26, 67,140, 67, 67, 67,140, 67, 67, 67, 67, 67, 67, 67, 8, + 67,140,140, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 67, 67, 67, 67, 4, 4, 67, 67, + 8, 67, 67, 67,141,142, 67, 67, 67, 67, 67, 67, 67, 67,140, 67, + 67, 67, 67, 67, 67, 26, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 90, 44, 44, 44, 44, 67, 67, 67, 67, 67, 90, 44, 44, + 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, + 67, 67, 67, 26, 67, 67, 67, 67, 26, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 8, 8, 8, 8, 67, 67, 67, 67, 67, 67, 67, 26, + 67, 67, 67, 67, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 67, 67, 67, 67, 67, 67, 8, 8,127,143, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,127,144,144,144,144,144,144, + 144,144,144,144,143, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,140, 26, 8, 8,140, 67, + 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, + 11, 11, 11, 11, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16,108, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,136, 67, 67,134, 34,145, 43, 32, 44, 44, 91, 2, 97, 2, + 16, 16, 16,146, 44, 44,146, 44, 36, 36, 36, 36, 44, 44, 44, 52, + 64, 44, 44, 44, 44, 44, 44, 57, 36, 36, 36, 61, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 36, 36, 61, 2,119,119, 2,123,124,119, 2, + 2, 2, 2, 6, 2,105,119, 2,119, 4, 4, 4, 4, 2, 2, 86, + 2, 2, 2, 2, 2,118, 2, 2,105,147, 2, 2, 2, 2, 2, 2, + 67, 64, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 55, 67, 67, + 67, 67, 44, 44, 44, 44, 44, 44, 67, 67, 67, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 44, 44, 1, 2,148,149, 4, 4, 4, 4, + 4, 67, 4, 4, 4, 4,150,151,152,103,103,103,103, 43, 43, 84, + 153, 40, 40, 67,103,154, 63, 67, 36, 36, 36, 61, 57,155,156, 69, + 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, + 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 90, + 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, + 157, 27, 27, 27, 27, 27, 27, 27, 36, 36,106, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,158, 2, 7, 7, 7, 7, 7, 36, 44, 44, + 32, 32, 32, 32, 32, 32, 32, 70, 51,159, 43, 43, 43, 43, 43, 86, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36,103,103,103,103,103, + 43, 2, 2, 2, 44, 44, 44, 44, 41, 41, 41,156, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 45, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,160, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 44, 32, 11, 11, 34,108, 44, 44, + 44, 44, 48, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 92, 85, 83, 67, 67, 80, 44, 27, 27, 27, 67,161, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 84, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 84, 84, 84, 84, 84, 84, 84, 84, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 84, 44, 44, 44, 44, 44, 91, + 36, 70, 84, 43, 43, 84, 43, 84,162, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 83, + 85, 83, 85, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 83, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 92, 83, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62,106, 2, 36, 36, 36, 36, 36, 92, 43, 84, + 2,106,163, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,112, 40, 40, + 16, 16, 16, 16,109, 41, 44, 44, 36, 92, 85, 84, 83,162, 85, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 164,164,164,164,164,164,164,164,165,165,165,165,165,165,165,165, + 16, 16, 16,108, 44, 44, 44, 44, 44,146, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,166, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41, 44, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,144, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36,161, 44, 2, 2, 2,167,128, 44, 44, 44, + 6,168,169,144,144,144,144,144,144,144,128,167,128, 2,125,170, + 2, 64, 2, 2,150,144,144,128, 2,171, 8,172, 66, 2, 44, 44, + 36, 36, 36, 36, 36, 36, 61, 79, 91, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,127,128, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,173, 56,174, 26, 8,140, 90, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,175, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 103,103,139, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 90, + 67, 67, 67, 67, 67, 67, 90, 44, 90, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,176, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 149, 36, 36, 36, 36,177, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 91, 36, 36, 44, 44, 36, 36, 36, 36, + 178,103,103, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 44, 44, 44, 44, 44, 91, 36, 36, 36, 44, 61, 36, 36, 36, + 36, 36, 36, 62, 61, 44, 61, 62, 36, 36, 36, 91, 27, 27, 27, 27, + 36, 36, 36, 77,157, 27, 27, 27, 44, 44, 44,175, 27, 27, 27, 27, + 36, 61, 36, 44, 44,175, 27, 27, 36, 36, 36, 27, 27, 27, 44, 91, + 36, 36, 36, 36, 36, 44, 44, 91, 36, 36, 36, 36, 44, 44, 27, 36, + 44, 27, 27, 27, 27, 27, 27, 27, 70, 43, 57, 80, 44, 44, 43, 43, + 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, 44, 43, 80, 44, 57, + 27, 27, 27, 27, 98, 44, 44, 44, 2, 2, 2, 2, 64, 44, 44, 44, + 36, 36, 36, 36, 36, 36,179, 30, 36, 36, 36, 36, 36, 36,179, 27, + 36, 36, 36, 36, 78, 36, 36, 36, 36, 36, 70, 80, 44,175, 27, 27, + 2, 2, 2, 64, 44, 44, 44, 44, 36, 36, 36, 44, 91, 2, 2, 2, + 36, 36, 36, 44, 27, 27, 27, 27, 36, 61, 44, 44, 27, 27, 27, 27, + 36, 44, 44, 44, 91, 2, 64, 44, 44, 44, 44, 44,175, 27, 27, 27, + 11, 47, 44, 44, 44, 44, 44, 44, 16,108, 44, 44, 44, 27, 27, 27, + 36, 36, 43, 43, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27, 98, + 36, 36, 36, 36, 36, 57,180, 44, 36, 44, 44, 44, 44, 44, 44, 44, + 27, 27, 27, 93, 44, 44, 44, 44,176, 27, 30, 2, 2, 44, 44, 44, + 36, 36,179, 27, 27, 27, 44, 44, 85, 96, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 43, 60, 2, 2, 2, 44, + 27, 27, 27, 7, 7, 7, 7, 7, 44, 44, 44, 44, 44, 44, 44, 57, + 84, 85, 43, 83, 85, 60,181, 2, 2, 44, 44, 44, 44, 44, 79, 44, + 43, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, 85, 43, + 43, 43, 80, 7, 7, 7, 7, 7, 2, 2, 92, 96, 44, 44, 44, 44, + 36, 70, 2, 61, 44, 44, 44, 44, 36, 92, 84, 43, 43, 43, 43, 83, + 96, 36, 63, 2, 59, 43, 60, 85, 7, 7, 7, 7, 7, 63, 63, 2, + 175, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 84, 85, 43, 84, 83, 43, 2, 2, 2, 80, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 84, 85, 43, 43, 43, 80, 44, 44, 43, 84, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 84, 83, 84, 88, 87, 88, 87, 84, 44, + 61, 44, 44, 87, 44, 44, 62, 36, 36, 84, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 92, 84, 43, 43, 43, 43, + 84, 43, 83, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 91, 71, + 84, 85, 43, 43, 83, 83, 84, 85, 83, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 92, 84, 43, 43, 44, 84, 84, 43, 85, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 84, 85, 43, 43, 43, 83, 85, 85, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 85, 84, + 43, 43, 43, 85, 61, 44, 44, 44, 84, 43, 43, 85, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 95, 43, 43, 43, 43, 85, 60, 44, 44, + 27, 98, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 84, 84, 84, 87, 88, 57, 83, 71, + 96, 85, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 92, 84, 43, 43, 44, 43, 84, 84, 71, 72, 88, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 83, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 83, 85, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 83, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 85, + 63, 2, 2, 44, 44, 44, 44, 44, 2, 36, 36, 36, 36, 36, 36, 36, + 44, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 87, 43, 43, 43, + 83, 43, 85, 80, 44, 44, 44, 44, 36, 36, 36, 61, 36, 62, 36, 36, + 70, 43, 43, 80, 44, 80, 43, 57, 43, 43, 43, 70, 44, 44, 44, 44, + 36, 36, 36, 62, 61, 36, 36, 36, 36, 36, 36, 36, 36, 84, 84, 88, + 43, 87, 85, 85, 61, 44, 44, 44, 36, 70, 83,162, 64, 44, 44, 44, + 27, 27, 89, 67, 67, 67, 56, 20,161, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 91,103,103,103,103,103,103,103,177, + 2, 2, 64, 44, 44, 44, 44, 44, 65, 65, 65, 65, 68, 44, 44, 44, + 43, 43, 60, 44, 44, 44, 44, 44, 43, 43, 43, 60, 2, 2, 67, 67, + 40, 40, 95, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7,175, 27, 27, + 27, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 44, 62, 36, + 27, 27, 27, 30, 2, 64, 44, 44, 36, 36, 36, 36, 36, 61, 44, 57, + 92, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 44, 44, 44, 57, 43, 74, 40, 40, 40, 40, 40, 40, + 40, 86, 80, 44, 44, 44, 44, 44, 84, 44, 44, 44, 44, 44, 44, 44, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 90, 55, 67, 67, 67, 67, 67,182, 85, 43, 67,182, 84, + 84,183, 65, 65, 65, 82, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 67, 67, 67, 90, 44, 44, 44, + 67, 43, 76, 44, 44, 44, 44, 44, 27, 27, 44, 44, 44, 44, 44, 44, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 16, 16,108, 16, 16, 16, 16, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 47, 11, + 44, 47, 48, 47, 48, 11, 47, 11, 11, 11, 11, 16, 16,146,146, 16, + 16, 16,146, 16, 16, 16, 16, 16, 16, 16, 11, 48, 11, 47, 48, 11, + 11, 11, 47, 11, 11, 11, 47, 16, 16, 16, 16, 16, 11, 48, 11, 47, + 11, 11, 47, 47, 44, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 16, 16, 16, 44, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 44, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 43, 43, 43, 76, 67, 50, 43, 43, 43, 43, 43, 43, 43, 43, 76, 67, + 67, 67, 50, 67, 67, 67, 67, 67, 67, 67, 76, 21, 2, 2, 44, 44, + 44, 44, 44, 44, 44, 57, 43, 43, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 44, 44, 94, + 36, 36, 61,175, 27, 27, 27, 27, 43, 43, 43, 80, 44, 44, 44, 44, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,157, 27, + 184, 27, 98, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,157, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 55, 67, 67, 67, 67, 67, 67, 67, + 27, 27, 27, 27, 27, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 44, + 44, 44, 44, 67, 67, 67, 67, 67, 67, 90, 44, 44, 44, 44, 44, 44, + 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, + 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 90, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 90, 44, 67, 90, 44, 44, + 67, 90, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65,165,165,165,165,165,165,165, 44, + 165,165,165,165,165,165,165, 0, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, + 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, + 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, + 1, 2, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, + 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 1, 12, 12, 10, + 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, + 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, + 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 17, 21, 7, 6, 11, 12, + 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, + 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, + 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, 18, 6, + 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, 14, 15, + 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, + 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, + 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 23, 26, 10, 21, 6, 10, + 4, 4, 3, 3, 7, 25, 21, 22, 17, 16, 16, 22, 16, 16, 25, 17, + 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, + 12, 17, 21, 1, 26, 10, 10, 1, 23, 15, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, + 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, + 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0, + 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17, + 18, 19, 20, 0, 0, 0, 21, 22, 0, 23, 24, 0, 0, 23, 25, 26, + 0, 23, 25, 0, 0, 23, 25, 0, 0, 23, 25, 0, 0, 0, 25, 0, + 0, 0, 27, 0, 0, 23, 25, 0, 0, 28, 25, 0, 0, 0, 29, 0, + 0, 30, 31, 0, 0, 32, 33, 0, 34, 35, 0, 36, 37, 0, 38, 0, + 0, 39, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 0, 0, 0, 0, 43, 0, + 0, 0, 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 0, 0, 47, 0, 48, 49, 0, 0, 50, 51, 52, 0, 53, 0, 54, + 0, 55, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 59, + 0, 0, 0, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 0, 0, 0, 64, + 0, 65, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 67, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, + 70, 71, 0, 0, 0, 0, 51, 72, 0, 73, 74, 0, 0, 75, 76, 0, + 0, 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, + 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 83, 0, 0, 0, 0, + 84, 85, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0, + 0, 0, 70, 63, 0, 90, 0, 0, 91, 92, 0, 75, 0, 0, 93, 0, + 0, 94, 0, 0, 0, 0, 0, 95, 0, 96, 25, 97, 0, 0, 0, 0, + 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 63,100, 0, + 0, 63, 0, 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, + 0, 90, 0, 0, 0, 0, 0, 0, 0,103,104, 0, 0, 0, 0, 76, + 0, 42,105, 0,106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 63, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,108, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,109, 0,110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,111, + 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,114,115, 0, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 117,118, 0, 0, 0, 0, 0, 0, 0,110, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,120, 0, 0, 0,121, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0, + 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45, + 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 47, + 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 52, 1, 1, 1, + 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 56, + 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 57, 0, 61, 0, 0, + 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 70, 71, 0, + 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, + 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 80, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, + 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 64, 0, 0, 81, + 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, + 0, 0, 0, 0, 0, 19, 84, 0, 63, 0, 0, 0, 0, 49, 1, 85, + 0, 0, 0, 0, 1, 54, 15, 86, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 56, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 88, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, + 0, 0, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47, 0, 59, 50, 0, + 21, 1, 21, 92, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, 95, 96, + 0, 0, 0, 0, 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0, + 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,101,102, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 19, 0, 1, 1, 50, + 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, + 1, 1, 1, 1, 50, 0, 0, 0, 0, 0, 52, 69, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, + 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104,105, 59, 38, + 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 38, 89, 0, + 0, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, 62, + 0,109, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 59, 0, 0, 0, 0, 0,110, 14, 54, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 0,111, 0, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 62, 63, 0, 0, 63, 0, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,111, 0, 0, 0, 0,112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0, + 64, 88, 0, 0, 0, 0, 0, 60,113, 0, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113, 0, 0, + 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 79, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 57, 0, 88,114, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 8, 91, 0, 0, + 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 0, 0,115, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,116, 0,117,118,119,120, 0, 52, 4, + 121, 49, 23, 0, 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0, + 38, 59, 0, 0, 0, 0, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1, + 48,104, 89, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 4,121, 0, 0, 0, 1,122, 0, 0, 0, 0, 0, + 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, + 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, + 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, + 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, + 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, + 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, + 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, + 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, + 0, 0, 0,220,230,230, 0,220,230,220,220,220, 27, 28, 29,230, + 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, + 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, + 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0, + 122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, + 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0, + 130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, + 0, 9, 9, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, + 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9, + 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220, 0,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230, + 230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 9, 7, 0, + 0, 7, 9, 0, 0, 0, 9, 7, 9, 9, 0, 0, 6, 6, 0, 0, + 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216, + 216,216,216, 0,220,220,220, 0,230,230, 7, 0, 16, 17, 17, 17, + 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, + 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, + 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, + 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, + 0, 0, 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 1, 2, 39, 40, + 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, + 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, + 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, + 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, + 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, + 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, + 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, + 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, + 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, + 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, + 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, + 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, + 0, 0, 0, 0, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, + 10, 11, 12, 12, 12, 12, 13, 14, 14, 14, 14, 15, 16, 17, 18, 19, + 20, 14, 21, 14, 22, 14, 14, 14, 14, 23, 24, 24, 25, 26, 14, 14, + 14, 14, 27, 28, 14, 14, 29, 30, 31, 32, 33, 34, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 14, 40, 7, 7, 41, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 42, 0, 0, 1, + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, + 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, + 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 80, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 83, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 96, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 70, 70, 98, 99,100,101,102,102,103,104,105,106,107,108,109,110, + 111,112, 97,113,114,115,116,117,118, 97,119,119,120, 97,121,122, + 123,124,125,126,127,128,129,130,131, 97,132,133,134,135,136,137, + 138,139,140,141,142, 97,143,144, 97,145,146,147,148, 97,149,150, + 151,152,153,154, 97, 97,155,156,157,158, 97,159, 97,160,161,161, + 161,161,161,161,161,162,163,161,164, 97, 97, 97, 97, 97,165,165, + 165,165,165,165,165,165,166, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97,167,167,167,167,168, 97, 97, 97,169,169, + 169,169,170,171,172,173, 97, 97, 97, 97,174,175,176,177,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178, + 178,178,178,178,178,178,178,178,178,178,178,178,178,179,178,178, + 178,178,178,178,180,180,180,181,182, 97, 97, 97, 97, 97,183,184, + 185,186,186,187, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 97,188,189, 97, 97, 97, 97, 97, 97, 59,190, + 191,192,193,194,195, 97,196,197,198, 59, 59,199, 59,200,201,201, + 201,201,201,202, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,203, 97, + 204, 97, 97,205, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,206,207, + 208, 97, 97, 97, 97, 97,209,210,211, 97,212,213, 97, 97,214,215, + 59,216,217, 97, 59, 59, 59, 59, 59, 59, 59,218,219,220,221,222, + 223,224,225,226, 59,227, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,228, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,229, 70,230, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,231, 70, 70, 70, 70, + 70, 70, 70, 70, 70,232, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70,233, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70, + 70, 70, 70, 70,234, 97, 97, 97, 97, 97, 97, 97, 97, 97,235, 97, + 236,237, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, + 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 2, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 2, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 2, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 2, 16, 16, + 16, 16, 2, 2, 16, 16, 2, 16, 16, 2, 2, 2, 2, 2, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, + 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, + 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, + 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, + 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, + 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, + 18, 18, 18, 2, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 45, 45, 45, 45, + 45, 45, 45, 2, 2, 2, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, + 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, + 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, + 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, + 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, + 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 2, + 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 2, 2, 2, 76, 76, + 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, + 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, + 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 2, + 2, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 1, 1, + 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 2, 55, 55, + 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, 61, + 61, 61, 61, 61, 61, 61, 2, 2, 2, 2, 2, 2, 2, 61, 61, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 1, 1, 1, 12, 12, 13, 13, 13, 13, 0, 0, 0, 0, 2, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 0, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, + 17, 17, 17, 17, 17, 0, 13, 13, 13, 13, 13, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, + 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 60, 60, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, + 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, + 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, + 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, + 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, + 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, + 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, + 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, + 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, + 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, 2, + 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 2, 2, 49, 49, + 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 1, 2, 2, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, + 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 1, 0, + 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, 41, 2, 2, 2, 2, 2,118,118, + 118,118,118,118,118,118,118,118,118, 2, 2, 2, 2, 2, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 2, 53, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59, + 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 51, 51, + 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 2, 2, 50, 50, 2, 2, 2, 2, 2, 2,135,135, + 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,106,106, + 106,106,106,106,106,106,104,104,104,104,104,104,104,104,104,104, + 104,104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,104,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, + 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 2,156,156,156,156,156,156,156,156,156,156, + 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 108,108,108,108,108, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, + 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, + 102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2, 2,102,102, + 2, 2, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, + 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2,133,133, + 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, + 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, + 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, + 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, + 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, + 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, + 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, + 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, + 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, + 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, + 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145, 2, + 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, 22, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, 63, + 63, 63, 63, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2, 63, 63, + 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 63, 63, + 63, 63, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 2, 80, 2, 2, 2, 2, 2, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,103,103, + 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, + 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, + 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,146,146, + 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99, 2, 2, + 2, 2, 2, 2, 2, 99,136,139, 0, 0,155, 2, 2, 2,136,136, + 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, + 155,155,155,155, 2, 2,136, 2, 2, 2, 2, 2, 2, 2, 17, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, + 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, + 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, + 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 0, 0, + 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,113,113, + 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, + 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, + 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, + 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, + 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, + 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 13, 2, + 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 2, 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, + 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, + 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, + 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, + 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, + 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, + 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0, + 107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, 0, + 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123,124, + 125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138,139, + 140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155, + 156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, + 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171, + 172, 0, 0, 0,173,174,175,176,177,178,179,180,181,182,183,184, + 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, + 201,202,203,204,205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[9080] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 72, 72, 72, 48, 73, 74, 75, 76, 32, + 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102, + 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114, + 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125, + 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136, + 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 184, 185, 48, 186, 48, 187, 184, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 72, 72, 72, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 240, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, + 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216, + 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277, + 278, 278, 278, 278, 278, 278, 278, 278, 279, 209, 280, 209, 209, 209, 209, 281, + 209, 282, 278, 283, 209, 284, 285, 209, 209, 209, 286, 72, 287, 72, 270, 270, + 270, 288, 209, 209, 209, 209, 289, 270, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 290, 291, 209, 209, 292, 209, 209, 209, 209, 209, 209, 293, 209, + 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278, + 299, 300, 278, 278, 278, 301, 278, 302, 209, 209, 209, 278, 303, 209, 209, 304, + 209, 305, 209, 209, 209, 209, 209, 209, 9, 9, 306, 11, 11, 307, 308, 309, + 13, 13, 13, 13, 13, 13, 310, 311, 11, 11, 312, 48, 48, 48, 313, 314, + 48, 315, 316, 316, 316, 316, 32, 32, 317, 318, 319, 320, 321, 322, 72, 72, + 209, 323, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 325, 72, 326, + 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332, + 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48, + 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 48, 48, 48, 48, 48, 48, 151, + 48, 339, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229, + 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346, + 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 356, + 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, + 64, 48, 365, 48, 366, 367, 48, 151, 77, 48, 48, 368, 369, 370, 371, 372, + 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, + 383, 384, 316, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, + 48, 48, 388, 48, 389, 48, 48, 206, 390, 390, 390, 390, 390, 390, 390, 390, + 391, 391, 391, 391, 391, 391, 391, 391, 48, 48, 48, 48, 48, 48, 204, 48, + 48, 48, 48, 48, 48, 207, 72, 72, 392, 393, 394, 395, 396, 48, 48, 48, + 48, 48, 48, 397, 398, 399, 48, 48, 48, 48, 48, 400, 72, 48, 48, 48, + 48, 401, 48, 48, 74, 72, 72, 402, 32, 403, 32, 404, 405, 406, 407, 73, + 48, 48, 48, 48, 48, 48, 48, 408, 409, 2, 3, 4, 5, 410, 411, 412, + 48, 413, 48, 200, 414, 415, 416, 417, 418, 48, 172, 419, 204, 204, 72, 72, + 48, 48, 48, 48, 48, 48, 48, 71, 420, 270, 270, 421, 271, 271, 271, 422, + 423, 424, 425, 72, 72, 209, 209, 426, 72, 72, 72, 72, 72, 72, 72, 72, + 48, 151, 48, 48, 48, 101, 427, 428, 48, 48, 429, 48, 430, 48, 48, 431, + 48, 432, 48, 48, 433, 434, 72, 72, 9, 9, 435, 11, 11, 48, 48, 48, + 48, 204, 192, 9, 9, 436, 11, 437, 48, 48, 74, 48, 48, 48, 438, 72, + 48, 48, 48, 315, 48, 199, 74, 72, 439, 48, 48, 440, 48, 441, 48, 442, + 48, 200, 443, 72, 72, 72, 48, 444, 48, 445, 48, 446, 72, 72, 72, 72, + 48, 48, 48, 447, 270, 448, 270, 270, 449, 450, 48, 451, 452, 453, 48, 454, + 48, 455, 72, 72, 456, 48, 457, 458, 48, 48, 48, 459, 48, 460, 48, 461, + 48, 462, 463, 72, 72, 72, 72, 72, 48, 48, 48, 48, 196, 72, 72, 72, + 9, 9, 9, 464, 11, 11, 11, 465, 48, 48, 466, 192, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72, + 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315, + 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192, + 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489, + 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192, + 497, 84, 85, 498, 499, 500, 501, 502, 48, 48, 48, 503, 504, 505, 469, 72, + 48, 48, 48, 506, 507, 192, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72, + 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72, + 48, 173, 517, 518, 72, 72, 72, 72, 48, 48, 490, 519, 72, 72, 72, 72, + 72, 72, 9, 9, 11, 11, 148, 520, 521, 522, 48, 523, 524, 192, 72, 72, + 72, 72, 525, 48, 48, 526, 527, 72, 528, 48, 48, 529, 530, 531, 48, 48, + 532, 533, 534, 72, 48, 48, 48, 196, 85, 48, 508, 535, 536, 148, 175, 537, + 48, 538, 539, 540, 72, 72, 72, 72, 541, 48, 48, 542, 543, 192, 544, 48, + 545, 546, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547, + 72, 72, 72, 101, 270, 548, 549, 550, 48, 207, 72, 72, 72, 72, 72, 72, + 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 388, 72, 72, 72, + 48, 48, 200, 553, 72, 72, 72, 72, 48, 48, 48, 48, 315, 72, 72, 72, + 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554, + 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72, + 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567, + 48, 48, 48, 48, 48, 48, 48, 74, 48, 48, 48, 48, 48, 199, 72, 72, + 196, 72, 72, 72, 72, 72, 72, 72, 48, 200, 72, 72, 72, 568, 569, 48, + 48, 48, 48, 48, 48, 48, 48, 206, 48, 48, 48, 48, 48, 48, 71, 151, + 196, 570, 571, 72, 72, 72, 72, 72, 209, 209, 209, 209, 209, 209, 209, 325, + 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72, + 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579, + 209, 209, 209, 209, 209, 286, 270, 452, 9, 580, 11, 581, 582, 583, 242, 9, + 584, 585, 586, 587, 588, 9, 580, 11, 589, 590, 11, 591, 592, 593, 594, 9, + 595, 11, 9, 580, 11, 581, 582, 11, 242, 9, 584, 594, 9, 595, 11, 9, + 580, 11, 596, 9, 597, 598, 599, 600, 11, 601, 9, 602, 603, 604, 605, 11, + 606, 9, 607, 11, 608, 609, 609, 609, 32, 32, 32, 610, 32, 32, 611, 612, + 613, 614, 45, 72, 72, 72, 72, 72, 615, 616, 617, 72, 72, 72, 72, 72, + 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621, + 48, 48, 48, 48, 622, 623, 72, 72, 9, 9, 584, 11, 624, 370, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72, + 488, 270, 627, 628, 72, 72, 72, 72, 629, 48, 630, 631, 632, 633, 634, 635, + 636, 206, 637, 206, 72, 72, 72, 638, 209, 209, 326, 209, 209, 209, 209, 209, + 209, 324, 335, 639, 639, 639, 209, 325, 640, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 641, 72, 72, 72, 642, 209, 643, 209, 209, 326, 577, 644, 325, 72, + 209, 209, 209, 209, 209, 209, 209, 645, 209, 209, 209, 209, 209, 646, 424, 424, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72, + 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72, + 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209, + 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72, + 209, 650, 209, 209, 287, 72, 72, 192, 48, 48, 48, 48, 48, 204, 72, 72, + 48, 48, 48, 205, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, + 48, 48, 469, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 101, 72, + 48, 204, 72, 72, 72, 72, 72, 72, 48, 48, 48, 48, 71, 72, 72, 72, + 651, 72, 652, 652, 652, 652, 652, 652, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 72, 391, 391, 391, 391, 391, 391, 391, 653, + 391, 391, 391, 391, 391, 391, 391, 654, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 4, 0, 4, + 2, 2, 5, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, + 0, 0, 0, 0, 7, 8, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 14, 15, 14, 14, 14, + 14, 14, 14, 14, 16, 17, 14, 14, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 21, + 21, 21, 22, 20, 21, 21, 21, 21, 21, 23, 24, 25, 25, 25, 25, 25, + 25, 26, 25, 25, 25, 27, 28, 26, 29, 30, 31, 32, 31, 31, 31, 31, + 33, 34, 35, 31, 31, 31, 36, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 29, 31, 31, 31, 31, 37, 38, 37, 37, 37, 37, 37, 37, + 37, 39, 31, 31, 31, 31, 31, 31, 40, 40, 40, 40, 40, 40, 41, 26, + 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 44, 46, + 47, 47, 47, 48, 37, 49, 26, 26, 26, 26, 26, 26, 31, 31, 50, 31, + 31, 26, 51, 31, 52, 31, 31, 31, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 54, 53, 55, 53, 53, 53, 56, 57, 58, 59, 59, 60, 61, 62, + 57, 63, 64, 65, 66, 59, 59, 67, 68, 69, 70, 71, 71, 72, 73, 74, + 69, 75, 76, 77, 78, 71, 79, 26, 80, 81, 82, 83, 83, 84, 85, 86, + 81, 87, 88, 26, 89, 83, 90, 91, 92, 93, 94, 95, 95, 96, 97, 98, + 93, 99, 100, 101, 102, 95, 95, 26, 103, 104, 105, 106, 107, 104, 108, 109, + 104, 105, 110, 26, 111, 108, 108, 112, 113, 114, 115, 113, 113, 115, 113, 116, + 114, 117, 118, 119, 120, 113, 121, 113, 122, 123, 124, 122, 122, 124, 125, 126, + 123, 127, 128, 129, 130, 122, 131, 26, 132, 133, 134, 132, 132, 132, 132, 132, + 133, 134, 135, 132, 136, 132, 132, 132, 137, 138, 139, 140, 138, 138, 141, 142, + 139, 143, 144, 138, 145, 138, 146, 26, 147, 148, 148, 148, 148, 148, 148, 149, + 148, 148, 148, 150, 26, 26, 26, 26, 151, 152, 153, 153, 154, 153, 153, 155, + 156, 155, 153, 157, 26, 26, 26, 26, 158, 158, 158, 158, 158, 158, 158, 158, + 158, 159, 158, 158, 158, 160, 159, 158, 158, 158, 158, 159, 158, 158, 158, 161, + 158, 161, 162, 163, 26, 26, 26, 26, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 165, 165, 165, 165, + 166, 167, 165, 165, 165, 165, 165, 168, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, 170, 170, + 170, 171, 172, 171, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 171, 172, + 171, 170, 172, 170, 170, 170, 170, 170, 170, 170, 171, 170, 170, 170, 170, 170, + 170, 170, 170, 173, 170, 170, 170, 174, 170, 170, 170, 175, 176, 176, 176, 176, + 176, 176, 176, 176, 176, 176, 177, 177, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, 179, 180, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 182, 181, 183, 184, 185, 186, 26, 187, 187, 188, 26, + 189, 189, 190, 26, 191, 192, 193, 26, 194, 194, 194, 194, 194, 194, 194, 194, + 194, 194, 194, 195, 194, 196, 194, 196, 197, 198, 199, 200, 199, 199, 199, 199, + 199, 199, 199, 199, 199, 199, 199, 201, 199, 199, 199, 199, 199, 202, 178, 178, + 178, 178, 178, 178, 178, 178, 203, 26, 204, 204, 204, 205, 204, 206, 204, 206, + 207, 204, 208, 208, 208, 209, 210, 26, 211, 211, 211, 211, 211, 212, 211, 211, + 211, 213, 211, 214, 194, 194, 194, 194, 215, 215, 215, 216, 217, 217, 217, 217, + 217, 217, 217, 218, 217, 217, 217, 219, 217, 220, 217, 220, 217, 221, 9, 9, + 222, 26, 26, 26, 26, 26, 26, 26, 223, 223, 223, 223, 223, 223, 223, 223, + 223, 224, 223, 223, 223, 223, 223, 225, 226, 226, 226, 226, 226, 226, 226, 226, + 227, 227, 227, 227, 227, 227, 228, 229, 230, 230, 230, 230, 230, 230, 230, 231, + 230, 232, 233, 233, 233, 233, 233, 233, 18, 234, 165, 165, 165, 165, 165, 235, + 226, 26, 236, 9, 237, 238, 239, 240, 2, 2, 2, 2, 241, 242, 2, 2, + 2, 2, 2, 243, 244, 245, 2, 246, 2, 2, 2, 2, 2, 2, 2, 247, + 9, 9, 9, 9, 9, 9, 9, 248, 14, 14, 249, 249, 14, 14, 14, 14, + 249, 249, 14, 250, 14, 14, 14, 249, 14, 14, 14, 14, 14, 14, 251, 14, + 251, 14, 252, 253, 14, 14, 254, 255, 0, 256, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 257, 0, 258, 259, 0, 260, 2, 261, 0, 0, 0, 0, + 26, 26, 9, 9, 9, 9, 222, 26, 0, 0, 0, 0, 262, 263, 4, 0, + 0, 264, 0, 0, 2, 2, 2, 2, 2, 265, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, 26, 26, 26, + 0, 266, 26, 26, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, + 267, 267, 267, 267, 267, 267, 267, 267, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 269, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 270, 270, 270, 270, 270, 271, 270, 270, + 270, 270, 270, 271, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 272, 273, 165, 165, 165, 165, 166, 167, 274, 274, + 274, 274, 274, 274, 274, 275, 276, 275, 170, 170, 172, 26, 172, 172, 172, 172, + 172, 172, 172, 172, 18, 18, 18, 18, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 266, 26, 26, 26, 26, 26, 277, 277, 277, 278, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 279, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 280, 26, 26, 26, 0, 281, 282, 0, 0, 0, 283, 284, 0, 285, + 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291, + 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294, + 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0, + 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 0, 0, 0, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 299, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 301, 300, 300, 300, 300, 300, 300, + 302, 26, 303, 303, 303, 303, 303, 303, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 305, 26, 26, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 306, 306, 306, 306, + 306, 306, 306, 306, 306, 306, 306, 26, 0, 0, 0, 0, 307, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 308, 2, 2, 2, 2, 2, 2, + 309, 310, 26, 26, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314, + 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316, + 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322, + 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326, + 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26, + 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334, + 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2, + 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, + 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169, + 296, 346, 169, 169, 169, 169, 169, 345, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352, + 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 354, 26, 51, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 355, + 26, 26, 31, 31, 31, 31, 31, 31, 31, 31, 356, 31, 31, 31, 31, 31, + 31, 26, 26, 26, 26, 26, 31, 357, 9, 9, 0, 314, 9, 358, 0, 0, + 0, 0, 359, 0, 260, 281, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 360, 361, 0, 0, 0, 1, 2, 2, 3, + 1, 2, 2, 3, 362, 291, 290, 291, 291, 291, 291, 363, 169, 169, 169, 296, + 364, 364, 364, 365, 260, 260, 26, 366, 367, 368, 367, 367, 369, 367, 367, 370, + 367, 371, 367, 371, 26, 26, 26, 26, 367, 367, 367, 367, 367, 367, 367, 367, + 367, 367, 367, 367, 367, 367, 367, 372, 373, 0, 0, 0, 0, 0, 374, 0, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 255, 0, 375, 376, 26, 26, 26, + 26, 26, 0, 0, 0, 0, 0, 377, 378, 378, 378, 379, 380, 380, 380, 380, + 380, 380, 381, 26, 382, 0, 0, 281, 383, 383, 383, 383, 384, 385, 386, 386, + 386, 387, 388, 388, 388, 388, 388, 389, 390, 390, 390, 391, 392, 392, 392, 392, + 393, 392, 394, 26, 26, 26, 26, 26, 395, 395, 395, 395, 395, 395, 395, 395, + 395, 395, 396, 396, 396, 396, 396, 396, 397, 397, 397, 398, 397, 399, 400, 400, + 400, 400, 401, 400, 400, 400, 400, 401, 402, 402, 402, 402, 402, 26, 403, 403, + 403, 403, 403, 403, 404, 405, 26, 26, 406, 406, 406, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, 26, + 406, 406, 408, 26, 406, 26, 26, 26, 409, 410, 411, 411, 411, 411, 412, 413, + 414, 414, 415, 414, 416, 416, 416, 416, 417, 417, 417, 418, 419, 417, 26, 26, + 26, 26, 26, 26, 420, 420, 421, 422, 423, 423, 423, 424, 425, 425, 425, 426, + 26, 26, 26, 26, 26, 26, 26, 26, 427, 427, 427, 427, 428, 428, 428, 429, + 428, 428, 430, 428, 428, 428, 428, 428, 431, 432, 433, 434, 435, 435, 436, 437, + 435, 438, 435, 438, 439, 439, 439, 439, 440, 440, 440, 440, 26, 26, 26, 26, + 441, 441, 441, 441, 442, 443, 442, 26, 444, 444, 444, 444, 444, 444, 445, 446, + 447, 447, 448, 447, 449, 449, 450, 449, 451, 451, 452, 453, 26, 454, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 455, 455, 455, 455, 455, 455, 455, 455, + 455, 456, 26, 26, 26, 26, 26, 26, 457, 457, 457, 457, 457, 457, 458, 26, + 457, 457, 457, 457, 457, 457, 458, 459, 460, 460, 460, 460, 460, 26, 460, 461, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 31, 31, 31, 462, 463, 463, 463, 463, 463, 464, 465, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 26, 467, 467, + 467, 467, 467, 468, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 469, 469, + 469, 470, 26, 26, 471, 471, 472, 26, 473, 473, 473, 473, 473, 473, 473, 473, + 473, 474, 475, 473, 473, 473, 26, 476, 477, 477, 477, 477, 477, 477, 477, 477, + 478, 479, 480, 480, 480, 481, 480, 482, 483, 483, 483, 483, 483, 483, 484, 483, + 483, 26, 485, 485, 485, 485, 486, 26, 487, 487, 487, 487, 487, 487, 487, 487, + 487, 487, 487, 487, 488, 138, 489, 26, 490, 490, 491, 490, 490, 490, 490, 492, + 26, 26, 26, 26, 26, 26, 26, 26, 493, 494, 495, 496, 495, 497, 498, 498, + 498, 498, 498, 498, 498, 499, 498, 500, 501, 502, 503, 504, 504, 505, 506, 507, + 502, 508, 509, 510, 511, 512, 512, 26, 513, 513, 513, 513, 513, 513, 513, 513, + 513, 513, 513, 514, 515, 26, 26, 26, 516, 516, 516, 516, 516, 516, 516, 516, + 516, 26, 516, 517, 26, 26, 26, 26, 518, 518, 518, 518, 518, 518, 519, 518, + 518, 518, 518, 519, 26, 26, 26, 26, 520, 520, 520, 520, 520, 520, 520, 520, + 521, 26, 520, 522, 199, 523, 26, 26, 524, 524, 524, 524, 524, 524, 524, 525, + 524, 526, 26, 26, 26, 26, 26, 26, 527, 527, 527, 528, 527, 529, 527, 527, + 26, 26, 26, 26, 26, 26, 26, 26, 530, 530, 530, 530, 530, 530, 530, 531, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 532, 532, 532, 532, + 532, 532, 532, 532, 532, 532, 533, 534, 535, 536, 537, 538, 538, 538, 539, 540, + 535, 26, 538, 541, 26, 26, 26, 26, 26, 26, 26, 26, 542, 543, 542, 542, + 542, 542, 542, 543, 544, 26, 26, 26, 545, 545, 545, 545, 545, 545, 545, 545, + 545, 26, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 547, 26, 26, 26, + 548, 548, 548, 548, 548, 548, 548, 549, 550, 551, 550, 550, 550, 550, 552, 550, + 553, 26, 550, 550, 550, 554, 555, 555, 555, 555, 556, 555, 555, 557, 558, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 559, 560, 561, 561, 561, 561, 559, 562, + 561, 26, 561, 563, 564, 565, 566, 566, 566, 567, 568, 569, 566, 570, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 571, 571, 571, 572, 26, 26, 26, 26, 26, 26, 573, 26, + 108, 108, 108, 108, 108, 108, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 578, 579, 26, 576, 576, 576, 576, 576, 576, 576, 576, + 580, 26, 26, 26, 26, 26, 26, 26, 581, 581, 581, 581, 581, 581, 581, 581, + 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 582, 581, 583, + 26, 26, 26, 26, 26, 26, 26, 26, 584, 584, 584, 584, 584, 584, 584, 584, + 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, + 585, 26, 26, 26, 26, 26, 26, 26, 306, 306, 306, 306, 306, 306, 306, 306, + 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 586, + 587, 587, 587, 588, 587, 589, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 590, 590, 590, 591, 591, 26, 592, 592, 592, 592, 592, 592, 592, 592, + 593, 26, 592, 594, 594, 592, 592, 595, 592, 592, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 597, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 598, 598, 598, 598, 598, 598, 598, 598, + 598, 599, 598, 598, 598, 598, 598, 598, 598, 600, 598, 598, 26, 26, 26, 26, + 26, 26, 26, 26, 601, 26, 347, 26, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 26, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, + 603, 603, 604, 26, 26, 26, 26, 26, 602, 605, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 606, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, + 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 26, 26, 26, 26, + 26, 26, 607, 26, 608, 26, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 610, 611, 611, 611, 611, 611, 611, 611, 611, + 611, 611, 611, 611, 611, 612, 611, 613, 611, 614, 611, 615, 281, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 616, 26, 0, 0, 0, 0, 260, 361, 0, 0, + 0, 0, 0, 0, 617, 618, 0, 619, 620, 621, 0, 0, 0, 622, 0, 0, + 0, 0, 0, 0, 0, 623, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14, + 249, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 260, 26, 0, 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 257, 0, 0, 0, 0, 0, 0, 0, 0, 257, 624, 625, 0, 626, + 627, 0, 0, 0, 0, 0, 0, 0, 269, 628, 257, 257, 0, 0, 0, 629, + 630, 631, 632, 0, 0, 0, 0, 0, 0, 0, 0, 0, 616, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 268, 0, 0, 0, 0, 0, 0, 633, 633, 633, 633, 633, 633, 633, 633, + 633, 633, 633, 633, 633, 633, 633, 633, 633, 634, 26, 635, 636, 633, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 271, 270, 270, 637, 638, 639, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 640, 640, 640, 640, 640, 641, 640, 642, + 640, 643, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 644, 644, 644, 644, 644, 644, 644, 645, 646, 646, 646, 646, 646, 646, 646, 646, + 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, + 647, 646, 648, 26, 26, 26, 26, 26, 649, 649, 649, 649, 649, 649, 649, 649, + 649, 650, 649, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 0, 375, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 616, + 26, 26, 26, 26, 26, 26, 26, 26, 652, 31, 31, 31, 653, 654, 655, 656, + 657, 658, 653, 659, 653, 655, 655, 660, 31, 661, 31, 662, 663, 661, 31, 662, + 26, 26, 26, 26, 26, 26, 354, 26, 0, 0, 0, 0, 0, 281, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 260, 361, 0, + 361, 0, 361, 0, 0, 0, 616, 26, 0, 0, 0, 0, 0, 616, 26, 26, + 26, 26, 26, 26, 664, 0, 0, 0, 665, 26, 0, 0, 0, 0, 0, 281, + 0, 623, 314, 26, 616, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 0, 375, 0, 375, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 623, 0, 281, 26, 26, 0, 281, 0, 0, 0, 0, 0, 0, + 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 616, 314, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 632, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 627, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 281, 26, 0, 616, 375, 266, 260, 26, 0, 0, 0, 623, 260, 26, + 266, 26, 260, 26, 26, 26, 26, 26, 0, 0, 359, 0, 0, 0, 0, 0, + 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 280, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 299, 26, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 347, 26, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 666, 26, 26, 26, 277, 277, 277, 280, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 667, 26, 26, 26, 26, 26, 26, 668, 26, 26, 26, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1446,1458,1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6504+(((_hb_ucd_u8[1264+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[8768+(((_hb_ucd_u8[7792+(((_hb_ucd_u8[7120+(((_hb_ucd_u8[6874+(u>>2>>3>>4)])<<4)+((u>>2>>3)&15u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9508+(((_hb_ucd_u8[9388+(((_hb_ucd_b4(9260+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[10974+(((_hb_ucd_u16[1960+(((_hb_ucd_u8[10286+(((_hb_ucd_u8[9836+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[5768+(((_hb_ucd_u8[16708+(((_hb_ucd_u8[16326+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#else + +static const uint8_t +_hb_ucd_u8[13344] = +{ + 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 15, 18, 15, 19, 20, 21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 23, + 5, 24, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 25, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,111, 34, 34, 34,111, + 118,119,120,121,122,123,124,125, 34,126,127,111,128,129,130,131, + 132,133,134,135,136,137,138,111,139,140,111,141,142,143,144,111, + 145,146,147,148,149,150,111,111,151,152,153,154,111,155,111,156, + 34, 34, 34, 34, 34, 34, 34, 34,157, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34,158,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34,159,160,161, 34,111,111,111,111,162,163,164,165, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,166,111,111,111,111,111,111, + 67, 67,167,168,169,128, 65,111,170,171,172,173,174,175,176,177, + 67, 67, 67, 67,178,179,111,111,111,111,111,111,111,111,111,111, + 180,111,181,111,111,182,111,111,111,111,111,111,111,111,111,111, + 34,183,184,111,111,111,111,111,128,185,186,111, 34,187,111,111, + 67, 67,188, 67, 67,111, 67,189, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67,190,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 191,111,180,180,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, + 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, + 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, + 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, + 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, + 43, 43, 43, 43, 43, 57, 59, 2, 62, 36, 36, 36, 36, 63, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 65, 43, 43, 43, 66, 47, 43, 43, 67, 68, 69, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72, + 63, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 64, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 63, 43, 43, 43, 43, 40, 21, 2, 40, 68, 20, + 36, 36, 36, 43, 43, 68, 43, 43, 43, 43, 68, 43, 68, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 43, 43, 2, + 36, 63, 43, 43, 43, 43, 43, 43, 43, 73, 43, 43, 43, 43, 43, 43, + 43, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 74, 64, 75, + 76, 43, 43, 43, 74, 75, 76, 75, 63, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 77, 36, 36, 36, 36, 36, 36, 36, + 63, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 75, + 76, 43, 43, 74, 75, 75, 76, 36, 36, 36, 36, 79, 75, 75, 36, 36, + 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, + 43, 74, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 75, + 76, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 64, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 63, 2, 2, 2, 2, 2, + 76, 43, 43, 43, 74, 75, 76, 43, 60, 20, 20, 20, 80, 43, 43, 43, + 43, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 76, + 76, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 74, 75, 75, 36, 36, + 71, 27, 27, 27, 27, 27, 27, 27, 43, 64, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 75, 74, 75, 75, 75, 75, 75, 76, 43, + 36, 36, 36, 79, 75, 75, 75, 75, 75, 75, 75, 7, 7, 7, 7, 7, + 27, 81, 61, 61, 53, 61, 61, 61, 74, 75, 64, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 43, 74, 75, 75, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 82, 27, 27, 27, 81, + 63, 75, 65, 36, 36, 36, 36, 36, 75, 75, 75, 74, 75, 75, 43, 43, + 43, 43, 74, 75, 75, 75, 75, 36, 83, 36, 36, 36, 36, 36, 36, 36, + 43, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 75, + 76, 43, 43, 75, 75, 75, 76, 70, 61, 61, 36, 79, 27, 27, 27, 84, + 27, 27, 27, 27, 81, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 74, + 75, 43, 43, 43, 75, 75, 75, 75, 7, 75, 2, 2, 2, 2, 2, 2, + 63, 36, 43, 43, 43, 43, 43, 85, 36, 36, 36, 68, 43, 43, 43, 57, + 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 43, 43, 43, 43, 64, 36, + 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, + 70, 61, 2, 2, 2, 2, 2, 2, 2, 86, 86, 61, 43, 61, 61, 61, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 75, + 63, 43, 43, 43, 43, 43, 43, 74, 43, 43, 57, 43, 36, 36, 63, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 69, 61, 61, 61, 61, + 2, 2, 86, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 79, 76, 43, + 74, 43, 43, 43, 76, 74, 76, 64, 36, 36, 36, 75, 43, 36, 36, 43, + 64, 75, 78, 79, 75, 75, 75, 36, 63, 43, 64, 36, 36, 36, 36, 36, + 36, 74, 76, 74, 75, 75, 76, 79, 7, 7, 7, 7, 7, 75, 76, 61, + 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 63, 43, + 2, 2, 2, 2, 87, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, + 88, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65, + 89, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 90, 91, 91, + 36, 36, 36, 36, 36, 58, 2, 92, 93, 36, 36, 36, 36, 36, 36, 36, + 36, 43, 43, 43, 43, 43, 43, 43, 36, 43, 57, 2, 2, 2, 2, 2, + 36, 36, 43, 76, 43, 43, 43, 75, 75, 75, 75, 74, 76, 43, 43, 43, + 43, 43, 2, 77, 2, 60, 63, 43, 7, 7, 7, 7, 7, 7, 7, 7, + 2, 2, 2, 94, 2, 56, 43, 59, 36, 95, 36, 36, 36, 36, 36, 36, + 36, 36, 63, 64, 36, 36, 36, 36, 36, 36, 36, 36, 63, 36, 36, 36, + 43, 74, 75, 76, 74, 75, 75, 75, 75, 74, 75, 75, 76, 43, 43, 43, + 61, 61, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 27, 27, 61, + 36, 36, 36, 63, 74, 76, 43, 2, 36, 36, 79, 74, 43, 43, 43, 43, + 74, 74, 76, 43, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 43, 43, + 2, 2, 2, 77, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 96, + 43, 43, 78, 36, 36, 36, 36, 36, 36, 36, 74, 43, 43, 74, 74, 75, + 75, 74, 78, 36, 36, 36, 36, 36, 86, 61, 61, 61, 61, 47, 43, 43, + 43, 43, 61, 61, 61, 61, 61, 61, 43, 78, 36, 36, 36, 36, 36, 36, + 79, 43, 43, 75, 43, 76, 43, 36, 36, 36, 36, 74, 43, 75, 76, 76, + 43, 75, 75, 75, 75, 75, 2, 2, 36, 36, 75, 75, 75, 75, 43, 43, + 43, 43, 75, 43, 43, 57, 2, 2, 7, 7, 7, 7, 7, 7, 83, 36, + 36, 36, 36, 36, 40, 40, 40, 2, 43, 57, 43, 43, 43, 43, 43, 43, + 74, 43, 43, 43, 64, 36, 63, 36, 36, 36, 64, 79, 43, 36, 36, 36, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 44, 16, 16, + 16, 16, 16, 16, 44, 16, 16, 16, 16, 16, 16, 16, 16, 97, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16, 98, 98, 98, 98, + 16, 16, 16, 16, 11, 11, 99,100, 41, 16, 16, 16, 11, 11, 99, 41, + 16, 16, 16, 16, 11, 11,101, 41,102,102,102,102,102,103, 59, 59, + 51, 51, 51, 2,104,105,104,105, 2, 2, 2, 2,106, 59, 59,107, + 2, 2, 2, 2,108,109, 2,110,111, 2,112,113, 2, 2, 2, 2, + 2, 9,111, 2, 2, 2, 2,114, 59, 59, 59, 59, 59, 59, 59, 59, + 115, 40, 27, 27, 27, 8,112,116, 27, 27, 27, 27, 27, 8,112, 91, + 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,117, 48, + 96, 48, 96, 43, 43, 43, 43, 43, 61,118, 61,119, 61, 34, 11, 16, + 11, 32,119, 61, 46, 11, 11, 61, 61, 61,118,118,118, 11, 11,120, + 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,121, + 92, 92, 92, 92, 92, 92, 92, 92, 92,122,123, 92,124, 61, 61, 61, + 8, 8,125, 61, 61, 8, 61, 61,125, 26, 61,125, 61, 61, 61,125, + 61, 61, 61, 61, 61, 61, 61, 8, 61,125,125, 61, 61, 61, 61, 61, + 61, 61, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 61, 61, 61, 61, 4, 4, 61, 61, 8, 61, 61, 61,126,127, 61, 61, + 61, 61, 61, 61, 61, 61,125, 61, 61, 61, 61, 61, 61, 26, 8, 8, + 8, 8, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, + 8, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 27, 61, 61, + 61, 61, 61, 61, 61, 27, 27, 27, 61, 61, 61, 26, 61, 61, 61, 61, + 26, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, + 61, 61, 61, 61, 61, 61, 61, 26, 61, 61, 61, 61, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, + 8, 8,112,128, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,112,129,129,129,129,129,129,129,129,129,129,128, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,125, 26, 8, 8,125, 61, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,121, 61, 61,119, 34,130, + 43, 32, 16, 16, 50, 2, 87, 2, 36, 36, 36, 36, 36, 36, 36, 95, + 2, 2, 2, 2, 2, 2, 2, 56, 2,104,104, 2,108,109,104, 2, + 2, 2, 2, 6, 2, 94,104, 2,104, 4, 4, 4, 4, 2, 2, 77, + 2, 2, 2, 2, 2, 51, 2, 2, 94,131, 2, 2, 2, 2, 2, 2, + 61, 2, 2, 2, 2, 2, 2, 2, 1, 2,132,133, 4, 4, 4, 4, + 4, 61, 4, 4, 4, 4,134, 91,135, 92, 92, 92, 92, 43, 43, 75, + 136, 40, 40, 61, 92,137, 58, 61, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 63,138,139, 62, 36, 36, 36, 36, 36, 58, 40, 62, + 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61, + 61, 61, 61, 61, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 95, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,141, 2, + 32, 32, 32, 32, 32, 32, 32, 63, 48,142, 43, 43, 43, 43, 43, 77, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 92, 92, 92, 92, 92, + 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,139, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 44, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,143, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 16, 32, 11, 11, 34, 16, 16, 16, + 16, 16, 34, 35, 40, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36, + 36, 79, 76, 74, 61, 61, 43, 43, 27, 27, 27, 61,144, 61, 61, 61, + 36, 36, 2, 2, 2, 2, 2, 2, 75, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 75, 75, 75, 75, 75, 75, 75, 75, 43, 43, 43, 43, 43, 2, + 43, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 63, 43, 43, 43, 43, 43, 75, 75, 75, 75, 75, 75,145, + 36, 63, 75, 43, 43, 75, 43, 75,145, 2, 2, 2, 2, 2, 2, 77, + 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36, + 36, 36, 36, 36, 63, 43, 43, 74, 76, 74, 76, 43, 43, 43, 43, 43, + 36, 63, 36, 36, 36, 36, 74, 75, 7, 7, 7, 7, 7, 7, 2, 2, + 62, 36, 36, 70, 61, 79, 74, 36, 64, 43, 64, 63, 64, 36, 36, 43, + 36, 36, 36, 36, 36, 36, 95, 2, 36, 36, 36, 36, 36, 79, 43, 75, + 2, 95,146, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,100, 40, 40, + 16, 16, 16, 16, 97, 41, 41, 41, 36, 79, 76, 75, 74,145, 76, 43, + 147,147,147,147,147,147,147,147,148,148,148,148,148,148,148,148, + 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,149, 36, 36, 36, + 36, 41, 41, 41, 41, 41, 41, 41, 41,150, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,129,151,151,151,151,151,151,151,151, + 36, 36, 36, 36, 36, 36,144, 61, 2, 2, 2,152,113, 2, 2, 2, + 6,153,154,129,129,129,129,129,129,129,113,152,113, 2,110,155, + 2, 2, 2, 2,134,129,129,113, 2,156, 8, 8, 60, 2, 2, 2, + 36, 36, 36, 36, 36, 36, 36,157, 2, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,112,113, 4, 2, 36, 36, 36, 36, 36, + 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 20,158, 53, 20, 26, 8,125, 61, 61, 61, 61, 61,159, 59, 61, 61, + 2, 2, 2, 87, 27, 27, 27, 27, 27, 27, 27, 81, 61, 61, 61, 61, + 92, 92,124, 27, 81, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61, + 61, 61, 61, 61, 61, 61, 47, 43,160,160,160,160,160,160,160,160, + 161, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 84, 36, + 133, 36, 36, 36, 36, 92, 92, 92, 36, 36, 36, 36, 36, 36, 36, 58, + 162, 92, 92, 92, 92, 92, 92, 92, 36, 36, 36, 58, 27, 27, 27, 27, + 36, 36, 36, 70,140, 27, 27, 27, 36, 36, 36,163, 27, 27, 27, 27, + 36, 36, 36, 36, 36,163, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30, + 36, 36, 36, 36, 36, 36, 27, 36, 63, 43, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 30, + 36, 36, 36, 36, 36, 36,163, 27, 36, 36, 36, 36, 71, 36, 36, 36, + 36, 36, 63, 43, 43,161, 27, 27, 36, 36, 36, 36, 58, 2, 2, 2, + 36, 36, 36, 36, 27, 27, 27, 27, 16, 16, 16, 16, 16, 27, 27, 27, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 63,164, 51, + 27, 27, 27, 84, 36, 36, 36, 36,161, 27, 30, 2, 2, 2, 2, 2, + 36, 36,163, 27, 27, 27, 27, 27, 76, 78, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 57, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,165, + 75, 76, 43, 74, 76, 57, 72, 2, 2, 2, 2, 2, 2, 2, 72, 59, + 36, 36, 36, 63, 43, 43, 76, 43, 43, 43, 43, 7, 7, 7, 7, 7, + 2, 2, 79, 78, 36, 36, 36, 36, 36, 63, 2, 36, 36, 36, 36, 36, + 36, 79, 75, 43, 43, 43, 43, 74, 78, 36, 58, 2, 56, 43, 57, 76, + 7, 7, 7, 7, 7, 58, 58, 2, 87, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 36, 36, 36, 36, 75, 76, 43, 75, 74, 43, 2, 2, 2, 43, + 36, 36, 36, 36, 36, 36, 36, 63, 74, 75, 75, 75, 75, 75, 75, 75, + 36, 36, 36, 79, 75, 75, 78, 36, 36, 75, 75, 43, 43, 43, 43, 43, + 36, 36, 79, 75, 43, 43, 43, 43, 75, 43, 74, 64, 36, 58, 2, 2, + 7, 7, 7, 7, 7, 2, 2, 64, 75, 76, 43, 43, 74, 74, 75, 76, + 74, 43, 36, 65, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 79, + 75, 43, 43, 43, 75, 75, 43, 76, 57, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 43, 43, 75, 76, 43, 43, 43, 74, 76, 76, + 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 76, 75, + 43, 43, 43, 76, 36, 36, 36, 36, 75, 43, 43, 76, 43, 43, 43, 43, + 7, 7, 7, 7, 7, 27, 2, 86, 43, 43, 43, 43, 76, 57, 2, 2, + 27, 27, 27, 27, 27, 27, 27, 84, 75, 75, 75, 75, 75, 76, 74, 64, + 78, 76, 2, 2, 2, 2, 2, 2, 79, 75, 43, 43, 43, 43, 75, 75, + 64, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 63, 43, 43, 43, 43, 64, 36, 36, 36, 63, 43, 43, 74, 63, 43, 57, + 2, 2, 2, 56, 43, 43, 43, 43, 63, 43, 43, 74, 76, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 74, 43, 2, 65, 2, + 43, 43, 43, 43, 43, 43, 43, 76, 58, 2, 2, 2, 2, 2, 2, 2, + 2, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 74, 43, 43, 43, + 74, 43, 76, 43, 43, 43, 43, 43, 43, 43, 43, 63, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 75, 75, 75, 43, 74, 76, 76, 36, 36, 36, 36, + 36, 63, 74,145, 2, 2, 2, 2, 27, 27, 81, 61, 61, 61, 53, 20, + 144, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21, + 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61, + 40, 40, 86, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,166, 27, 27, + 27, 84, 36, 36, 36, 36, 36, 36, 27, 27, 27, 30, 2, 2, 2, 2, + 79, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76, + 43, 67, 40, 40, 40, 40, 40, 40, 40, 77, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,167, 76, 43, 61,167, 75, + 75,168, 59, 59, 59, 73, 43, 43, 43, 69, 47, 43, 43, 43, 61, 61, + 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 69, 61, 61, 61, 61, 61, + 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, + 43, 43, 43, 69, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 69, 61, + 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 69, 21, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 67, 40, 40, 40, 40, + 7, 7, 7, 7, 7, 7, 7, 70, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 7, 7,169, 16, 16, 43, 43, 43, 67, 40, 40, + 27, 27, 27, 27, 27, 27,140, 27,170, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 81, 61, + 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, + 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, + 21, 23, 26, 26, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, + 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 1, 12, 12, 10, 10, 10, + 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, + 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, + 14, 14, 14, 7, 17, 21, 7, 6, 11, 12, 5, 6, 8, 8, 8, 24, + 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, 22, 20, 27, 28, 1, 29, + 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, + 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, 5, 26, 14, 9, 5, 14, + 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 22, 21, + 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, + 26, 15, 6, 21, 11, 21, 24, 9, 23, 26, 10, 21, 6, 10, 4, 4, + 3, 3, 7, 25, 24, 7, 22, 22, 21, 22, 17, 16, 16, 22, 16, 16, + 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, 21, 14, 7, 15, + 12, 17, 13, 12, 13, 15, 26, 10, 10, 1, 13, 23, 23, 15, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, + 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, + 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 34, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, + 9, 10, 0, 11, 12, 13, 0, 14, 15, 16, 15, 17, 15, 18, 15, 18, + 15, 18, 0, 18, 0, 19, 15, 18, 20, 18, 0, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 0, 0, 0, 33, 0, 0, 34, 0, 0, 35, 0, + 36, 0, 0, 0, 37, 38, 39, 40, 41, 42, 43, 44, 45, 0, 0, 46, + 0, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 49, + 0, 50, 0, 51, 52, 0, 53, 0, 0, 0, 0, 0, 0, 54, 55, 56, + 0, 0, 0, 0, 57, 0, 0, 58, 59, 60, 61, 62, 0, 0, 63, 64, + 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 69, + 0, 70, 0, 0, 71, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, + 73, 0, 0, 0, 0, 0, 74, 0, 0, 75, 0, 0, 0, 76, 77, 0, + 78, 61, 0, 79, 80, 0, 0, 81, 82, 83, 0, 0, 0, 84, 0, 85, + 0, 0, 50, 86, 50, 0, 87, 0, 88, 0, 0, 0, 77, 0, 0, 0, + 89, 90, 0, 91, 92, 93, 94, 0, 0, 0, 0, 0, 50, 0, 0, 0, + 0, 95, 96, 0, 0, 0, 0, 97, 98, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 99, 0, 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,101,102, 0, 0,103, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, + 98, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0, 0, 0,106, + 0,107, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, + 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, + 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, + 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 28, 29, + 0, 0, 0, 30, 31, 32, 0, 0, 31, 0, 0, 33, 31, 0, 0, 0, + 31, 34, 0, 0, 0, 0, 0, 35, 36, 0, 0, 0, 0, 0, 0, 37, + 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 41, 0, 42, + 0, 0, 0, 43, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 46, + 47, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 49, 0, 50, 0, 0, + 0, 0, 51, 0, 0, 0, 0, 52, 0, 53, 0, 0, 0, 0, 54, 55, + 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 49, 0, 59, 60, + 0, 0, 61, 0, 0, 0, 62, 63, 0, 0, 0, 64, 0, 65, 66, 67, + 68, 69, 1, 70, 0, 71, 72, 73, 0, 0, 74, 75, 0, 0, 0, 76, + 0, 0, 1, 1, 0, 0, 77, 0, 0, 78, 0, 0, 0, 0, 74, 79, + 0, 80, 0, 0, 0, 0, 0, 75, 81, 0, 82, 0, 49, 0, 1, 75, + 0, 0, 83, 0, 0, 84, 0, 0, 0, 0, 0, 85, 54, 0, 0, 0, + 0, 0, 0, 86, 87, 0, 0, 81, 0, 0, 31, 0, 0, 88, 0, 0, + 0, 0, 89, 0, 0, 0, 0, 47, 0, 0, 57, 0, 0, 0, 0, 90, + 91, 0, 0, 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 0, 95, 0, + 96, 57, 0, 0, 81, 0, 0, 76, 0, 0, 0, 97, 98, 0, 0, 99, + 100, 0, 0, 0, 0, 0, 0,101, 0, 0,102, 0, 0, 0, 0,103, + 31, 0,104,105,106, 33, 0, 0,107, 0, 0, 0,108, 0, 0, 0, + 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 85, 0, 0, 0, + 0, 0, 54, 0, 0, 0, 0, 49,112, 0, 0, 0, 0,113, 0, 0, + 114, 0, 0, 0, 0,112, 0, 0, 0, 0, 0,115, 0, 0, 0,116, + 0, 0, 0,117, 0,118, 0, 0, 0, 0,119,120,121, 0,122, 0, + 123, 0, 0, 0,124,125,126, 0, 0, 0,127, 0, 0,128, 0, 0, + 129, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 52, 1, 1, 1, 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 56, + 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 60, 0, 61, 0, 0, + 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, + 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 70, 71, 0, + 72, 73, 74, 75, 76, 77, 0, 0, 0, 78, 0, 0, 0, 79, 80, 0, + 0, 0, 0, 47, 0, 0, 0, 49, 0, 63, 0, 0, 64, 0, 0, 81, + 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, 84, 0, 63, 0, 0, 0, + 0, 49, 1, 85, 1, 54, 15, 86, 84, 0, 0, 0, 0, 56, 0, 0, + 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 88, 0, 0, + 87, 0, 0, 0, 0, 79, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47, + 0, 59, 50, 0, 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, + 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0, 0, 0, 0, 19, + 50, 0, 0, 0, 0, 0, 0, 62, 0, 0,101,102, 0, 0,103, 0, + 0, 1, 1, 50, 0, 0, 0, 38, 0, 64, 0, 0, 0, 0, 0, 63, + 0, 0, 52, 69, 62, 0, 0, 0, 79, 0, 0, 0,104,105, 59, 38, + 81, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 38, 89, 0, + 0, 0, 0,107, 0, 0,108, 62, 0,109, 0, 0, 0, 1, 0, 0, + 0, 0, 19, 59, 0,110, 14, 54, 0, 0,111, 0, 89, 0, 0, 0, + 62, 63, 0, 0, 63, 0, 88, 0, 0,111, 0, 0, 0, 0,112, 0, + 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0, 64, 88, 0, 0, + 113, 0, 0, 0, 56, 0, 0, 0, 0,113, 0, 0, 0, 0, 62, 0, + 0, 0, 0, 80, 0, 62, 0, 0, 0, 0, 57, 0, 88,114, 0, 0, + 8, 91, 0, 0, 1, 89, 0, 0,115, 0, 0, 0, 0, 0, 0,116, + 0,117,118,119,120, 0, 52, 4,121, 49, 23, 0, 0, 0, 38, 50, + 38, 59, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1, 48,104, 89, 0, + 0, 0, 0, 1, 4,121, 0, 0, 0, 1,122, 0, 0, 0, 0, 0, + 230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, + 220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, + 1,220,220,220,220,230,230,230,230,240,230,220,220,220,230,230, + 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, + 233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230, + 230,230,220,230,230,230,222,220,230,230,220,220,230,222,228,230, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, + 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, + 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, + 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230, + 230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220, + 220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, + 0,230,230,230,230,230, 0, 0, 0,220,220,220, 0, 0, 0,220, + 230,230, 0,220,230,220,220,220, 27, 28, 29,230, 7, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, + 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, + 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, + 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, + 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, + 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0, + 230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9, 0, 0, 7, 0, + 230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220, + 202,230,230,230,230,230,232,228,228,220, 0,230,233,220,230,220, + 230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, + 0, 0,218,228,232,222,224,224, 0, 8, 8, 0,230, 0,230,230, + 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, + 0,230,220, 0, 0, 0,220,220, 0, 9, 7, 0, 0, 7, 9, 0, + 0, 0, 9, 7, 9, 9, 0, 0, 6, 6, 0, 0, 0, 0, 1, 0, + 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, + 220,220,220, 0,230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, + 84, 97,135,145, 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, + 3, 3, 5, 3, 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, + 9, 10, 11, 12, 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, + 3, 3, 3, 3, 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, + 23, 3, 3, 3, 3, 3, 3, 3, 24, 3, 3, 3, 3, 3, 3, 3, + 3, 25, 3, 3, 26, 27, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 9, 0, 0, 0, 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, + 19, 20, 21, 22, 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, + 0, 31, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, + 32, 33, 9, 34, 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, + 42, 43, 44, 31, 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, + 0, 0, 0, 0, 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, + 47, 47, 50, 51, 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, + 6, 55, 0, 14, 19, 1, 0, 0, 0, 19, 56, 31, 0, 0, 0, 0, + 0, 0, 0, 57, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 58, 59, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 8, 9, + 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, 17, 0, + 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, 0, 0, + 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, 0, 0, + 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, 29, 30, + 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, 5, 0, + 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, 34, 1, + 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, 0, 0, + 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, 40, 0, + 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, 1, 8, + 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, 1, 44, + 0, 0, 0, 45, 8, 9, 1, 0, 1, 0, 1, 1, 8, 21, 21, 9, + 0, 4, 5, 8, 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, + 11, 11, 11, 12, 12, 12, 12, 13, 14, 15, 16, 17, 18, 12, 19, 12, + 20, 12, 12, 12, 12, 21, 22, 22, 22, 23, 12, 12, 12, 12, 24, 25, + 12, 12, 26, 27, 28, 29, 30, 31, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 32, 12, 33, 7, 7, 34, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 35, 0, 0, 1, 2, 2, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, + 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, 53, 54, 55, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 56, 56, 56, 56, + 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, + 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, 95, 96, 97, 87, 98, + 99,100,101,102,103, 87,104,104,104, 87,105,106,107,108,109,110, + 111,112,113,114,115, 87, 89,116,117,118,119,120,121,122,123,124, + 125, 87,126,127, 87,128,129,130,131, 87,132,133,134,135,136,137, + 87, 87,138,139,140,141, 87,142, 87,143,144,144,144,144,144,144, + 144,144,144,144,144, 87, 87, 87, 87, 87,145,145,145,145,145,145, + 145,145,145, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87,146,146,146,146,146, 87, 87, 87,147,147,147,147,148,149, + 150,150, 87, 87, 87, 87,151,151,152,153,154,154,154,154,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154, + 155,155,155,155,154, 87, 87, 87, 87, 87,156,157,158,159,159,159, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, + 87, 87,160,161, 87, 87, 87, 87, 87, 87, 56, 56,162,163, 51, 56, + 56, 87, 56, 56, 56, 56, 56, 56, 56, 56,164,164,164,164,164,164, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,165, 87,166, 87, 87,167, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,168,168,169, 87, 87, 87, + 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, 56, 56, 56,170, 87, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62, + 62, 62, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62, + 62, 87, 87, 87, 87, 87, 87, 87, 87, 87, 56, 87,171,171, 0, 1, + 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 1, 2, + 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, + 8, 9, 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 13, 13, + 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 16, 16, + 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 19, 20, 21, 21, 22, 23, + 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, 26, 26, 26, 26, 26, 21, + 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 26, 26, 21, 21, 21, 21, 21, 21, 31, 21, 32, 32, 32, 32, 32, 33, + 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, + 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, + 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, + 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, + 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, + 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 52, 52, + 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 57, 57, 57, 57, + 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, 64, 64, + 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 55, 55, + 55, 55, 67, 67, 67, 67, 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, + 64, 64, 70, 70, 71, 71, 71, 71, 71, 71, 71, 71, 71, 8, 8, 8, + 8, 8, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, + 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, 73, 77, + 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, 8, 8, + 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, + 0, 86, 0, 4, 0, 0, 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, + 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, + 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, 50, 50, 93, 93, 93, + 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, + 99, 99, 99,100,101,102,102,102,102,103,104,104,104,105, 52, 52, + 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, 52, 0, 0, 0, 0, + 52,106, 0, 0, 0, 0, 0,102,102,107,102,102,102,102,102,108, + 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109,109,109,109,109,109, + 109,109,109,109,109,109,109,110,110,110,111,111,111,111,111,111, + 111,111,111,111,111,111, 13, 13, 13, 13, 13, 13,112,112,112,112, + 112,112, 0, 0,113, 4, 4, 4, 4, 4,114, 4, 4, 4, 4, 4, + 4, 4,115,115,115, 0,116,116,116,116,117,117,117,117,117,117, + 32, 32,118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, + 49, 49,123,123,123,123,123,123, 49, 49,124,124,124,124,124,124, + 125,125, 53, 53, 53, 4, 4,126,127, 54, 54, 54, 54, 54,125,125, + 125,125,128,128,128,128,128,128,128,128, 4,129, 18, 18, 18, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,130, 0, 21, + 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, + 21,132, 0, 0, 1, 2, 1, 2,133,101,102,134, 52, 52, 52, 52, + 0, 0,135,135,135,135,135,135,135,135, 0, 0, 0, 0, 11, 11, + 11, 11, 11, 0, 11, 11, 11, 0, 0,136,137,137,138,138,138,138, + 139, 0,140,140,140,141,141,142,142,142,143,143,144,144,144,144, + 144,144,145,145,145,145,145,146,146,146,147,147,147,148,148,148, + 148,148,149,149,149,150,150,150,150,150,151,151,151,151,151,151, + 151,151,152,152,152,152,153,153,154,154,155,155,155,155,155,155, + 156,156,157,157,158,158,158,158,158,158,159,159,160,160,160,160, + 160,160,161,161,161,161,161,161,162,162,163,163,163,163,164,164, + 164,164,165,165,165,165,166,166,167,167,168,168,168,168,168,168, + 168,168,169,169,169,169,169,169,169,169,170,170,170,170,170,170, + 170,170,171,171,171,171,171,171,171,171,172,172,172,172,172,172, + 172,172,173,173,173,174,174,174,174,174,175,175,175,175,175,175, + 176,176,177,177,177,177,177,177,177,177,178,178,178,178,178,179, + 179,179,180,180,180,180,180,181,181,181,182,182,182,182,182,182, + 183, 43,184,184,184,184,184,184,184,184,185,185,185,186,186,186, + 186,186,187,187,187,188,187,187,187,187,189,189,189,189,189,189, + 189,189,190,190,190,190,190,190,190,190,191,191,191,191,191,191, + 191,191,192,192,192,192,192,192, 66, 66,193,193,193,193,193,193, + 193,193,194,194,194,194,194,194,194,194,195,195,195,195,195,195, + 195,195,196,196,196,196,196,196,196,196,197,197,197,197,197,197, + 197,197,198,198,198,198,198,198,198,198,199,199,199,199,199,200, + 200,200,200,200,200,200,201,201,201,201,202,202,202,202,202,202, + 202,203,203,203,203,203,203,203,203,203,204,204,204,204,204,204, + 205,205,205,205,205,205,205,205,205,205,206,206,206,206,206,206, + 206,206,110,110,110,110, 39, 39, 39, 39,207,207,207,207,207,207, + 207,207,208,208,208,208,208,208,208,208,209,209,209,209,209,209, + 209,209,112,112,112,112,112,112,112,112,112,112,112,112,210,210, + 210,210,211,211,211,211,211,211,211,211,212,212,212,212,212,212, + 212,212,213,213,213,213,213,213,213,213,214,214,214,214,214,214, + 214,214,214,214,214,214,214,214,215, 94,216,216,216,216,216,216, + 216,216,217,217,217,217,217,217,217,217,218, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 219,220,220,220,220,220,220,220,220,220,221,221,221,221,221,221, + 221,221,221,221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 222,223,224, 0,225, 0, 0, 0, 0, 0,226,226,226,226,226,226, + 226,226, 91, 91, 91, 91, 91, 91, 91, 91,227,227,227,227,227,227, + 227,227,228,228,228,228,228,228,228,228,229,229,229,229,229,229, + 229,229,230,230,230,230,230,230,230,230,231, 0, 0, 0, 0, 0, + 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 1, 2, + 2, 2, 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, + 8, 10, 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, + 14, 14, 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, + 19, 19, 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, + 20, 20, 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, + 20, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, + 30, 30, 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, + 33, 33, 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, + 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, + 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, + 46, 47, 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, + 52, 52, 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, + 60, 60, 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, + 0, 0, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, + 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, + 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, + 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, + 7, 7, 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, + 2, 89, 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, + 0, 86, 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, + 0, 4, 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, + 99, 99,100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0, + 100, 0,105,106,106,106,106,106,106,106,106,106,107,105,108,109, + 109,109,109,109,109,109,109,109,110,108,111,111,111,111,112, 55, + 55, 55, 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114, + 114,114,115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, + 2, 2, 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120, + 120,120,121,121,121,121,121,121,121,122,123,123,123,123,124,124, + 124,124,124,124,124,125,126,126,126,126,127,127,127,127,128,128, + 128,128, 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, + 17, 18, 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135, + 109,109,109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139, + 139,139,140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142, + 142,142,143,143,143,143,144,144,144,144,145,145,145,145,146,146, + 146,146,147,147,147,147,148,148,148,148,149,149,149,149,150,150, + 150,150,151,151,151,151,152,152,152,152,153,153,153,153,154,154, + 154,154,155,155,155,155,156,156,156,156,157,157,157,157,158,158, + 158,158,159,159,159,159,160,160,160,160,161,161,161,161,162,162, + 162,162,163,163,163,163,164,164,164,164,165,165,165,165,166,166, + 166,166,167,167,167,167,168,168,168,168,169,169,169,169,170,170, + 170,170,171,171,171,171,172,172,172,172,173,173,173,173,174,174, + 174,174,175,175,175,175,176,176,176,176,177,177,177,177,178,178, + 178,178,179,179,179,179,180,180,180,180,181,181,181,181,182,182, + 182,182,183,183,183,183,184, 45, 45, 45,185,185,185,185,186,186, + 186,186,187,187,187,187,188,188,188,188,188,188,189,188,190,190, + 190,190,191,191,191,191,192,192,192,192,193,193,193,193,194,194, + 194,194,195,195,195,195,196,196,196,196,197,197,197,197,198,198, + 198,198,199,199,199,199,200,200,200,200,201,201,201,201,202,202, + 202,202,203,203,203,203,204,204,204,204,205,205,205,205,206,206, + 206,206,207,207,207,207,208,208,208,208,209,209,209,209,210,210, + 210,210,211,211,211,211,212,212,212,212,213,213,213,213,214,214, + 214,214,215,215,215,215,216,217,217,217,218,218,218,218,217,217, + 217,217,219,106,106,106,106,109,109,109,220,220,220,220,221,221, + 221,221, 0,222, 86, 0, 0, 0,222, 7, 82,138, 7, 0, 0, 0, + 223, 86,224,224,224,224,225,225,225,225,226,226,226,226,227,227, + 227,227,228,228,228,228,229, 0, 0, 0, 0, 0, 0, 0, 0, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, + 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, + 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, + 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, + 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, + 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, + 64, 64, 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, + 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, + 22, 22, 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, + 36, 36, 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, + 29, 29, 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, + 0, 0, 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, + 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, + 52, 52, 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, + 62, 62, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, + 73, 73, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, + 0, 0, 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, + 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, + 19, 9, 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, + 27, 27, 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, + 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, + 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, + 12, 0, 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, + 79, 79, 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, + 84, 0, 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, + 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, + 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, + 49, 49, 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, + 42, 42, 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, + 59, 59, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135, + 135,135,106,106,106,106,104,104,104,104,110,110,110,110, 47, 47, + 47, 47, 81, 81, 81, 81,120,120,120,120,116,116,116,116,128,128, + 128,128, 66, 66, 66, 66, 72, 72, 72, 72, 98, 98, 98, 98, 97, 97, + 97, 97, 57, 57, 57, 57, 88, 88, 88, 88,117,117,117,117,112,112, + 112,112, 78, 78, 78, 78, 83, 83, 83, 83, 82, 82, 82, 82,122,122, + 122,122, 89, 89, 89, 89,130,130,130,130,144,144,144,144,156,156, + 156,156,147,147,147,147,148,148,148,148,153,153,153,153,149,149, + 149,149, 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, 96, 96, + 96, 96,111,111,111,111,100,100,100,100,100, 36, 36, 36,108,108, + 108,108,129,129,129,129,109,109,109,109,107,107,107,107,107,107, + 107, 1,137,137,137,137,124,124,124,124,123,123,123,123,114,114, + 114,114,102,102,102,102,126,126,126,126,142,142,142,142,125,125, + 125,125,154,154,154,154,150,150,150,150,141,141,141,141,140,140, + 140,140,121,121,121,121,133,133,133,133,134,134,134,134,138,138, + 138,138,143,143,143,143,145,145,145,145, 63, 63, 63, 63, 80, 80, + 80, 80,127,127,127,127,115,115,115,115,103,103,103,103,119,119, + 119,119,146,146,146,146, 99, 99, 99, 99,136,139, 0, 0,155,155, + 155,155,136,136,136,136, 17, 15, 15, 15,139,139,139,139,105,105, + 105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151,151, + 151,151,152,152,152,152,113,113,113,113,132,132,132,132, 15, 0, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, + 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, + 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, + 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, + 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, + 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, + 0, 0,164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,169,170, 0, 0, 0, 0,171,172, 0, 0, 0, + 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, + 205,206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, +}; +static const uint16_t +_hb_ucd_u16[4848] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 47, 47, 47, 47, 47, 68, 69, 32, + 70, 47, 47, 71, 72, 73, 74, 75, 76, 47, 47, 77, 78, 79, 80, 81, + 82, 47, 47, 83, 84, 85, 86, 87, 82, 47, 47, 77, 88, 47, 80, 89, + 90, 47, 47, 91, 92, 93, 80, 94, 95, 47, 47, 96, 97, 98, 99, 100, + 101, 47, 47, 102, 103, 104, 80, 105, 106, 47, 47, 91, 107, 108, 80, 109, + 110, 47, 47, 111, 112, 113, 80, 114, 90, 47, 47, 47, 115, 116, 99, 117, + 47, 47, 47, 118, 119, 120, 66, 66, 47, 47, 47, 121, 122, 123, 47, 47, + 124, 125, 126, 127, 47, 47, 47, 128, 129, 32, 32, 130, 131, 132, 66, 66, + 47, 47, 133, 134, 120, 135, 136, 137, 138, 139, 9, 9, 9, 11, 11, 140, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 141, 142, 143, + 47, 144, 9, 9, 9, 9, 9, 145, 146, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 147, 47, 148, 149, 47, 47, 47, 47, 150, 151, + 47, 152, 47, 153, 47, 152, 47, 152, 47, 47, 47, 154, 155, 156, 157, 143, + 158, 157, 47, 47, 159, 47, 47, 47, 160, 47, 161, 47, 47, 47, 47, 47, + 47, 47, 162, 163, 164, 47, 47, 47, 47, 47, 47, 47, 47, 165, 144, 144, + 47, 166, 47, 47, 47, 167, 168, 169, 157, 157, 170, 171, 32, 32, 32, 32, + 172, 47, 47, 173, 174, 120, 175, 176, 177, 47, 178, 61, 47, 47, 179, 180, + 47, 47, 181, 182, 183, 61, 47, 184, 11, 9, 9, 9, 66, 185, 186, 187, + 11, 11, 188, 27, 27, 27, 189, 190, 11, 191, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 192, 13, 13, 13, 13, 13, 13, + 193, 193, 193, 193, 193, 194, 193, 11, 195, 195, 195, 196, 197, 198, 198, 197, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 27, 208, 208, 208, 209, 210, 32, + 211, 212, 213, 214, 215, 143, 216, 216, 217, 218, 219, 144, 220, 221, 144, 222, + 223, 223, 223, 223, 223, 223, 223, 223, 224, 144, 225, 144, 144, 144, 144, 226, + 144, 227, 223, 228, 144, 229, 230, 144, 144, 144, 144, 144, 144, 144, 143, 143, + 143, 231, 144, 144, 144, 144, 232, 143, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 233, 234, 144, 144, 235, 144, 144, 144, 144, 144, 144, 236, 144, + 144, 144, 144, 144, 144, 144, 237, 238, 143, 239, 144, 144, 240, 223, 241, 223, + 242, 243, 223, 223, 223, 244, 223, 245, 144, 144, 144, 223, 246, 144, 144, 144, + 9, 9, 9, 11, 11, 11, 247, 248, 13, 13, 13, 13, 13, 13, 249, 250, + 11, 11, 11, 47, 47, 47, 251, 252, 47, 47, 47, 47, 47, 47, 32, 32, + 253, 254, 255, 256, 257, 258, 66, 66, 259, 260, 261, 262, 263, 47, 47, 47, + 47, 264, 146, 47, 47, 47, 47, 265, 47, 266, 47, 47, 144, 144, 144, 47, + 144, 144, 267, 144, 268, 269, 144, 144, 267, 144, 144, 269, 144, 144, 144, 144, + 47, 47, 47, 47, 144, 144, 144, 144, 47, 270, 47, 47, 47, 47, 47, 47, + 47, 144, 144, 144, 144, 47, 47, 184, 271, 47, 61, 47, 13, 13, 272, 273, + 13, 274, 47, 47, 47, 47, 275, 276, 31, 277, 278, 279, 13, 13, 13, 280, + 281, 282, 283, 284, 285, 11, 11, 286, 287, 47, 288, 289, 47, 47, 47, 290, + 291, 47, 47, 292, 293, 157, 32, 294, 61, 47, 295, 47, 296, 297, 47, 47, + 70, 47, 47, 298, 299, 300, 301, 61, 47, 47, 302, 303, 304, 305, 47, 306, + 47, 47, 47, 307, 58, 308, 309, 310, 47, 47, 47, 11, 11, 311, 312, 11, + 11, 11, 11, 11, 47, 47, 313, 157, 314, 314, 314, 314, 314, 314, 314, 314, + 315, 315, 315, 315, 315, 315, 315, 315, 11, 316, 317, 47, 47, 47, 47, 47, + 47, 47, 47, 318, 31, 319, 47, 47, 47, 47, 47, 320, 321, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 322, 32, 323, 32, 324, 325, 326, 327, 47, + 47, 47, 47, 47, 47, 47, 47, 328, 329, 2, 3, 4, 5, 330, 331, 332, + 47, 333, 47, 47, 47, 47, 334, 335, 336, 143, 143, 337, 216, 216, 216, 338, + 339, 144, 144, 144, 144, 144, 144, 340, 341, 341, 341, 341, 341, 341, 341, 341, + 47, 47, 47, 47, 47, 47, 342, 143, 47, 47, 343, 47, 344, 47, 47, 60, + 47, 345, 47, 47, 47, 346, 216, 216, 9, 9, 145, 11, 11, 47, 47, 47, + 47, 47, 157, 9, 9, 145, 11, 11, 47, 47, 47, 47, 47, 47, 345, 66, + 47, 47, 47, 47, 47, 347, 47, 348, 47, 47, 349, 143, 143, 143, 47, 350, + 47, 351, 47, 345, 66, 66, 66, 66, 47, 47, 47, 352, 143, 143, 143, 143, + 353, 47, 47, 354, 143, 66, 47, 355, 47, 356, 143, 143, 357, 47, 358, 66, + 47, 47, 47, 359, 47, 360, 47, 360, 47, 359, 142, 143, 143, 143, 143, 143, + 9, 9, 9, 9, 11, 11, 11, 361, 47, 47, 362, 157, 157, 157, 157, 157, + 143, 143, 143, 143, 143, 143, 143, 143, 47, 47, 363, 47, 47, 47, 47, 47, + 47, 356, 364, 47, 60, 365, 66, 66, 47, 47, 47, 47, 366, 143, 47, 47, + 367, 47, 47, 354, 368, 369, 370, 371, 177, 47, 47, 372, 373, 47, 47, 157, + 95, 47, 374, 375, 376, 47, 47, 377, 177, 47, 47, 378, 379, 380, 381, 143, + 47, 47, 382, 383, 32, 32, 32, 32, 47, 47, 359, 47, 47, 384, 169, 157, + 90, 47, 47, 111, 385, 386, 387, 32, 47, 47, 47, 388, 389, 390, 47, 47, + 47, 47, 47, 391, 392, 157, 157, 157, 47, 47, 393, 394, 395, 396, 32, 32, + 47, 47, 47, 397, 398, 157, 66, 66, 47, 47, 399, 400, 157, 157, 157, 157, + 47, 141, 401, 402, 144, 144, 144, 144, 47, 47, 382, 403, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 126, 404, 47, 47, 47, 405, 406, 157, 157, 157, + 47, 47, 47, 47, 47, 407, 408, 409, 410, 47, 47, 411, 412, 413, 47, 47, + 414, 415, 66, 66, 47, 47, 47, 47, 47, 47, 393, 416, 417, 126, 143, 418, + 47, 152, 419, 420, 32, 32, 32, 32, 47, 47, 47, 353, 421, 157, 47, 47, + 422, 423, 157, 157, 157, 157, 157, 157, 47, 47, 47, 47, 47, 47, 47, 424, + 47, 47, 47, 47, 143, 425, 426, 427, 216, 216, 216, 216, 216, 216, 216, 66, + 47, 47, 47, 205, 205, 205, 205, 205, 47, 47, 47, 47, 47, 47, 300, 66, + 47, 47, 47, 47, 47, 47, 47, 428, 47, 47, 47, 429, 430, 431, 432, 47, + 9, 9, 9, 9, 9, 9, 11, 11, 143, 433, 66, 66, 66, 66, 66, 66, + 47, 47, 47, 47, 384, 434, 409, 409, 435, 436, 27, 27, 27, 27, 437, 409, + 47, 438, 205, 205, 205, 205, 205, 205, 144, 144, 144, 144, 144, 144, 439, 440, + 441, 144, 442, 144, 144, 144, 144, 144, 144, 144, 144, 144, 443, 144, 144, 144, + 9, 444, 11, 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, + 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 445, 446, 11, + 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 193, 9, 450, 451, 452, 453, + 11, 454, 9, 455, 456, 457, 458, 11, 459, 9, 460, 11, 461, 157, 157, 157, + 32, 32, 32, 462, 32, 32, 463, 464, 465, 466, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 467, 468, 144, 144, 144, + 47, 47, 47, 47, 47, 47, 469, 470, 47, 47, 47, 47, 349, 32, 32, 32, + 9, 9, 447, 11, 471, 300, 66, 66, 143, 143, 472, 473, 143, 143, 143, 143, + 143, 143, 474, 143, 143, 143, 143, 143, 47, 47, 47, 47, 47, 47, 47, 223, + 475, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 476, + 144, 144, 144, 144, 144, 144, 144, 157, 205, 205, 205, 205, 205, 205, 205, 205, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486, + 1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, + 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, + 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[92] = +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114112u?_hb_ucd_u8[4920+(((_hb_ucd_u8[1104+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>3>>5])<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[6796+(((_hb_ucd_u8[6276+(((_hb_ucd_u8[5844+(((_hb_ucd_u8[5508+(((_hb_ucd_u8[5262+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[7672+(((_hb_ucd_u8[7448+(((_hb_ucd_u8[7352+(((_hb_ucd_b4(7288+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918016u?_hb_ucd_u8[11242+(((_hb_ucd_u8[10314+(((_hb_ucd_u8[8938+(((_hb_ucd_u8[8362+(((_hb_ucd_u8[7912+(u>>2>>2>>3>>4)])<<4)+((u>>2>>2>>3)&15u))])<<3)+((u>>2>>2)&7u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[1536+(((_hb_ucd_u8[12544+(((_hb_ucd_u8[12162+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + +#endif + + +#endif /* HB_UCD_TABLE_HH */ + +/* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc b/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc new file mode 100644 index 0000000000000..f56f05f170781 --- /dev/null +++ b/src/java.desktop/share/native/libharfbuzz/hb-ucd.cc @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2012 Grigori Goronzy + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-machinery.hh" + +#include "hb-ucd-table.hh" + +static hb_unicode_combining_class_t +hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode); +} + +static hb_unicode_general_category_t +hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_general_category_t) _hb_ucd_gc (unicode); +} + +static hb_codepoint_t +hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode + _hb_ucd_bmg (unicode); +} + +static hb_script_t +hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return _hb_ucd_sc_map[_hb_ucd_sc (unicode)]; +} + + +#define SBASE 0xAC00u +#define LBASE 0x1100u +#define VBASE 0x1161u +#define TBASE 0x11A7u +#define SCOUNT 11172u +#define LCOUNT 19u +#define VCOUNT 21u +#define TCOUNT 28u +#define NCOUNT (VCOUNT * TCOUNT) + +static inline bool +_hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) +{ + unsigned si = ab - SBASE; + + if (si >= SCOUNT) + return false; + + if (si % TCOUNT) + { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return true; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return true; + } +} + +static inline bool +_hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) +{ + if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) && + !((a - SBASE) % TCOUNT)) + { + /* LV,T */ + *ab = a + (b - TBASE); + return true; + } + else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) + { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *ab = SBASE + li * NCOUNT + vi * TCOUNT; + return true; + } + else + return false; +} + +static int +_cmp_pair (const void *_key, const void *_item) +{ + uint64_t& a = * (uint64_t*) _key; + uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} +static int +_cmp_pair_11_7_14 (const void *_key, const void *_item) +{ + uint32_t& a = * (uint32_t*) _key; + uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} + +static hb_bool_t +hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_compose_hangul (a, b, ab)) return true; + + hb_codepoint_t u = 0; + + if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) + { + uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); + const uint32_t *v = hb_bsearch (k, + _hb_ucd_dm2_u32_map, + ARRAY_LENGTH (_hb_ucd_dm2_u32_map), + sizeof (*_hb_ucd_dm2_u32_map), + _cmp_pair_11_7_14); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v); + } + else + { + uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); + const uint64_t *v = hb_bsearch (k, + _hb_ucd_dm2_u64_map, + ARRAY_LENGTH (_hb_ucd_dm2_u64_map), + sizeof (*_hb_ucd_dm2_u64_map), + _cmp_pair); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_3 (*v); + } + + if (unlikely (!u)) return false; + *ab = u; + return true; +} + +static hb_bool_t +hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, + void *user_data HB_UNUSED) +{ + if (_hb_ucd_decompose_hangul (ab, a, b)) return true; + + unsigned i = _hb_ucd_dm (ab); + + if (likely (!i)) return false; + i--; + + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) + { + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) + *a = _hb_ucd_dm1_p0_map[i]; + else + { + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); + *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; + } + *b = 0; + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); + + if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) + { + uint32_t v = _hb_ucd_dm2_u32_map[i]; + *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); + *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); + + uint64_t v = _hb_ucd_dm2_u64_map[i]; + *a = HB_CODEPOINT_DECODE3_1 (v); + *b = HB_CODEPOINT_DECODE3_2 (v); + return true; +} + + +#if HB_USE_ATEXIT +static void free_static_ucd_funcs (); +#endif + +static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t +{ + static hb_unicode_funcs_t *create () + { + hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); + + hb_unicode_funcs_set_combining_class_func (funcs, hb_ucd_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + +#if HB_USE_ATEXIT + atexit (free_static_ucd_funcs); +#endif + + return funcs; + } +} static_ucd_funcs; + +#if HB_USE_ATEXIT +static +void free_static_ucd_funcs () +{ + static_ucd_funcs.free_instance (); +} +#endif + +hb_unicode_funcs_t * +hb_ucd_get_unicode_funcs () +{ +#ifdef HB_NO_UCD + return hb_unicode_funcs_get_empty (); +#endif + return static_ucd_funcs.get_unconst (); +} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc b/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc deleted file mode 100644 index d80f6e97b8d67..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn.cc +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "hb.hh" - -#include "hb-machinery.hh" - -#include "ucdn.h" - -static const hb_script_t ucdn_script_translate[] = -{ - HB_SCRIPT_COMMON, - HB_SCRIPT_LATIN, - HB_SCRIPT_GREEK, - HB_SCRIPT_CYRILLIC, - HB_SCRIPT_ARMENIAN, - HB_SCRIPT_HEBREW, - HB_SCRIPT_ARABIC, - HB_SCRIPT_SYRIAC, - HB_SCRIPT_THAANA, - HB_SCRIPT_DEVANAGARI, - HB_SCRIPT_BENGALI, - HB_SCRIPT_GURMUKHI, - HB_SCRIPT_GUJARATI, - HB_SCRIPT_ORIYA, - HB_SCRIPT_TAMIL, - HB_SCRIPT_TELUGU, - HB_SCRIPT_KANNADA, - HB_SCRIPT_MALAYALAM, - HB_SCRIPT_SINHALA, - HB_SCRIPT_THAI, - HB_SCRIPT_LAO, - HB_SCRIPT_TIBETAN, - HB_SCRIPT_MYANMAR, - HB_SCRIPT_GEORGIAN, - HB_SCRIPT_HANGUL, - HB_SCRIPT_ETHIOPIC, - HB_SCRIPT_CHEROKEE, - HB_SCRIPT_CANADIAN_SYLLABICS, - HB_SCRIPT_OGHAM, - HB_SCRIPT_RUNIC, - HB_SCRIPT_KHMER, - HB_SCRIPT_MONGOLIAN, - HB_SCRIPT_HIRAGANA, - HB_SCRIPT_KATAKANA, - HB_SCRIPT_BOPOMOFO, - HB_SCRIPT_HAN, - HB_SCRIPT_YI, - HB_SCRIPT_OLD_ITALIC, - HB_SCRIPT_GOTHIC, - HB_SCRIPT_DESERET, - HB_SCRIPT_INHERITED, - HB_SCRIPT_TAGALOG, - HB_SCRIPT_HANUNOO, - HB_SCRIPT_BUHID, - HB_SCRIPT_TAGBANWA, - HB_SCRIPT_LIMBU, - HB_SCRIPT_TAI_LE, - HB_SCRIPT_LINEAR_B, - HB_SCRIPT_UGARITIC, - HB_SCRIPT_SHAVIAN, - HB_SCRIPT_OSMANYA, - HB_SCRIPT_CYPRIOT, - HB_SCRIPT_BRAILLE, - HB_SCRIPT_BUGINESE, - HB_SCRIPT_COPTIC, - HB_SCRIPT_NEW_TAI_LUE, - HB_SCRIPT_GLAGOLITIC, - HB_SCRIPT_TIFINAGH, - HB_SCRIPT_SYLOTI_NAGRI, - HB_SCRIPT_OLD_PERSIAN, - HB_SCRIPT_KHAROSHTHI, - HB_SCRIPT_BALINESE, - HB_SCRIPT_CUNEIFORM, - HB_SCRIPT_PHOENICIAN, - HB_SCRIPT_PHAGS_PA, - HB_SCRIPT_NKO, - HB_SCRIPT_SUNDANESE, - HB_SCRIPT_LEPCHA, - HB_SCRIPT_OL_CHIKI, - HB_SCRIPT_VAI, - HB_SCRIPT_SAURASHTRA, - HB_SCRIPT_KAYAH_LI, - HB_SCRIPT_REJANG, - HB_SCRIPT_LYCIAN, - HB_SCRIPT_CARIAN, - HB_SCRIPT_LYDIAN, - HB_SCRIPT_CHAM, - HB_SCRIPT_TAI_THAM, - HB_SCRIPT_TAI_VIET, - HB_SCRIPT_AVESTAN, - HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, - HB_SCRIPT_SAMARITAN, - HB_SCRIPT_LISU, - HB_SCRIPT_BAMUM, - HB_SCRIPT_JAVANESE, - HB_SCRIPT_MEETEI_MAYEK, - HB_SCRIPT_IMPERIAL_ARAMAIC, - HB_SCRIPT_OLD_SOUTH_ARABIAN, - HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, - HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, - HB_SCRIPT_OLD_TURKIC, - HB_SCRIPT_KAITHI, - HB_SCRIPT_BATAK, - HB_SCRIPT_BRAHMI, - HB_SCRIPT_MANDAIC, - HB_SCRIPT_CHAKMA, - HB_SCRIPT_MEROITIC_CURSIVE, - HB_SCRIPT_MEROITIC_HIEROGLYPHS, - HB_SCRIPT_MIAO, - HB_SCRIPT_SHARADA, - HB_SCRIPT_SORA_SOMPENG, - HB_SCRIPT_TAKRI, - HB_SCRIPT_UNKNOWN, - HB_SCRIPT_BASSA_VAH, - HB_SCRIPT_CAUCASIAN_ALBANIAN, - HB_SCRIPT_DUPLOYAN, - HB_SCRIPT_ELBASAN, - HB_SCRIPT_GRANTHA, - HB_SCRIPT_KHOJKI, - HB_SCRIPT_KHUDAWADI, - HB_SCRIPT_LINEAR_A, - HB_SCRIPT_MAHAJANI, - HB_SCRIPT_MANICHAEAN, - HB_SCRIPT_MENDE_KIKAKUI, - HB_SCRIPT_MODI, - HB_SCRIPT_MRO, - HB_SCRIPT_NABATAEAN, - HB_SCRIPT_OLD_NORTH_ARABIAN, - HB_SCRIPT_OLD_PERMIC, - HB_SCRIPT_PAHAWH_HMONG, - HB_SCRIPT_PALMYRENE, - HB_SCRIPT_PAU_CIN_HAU, - HB_SCRIPT_PSALTER_PAHLAVI, - HB_SCRIPT_SIDDHAM, - HB_SCRIPT_TIRHUTA, - HB_SCRIPT_WARANG_CITI, - HB_SCRIPT_AHOM, - HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, - HB_SCRIPT_HATRAN, - HB_SCRIPT_MULTANI, - HB_SCRIPT_OLD_HUNGARIAN, - HB_SCRIPT_SIGNWRITING, - HB_SCRIPT_ADLAM, - HB_SCRIPT_BHAIKSUKI, - HB_SCRIPT_MARCHEN, - HB_SCRIPT_NEWA, - HB_SCRIPT_OSAGE, - HB_SCRIPT_TANGUT, - HB_SCRIPT_MASARAM_GONDI, - HB_SCRIPT_NUSHU, - HB_SCRIPT_SOYOMBO, - HB_SCRIPT_ZANABAZAR_SQUARE, - HB_SCRIPT_DOGRA, - HB_SCRIPT_GUNJALA_GONDI, - HB_SCRIPT_HANIFI_ROHINGYA, - HB_SCRIPT_MAKASAR, - HB_SCRIPT_MEDEFAIDRIN, - HB_SCRIPT_OLD_SOGDIAN, - HB_SCRIPT_SOGDIAN, -}; - -static hb_unicode_combining_class_t -hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode); -} - -static hb_unicode_general_category_t -hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return (hb_unicode_general_category_t)ucdn_get_general_category(unicode); -} - -static hb_codepoint_t -hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return ucdn_mirror(unicode); -} - -static hb_script_t -hb_ucdn_script(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t unicode, - void *user_data HB_UNUSED) -{ - return ucdn_script_translate[ucdn_get_script(unicode)]; -} - -static hb_bool_t -hb_ucdn_compose(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab, - void *user_data HB_UNUSED) -{ - return ucdn_compose(ab, a, b); -} - -static hb_bool_t -hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs HB_UNUSED, - hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b, - void *user_data HB_UNUSED) -{ - return ucdn_decompose(ab, a, b); -} - - -#if HB_USE_ATEXIT -static void free_static_ucdn_funcs (); -#endif - -static struct hb_ucdn_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t -{ - static hb_unicode_funcs_t *create () - { - hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr); - - hb_unicode_funcs_set_combining_class_func (funcs, hb_ucdn_combining_class, nullptr, nullptr); - hb_unicode_funcs_set_general_category_func (funcs, hb_ucdn_general_category, nullptr, nullptr); - hb_unicode_funcs_set_mirroring_func (funcs, hb_ucdn_mirroring, nullptr, nullptr); - hb_unicode_funcs_set_script_func (funcs, hb_ucdn_script, nullptr, nullptr); - hb_unicode_funcs_set_compose_func (funcs, hb_ucdn_compose, nullptr, nullptr); - hb_unicode_funcs_set_decompose_func (funcs, hb_ucdn_decompose, nullptr, nullptr); - - hb_unicode_funcs_make_immutable (funcs); - -#if HB_USE_ATEXIT - atexit (free_static_ucdn_funcs); -#endif - - return funcs; - } -} static_ucdn_funcs; - -#if HB_USE_ATEXIT -static -void free_static_ucdn_funcs () -{ - static_ucdn_funcs.free_instance (); -} -#endif - -extern "C" HB_INTERNAL -hb_unicode_funcs_t * -hb_ucdn_get_unicode_funcs (); - -hb_unicode_funcs_t * -hb_ucdn_get_unicode_funcs () -{ - return static_ucdn_funcs.get_unconst (); -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c deleted file mode 100644 index 30747fea2518c..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include "ucdn.h" - -typedef struct { - unsigned char category; - unsigned char combining; - unsigned char bidi_class; - unsigned char east_asian_width; - unsigned char script; - unsigned char linebreak_class; -} UCDRecord; - -typedef struct { - unsigned short from, to; -} MirrorPair; - -typedef struct { - unsigned short from, to; - unsigned char type; -} BracketPair; - -typedef struct { - unsigned int start; - short count, index; -} Reindex; - -#include "ucdn_db.h" - -/* constants required for Hangul (de)composition */ -#define SBASE 0xAC00 -#define LBASE 0x1100 -#define VBASE 0x1161 -#define TBASE 0x11A7 -#define SCOUNT 11172 -#define LCOUNT 19 -#define VCOUNT 21 -#define TCOUNT 28 -#define NCOUNT (VCOUNT * TCOUNT) - -static const UCDRecord *get_ucd_record(uint32_t code) -{ - int index, offset; - - if (code >= 0x110000) - index = 0; - else { - index = index0[code >> (SHIFT1+SHIFT2)] << SHIFT1; - offset = (code >> SHIFT2) & ((1<= 0x110000) - index = 0; - else { - index = decomp_index0[code >> (DECOMP_SHIFT1+DECOMP_SHIFT2)] - << DECOMP_SHIFT1; - offset = (code >> DECOMP_SHIFT2) & ((1<start < rb->start) - return -1; - else if (ra->start > (rb->start + rb->count)) - return 1; - else - return 0; -} - -static int get_comp_index(uint32_t code, const Reindex *idx, size_t len) -{ - Reindex *res; - Reindex r = {0, 0, 0}; - r.start = code; - res = (Reindex *) bsearch(&r, idx, len, sizeof(Reindex), compare_reindex); - - if (res != NULL) - return res->index + (code - res->start); - else - return -1; -} - -static int compare_mp(const void *a, const void *b) -{ - MirrorPair *mpa = (MirrorPair *)a; - MirrorPair *mpb = (MirrorPair *)b; - return mpa->from - mpb->from; -} - -static int compare_bp(const void *a, const void *b) -{ - BracketPair *bpa = (BracketPair *)a; - BracketPair *bpb = (BracketPair *)b; - return bpa->from - bpb->from; -} - -static BracketPair *search_bp(uint32_t code) -{ - BracketPair bp = {0,0,2}; - BracketPair *res; - - bp.from = code; - res = (BracketPair *) bsearch(&bp, bracket_pairs, BIDI_BRACKET_LEN, - sizeof(BracketPair), compare_bp); - return res; -} - -static int hangul_pair_decompose(uint32_t code, uint32_t *a, uint32_t *b) -{ - int si = code - SBASE; - - if (si < 0 || si >= SCOUNT) - return 0; - - if (si % TCOUNT) { - /* LV,T */ - *a = SBASE + (si / TCOUNT) * TCOUNT; - *b = TBASE + (si % TCOUNT); - return 3; - } else { - /* L,V */ - *a = LBASE + (si / NCOUNT); - *b = VBASE + (si % NCOUNT) / TCOUNT; - return 2; - } -} - -static int hangul_pair_compose(uint32_t *code, uint32_t a, uint32_t b) -{ - if (a >= SBASE && a < (SBASE + SCOUNT) && b >= TBASE && b < (TBASE + TCOUNT)) { - /* LV,T */ - *code = a + (b - TBASE); - return 3; - } else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) { - /* L,V */ - int li = a - LBASE; - int vi = b - VBASE; - *code = SBASE + li * NCOUNT + vi * TCOUNT; - return 2; - } else { - return 0; - } -} - -static uint32_t decode_utf16(const unsigned short **code_ptr) -{ - const unsigned short *code = *code_ptr; - - if (code[0] < 0xd800 || code[0] > 0xdc00) { - *code_ptr += 1; - return (uint32_t)code[0]; - } else { - *code_ptr += 2; - return 0x10000 + ((uint32_t)code[1] - 0xdc00) + - (((uint32_t)code[0] - 0xd800) << 10); - } -} - -const char *ucdn_get_unicode_version(void) -{ - return UNIDATA_VERSION; -} - -int ucdn_get_combining_class(uint32_t code) -{ - return get_ucd_record(code)->combining; -} - -int ucdn_get_east_asian_width(uint32_t code) -{ - return get_ucd_record(code)->east_asian_width; -} - -int ucdn_get_general_category(uint32_t code) -{ - return get_ucd_record(code)->category; -} - -int ucdn_get_bidi_class(uint32_t code) -{ - return get_ucd_record(code)->bidi_class; -} - -int ucdn_get_mirrored(uint32_t code) -{ - return ucdn_mirror(code) != code; -} - -int ucdn_get_script(uint32_t code) -{ - return get_ucd_record(code)->script; -} - -int ucdn_get_linebreak_class(uint32_t code) -{ - return get_ucd_record(code)->linebreak_class; -} - -int ucdn_get_resolved_linebreak_class(uint32_t code) -{ - const UCDRecord *record = get_ucd_record(code); - - switch (record->linebreak_class) - { - case UCDN_LINEBREAK_CLASS_AI: - case UCDN_LINEBREAK_CLASS_SG: - case UCDN_LINEBREAK_CLASS_XX: - return UCDN_LINEBREAK_CLASS_AL; - - case UCDN_LINEBREAK_CLASS_SA: - if (record->category == UCDN_GENERAL_CATEGORY_MC || - record->category == UCDN_GENERAL_CATEGORY_MN) - return UCDN_LINEBREAK_CLASS_CM; - return UCDN_LINEBREAK_CLASS_AL; - - case UCDN_LINEBREAK_CLASS_CJ: - return UCDN_LINEBREAK_CLASS_NS; - - case UCDN_LINEBREAK_CLASS_CB: - return UCDN_LINEBREAK_CLASS_B2; - - case UCDN_LINEBREAK_CLASS_NL: - return UCDN_LINEBREAK_CLASS_BK; - - default: - return record->linebreak_class; - } -} - -uint32_t ucdn_mirror(uint32_t code) -{ - MirrorPair mp = {0}; - MirrorPair *res; - - mp.from = code; - res = (MirrorPair *) bsearch(&mp, mirror_pairs, BIDI_MIRROR_LEN, - sizeof(MirrorPair), compare_mp); - - if (res == NULL) - return code; - else - return res->to; -} - -uint32_t ucdn_paired_bracket(uint32_t code) -{ - BracketPair *res = search_bp(code); - if (res == NULL) - return code; - else - return res->to; -} - -int ucdn_paired_bracket_type(uint32_t code) -{ - BracketPair *res = search_bp(code); - if (res == NULL) - return UCDN_BIDI_PAIRED_BRACKET_TYPE_NONE; - else - return res->type; -} - -int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b) -{ - const unsigned short *rec; - int len; - - if (hangul_pair_decompose(code, a, b)) - return 1; - - rec = get_decomp_record(code); - len = rec[0] >> 8; - - if ((rec[0] & 0xff) != 0 || len == 0) - return 0; - - rec++; - *a = decode_utf16(&rec); - if (len > 1) - *b = decode_utf16(&rec); - else - *b = 0; - - return 1; -} - -int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b) -{ - int l, r, index, indexi, offset; - - if (hangul_pair_compose(code, a, b)) - return 1; - - l = get_comp_index(a, nfc_first, sizeof(nfc_first) / sizeof(Reindex)); - r = get_comp_index(b, nfc_last, sizeof(nfc_last) / sizeof(Reindex)); - - if (l < 0 || r < 0) - return 0; - - indexi = l * TOTAL_LAST + r; - index = comp_index0[indexi >> (COMP_SHIFT1+COMP_SHIFT2)] << COMP_SHIFT1; - offset = (indexi >> COMP_SHIFT2) & ((1<> 8; - - if (len == 0) - return 0; - - rec++; - for (i = 0; i < len; i++) - decomposed[i] = decode_utf16(&rec); - - return len; -} diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h deleted file mode 100644 index 05d46d2b1be90..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn.h +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (C) 2012 Grigori Goronzy - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UCDN_H -#define UCDN_H - - - -#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__MINGW32__) -# define HB_BEGIN_VISIBILITY _Pragma ("GCC visibility push(hidden)") -# define HB_END_VISIBILITY _Pragma ("GCC visibility pop") -#else -# define HB_BEGIN_VISIBILITY -# define HB_END_VISIBILITY -#endif -#ifdef __cplusplus -# define HB_BEGIN_HEADER extern "C" { HB_BEGIN_VISIBILITY -# define HB_END_HEADER HB_END_VISIBILITY } -#else -# define HB_BEGIN_HEADER HB_BEGIN_VISIBILITY -# define HB_END_HEADER HB_END_VISIBILITY -#endif - -HB_BEGIN_HEADER - -#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ - defined (_sgi) || defined (__sun) || defined (sun) || \ - defined (__digital__) || defined (__HP_cc) -# include -#elif defined (_AIX) -# include -#elif defined (_MSC_VER) && _MSC_VER < 1600 -/* VS 2010 (_MSC_VER 1600) has stdint.h */ -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -# include -#endif - - -#define UCDN_EAST_ASIAN_F 0 -#define UCDN_EAST_ASIAN_H 1 -#define UCDN_EAST_ASIAN_W 2 -#define UCDN_EAST_ASIAN_NA 3 -#define UCDN_EAST_ASIAN_A 4 -#define UCDN_EAST_ASIAN_N 5 - -#define UCDN_SCRIPT_COMMON 0 -#define UCDN_SCRIPT_LATIN 1 -#define UCDN_SCRIPT_GREEK 2 -#define UCDN_SCRIPT_CYRILLIC 3 -#define UCDN_SCRIPT_ARMENIAN 4 -#define UCDN_SCRIPT_HEBREW 5 -#define UCDN_SCRIPT_ARABIC 6 -#define UCDN_SCRIPT_SYRIAC 7 -#define UCDN_SCRIPT_THAANA 8 -#define UCDN_SCRIPT_DEVANAGARI 9 -#define UCDN_SCRIPT_BENGALI 10 -#define UCDN_SCRIPT_GURMUKHI 11 -#define UCDN_SCRIPT_GUJARATI 12 -#define UCDN_SCRIPT_ORIYA 13 -#define UCDN_SCRIPT_TAMIL 14 -#define UCDN_SCRIPT_TELUGU 15 -#define UCDN_SCRIPT_KANNADA 16 -#define UCDN_SCRIPT_MALAYALAM 17 -#define UCDN_SCRIPT_SINHALA 18 -#define UCDN_SCRIPT_THAI 19 -#define UCDN_SCRIPT_LAO 20 -#define UCDN_SCRIPT_TIBETAN 21 -#define UCDN_SCRIPT_MYANMAR 22 -#define UCDN_SCRIPT_GEORGIAN 23 -#define UCDN_SCRIPT_HANGUL 24 -#define UCDN_SCRIPT_ETHIOPIC 25 -#define UCDN_SCRIPT_CHEROKEE 26 -#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 -#define UCDN_SCRIPT_OGHAM 28 -#define UCDN_SCRIPT_RUNIC 29 -#define UCDN_SCRIPT_KHMER 30 -#define UCDN_SCRIPT_MONGOLIAN 31 -#define UCDN_SCRIPT_HIRAGANA 32 -#define UCDN_SCRIPT_KATAKANA 33 -#define UCDN_SCRIPT_BOPOMOFO 34 -#define UCDN_SCRIPT_HAN 35 -#define UCDN_SCRIPT_YI 36 -#define UCDN_SCRIPT_OLD_ITALIC 37 -#define UCDN_SCRIPT_GOTHIC 38 -#define UCDN_SCRIPT_DESERET 39 -#define UCDN_SCRIPT_INHERITED 40 -#define UCDN_SCRIPT_TAGALOG 41 -#define UCDN_SCRIPT_HANUNOO 42 -#define UCDN_SCRIPT_BUHID 43 -#define UCDN_SCRIPT_TAGBANWA 44 -#define UCDN_SCRIPT_LIMBU 45 -#define UCDN_SCRIPT_TAI_LE 46 -#define UCDN_SCRIPT_LINEAR_B 47 -#define UCDN_SCRIPT_UGARITIC 48 -#define UCDN_SCRIPT_SHAVIAN 49 -#define UCDN_SCRIPT_OSMANYA 50 -#define UCDN_SCRIPT_CYPRIOT 51 -#define UCDN_SCRIPT_BRAILLE 52 -#define UCDN_SCRIPT_BUGINESE 53 -#define UCDN_SCRIPT_COPTIC 54 -#define UCDN_SCRIPT_NEW_TAI_LUE 55 -#define UCDN_SCRIPT_GLAGOLITIC 56 -#define UCDN_SCRIPT_TIFINAGH 57 -#define UCDN_SCRIPT_SYLOTI_NAGRI 58 -#define UCDN_SCRIPT_OLD_PERSIAN 59 -#define UCDN_SCRIPT_KHAROSHTHI 60 -#define UCDN_SCRIPT_BALINESE 61 -#define UCDN_SCRIPT_CUNEIFORM 62 -#define UCDN_SCRIPT_PHOENICIAN 63 -#define UCDN_SCRIPT_PHAGS_PA 64 -#define UCDN_SCRIPT_NKO 65 -#define UCDN_SCRIPT_SUNDANESE 66 -#define UCDN_SCRIPT_LEPCHA 67 -#define UCDN_SCRIPT_OL_CHIKI 68 -#define UCDN_SCRIPT_VAI 69 -#define UCDN_SCRIPT_SAURASHTRA 70 -#define UCDN_SCRIPT_KAYAH_LI 71 -#define UCDN_SCRIPT_REJANG 72 -#define UCDN_SCRIPT_LYCIAN 73 -#define UCDN_SCRIPT_CARIAN 74 -#define UCDN_SCRIPT_LYDIAN 75 -#define UCDN_SCRIPT_CHAM 76 -#define UCDN_SCRIPT_TAI_THAM 77 -#define UCDN_SCRIPT_TAI_VIET 78 -#define UCDN_SCRIPT_AVESTAN 79 -#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 -#define UCDN_SCRIPT_SAMARITAN 81 -#define UCDN_SCRIPT_LISU 82 -#define UCDN_SCRIPT_BAMUM 83 -#define UCDN_SCRIPT_JAVANESE 84 -#define UCDN_SCRIPT_MEETEI_MAYEK 85 -#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 -#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 -#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 -#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 -#define UCDN_SCRIPT_OLD_TURKIC 90 -#define UCDN_SCRIPT_KAITHI 91 -#define UCDN_SCRIPT_BATAK 92 -#define UCDN_SCRIPT_BRAHMI 93 -#define UCDN_SCRIPT_MANDAIC 94 -#define UCDN_SCRIPT_CHAKMA 95 -#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 -#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 -#define UCDN_SCRIPT_MIAO 98 -#define UCDN_SCRIPT_SHARADA 99 -#define UCDN_SCRIPT_SORA_SOMPENG 100 -#define UCDN_SCRIPT_TAKRI 101 -#define UCDN_SCRIPT_UNKNOWN 102 -#define UCDN_SCRIPT_BASSA_VAH 103 -#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 -#define UCDN_SCRIPT_DUPLOYAN 105 -#define UCDN_SCRIPT_ELBASAN 106 -#define UCDN_SCRIPT_GRANTHA 107 -#define UCDN_SCRIPT_KHOJKI 108 -#define UCDN_SCRIPT_KHUDAWADI 109 -#define UCDN_SCRIPT_LINEAR_A 110 -#define UCDN_SCRIPT_MAHAJANI 111 -#define UCDN_SCRIPT_MANICHAEAN 112 -#define UCDN_SCRIPT_MENDE_KIKAKUI 113 -#define UCDN_SCRIPT_MODI 114 -#define UCDN_SCRIPT_MRO 115 -#define UCDN_SCRIPT_NABATAEAN 116 -#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 -#define UCDN_SCRIPT_OLD_PERMIC 118 -#define UCDN_SCRIPT_PAHAWH_HMONG 119 -#define UCDN_SCRIPT_PALMYRENE 120 -#define UCDN_SCRIPT_PAU_CIN_HAU 121 -#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 -#define UCDN_SCRIPT_SIDDHAM 123 -#define UCDN_SCRIPT_TIRHUTA 124 -#define UCDN_SCRIPT_WARANG_CITI 125 -#define UCDN_SCRIPT_AHOM 126 -#define UCDN_SCRIPT_ANATOLIAN_HIEROGLYPHS 127 -#define UCDN_SCRIPT_HATRAN 128 -#define UCDN_SCRIPT_MULTANI 129 -#define UCDN_SCRIPT_OLD_HUNGARIAN 130 -#define UCDN_SCRIPT_SIGNWRITING 131 -#define UCDN_SCRIPT_ADLAM 132 -#define UCDN_SCRIPT_BHAIKSUKI 133 -#define UCDN_SCRIPT_MARCHEN 134 -#define UCDN_SCRIPT_NEWA 135 -#define UCDN_SCRIPT_OSAGE 136 -#define UCDN_SCRIPT_TANGUT 137 -#define UCDN_SCRIPT_MASARAM_GONDI 138 -#define UCDN_SCRIPT_NUSHU 139 -#define UCDN_SCRIPT_SOYOMBO 140 -#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141 - -#define UCDN_LINEBREAK_CLASS_OP 0 -#define UCDN_LINEBREAK_CLASS_CL 1 -#define UCDN_LINEBREAK_CLASS_CP 2 -#define UCDN_LINEBREAK_CLASS_QU 3 -#define UCDN_LINEBREAK_CLASS_GL 4 -#define UCDN_LINEBREAK_CLASS_NS 5 -#define UCDN_LINEBREAK_CLASS_EX 6 -#define UCDN_LINEBREAK_CLASS_SY 7 -#define UCDN_LINEBREAK_CLASS_IS 8 -#define UCDN_LINEBREAK_CLASS_PR 9 -#define UCDN_LINEBREAK_CLASS_PO 10 -#define UCDN_LINEBREAK_CLASS_NU 11 -#define UCDN_LINEBREAK_CLASS_AL 12 -#define UCDN_LINEBREAK_CLASS_HL 13 -#define UCDN_LINEBREAK_CLASS_ID 14 -#define UCDN_LINEBREAK_CLASS_IN 15 -#define UCDN_LINEBREAK_CLASS_HY 16 -#define UCDN_LINEBREAK_CLASS_BA 17 -#define UCDN_LINEBREAK_CLASS_BB 18 -#define UCDN_LINEBREAK_CLASS_B2 19 -#define UCDN_LINEBREAK_CLASS_ZW 20 -#define UCDN_LINEBREAK_CLASS_CM 21 -#define UCDN_LINEBREAK_CLASS_WJ 22 -#define UCDN_LINEBREAK_CLASS_H2 23 -#define UCDN_LINEBREAK_CLASS_H3 24 -#define UCDN_LINEBREAK_CLASS_JL 25 -#define UCDN_LINEBREAK_CLASS_JV 26 -#define UCDN_LINEBREAK_CLASS_JT 27 -#define UCDN_LINEBREAK_CLASS_RI 28 -#define UCDN_LINEBREAK_CLASS_AI 29 -#define UCDN_LINEBREAK_CLASS_BK 30 -#define UCDN_LINEBREAK_CLASS_CB 31 -#define UCDN_LINEBREAK_CLASS_CJ 32 -#define UCDN_LINEBREAK_CLASS_CR 33 -#define UCDN_LINEBREAK_CLASS_LF 34 -#define UCDN_LINEBREAK_CLASS_NL 35 -#define UCDN_LINEBREAK_CLASS_SA 36 -#define UCDN_LINEBREAK_CLASS_SG 37 -#define UCDN_LINEBREAK_CLASS_SP 38 -#define UCDN_LINEBREAK_CLASS_XX 39 -#define UCDN_LINEBREAK_CLASS_ZWJ 40 -#define UCDN_LINEBREAK_CLASS_EB 41 -#define UCDN_LINEBREAK_CLASS_EM 42 - -#define UCDN_GENERAL_CATEGORY_CC 0 -#define UCDN_GENERAL_CATEGORY_CF 1 -#define UCDN_GENERAL_CATEGORY_CN 2 -#define UCDN_GENERAL_CATEGORY_CO 3 -#define UCDN_GENERAL_CATEGORY_CS 4 -#define UCDN_GENERAL_CATEGORY_LL 5 -#define UCDN_GENERAL_CATEGORY_LM 6 -#define UCDN_GENERAL_CATEGORY_LO 7 -#define UCDN_GENERAL_CATEGORY_LT 8 -#define UCDN_GENERAL_CATEGORY_LU 9 -#define UCDN_GENERAL_CATEGORY_MC 10 -#define UCDN_GENERAL_CATEGORY_ME 11 -#define UCDN_GENERAL_CATEGORY_MN 12 -#define UCDN_GENERAL_CATEGORY_ND 13 -#define UCDN_GENERAL_CATEGORY_NL 14 -#define UCDN_GENERAL_CATEGORY_NO 15 -#define UCDN_GENERAL_CATEGORY_PC 16 -#define UCDN_GENERAL_CATEGORY_PD 17 -#define UCDN_GENERAL_CATEGORY_PE 18 -#define UCDN_GENERAL_CATEGORY_PF 19 -#define UCDN_GENERAL_CATEGORY_PI 20 -#define UCDN_GENERAL_CATEGORY_PO 21 -#define UCDN_GENERAL_CATEGORY_PS 22 -#define UCDN_GENERAL_CATEGORY_SC 23 -#define UCDN_GENERAL_CATEGORY_SK 24 -#define UCDN_GENERAL_CATEGORY_SM 25 -#define UCDN_GENERAL_CATEGORY_SO 26 -#define UCDN_GENERAL_CATEGORY_ZL 27 -#define UCDN_GENERAL_CATEGORY_ZP 28 -#define UCDN_GENERAL_CATEGORY_ZS 29 - -#define UCDN_BIDI_CLASS_L 0 -#define UCDN_BIDI_CLASS_LRE 1 -#define UCDN_BIDI_CLASS_LRO 2 -#define UCDN_BIDI_CLASS_R 3 -#define UCDN_BIDI_CLASS_AL 4 -#define UCDN_BIDI_CLASS_RLE 5 -#define UCDN_BIDI_CLASS_RLO 6 -#define UCDN_BIDI_CLASS_PDF 7 -#define UCDN_BIDI_CLASS_EN 8 -#define UCDN_BIDI_CLASS_ES 9 -#define UCDN_BIDI_CLASS_ET 10 -#define UCDN_BIDI_CLASS_AN 11 -#define UCDN_BIDI_CLASS_CS 12 -#define UCDN_BIDI_CLASS_NSM 13 -#define UCDN_BIDI_CLASS_BN 14 -#define UCDN_BIDI_CLASS_B 15 -#define UCDN_BIDI_CLASS_S 16 -#define UCDN_BIDI_CLASS_WS 17 -#define UCDN_BIDI_CLASS_ON 18 -#define UCDN_BIDI_CLASS_LRI 19 -#define UCDN_BIDI_CLASS_RLI 20 -#define UCDN_BIDI_CLASS_FSI 21 -#define UCDN_BIDI_CLASS_PDI 22 - -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_OPEN 0 -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_CLOSE 1 -#define UCDN_BIDI_PAIRED_BRACKET_TYPE_NONE 2 - -/** - * Return version of the Unicode database. - * - * @return Unicode database version - */ -const char *ucdn_get_unicode_version(void); - -/** - * Get combining class of a codepoint. - * - * @param code Unicode codepoint - * @return combining class value, as defined in UAX#44 - */ -int ucdn_get_combining_class(uint32_t code); - -/** - * Get east-asian width of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_EAST_ASIAN_* and as defined in UAX#11. - */ -int ucdn_get_east_asian_width(uint32_t code); - -/** - * Get general category of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_GENERAL_CATEGORY_* and as defined in - * UAX#44. - */ -int ucdn_get_general_category(uint32_t code); - -/** - * Get bidirectional class of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_BIDI_CLASS_* and as defined in UAX#44. - */ -int ucdn_get_bidi_class(uint32_t code); - -/** - * Get script of a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_SCRIPT_* and as defined in UAX#24. - */ -int ucdn_get_script(uint32_t code); - -/** - * Get unresolved linebreak class of a codepoint. This does not take - * rule LB1 of UAX#14 into account. See ucdn_get_resolved_linebreak_class() - * for resolved linebreak classes. - * - * @param code Unicode codepoint - * @return value according to UCDN_LINEBREAK_* and as defined in UAX#14. - */ -int ucdn_get_linebreak_class(uint32_t code); - -/** - * Get resolved linebreak class of a codepoint. This resolves characters - * in the AI, SG, XX, SA and CJ classes according to rule LB1 of UAX#14. - * In addition the CB class is resolved as the equivalent B2 class and - * the NL class is resolved as the equivalent BK class. - * - * @param code Unicode codepoint - * @return value according to UCDN_LINEBREAK_* and as defined in UAX#14. - */ -int ucdn_get_resolved_linebreak_class(uint32_t code); - -/** - * Check if codepoint can be mirrored. - * - * @param code Unicode codepoint - * @return 1 if mirrored character exists, otherwise 0 - */ -int ucdn_get_mirrored(uint32_t code); - -/** - * Mirror a codepoint. - * - * @param code Unicode codepoint - * @return mirrored codepoint or the original codepoint if no - * mirrored character exists - */ -uint32_t ucdn_mirror(uint32_t code); - -/** - * Get paired bracket for a codepoint. - * - * @param code Unicode codepoint - * @return paired bracket codepoint or the original codepoint if no - * paired bracket character exists - */ -uint32_t ucdn_paired_bracket(uint32_t code); - -/** - * Get paired bracket type for a codepoint. - * - * @param code Unicode codepoint - * @return value according to UCDN_BIDI_PAIRED_BRACKET_TYPE_* and as defined - * in UAX#9. - * - */ -int ucdn_paired_bracket_type(uint32_t code); - -/** - * Pairwise canonical decomposition of a codepoint. This includes - * Hangul Jamo decomposition (see chapter 3.12 of the Unicode core - * specification). - * - * Hangul is decomposed into L and V jamos for LV forms, and an - * LV precomposed syllable and a T jamo for LVT forms. - * - * @param code Unicode codepoint - * @param a filled with first codepoint of decomposition - * @param b filled with second codepoint of decomposition, or 0 - * @return success - */ -int ucdn_decompose(uint32_t code, uint32_t *a, uint32_t *b); - -/** - * Compatibility decomposition of a codepoint. - * - * @param code Unicode codepoint - * @param decomposed filled with decomposition, must be able to hold 18 - * characters - * @return length of decomposition or 0 in case none exists - */ -int ucdn_compat_decompose(uint32_t code, uint32_t *decomposed); - -/** - * Pairwise canonical composition of two codepoints. This includes - * Hangul Jamo composition (see chapter 3.12 of the Unicode core - * specification). - * - * Hangul composition expects either L and V jamos, or an LV - * precomposed syllable and a T jamo. This is exactly the inverse - * of pairwise Hangul decomposition. - * - * @param code filled with composition - * @param a first codepoint - * @param b second codepoint - * @return success - */ -int ucdn_compose(uint32_t *code, uint32_t a, uint32_t b); - -HB_END_HEADER - -#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h b/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h deleted file mode 100644 index d72c7ba0dffbd..0000000000000 --- a/src/java.desktop/share/native/libharfbuzz/hb-ucdn/ucdn_db.h +++ /dev/null @@ -1,5730 +0,0 @@ -/* this file was generated by makeunicodedata.py 3.2 */ - -#define UNIDATA_VERSION "11.0.0" -/* a list of unique database records */ -static const UCDRecord ucd_records[] = { - {2, 0, 18, 5, 102, 39}, - {0, 0, 14, 5, 0, 21}, - {0, 0, 16, 5, 0, 17}, - {0, 0, 15, 5, 0, 34}, - {0, 0, 16, 5, 0, 30}, - {0, 0, 17, 5, 0, 30}, - {0, 0, 15, 5, 0, 33}, - {0, 0, 15, 5, 0, 21}, - {0, 0, 16, 5, 0, 21}, - {29, 0, 17, 3, 0, 38}, - {21, 0, 18, 3, 0, 6}, - {21, 0, 18, 3, 0, 3}, - {21, 0, 10, 3, 0, 12}, - {23, 0, 10, 3, 0, 9}, - {21, 0, 10, 3, 0, 10}, - {21, 0, 18, 3, 0, 12}, - {22, 0, 18, 3, 0, 0}, - {18, 0, 18, 3, 0, 2}, - {25, 0, 9, 3, 0, 9}, - {21, 0, 12, 3, 0, 8}, - {17, 0, 9, 3, 0, 16}, - {21, 0, 12, 3, 0, 7}, - {13, 0, 8, 3, 0, 11}, - {21, 0, 18, 3, 0, 8}, - {25, 0, 18, 3, 0, 12}, - {9, 0, 0, 3, 1, 12}, - {21, 0, 18, 3, 0, 9}, - {24, 0, 18, 3, 0, 12}, - {16, 0, 18, 3, 0, 12}, - {5, 0, 0, 3, 1, 12}, - {25, 0, 18, 3, 0, 17}, - {18, 0, 18, 3, 0, 1}, - {0, 0, 15, 5, 0, 35}, - {29, 0, 12, 5, 0, 4}, - {21, 0, 18, 4, 0, 0}, - {23, 0, 10, 3, 0, 10}, - {23, 0, 10, 4, 0, 9}, - {26, 0, 18, 3, 0, 12}, - {21, 0, 18, 4, 0, 29}, - {24, 0, 18, 4, 0, 29}, - {26, 0, 18, 5, 0, 12}, - {7, 0, 0, 4, 1, 29}, - {20, 0, 18, 5, 0, 3}, - {1, 0, 14, 4, 0, 17}, - {26, 0, 18, 4, 0, 12}, - {26, 0, 10, 4, 0, 10}, - {25, 0, 10, 4, 0, 9}, - {15, 0, 8, 4, 0, 29}, - {24, 0, 18, 4, 0, 18}, - {5, 0, 0, 5, 0, 12}, - {19, 0, 18, 5, 0, 3}, - {15, 0, 18, 4, 0, 29}, - {9, 0, 0, 5, 1, 12}, - {9, 0, 0, 4, 1, 12}, - {25, 0, 18, 4, 0, 29}, - {5, 0, 0, 4, 1, 12}, - {5, 0, 0, 5, 1, 12}, - {7, 0, 0, 5, 1, 12}, - {8, 0, 0, 5, 1, 12}, - {6, 0, 0, 5, 1, 12}, - {6, 0, 18, 5, 0, 12}, - {6, 0, 0, 5, 0, 12}, - {24, 0, 18, 5, 0, 12}, - {24, 0, 18, 4, 0, 12}, - {6, 0, 18, 4, 0, 29}, - {6, 0, 18, 5, 0, 18}, - {6, 0, 0, 4, 0, 29}, - {24, 0, 18, 5, 34, 12}, - {12, 230, 13, 4, 40, 21}, - {12, 232, 13, 4, 40, 21}, - {12, 220, 13, 4, 40, 21}, - {12, 216, 13, 4, 40, 21}, - {12, 202, 13, 4, 40, 21}, - {12, 1, 13, 4, 40, 21}, - {12, 240, 13, 4, 40, 21}, - {12, 0, 13, 4, 40, 4}, - {12, 233, 13, 4, 40, 4}, - {12, 234, 13, 4, 40, 4}, - {9, 0, 0, 5, 2, 12}, - {5, 0, 0, 5, 2, 12}, - {24, 0, 18, 5, 2, 12}, - {2, 0, 18, 5, 102, 39}, - {6, 0, 0, 5, 2, 12}, - {21, 0, 18, 5, 0, 8}, - {21, 0, 18, 5, 0, 12}, - {9, 0, 0, 4, 2, 12}, - {5, 0, 0, 4, 2, 12}, - {9, 0, 0, 5, 54, 12}, - {5, 0, 0, 5, 54, 12}, - {25, 0, 18, 5, 2, 12}, - {9, 0, 0, 5, 3, 12}, - {9, 0, 0, 4, 3, 12}, - {5, 0, 0, 4, 3, 12}, - {5, 0, 0, 5, 3, 12}, - {26, 0, 0, 5, 3, 12}, - {12, 230, 13, 5, 3, 21}, - {12, 230, 13, 5, 40, 21}, - {11, 0, 13, 5, 3, 21}, - {9, 0, 0, 5, 4, 12}, - {6, 0, 0, 5, 4, 12}, - {21, 0, 0, 5, 4, 12}, - {5, 0, 0, 5, 4, 12}, - {21, 0, 0, 5, 0, 8}, - {17, 0, 18, 5, 4, 17}, - {26, 0, 18, 5, 4, 12}, - {23, 0, 10, 5, 4, 9}, - {12, 220, 13, 5, 5, 21}, - {12, 230, 13, 5, 5, 21}, - {12, 222, 13, 5, 5, 21}, - {12, 228, 13, 5, 5, 21}, - {12, 10, 13, 5, 5, 21}, - {12, 11, 13, 5, 5, 21}, - {12, 12, 13, 5, 5, 21}, - {12, 13, 13, 5, 5, 21}, - {12, 14, 13, 5, 5, 21}, - {12, 15, 13, 5, 5, 21}, - {12, 16, 13, 5, 5, 21}, - {12, 17, 13, 5, 5, 21}, - {12, 18, 13, 5, 5, 21}, - {12, 19, 13, 5, 5, 21}, - {12, 20, 13, 5, 5, 21}, - {12, 21, 13, 5, 5, 21}, - {12, 22, 13, 5, 5, 21}, - {17, 0, 3, 5, 5, 17}, - {12, 23, 13, 5, 5, 21}, - {21, 0, 3, 5, 5, 12}, - {12, 24, 13, 5, 5, 21}, - {12, 25, 13, 5, 5, 21}, - {21, 0, 3, 5, 5, 6}, - {7, 0, 3, 5, 5, 13}, - {1, 0, 11, 5, 6, 12}, - {1, 0, 11, 5, 0, 12}, - {25, 0, 18, 5, 6, 12}, - {25, 0, 4, 5, 6, 12}, - {21, 0, 10, 5, 6, 10}, - {23, 0, 4, 5, 6, 10}, - {21, 0, 12, 5, 0, 8}, - {21, 0, 4, 5, 6, 8}, - {26, 0, 18, 5, 6, 12}, - {12, 230, 13, 5, 6, 21}, - {12, 30, 13, 5, 6, 21}, - {12, 31, 13, 5, 6, 21}, - {12, 32, 13, 5, 6, 21}, - {21, 0, 4, 5, 0, 6}, - {1, 0, 4, 5, 6, 21}, - {21, 0, 4, 5, 6, 6}, - {7, 0, 4, 5, 6, 12}, - {6, 0, 4, 5, 0, 12}, - {12, 27, 13, 5, 40, 21}, - {12, 28, 13, 5, 40, 21}, - {12, 29, 13, 5, 40, 21}, - {12, 30, 13, 5, 40, 21}, - {12, 31, 13, 5, 40, 21}, - {12, 32, 13, 5, 40, 21}, - {12, 33, 13, 5, 40, 21}, - {12, 34, 13, 5, 40, 21}, - {12, 220, 13, 5, 40, 21}, - {12, 220, 13, 5, 6, 21}, - {13, 0, 11, 5, 6, 11}, - {21, 0, 11, 5, 6, 11}, - {21, 0, 4, 5, 6, 12}, - {12, 35, 13, 5, 40, 21}, - {6, 0, 4, 5, 6, 12}, - {13, 0, 8, 5, 6, 11}, - {26, 0, 4, 5, 6, 12}, - {21, 0, 4, 5, 7, 12}, - {1, 0, 4, 5, 7, 12}, - {7, 0, 4, 5, 7, 12}, - {12, 36, 13, 5, 7, 21}, - {12, 230, 13, 5, 7, 21}, - {12, 220, 13, 5, 7, 21}, - {7, 0, 4, 5, 8, 12}, - {12, 0, 13, 5, 8, 21}, - {13, 0, 3, 5, 65, 11}, - {7, 0, 3, 5, 65, 12}, - {12, 230, 13, 5, 65, 21}, - {12, 220, 13, 5, 65, 21}, - {6, 0, 3, 5, 65, 12}, - {26, 0, 18, 5, 65, 12}, - {21, 0, 18, 5, 65, 12}, - {21, 0, 18, 5, 65, 8}, - {21, 0, 18, 5, 65, 6}, - {23, 0, 3, 5, 65, 9}, - {7, 0, 3, 5, 81, 12}, - {12, 230, 13, 5, 81, 21}, - {6, 0, 3, 5, 81, 12}, - {21, 0, 3, 5, 81, 12}, - {7, 0, 3, 5, 94, 12}, - {12, 220, 13, 5, 94, 21}, - {21, 0, 3, 5, 94, 12}, - {12, 27, 13, 5, 6, 21}, - {12, 28, 13, 5, 6, 21}, - {12, 29, 13, 5, 6, 21}, - {12, 0, 13, 5, 9, 21}, - {10, 0, 0, 5, 9, 21}, - {7, 0, 0, 5, 9, 12}, - {12, 7, 13, 5, 9, 21}, - {12, 9, 13, 5, 9, 21}, - {12, 230, 13, 5, 9, 21}, - {21, 0, 0, 5, 0, 17}, - {13, 0, 0, 5, 9, 11}, - {21, 0, 0, 5, 9, 12}, - {6, 0, 0, 5, 9, 12}, - {7, 0, 0, 5, 10, 12}, - {12, 0, 13, 5, 10, 21}, - {10, 0, 0, 5, 10, 21}, - {12, 7, 13, 5, 10, 21}, - {12, 9, 13, 5, 10, 21}, - {13, 0, 0, 5, 10, 11}, - {23, 0, 10, 5, 10, 10}, - {15, 0, 0, 5, 10, 12}, - {15, 0, 0, 5, 10, 10}, - {26, 0, 0, 5, 10, 12}, - {23, 0, 10, 5, 10, 9}, - {21, 0, 0, 5, 10, 12}, - {12, 230, 13, 5, 10, 21}, - {12, 0, 13, 5, 11, 21}, - {10, 0, 0, 5, 11, 21}, - {7, 0, 0, 5, 11, 12}, - {12, 7, 13, 5, 11, 21}, - {12, 9, 13, 5, 11, 21}, - {13, 0, 0, 5, 11, 11}, - {21, 0, 0, 5, 11, 12}, - {12, 0, 13, 5, 12, 21}, - {10, 0, 0, 5, 12, 21}, - {7, 0, 0, 5, 12, 12}, - {12, 7, 13, 5, 12, 21}, - {12, 9, 13, 5, 12, 21}, - {13, 0, 0, 5, 12, 11}, - {21, 0, 0, 5, 12, 12}, - {23, 0, 10, 5, 12, 9}, - {12, 0, 13, 5, 13, 21}, - {10, 0, 0, 5, 13, 21}, - {7, 0, 0, 5, 13, 12}, - {12, 7, 13, 5, 13, 21}, - {12, 9, 13, 5, 13, 21}, - {13, 0, 0, 5, 13, 11}, - {26, 0, 0, 5, 13, 12}, - {15, 0, 0, 5, 13, 12}, - {12, 0, 13, 5, 14, 21}, - {7, 0, 0, 5, 14, 12}, - {10, 0, 0, 5, 14, 21}, - {12, 9, 13, 5, 14, 21}, - {13, 0, 0, 5, 14, 11}, - {15, 0, 0, 5, 14, 12}, - {26, 0, 18, 5, 14, 12}, - {23, 0, 10, 5, 14, 9}, - {12, 0, 13, 5, 15, 21}, - {10, 0, 0, 5, 15, 21}, - {7, 0, 0, 5, 15, 12}, - {12, 9, 13, 5, 15, 21}, - {12, 84, 13, 5, 15, 21}, - {12, 91, 13, 5, 15, 21}, - {13, 0, 0, 5, 15, 11}, - {15, 0, 18, 5, 15, 12}, - {26, 0, 0, 5, 15, 12}, - {7, 0, 0, 5, 16, 12}, - {12, 0, 13, 5, 16, 21}, - {10, 0, 0, 5, 16, 21}, - {21, 0, 0, 5, 16, 18}, - {12, 7, 13, 5, 16, 21}, - {12, 0, 0, 5, 16, 21}, - {12, 9, 13, 5, 16, 21}, - {13, 0, 0, 5, 16, 11}, - {12, 0, 13, 5, 17, 21}, - {10, 0, 0, 5, 17, 21}, - {7, 0, 0, 5, 17, 12}, - {12, 9, 13, 5, 17, 21}, - {26, 0, 0, 5, 17, 12}, - {15, 0, 0, 5, 17, 12}, - {13, 0, 0, 5, 17, 11}, - {26, 0, 0, 5, 17, 10}, - {10, 0, 0, 5, 18, 21}, - {7, 0, 0, 5, 18, 12}, - {12, 9, 13, 5, 18, 21}, - {12, 0, 13, 5, 18, 21}, - {13, 0, 0, 5, 18, 11}, - {21, 0, 0, 5, 18, 12}, - {7, 0, 0, 5, 19, 36}, - {12, 0, 13, 5, 19, 36}, - {12, 103, 13, 5, 19, 36}, - {12, 9, 13, 5, 19, 36}, - {23, 0, 10, 5, 0, 9}, - {6, 0, 0, 5, 19, 36}, - {12, 107, 13, 5, 19, 36}, - {21, 0, 0, 5, 19, 12}, - {13, 0, 0, 5, 19, 11}, - {21, 0, 0, 5, 19, 17}, - {7, 0, 0, 5, 20, 36}, - {12, 0, 13, 5, 20, 36}, - {12, 118, 13, 5, 20, 36}, - {6, 0, 0, 5, 20, 36}, - {12, 122, 13, 5, 20, 36}, - {13, 0, 0, 5, 20, 11}, - {7, 0, 0, 5, 21, 12}, - {26, 0, 0, 5, 21, 18}, - {21, 0, 0, 5, 21, 18}, - {21, 0, 0, 5, 21, 12}, - {21, 0, 0, 5, 21, 4}, - {21, 0, 0, 5, 21, 17}, - {21, 0, 0, 5, 21, 6}, - {26, 0, 0, 5, 21, 12}, - {12, 220, 13, 5, 21, 21}, - {13, 0, 0, 5, 21, 11}, - {15, 0, 0, 5, 21, 12}, - {26, 0, 0, 5, 21, 17}, - {12, 216, 13, 5, 21, 21}, - {22, 0, 18, 5, 21, 0}, - {18, 0, 18, 5, 21, 1}, - {10, 0, 0, 5, 21, 21}, - {12, 129, 13, 5, 21, 21}, - {12, 130, 13, 5, 21, 21}, - {12, 0, 13, 5, 21, 21}, - {12, 132, 13, 5, 21, 21}, - {10, 0, 0, 5, 21, 17}, - {12, 230, 13, 5, 21, 21}, - {12, 9, 13, 5, 21, 21}, - {26, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 22, 36}, - {10, 0, 0, 5, 22, 36}, - {12, 0, 13, 5, 22, 36}, - {12, 7, 13, 5, 22, 36}, - {12, 9, 13, 5, 22, 36}, - {13, 0, 0, 5, 22, 11}, - {21, 0, 0, 5, 22, 17}, - {21, 0, 0, 5, 22, 12}, - {12, 220, 13, 5, 22, 36}, - {26, 0, 0, 5, 22, 36}, - {9, 0, 0, 5, 23, 12}, - {5, 0, 0, 5, 23, 12}, - {21, 0, 0, 5, 0, 12}, - {6, 0, 0, 5, 23, 12}, - {7, 0, 0, 2, 24, 25}, - {7, 0, 0, 5, 24, 26}, - {7, 0, 0, 5, 24, 27}, - {7, 0, 0, 5, 25, 12}, - {12, 230, 13, 5, 25, 21}, - {21, 0, 0, 5, 25, 12}, - {21, 0, 0, 5, 25, 17}, - {15, 0, 0, 5, 25, 12}, - {26, 0, 18, 5, 25, 12}, - {9, 0, 0, 5, 26, 12}, - {5, 0, 0, 5, 26, 12}, - {17, 0, 18, 5, 27, 17}, - {7, 0, 0, 5, 27, 12}, - {21, 0, 0, 5, 27, 12}, - {29, 0, 17, 5, 28, 17}, - {7, 0, 0, 5, 28, 12}, - {22, 0, 18, 5, 28, 0}, - {18, 0, 18, 5, 28, 1}, - {7, 0, 0, 5, 29, 12}, - {14, 0, 0, 5, 29, 12}, - {7, 0, 0, 5, 41, 12}, - {12, 0, 13, 5, 41, 21}, - {12, 9, 13, 5, 41, 21}, - {7, 0, 0, 5, 42, 12}, - {12, 0, 13, 5, 42, 21}, - {12, 9, 13, 5, 42, 21}, - {7, 0, 0, 5, 43, 12}, - {12, 0, 13, 5, 43, 21}, - {7, 0, 0, 5, 44, 12}, - {12, 0, 13, 5, 44, 21}, - {7, 0, 0, 5, 30, 36}, - {12, 0, 13, 5, 30, 36}, - {10, 0, 0, 5, 30, 36}, - {12, 9, 13, 5, 30, 36}, - {21, 0, 0, 5, 30, 17}, - {21, 0, 0, 5, 30, 5}, - {6, 0, 0, 5, 30, 36}, - {21, 0, 0, 5, 30, 12}, - {23, 0, 10, 5, 30, 9}, - {12, 230, 13, 5, 30, 36}, - {13, 0, 0, 5, 30, 11}, - {15, 0, 18, 5, 30, 12}, - {21, 0, 18, 5, 31, 12}, - {21, 0, 18, 5, 0, 6}, - {21, 0, 18, 5, 31, 17}, - {21, 0, 18, 5, 0, 17}, - {17, 0, 18, 5, 31, 18}, - {21, 0, 18, 5, 31, 6}, - {12, 0, 13, 5, 31, 21}, - {1, 0, 14, 5, 31, 4}, - {13, 0, 0, 5, 31, 11}, - {7, 0, 0, 5, 31, 12}, - {6, 0, 0, 5, 31, 12}, - {12, 228, 13, 5, 31, 21}, - {7, 0, 0, 5, 45, 12}, - {12, 0, 13, 5, 45, 21}, - {10, 0, 0, 5, 45, 21}, - {12, 222, 13, 5, 45, 21}, - {12, 230, 13, 5, 45, 21}, - {12, 220, 13, 5, 45, 21}, - {26, 0, 18, 5, 45, 12}, - {21, 0, 18, 5, 45, 6}, - {13, 0, 0, 5, 45, 11}, - {7, 0, 0, 5, 46, 36}, - {7, 0, 0, 5, 55, 36}, - {13, 0, 0, 5, 55, 11}, - {15, 0, 0, 5, 55, 36}, - {26, 0, 18, 5, 55, 36}, - {26, 0, 18, 5, 30, 12}, - {7, 0, 0, 5, 53, 12}, - {12, 230, 13, 5, 53, 21}, - {12, 220, 13, 5, 53, 21}, - {10, 0, 0, 5, 53, 21}, - {12, 0, 13, 5, 53, 21}, - {21, 0, 0, 5, 53, 12}, - {7, 0, 0, 5, 77, 36}, - {10, 0, 0, 5, 77, 36}, - {12, 0, 13, 5, 77, 36}, - {12, 9, 13, 5, 77, 36}, - {12, 230, 13, 5, 77, 36}, - {12, 220, 13, 5, 77, 21}, - {13, 0, 0, 5, 77, 11}, - {21, 0, 0, 5, 77, 36}, - {6, 0, 0, 5, 77, 36}, - {11, 0, 13, 5, 40, 21}, - {12, 0, 13, 5, 61, 21}, - {10, 0, 0, 5, 61, 21}, - {7, 0, 0, 5, 61, 12}, - {12, 7, 13, 5, 61, 21}, - {10, 9, 0, 5, 61, 21}, - {13, 0, 0, 5, 61, 11}, - {21, 0, 0, 5, 61, 17}, - {21, 0, 0, 5, 61, 12}, - {26, 0, 0, 5, 61, 12}, - {12, 230, 13, 5, 61, 21}, - {12, 220, 13, 5, 61, 21}, - {12, 0, 13, 5, 66, 21}, - {10, 0, 0, 5, 66, 21}, - {7, 0, 0, 5, 66, 12}, - {10, 9, 0, 5, 66, 21}, - {12, 9, 13, 5, 66, 21}, - {13, 0, 0, 5, 66, 11}, - {7, 0, 0, 5, 92, 12}, - {12, 7, 13, 5, 92, 21}, - {10, 0, 0, 5, 92, 21}, - {12, 0, 13, 5, 92, 21}, - {10, 9, 0, 5, 92, 21}, - {21, 0, 0, 5, 92, 12}, - {7, 0, 0, 5, 67, 12}, - {10, 0, 0, 5, 67, 21}, - {12, 0, 13, 5, 67, 21}, - {12, 7, 13, 5, 67, 21}, - {21, 0, 0, 5, 67, 17}, - {13, 0, 0, 5, 67, 11}, - {13, 0, 0, 5, 68, 11}, - {7, 0, 0, 5, 68, 12}, - {6, 0, 0, 5, 68, 12}, - {21, 0, 0, 5, 68, 17}, - {21, 0, 0, 5, 66, 12}, - {12, 1, 13, 5, 40, 21}, - {10, 0, 0, 5, 0, 21}, - {7, 0, 0, 5, 0, 12}, - {6, 0, 0, 5, 3, 12}, - {12, 234, 13, 5, 40, 21}, - {12, 214, 13, 5, 40, 21}, - {12, 202, 13, 5, 40, 21}, - {12, 232, 13, 5, 40, 21}, - {12, 228, 13, 5, 40, 21}, - {12, 233, 13, 5, 40, 21}, - {8, 0, 0, 5, 2, 12}, - {24, 0, 18, 5, 2, 18}, - {29, 0, 17, 5, 0, 17}, - {29, 0, 17, 5, 0, 4}, - {1, 0, 14, 5, 0, 20}, - {1, 0, 14, 5, 40, 21}, - {1, 0, 14, 5, 40, 40}, - {1, 0, 0, 5, 0, 21}, - {1, 0, 3, 5, 0, 21}, - {17, 0, 18, 4, 0, 17}, - {17, 0, 18, 5, 0, 4}, - {17, 0, 18, 5, 0, 17}, - {17, 0, 18, 4, 0, 19}, - {17, 0, 18, 4, 0, 29}, - {20, 0, 18, 4, 0, 3}, - {19, 0, 18, 4, 0, 3}, - {22, 0, 18, 5, 0, 0}, - {21, 0, 18, 4, 0, 12}, - {21, 0, 18, 4, 0, 15}, - {21, 0, 18, 4, 0, 17}, - {27, 0, 17, 5, 0, 30}, - {28, 0, 15, 5, 0, 30}, - {1, 0, 1, 5, 0, 21}, - {1, 0, 5, 5, 0, 21}, - {1, 0, 7, 5, 0, 21}, - {1, 0, 2, 5, 0, 21}, - {1, 0, 6, 5, 0, 21}, - {21, 0, 10, 4, 0, 10}, - {21, 0, 10, 5, 0, 10}, - {21, 0, 18, 4, 0, 10}, - {21, 0, 18, 5, 0, 10}, - {21, 0, 18, 5, 0, 5}, - {16, 0, 18, 5, 0, 12}, - {25, 0, 12, 5, 0, 8}, - {18, 0, 18, 5, 0, 1}, - {25, 0, 18, 5, 0, 12}, - {1, 0, 14, 5, 0, 22}, - {1, 0, 14, 5, 0, 12}, - {1, 0, 19, 5, 0, 21}, - {1, 0, 20, 5, 0, 21}, - {1, 0, 21, 5, 0, 21}, - {1, 0, 22, 5, 0, 21}, - {1, 0, 14, 5, 0, 21}, - {15, 0, 8, 5, 0, 12}, - {25, 0, 9, 5, 0, 12}, - {6, 0, 0, 4, 1, 29}, - {23, 0, 10, 5, 0, 10}, - {23, 0, 10, 1, 0, 9}, - {2, 0, 18, 5, 102, 9}, - {9, 0, 0, 5, 0, 12}, - {26, 0, 18, 4, 0, 10}, - {26, 0, 18, 4, 0, 29}, - {5, 0, 0, 4, 0, 29}, - {26, 0, 18, 4, 0, 9}, - {9, 0, 0, 4, 1, 29}, - {26, 0, 10, 5, 0, 12}, - {15, 0, 18, 5, 0, 12}, - {15, 0, 18, 4, 0, 12}, - {15, 0, 18, 5, 0, 29}, - {14, 0, 0, 4, 1, 29}, - {14, 0, 0, 5, 1, 12}, - {25, 0, 9, 5, 0, 9}, - {25, 0, 10, 5, 0, 9}, - {25, 0, 18, 5, 0, 15}, - {26, 0, 18, 2, 0, 14}, - {22, 0, 18, 2, 0, 0}, - {18, 0, 18, 2, 0, 1}, - {26, 0, 18, 2, 0, 12}, - {26, 0, 18, 5, 0, 14}, - {26, 0, 0, 4, 0, 29}, - {26, 0, 18, 5, 0, 29}, - {25, 0, 18, 2, 0, 12}, - {26, 0, 18, 4, 0, 14}, - {26, 0, 18, 5, 0, 41}, - {26, 0, 18, 4, 0, 41}, - {26, 0, 18, 2, 0, 41}, - {26, 0, 18, 2, 0, 29}, - {26, 0, 18, 5, 0, 3}, - {26, 0, 18, 5, 0, 6}, - {26, 0, 0, 5, 52, 12}, - {9, 0, 0, 5, 56, 12}, - {5, 0, 0, 5, 56, 12}, - {26, 0, 18, 5, 54, 12}, - {12, 230, 13, 5, 54, 21}, - {21, 0, 18, 5, 54, 6}, - {21, 0, 18, 5, 54, 17}, - {15, 0, 18, 5, 54, 12}, - {7, 0, 0, 5, 57, 12}, - {6, 0, 0, 5, 57, 12}, - {21, 0, 0, 5, 57, 17}, - {12, 9, 13, 5, 57, 21}, - {21, 0, 18, 5, 0, 3}, - {21, 0, 18, 5, 0, 0}, - {17, 0, 18, 5, 0, 12}, - {17, 0, 18, 5, 0, 19}, - {26, 0, 18, 2, 35, 14}, - {29, 0, 17, 0, 0, 17}, - {21, 0, 18, 2, 0, 1}, - {21, 0, 18, 2, 0, 14}, - {6, 0, 0, 2, 35, 5}, - {7, 0, 0, 2, 0, 14}, - {14, 0, 0, 2, 35, 14}, - {17, 0, 18, 2, 0, 5}, - {12, 218, 13, 2, 40, 21}, - {12, 228, 13, 2, 40, 21}, - {12, 232, 13, 2, 40, 21}, - {12, 222, 13, 2, 40, 21}, - {10, 224, 0, 2, 24, 21}, - {17, 0, 18, 2, 0, 14}, - {6, 0, 0, 2, 0, 14}, - {6, 0, 0, 2, 0, 21}, - {7, 0, 0, 2, 0, 5}, - {7, 0, 0, 2, 32, 32}, - {7, 0, 0, 2, 32, 14}, - {12, 8, 13, 2, 40, 21}, - {24, 0, 18, 2, 0, 5}, - {6, 0, 0, 2, 32, 5}, - {7, 0, 0, 2, 33, 32}, - {7, 0, 0, 2, 33, 14}, - {21, 0, 18, 2, 0, 5}, - {6, 0, 0, 2, 0, 32}, - {6, 0, 0, 2, 33, 5}, - {7, 0, 0, 2, 34, 14}, - {7, 0, 0, 2, 24, 14}, - {26, 0, 0, 2, 0, 14}, - {15, 0, 0, 2, 0, 14}, - {26, 0, 0, 2, 24, 14}, - {26, 0, 18, 2, 24, 14}, - {15, 0, 0, 4, 0, 29}, - {15, 0, 18, 2, 0, 14}, - {26, 0, 0, 2, 33, 14}, - {7, 0, 0, 2, 35, 14}, - {2, 0, 18, 2, 102, 14}, - {7, 0, 0, 2, 36, 14}, - {6, 0, 0, 2, 36, 5}, - {26, 0, 18, 2, 36, 14}, - {7, 0, 0, 5, 82, 12}, - {6, 0, 0, 5, 82, 12}, - {21, 0, 0, 5, 82, 17}, - {7, 0, 0, 5, 69, 12}, - {6, 0, 0, 5, 69, 12}, - {21, 0, 18, 5, 69, 17}, - {21, 0, 18, 5, 69, 6}, - {13, 0, 0, 5, 69, 11}, - {7, 0, 0, 5, 3, 12}, - {21, 0, 18, 5, 3, 12}, - {6, 0, 18, 5, 3, 12}, - {7, 0, 0, 5, 83, 12}, - {14, 0, 0, 5, 83, 12}, - {12, 230, 13, 5, 83, 21}, - {21, 0, 0, 5, 83, 12}, - {21, 0, 0, 5, 83, 17}, - {24, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 58, 12}, - {12, 0, 13, 5, 58, 21}, - {12, 9, 13, 5, 58, 21}, - {10, 0, 0, 5, 58, 21}, - {26, 0, 18, 5, 58, 12}, - {15, 0, 0, 5, 0, 12}, - {7, 0, 0, 5, 64, 12}, - {21, 0, 18, 5, 64, 18}, - {21, 0, 18, 5, 64, 6}, - {10, 0, 0, 5, 70, 21}, - {7, 0, 0, 5, 70, 12}, - {12, 9, 13, 5, 70, 21}, - {12, 0, 13, 5, 70, 21}, - {21, 0, 0, 5, 70, 17}, - {13, 0, 0, 5, 70, 11}, - {21, 0, 0, 5, 9, 18}, - {13, 0, 0, 5, 71, 11}, - {7, 0, 0, 5, 71, 12}, - {12, 0, 13, 5, 71, 21}, - {12, 220, 13, 5, 71, 21}, - {21, 0, 0, 5, 71, 17}, - {7, 0, 0, 5, 72, 12}, - {12, 0, 13, 5, 72, 21}, - {10, 0, 0, 5, 72, 21}, - {10, 9, 0, 5, 72, 21}, - {21, 0, 0, 5, 72, 12}, - {12, 0, 13, 5, 84, 21}, - {10, 0, 0, 5, 84, 21}, - {7, 0, 0, 5, 84, 12}, - {12, 7, 13, 5, 84, 21}, - {10, 9, 0, 5, 84, 21}, - {21, 0, 0, 5, 84, 12}, - {21, 0, 0, 5, 84, 17}, - {13, 0, 0, 5, 84, 11}, - {6, 0, 0, 5, 22, 36}, - {7, 0, 0, 5, 76, 12}, - {12, 0, 13, 5, 76, 21}, - {10, 0, 0, 5, 76, 21}, - {13, 0, 0, 5, 76, 11}, - {21, 0, 0, 5, 76, 12}, - {21, 0, 0, 5, 76, 17}, - {7, 0, 0, 5, 78, 36}, - {12, 230, 13, 5, 78, 36}, - {12, 220, 13, 5, 78, 36}, - {6, 0, 0, 5, 78, 36}, - {21, 0, 0, 5, 78, 36}, - {7, 0, 0, 5, 85, 12}, - {10, 0, 0, 5, 85, 21}, - {12, 0, 13, 5, 85, 21}, - {21, 0, 0, 5, 85, 17}, - {6, 0, 0, 5, 85, 12}, - {12, 9, 13, 5, 85, 21}, - {13, 0, 0, 5, 85, 11}, - {7, 0, 0, 2, 24, 23}, - {7, 0, 0, 2, 24, 24}, - {4, 0, 0, 5, 102, 37}, - {3, 0, 0, 4, 102, 39}, - {12, 26, 13, 5, 5, 21}, - {25, 0, 9, 5, 5, 12}, - {24, 0, 4, 5, 6, 12}, - {12, 0, 13, 4, 40, 21}, - {21, 0, 18, 2, 0, 8}, - {21, 0, 18, 2, 0, 6}, - {21, 0, 18, 2, 0, 15}, - {16, 0, 18, 2, 0, 14}, - {21, 0, 12, 2, 0, 1}, - {21, 0, 12, 2, 0, 5}, - {21, 0, 10, 2, 0, 14}, - {25, 0, 9, 2, 0, 14}, - {17, 0, 9, 2, 0, 14}, - {25, 0, 18, 2, 0, 14}, - {23, 0, 10, 2, 0, 9}, - {21, 0, 10, 2, 0, 10}, - {21, 0, 18, 0, 0, 6}, - {21, 0, 18, 0, 0, 14}, - {21, 0, 10, 0, 0, 14}, - {23, 0, 10, 0, 0, 9}, - {21, 0, 10, 0, 0, 10}, - {22, 0, 18, 0, 0, 0}, - {18, 0, 18, 0, 0, 1}, - {25, 0, 9, 0, 0, 14}, - {21, 0, 12, 0, 0, 1}, - {17, 0, 9, 0, 0, 14}, - {21, 0, 12, 0, 0, 14}, - {13, 0, 8, 0, 0, 14}, - {21, 0, 12, 0, 0, 5}, - {21, 0, 18, 0, 0, 5}, - {25, 0, 18, 0, 0, 14}, - {9, 0, 0, 0, 1, 14}, - {24, 0, 18, 0, 0, 14}, - {16, 0, 18, 0, 0, 14}, - {5, 0, 0, 0, 1, 14}, - {21, 0, 18, 1, 0, 1}, - {22, 0, 18, 1, 0, 0}, - {18, 0, 18, 1, 0, 1}, - {21, 0, 18, 1, 0, 5}, - {7, 0, 0, 1, 33, 14}, - {7, 0, 0, 1, 33, 32}, - {6, 0, 0, 1, 0, 32}, - {6, 0, 0, 1, 0, 5}, - {7, 0, 0, 1, 24, 14}, - {23, 0, 10, 0, 0, 10}, - {26, 0, 18, 0, 0, 14}, - {26, 0, 18, 1, 0, 12}, - {25, 0, 18, 1, 0, 12}, - {1, 0, 18, 5, 0, 21}, - {26, 0, 18, 5, 0, 31}, - {7, 0, 0, 5, 47, 12}, - {14, 0, 18, 5, 2, 12}, - {15, 0, 18, 5, 2, 12}, - {26, 0, 18, 5, 2, 12}, - {26, 0, 0, 5, 2, 12}, - {7, 0, 0, 5, 73, 12}, - {7, 0, 0, 5, 74, 12}, - {7, 0, 0, 5, 37, 12}, - {15, 0, 0, 5, 37, 12}, - {7, 0, 0, 5, 38, 12}, - {14, 0, 0, 5, 38, 12}, - {7, 0, 0, 5, 118, 12}, - {12, 230, 13, 5, 118, 21}, - {7, 0, 0, 5, 48, 12}, - {21, 0, 0, 5, 48, 17}, - {7, 0, 0, 5, 59, 12}, - {21, 0, 0, 5, 59, 17}, - {14, 0, 0, 5, 59, 12}, - {9, 0, 0, 5, 39, 12}, - {5, 0, 0, 5, 39, 12}, - {7, 0, 0, 5, 49, 12}, - {7, 0, 0, 5, 50, 12}, - {13, 0, 0, 5, 50, 11}, - {9, 0, 0, 5, 136, 12}, - {5, 0, 0, 5, 136, 12}, - {7, 0, 0, 5, 106, 12}, - {7, 0, 0, 5, 104, 12}, - {21, 0, 0, 5, 104, 12}, - {7, 0, 0, 5, 110, 12}, - {7, 0, 3, 5, 51, 12}, - {7, 0, 3, 5, 86, 12}, - {21, 0, 3, 5, 86, 17}, - {15, 0, 3, 5, 86, 12}, - {7, 0, 3, 5, 120, 12}, - {26, 0, 3, 5, 120, 12}, - {15, 0, 3, 5, 120, 12}, - {7, 0, 3, 5, 116, 12}, - {15, 0, 3, 5, 116, 12}, - {7, 0, 3, 5, 128, 12}, - {15, 0, 3, 5, 128, 12}, - {7, 0, 3, 5, 63, 12}, - {15, 0, 3, 5, 63, 12}, - {21, 0, 18, 5, 63, 17}, - {7, 0, 3, 5, 75, 12}, - {21, 0, 3, 5, 75, 12}, - {7, 0, 3, 5, 97, 12}, - {7, 0, 3, 5, 96, 12}, - {15, 0, 3, 5, 96, 12}, - {7, 0, 3, 5, 60, 12}, - {12, 0, 13, 5, 60, 21}, - {12, 220, 13, 5, 60, 21}, - {12, 230, 13, 5, 60, 21}, - {12, 1, 13, 5, 60, 21}, - {12, 9, 13, 5, 60, 21}, - {15, 0, 3, 5, 60, 12}, - {21, 0, 3, 5, 60, 17}, - {21, 0, 3, 5, 60, 12}, - {7, 0, 3, 5, 87, 12}, - {15, 0, 3, 5, 87, 12}, - {21, 0, 3, 5, 87, 12}, - {7, 0, 3, 5, 117, 12}, - {15, 0, 3, 5, 117, 12}, - {7, 0, 3, 5, 112, 12}, - {26, 0, 3, 5, 112, 12}, - {12, 230, 13, 5, 112, 21}, - {12, 220, 13, 5, 112, 21}, - {15, 0, 3, 5, 112, 12}, - {21, 0, 3, 5, 112, 17}, - {21, 0, 3, 5, 112, 15}, - {7, 0, 3, 5, 79, 12}, - {21, 0, 18, 5, 79, 17}, - {7, 0, 3, 5, 88, 12}, - {15, 0, 3, 5, 88, 12}, - {7, 0, 3, 5, 89, 12}, - {15, 0, 3, 5, 89, 12}, - {7, 0, 3, 5, 122, 12}, - {21, 0, 3, 5, 122, 12}, - {15, 0, 3, 5, 122, 12}, - {7, 0, 3, 5, 90, 12}, - {9, 0, 3, 5, 130, 12}, - {5, 0, 3, 5, 130, 12}, - {15, 0, 3, 5, 130, 12}, - {7, 0, 4, 5, 144, 12}, - {12, 230, 13, 5, 144, 21}, - {13, 0, 11, 5, 144, 11}, - {15, 0, 11, 5, 6, 12}, - {7, 0, 3, 5, 147, 12}, - {15, 0, 3, 5, 147, 12}, - {7, 0, 4, 5, 148, 12}, - {12, 220, 13, 5, 148, 21}, - {12, 230, 13, 5, 148, 21}, - {15, 0, 4, 5, 148, 12}, - {21, 0, 4, 5, 148, 12}, - {10, 0, 0, 5, 93, 21}, - {12, 0, 13, 5, 93, 21}, - {7, 0, 0, 5, 93, 12}, - {12, 9, 13, 5, 93, 21}, - {21, 0, 0, 5, 93, 17}, - {21, 0, 0, 5, 93, 12}, - {15, 0, 18, 5, 93, 12}, - {13, 0, 0, 5, 93, 11}, - {12, 0, 13, 5, 91, 21}, - {10, 0, 0, 5, 91, 21}, - {7, 0, 0, 5, 91, 12}, - {12, 9, 13, 5, 91, 21}, - {12, 7, 13, 5, 91, 21}, - {21, 0, 0, 5, 91, 12}, - {1, 0, 0, 5, 91, 12}, - {21, 0, 0, 5, 91, 17}, - {7, 0, 0, 5, 100, 12}, - {13, 0, 0, 5, 100, 11}, - {12, 230, 13, 5, 95, 21}, - {7, 0, 0, 5, 95, 12}, - {12, 0, 13, 5, 95, 21}, - {10, 0, 0, 5, 95, 21}, - {12, 9, 13, 5, 95, 21}, - {13, 0, 0, 5, 95, 11}, - {21, 0, 0, 5, 95, 17}, - {7, 0, 0, 5, 111, 12}, - {12, 7, 13, 5, 111, 21}, - {21, 0, 0, 5, 111, 12}, - {21, 0, 0, 5, 111, 18}, - {12, 0, 13, 5, 99, 21}, - {10, 0, 0, 5, 99, 21}, - {7, 0, 0, 5, 99, 12}, - {10, 9, 0, 5, 99, 21}, - {21, 0, 0, 5, 99, 17}, - {21, 0, 0, 5, 99, 12}, - {12, 7, 13, 5, 99, 21}, - {13, 0, 0, 5, 99, 11}, - {21, 0, 0, 5, 99, 18}, - {15, 0, 0, 5, 18, 12}, - {7, 0, 0, 5, 108, 12}, - {10, 0, 0, 5, 108, 21}, - {12, 0, 13, 5, 108, 21}, - {10, 9, 0, 5, 108, 21}, - {12, 7, 13, 5, 108, 21}, - {21, 0, 0, 5, 108, 17}, - {21, 0, 0, 5, 108, 12}, - {7, 0, 0, 5, 129, 12}, - {21, 0, 0, 5, 129, 17}, - {7, 0, 0, 5, 109, 12}, - {12, 0, 13, 5, 109, 21}, - {10, 0, 0, 5, 109, 21}, - {12, 7, 13, 5, 109, 21}, - {12, 9, 13, 5, 109, 21}, - {13, 0, 0, 5, 109, 11}, - {12, 0, 13, 5, 107, 21}, - {10, 0, 0, 5, 107, 21}, - {7, 0, 0, 5, 107, 12}, - {12, 7, 13, 5, 40, 21}, - {12, 7, 13, 5, 107, 21}, - {10, 9, 0, 5, 107, 21}, - {12, 230, 13, 5, 107, 21}, - {7, 0, 0, 5, 135, 12}, - {10, 0, 0, 5, 135, 21}, - {12, 0, 13, 5, 135, 21}, - {12, 9, 13, 5, 135, 21}, - {12, 7, 13, 5, 135, 21}, - {21, 0, 0, 5, 135, 17}, - {21, 0, 0, 5, 135, 12}, - {13, 0, 0, 5, 135, 11}, - {12, 230, 13, 5, 135, 21}, - {7, 0, 0, 5, 124, 12}, - {10, 0, 0, 5, 124, 21}, - {12, 0, 13, 5, 124, 21}, - {12, 9, 13, 5, 124, 21}, - {12, 7, 13, 5, 124, 21}, - {21, 0, 0, 5, 124, 12}, - {13, 0, 0, 5, 124, 11}, - {7, 0, 0, 5, 123, 12}, - {10, 0, 0, 5, 123, 21}, - {12, 0, 13, 5, 123, 21}, - {12, 9, 13, 5, 123, 21}, - {12, 7, 13, 5, 123, 21}, - {21, 0, 0, 5, 123, 18}, - {21, 0, 0, 5, 123, 17}, - {21, 0, 0, 5, 123, 6}, - {21, 0, 0, 5, 123, 12}, - {7, 0, 0, 5, 114, 12}, - {10, 0, 0, 5, 114, 21}, - {12, 0, 13, 5, 114, 21}, - {12, 9, 13, 5, 114, 21}, - {21, 0, 0, 5, 114, 17}, - {21, 0, 0, 5, 114, 12}, - {13, 0, 0, 5, 114, 11}, - {21, 0, 18, 5, 31, 18}, - {7, 0, 0, 5, 101, 12}, - {12, 0, 13, 5, 101, 21}, - {10, 0, 0, 5, 101, 21}, - {10, 9, 0, 5, 101, 21}, - {12, 7, 13, 5, 101, 21}, - {13, 0, 0, 5, 101, 11}, - {7, 0, 0, 5, 126, 36}, - {12, 0, 13, 5, 126, 36}, - {10, 0, 0, 5, 126, 36}, - {12, 9, 13, 5, 126, 36}, - {13, 0, 0, 5, 126, 11}, - {15, 0, 0, 5, 126, 36}, - {21, 0, 0, 5, 126, 17}, - {26, 0, 0, 5, 126, 36}, - {7, 0, 0, 5, 142, 12}, - {10, 0, 0, 5, 142, 21}, - {12, 0, 13, 5, 142, 21}, - {12, 9, 13, 5, 142, 21}, - {12, 7, 13, 5, 142, 21}, - {21, 0, 0, 5, 142, 12}, - {9, 0, 0, 5, 125, 12}, - {5, 0, 0, 5, 125, 12}, - {13, 0, 0, 5, 125, 11}, - {15, 0, 0, 5, 125, 12}, - {7, 0, 0, 5, 125, 12}, - {7, 0, 0, 5, 141, 12}, - {12, 0, 13, 5, 141, 21}, - {12, 0, 0, 5, 141, 21}, - {12, 9, 13, 5, 141, 21}, - {10, 0, 0, 5, 141, 21}, - {21, 0, 0, 5, 141, 18}, - {21, 0, 0, 5, 141, 12}, - {21, 0, 0, 5, 141, 17}, - {7, 0, 0, 5, 140, 12}, - {12, 0, 13, 5, 140, 21}, - {10, 0, 0, 5, 140, 21}, - {12, 9, 13, 5, 140, 21}, - {21, 0, 0, 5, 140, 17}, - {21, 0, 0, 5, 140, 18}, - {7, 0, 0, 5, 121, 12}, - {7, 0, 0, 5, 133, 12}, - {10, 0, 0, 5, 133, 21}, - {12, 0, 13, 5, 133, 21}, - {12, 9, 0, 5, 133, 21}, - {21, 0, 0, 5, 133, 17}, - {13, 0, 0, 5, 133, 11}, - {15, 0, 0, 5, 133, 12}, - {21, 0, 0, 5, 134, 18}, - {21, 0, 0, 5, 134, 6}, - {7, 0, 0, 5, 134, 12}, - {12, 0, 13, 5, 134, 21}, - {10, 0, 0, 5, 134, 21}, - {7, 0, 0, 5, 138, 12}, - {12, 0, 13, 5, 138, 21}, - {12, 7, 13, 5, 138, 21}, - {12, 9, 13, 5, 138, 21}, - {13, 0, 0, 5, 138, 11}, - {7, 0, 0, 5, 143, 12}, - {10, 0, 0, 5, 143, 21}, - {12, 0, 13, 5, 143, 21}, - {12, 9, 13, 5, 143, 21}, - {13, 0, 0, 5, 143, 11}, - {7, 0, 0, 5, 145, 12}, - {12, 0, 13, 5, 145, 21}, - {10, 0, 0, 5, 145, 21}, - {21, 0, 0, 5, 145, 12}, - {7, 0, 0, 5, 62, 12}, - {14, 0, 0, 5, 62, 12}, - {21, 0, 0, 5, 62, 17}, - {7, 0, 0, 5, 80, 12}, - {7, 0, 0, 5, 80, 0}, - {7, 0, 0, 5, 80, 1}, - {7, 0, 0, 5, 127, 12}, - {7, 0, 0, 5, 127, 0}, - {7, 0, 0, 5, 127, 1}, - {7, 0, 0, 5, 115, 12}, - {13, 0, 0, 5, 115, 11}, - {21, 0, 0, 5, 115, 17}, - {7, 0, 0, 5, 103, 12}, - {12, 1, 13, 5, 103, 21}, - {21, 0, 0, 5, 103, 17}, - {7, 0, 0, 5, 119, 12}, - {12, 230, 13, 5, 119, 21}, - {21, 0, 0, 5, 119, 17}, - {21, 0, 0, 5, 119, 12}, - {26, 0, 0, 5, 119, 12}, - {6, 0, 0, 5, 119, 12}, - {13, 0, 0, 5, 119, 11}, - {15, 0, 0, 5, 119, 12}, - {9, 0, 0, 5, 146, 12}, - {5, 0, 0, 5, 146, 12}, - {15, 0, 0, 5, 146, 12}, - {21, 0, 0, 5, 146, 17}, - {21, 0, 0, 5, 146, 12}, - {7, 0, 0, 5, 98, 12}, - {10, 0, 0, 5, 98, 21}, - {12, 0, 13, 5, 98, 21}, - {6, 0, 0, 5, 98, 12}, - {6, 0, 0, 2, 137, 5}, - {6, 0, 0, 2, 139, 5}, - {7, 0, 0, 2, 137, 14}, - {7, 0, 0, 2, 139, 14}, - {7, 0, 0, 5, 105, 12}, - {26, 0, 0, 5, 105, 12}, - {12, 0, 13, 5, 105, 21}, - {12, 1, 13, 5, 105, 21}, - {21, 0, 0, 5, 105, 17}, - {10, 216, 0, 5, 0, 21}, - {10, 226, 0, 5, 0, 21}, - {12, 230, 13, 5, 2, 21}, - {25, 0, 0, 5, 0, 12}, - {13, 0, 8, 5, 0, 11}, - {26, 0, 0, 5, 131, 12}, - {12, 0, 13, 5, 131, 21}, - {21, 0, 0, 5, 131, 17}, - {21, 0, 0, 5, 131, 12}, - {12, 230, 13, 5, 56, 21}, - {7, 0, 3, 5, 113, 12}, - {15, 0, 3, 5, 113, 12}, - {12, 220, 13, 5, 113, 21}, - {9, 0, 3, 5, 132, 12}, - {5, 0, 3, 5, 132, 12}, - {12, 230, 13, 5, 132, 21}, - {12, 7, 13, 5, 132, 21}, - {13, 0, 3, 5, 132, 11}, - {21, 0, 3, 5, 132, 0}, - {15, 0, 4, 5, 0, 12}, - {26, 0, 4, 5, 0, 10}, - {23, 0, 4, 5, 0, 10}, - {2, 0, 18, 5, 102, 14}, - {26, 0, 0, 2, 0, 29}, - {26, 0, 0, 5, 0, 28}, - {26, 0, 0, 2, 32, 14}, - {24, 0, 18, 2, 0, 42}, - {26, 0, 18, 5, 0, 5}, -}; - -#define BIDI_MIRROR_LEN 420 -static const MirrorPair mirror_pairs[] = { - {40, 41}, - {41, 40}, - {60, 62}, - {62, 60}, - {91, 93}, - {93, 91}, - {123, 125}, - {125, 123}, - {171, 187}, - {187, 171}, - {3898, 3899}, - {3899, 3898}, - {3900, 3901}, - {3901, 3900}, - {5787, 5788}, - {5788, 5787}, - {8249, 8250}, - {8250, 8249}, - {8261, 8262}, - {8262, 8261}, - {8317, 8318}, - {8318, 8317}, - {8333, 8334}, - {8334, 8333}, - {8712, 8715}, - {8713, 8716}, - {8714, 8717}, - {8715, 8712}, - {8716, 8713}, - {8717, 8714}, - {8725, 10741}, - {8735, 11262}, - {8736, 10659}, - {8737, 10651}, - {8738, 10656}, - {8740, 10990}, - {8764, 8765}, - {8765, 8764}, - {8771, 8909}, - {8773, 8780}, - {8780, 8773}, - {8786, 8787}, - {8787, 8786}, - {8788, 8789}, - {8789, 8788}, - {8804, 8805}, - {8805, 8804}, - {8806, 8807}, - {8807, 8806}, - {8808, 8809}, - {8809, 8808}, - {8810, 8811}, - {8811, 8810}, - {8814, 8815}, - {8815, 8814}, - {8816, 8817}, - {8817, 8816}, - {8818, 8819}, - {8819, 8818}, - {8820, 8821}, - {8821, 8820}, - {8822, 8823}, - {8823, 8822}, - {8824, 8825}, - {8825, 8824}, - {8826, 8827}, - {8827, 8826}, - {8828, 8829}, - {8829, 8828}, - {8830, 8831}, - {8831, 8830}, - {8832, 8833}, - {8833, 8832}, - {8834, 8835}, - {8835, 8834}, - {8836, 8837}, - {8837, 8836}, - {8838, 8839}, - {8839, 8838}, - {8840, 8841}, - {8841, 8840}, - {8842, 8843}, - {8843, 8842}, - {8847, 8848}, - {8848, 8847}, - {8849, 8850}, - {8850, 8849}, - {8856, 10680}, - {8866, 8867}, - {8867, 8866}, - {8870, 10974}, - {8872, 10980}, - {8873, 10979}, - {8875, 10981}, - {8880, 8881}, - {8881, 8880}, - {8882, 8883}, - {8883, 8882}, - {8884, 8885}, - {8885, 8884}, - {8886, 8887}, - {8887, 8886}, - {8888, 10204}, - {8905, 8906}, - {8906, 8905}, - {8907, 8908}, - {8908, 8907}, - {8909, 8771}, - {8912, 8913}, - {8913, 8912}, - {8918, 8919}, - {8919, 8918}, - {8920, 8921}, - {8921, 8920}, - {8922, 8923}, - {8923, 8922}, - {8924, 8925}, - {8925, 8924}, - {8926, 8927}, - {8927, 8926}, - {8928, 8929}, - {8929, 8928}, - {8930, 8931}, - {8931, 8930}, - {8932, 8933}, - {8933, 8932}, - {8934, 8935}, - {8935, 8934}, - {8936, 8937}, - {8937, 8936}, - {8938, 8939}, - {8939, 8938}, - {8940, 8941}, - {8941, 8940}, - {8944, 8945}, - {8945, 8944}, - {8946, 8954}, - {8947, 8955}, - {8948, 8956}, - {8950, 8957}, - {8951, 8958}, - {8954, 8946}, - {8955, 8947}, - {8956, 8948}, - {8957, 8950}, - {8958, 8951}, - {8968, 8969}, - {8969, 8968}, - {8970, 8971}, - {8971, 8970}, - {9001, 9002}, - {9002, 9001}, - {10088, 10089}, - {10089, 10088}, - {10090, 10091}, - {10091, 10090}, - {10092, 10093}, - {10093, 10092}, - {10094, 10095}, - {10095, 10094}, - {10096, 10097}, - {10097, 10096}, - {10098, 10099}, - {10099, 10098}, - {10100, 10101}, - {10101, 10100}, - {10179, 10180}, - {10180, 10179}, - {10181, 10182}, - {10182, 10181}, - {10184, 10185}, - {10185, 10184}, - {10187, 10189}, - {10189, 10187}, - {10197, 10198}, - {10198, 10197}, - {10204, 8888}, - {10205, 10206}, - {10206, 10205}, - {10210, 10211}, - {10211, 10210}, - {10212, 10213}, - {10213, 10212}, - {10214, 10215}, - {10215, 10214}, - {10216, 10217}, - {10217, 10216}, - {10218, 10219}, - {10219, 10218}, - {10220, 10221}, - {10221, 10220}, - {10222, 10223}, - {10223, 10222}, - {10627, 10628}, - {10628, 10627}, - {10629, 10630}, - {10630, 10629}, - {10631, 10632}, - {10632, 10631}, - {10633, 10634}, - {10634, 10633}, - {10635, 10636}, - {10636, 10635}, - {10637, 10640}, - {10638, 10639}, - {10639, 10638}, - {10640, 10637}, - {10641, 10642}, - {10642, 10641}, - {10643, 10644}, - {10644, 10643}, - {10645, 10646}, - {10646, 10645}, - {10647, 10648}, - {10648, 10647}, - {10651, 8737}, - {10656, 8738}, - {10659, 8736}, - {10660, 10661}, - {10661, 10660}, - {10664, 10665}, - {10665, 10664}, - {10666, 10667}, - {10667, 10666}, - {10668, 10669}, - {10669, 10668}, - {10670, 10671}, - {10671, 10670}, - {10680, 8856}, - {10688, 10689}, - {10689, 10688}, - {10692, 10693}, - {10693, 10692}, - {10703, 10704}, - {10704, 10703}, - {10705, 10706}, - {10706, 10705}, - {10708, 10709}, - {10709, 10708}, - {10712, 10713}, - {10713, 10712}, - {10714, 10715}, - {10715, 10714}, - {10728, 10729}, - {10729, 10728}, - {10741, 8725}, - {10744, 10745}, - {10745, 10744}, - {10748, 10749}, - {10749, 10748}, - {10795, 10796}, - {10796, 10795}, - {10797, 10798}, - {10798, 10797}, - {10804, 10805}, - {10805, 10804}, - {10812, 10813}, - {10813, 10812}, - {10852, 10853}, - {10853, 10852}, - {10873, 10874}, - {10874, 10873}, - {10875, 10876}, - {10876, 10875}, - {10877, 10878}, - {10878, 10877}, - {10879, 10880}, - {10880, 10879}, - {10881, 10882}, - {10882, 10881}, - {10883, 10884}, - {10884, 10883}, - {10885, 10886}, - {10886, 10885}, - {10887, 10888}, - {10888, 10887}, - {10889, 10890}, - {10890, 10889}, - {10891, 10892}, - {10892, 10891}, - {10893, 10894}, - {10894, 10893}, - {10895, 10896}, - {10896, 10895}, - {10897, 10898}, - {10898, 10897}, - {10899, 10900}, - {10900, 10899}, - {10901, 10902}, - {10902, 10901}, - {10903, 10904}, - {10904, 10903}, - {10905, 10906}, - {10906, 10905}, - {10907, 10908}, - {10908, 10907}, - {10909, 10910}, - {10910, 10909}, - {10911, 10912}, - {10912, 10911}, - {10913, 10914}, - {10914, 10913}, - {10918, 10919}, - {10919, 10918}, - {10920, 10921}, - {10921, 10920}, - {10922, 10923}, - {10923, 10922}, - {10924, 10925}, - {10925, 10924}, - {10927, 10928}, - {10928, 10927}, - {10929, 10930}, - {10930, 10929}, - {10931, 10932}, - {10932, 10931}, - {10933, 10934}, - {10934, 10933}, - {10935, 10936}, - {10936, 10935}, - {10937, 10938}, - {10938, 10937}, - {10939, 10940}, - {10940, 10939}, - {10941, 10942}, - {10942, 10941}, - {10943, 10944}, - {10944, 10943}, - {10945, 10946}, - {10946, 10945}, - {10947, 10948}, - {10948, 10947}, - {10949, 10950}, - {10950, 10949}, - {10951, 10952}, - {10952, 10951}, - {10953, 10954}, - {10954, 10953}, - {10955, 10956}, - {10956, 10955}, - {10957, 10958}, - {10958, 10957}, - {10959, 10960}, - {10960, 10959}, - {10961, 10962}, - {10962, 10961}, - {10963, 10964}, - {10964, 10963}, - {10965, 10966}, - {10966, 10965}, - {10974, 8870}, - {10979, 8873}, - {10980, 8872}, - {10981, 8875}, - {10988, 10989}, - {10989, 10988}, - {10990, 8740}, - {10999, 11000}, - {11000, 10999}, - {11001, 11002}, - {11002, 11001}, - {11262, 8735}, - {11778, 11779}, - {11779, 11778}, - {11780, 11781}, - {11781, 11780}, - {11785, 11786}, - {11786, 11785}, - {11788, 11789}, - {11789, 11788}, - {11804, 11805}, - {11805, 11804}, - {11808, 11809}, - {11809, 11808}, - {11810, 11811}, - {11811, 11810}, - {11812, 11813}, - {11813, 11812}, - {11814, 11815}, - {11815, 11814}, - {11816, 11817}, - {11817, 11816}, - {12296, 12297}, - {12297, 12296}, - {12298, 12299}, - {12299, 12298}, - {12300, 12301}, - {12301, 12300}, - {12302, 12303}, - {12303, 12302}, - {12304, 12305}, - {12305, 12304}, - {12308, 12309}, - {12309, 12308}, - {12310, 12311}, - {12311, 12310}, - {12312, 12313}, - {12313, 12312}, - {12314, 12315}, - {12315, 12314}, - {65113, 65114}, - {65114, 65113}, - {65115, 65116}, - {65116, 65115}, - {65117, 65118}, - {65118, 65117}, - {65124, 65125}, - {65125, 65124}, - {65288, 65289}, - {65289, 65288}, - {65308, 65310}, - {65310, 65308}, - {65339, 65341}, - {65341, 65339}, - {65371, 65373}, - {65373, 65371}, - {65375, 65376}, - {65376, 65375}, - {65378, 65379}, - {65379, 65378}, -}; - -#define BIDI_BRACKET_LEN 120 -static const BracketPair bracket_pairs[] = { - {40, 41, 0}, - {41, 40, 1}, - {91, 93, 0}, - {93, 91, 1}, - {123, 125, 0}, - {125, 123, 1}, - {3898, 3899, 0}, - {3899, 3898, 1}, - {3900, 3901, 0}, - {3901, 3900, 1}, - {5787, 5788, 0}, - {5788, 5787, 1}, - {8261, 8262, 0}, - {8262, 8261, 1}, - {8317, 8318, 0}, - {8318, 8317, 1}, - {8333, 8334, 0}, - {8334, 8333, 1}, - {8968, 8969, 0}, - {8969, 8968, 1}, - {8970, 8971, 0}, - {8971, 8970, 1}, - {9001, 9002, 0}, - {9002, 9001, 1}, - {10088, 10089, 0}, - {10089, 10088, 1}, - {10090, 10091, 0}, - {10091, 10090, 1}, - {10092, 10093, 0}, - {10093, 10092, 1}, - {10094, 10095, 0}, - {10095, 10094, 1}, - {10096, 10097, 0}, - {10097, 10096, 1}, - {10098, 10099, 0}, - {10099, 10098, 1}, - {10100, 10101, 0}, - {10101, 10100, 1}, - {10181, 10182, 0}, - {10182, 10181, 1}, - {10214, 10215, 0}, - {10215, 10214, 1}, - {10216, 10217, 0}, - {10217, 10216, 1}, - {10218, 10219, 0}, - {10219, 10218, 1}, - {10220, 10221, 0}, - {10221, 10220, 1}, - {10222, 10223, 0}, - {10223, 10222, 1}, - {10627, 10628, 0}, - {10628, 10627, 1}, - {10629, 10630, 0}, - {10630, 10629, 1}, - {10631, 10632, 0}, - {10632, 10631, 1}, - {10633, 10634, 0}, - {10634, 10633, 1}, - {10635, 10636, 0}, - {10636, 10635, 1}, - {10637, 10640, 0}, - {10638, 10639, 1}, - {10639, 10638, 0}, - {10640, 10637, 1}, - {10641, 10642, 0}, - {10642, 10641, 1}, - {10643, 10644, 0}, - {10644, 10643, 1}, - {10645, 10646, 0}, - {10646, 10645, 1}, - {10647, 10648, 0}, - {10648, 10647, 1}, - {10712, 10713, 0}, - {10713, 10712, 1}, - {10714, 10715, 0}, - {10715, 10714, 1}, - {10748, 10749, 0}, - {10749, 10748, 1}, - {11810, 11811, 0}, - {11811, 11810, 1}, - {11812, 11813, 0}, - {11813, 11812, 1}, - {11814, 11815, 0}, - {11815, 11814, 1}, - {11816, 11817, 0}, - {11817, 11816, 1}, - {12296, 12297, 0}, - {12297, 12296, 1}, - {12298, 12299, 0}, - {12299, 12298, 1}, - {12300, 12301, 0}, - {12301, 12300, 1}, - {12302, 12303, 0}, - {12303, 12302, 1}, - {12304, 12305, 0}, - {12305, 12304, 1}, - {12308, 12309, 0}, - {12309, 12308, 1}, - {12310, 12311, 0}, - {12311, 12310, 1}, - {12312, 12313, 0}, - {12313, 12312, 1}, - {12314, 12315, 0}, - {12315, 12314, 1}, - {65113, 65114, 0}, - {65114, 65113, 1}, - {65115, 65116, 0}, - {65116, 65115, 1}, - {65117, 65118, 0}, - {65118, 65117, 1}, - {65288, 65289, 0}, - {65289, 65288, 1}, - {65339, 65341, 0}, - {65341, 65339, 1}, - {65371, 65373, 0}, - {65373, 65371, 1}, - {65375, 65376, 0}, - {65376, 65375, 1}, - {65378, 65379, 0}, - {65379, 65378, 1}, -}; - -/* Reindexing of NFC first characters. */ -#define TOTAL_FIRST 376 -#define TOTAL_LAST 62 -static const Reindex nfc_first[] = { - { 60, 2, 0}, - { 65, 15, 3}, - { 82, 8, 19}, - { 97, 15, 28}, - { 114, 8, 44}, - { 168, 0, 53}, - { 194, 0, 54}, - { 196, 3, 55}, - { 202, 0, 59}, - { 207, 0, 60}, - { 212, 2, 61}, - { 216, 0, 64}, - { 220, 0, 65}, - { 226, 0, 66}, - { 228, 3, 67}, - { 234, 0, 71}, - { 239, 0, 72}, - { 244, 2, 73}, - { 248, 0, 76}, - { 252, 0, 77}, - { 258, 1, 78}, - { 274, 1, 80}, - { 332, 1, 82}, - { 346, 1, 84}, - { 352, 1, 86}, - { 360, 3, 88}, - { 383, 0, 92}, - { 416, 1, 93}, - { 431, 1, 95}, - { 439, 0, 97}, - { 490, 1, 98}, - { 550, 3, 100}, - { 558, 1, 104}, - { 658, 0, 106}, - { 913, 0, 107}, - { 917, 0, 108}, - { 919, 0, 109}, - { 921, 0, 110}, - { 927, 0, 111}, - { 929, 0, 112}, - { 933, 0, 113}, - { 937, 0, 114}, - { 940, 0, 115}, - { 942, 0, 116}, - { 945, 0, 117}, - { 949, 0, 118}, - { 951, 0, 119}, - { 953, 0, 120}, - { 959, 0, 121}, - { 961, 0, 122}, - { 965, 0, 123}, - { 969, 2, 124}, - { 974, 0, 127}, - { 978, 0, 128}, - { 1030, 0, 129}, - { 1040, 0, 130}, - { 1043, 0, 131}, - { 1045, 3, 132}, - { 1050, 0, 136}, - { 1054, 0, 137}, - { 1059, 0, 138}, - { 1063, 0, 139}, - { 1067, 0, 140}, - { 1069, 0, 141}, - { 1072, 0, 142}, - { 1075, 0, 143}, - { 1077, 3, 144}, - { 1082, 0, 148}, - { 1086, 0, 149}, - { 1091, 0, 150}, - { 1095, 0, 151}, - { 1099, 0, 152}, - { 1101, 0, 153}, - { 1110, 0, 154}, - { 1140, 1, 155}, - { 1240, 1, 157}, - { 1256, 1, 159}, - { 1575, 0, 161}, - { 1608, 0, 162}, - { 1610, 0, 163}, - { 1729, 0, 164}, - { 1746, 0, 165}, - { 1749, 0, 166}, - { 2344, 0, 167}, - { 2352, 0, 168}, - { 2355, 0, 169}, - { 2503, 0, 170}, - { 2887, 0, 171}, - { 2962, 0, 172}, - { 3014, 1, 173}, - { 3142, 0, 175}, - { 3263, 0, 176}, - { 3270, 0, 177}, - { 3274, 0, 178}, - { 3398, 1, 179}, - { 3545, 0, 181}, - { 3548, 0, 182}, - { 4133, 0, 183}, - { 6917, 0, 184}, - { 6919, 0, 185}, - { 6921, 0, 186}, - { 6923, 0, 187}, - { 6925, 0, 188}, - { 6929, 0, 189}, - { 6970, 0, 190}, - { 6972, 0, 191}, - { 6974, 1, 192}, - { 6978, 0, 194}, - { 7734, 1, 195}, - { 7770, 1, 197}, - { 7778, 1, 199}, - { 7840, 1, 201}, - { 7864, 1, 203}, - { 7884, 1, 205}, - { 7936, 17, 207}, - { 7960, 1, 225}, - { 7968, 17, 227}, - { 7992, 1, 245}, - { 8000, 1, 247}, - { 8008, 1, 249}, - { 8016, 1, 251}, - { 8025, 0, 253}, - { 8032, 16, 254}, - { 8052, 0, 271}, - { 8060, 0, 272}, - { 8118, 0, 273}, - { 8127, 0, 274}, - { 8134, 0, 275}, - { 8182, 0, 276}, - { 8190, 0, 277}, - { 8592, 0, 278}, - { 8594, 0, 279}, - { 8596, 0, 280}, - { 8656, 0, 281}, - { 8658, 0, 282}, - { 8660, 0, 283}, - { 8707, 0, 284}, - { 8712, 0, 285}, - { 8715, 0, 286}, - { 8739, 0, 287}, - { 8741, 0, 288}, - { 8764, 0, 289}, - { 8771, 0, 290}, - { 8773, 0, 291}, - { 8776, 0, 292}, - { 8781, 0, 293}, - { 8801, 0, 294}, - { 8804, 1, 295}, - { 8818, 1, 297}, - { 8822, 1, 299}, - { 8826, 3, 301}, - { 8834, 1, 305}, - { 8838, 1, 307}, - { 8849, 1, 309}, - { 8866, 0, 311}, - { 8872, 1, 312}, - { 8875, 0, 314}, - { 8882, 3, 315}, - { 12358, 0, 319}, - { 12363, 0, 320}, - { 12365, 0, 321}, - { 12367, 0, 322}, - { 12369, 0, 323}, - { 12371, 0, 324}, - { 12373, 0, 325}, - { 12375, 0, 326}, - { 12377, 0, 327}, - { 12379, 0, 328}, - { 12381, 0, 329}, - { 12383, 0, 330}, - { 12385, 0, 331}, - { 12388, 0, 332}, - { 12390, 0, 333}, - { 12392, 0, 334}, - { 12399, 0, 335}, - { 12402, 0, 336}, - { 12405, 0, 337}, - { 12408, 0, 338}, - { 12411, 0, 339}, - { 12445, 0, 340}, - { 12454, 0, 341}, - { 12459, 0, 342}, - { 12461, 0, 343}, - { 12463, 0, 344}, - { 12465, 0, 345}, - { 12467, 0, 346}, - { 12469, 0, 347}, - { 12471, 0, 348}, - { 12473, 0, 349}, - { 12475, 0, 350}, - { 12477, 0, 351}, - { 12479, 0, 352}, - { 12481, 0, 353}, - { 12484, 0, 354}, - { 12486, 0, 355}, - { 12488, 0, 356}, - { 12495, 0, 357}, - { 12498, 0, 358}, - { 12501, 0, 359}, - { 12504, 0, 360}, - { 12507, 0, 361}, - { 12527, 3, 362}, - { 12541, 0, 366}, - { 69785, 0, 367}, - { 69787, 0, 368}, - { 69797, 0, 369}, - { 69937, 1, 370}, - { 70471, 0, 372}, - { 70841, 0, 373}, - { 71096, 1, 374}, - {0,0,0} -}; - -static const Reindex nfc_last[] = { - { 768, 4, 0}, - { 774, 6, 5}, - { 783, 0, 12}, - { 785, 0, 13}, - { 787, 1, 14}, - { 795, 0, 16}, - { 803, 5, 17}, - { 813, 1, 23}, - { 816, 1, 25}, - { 824, 0, 27}, - { 834, 0, 28}, - { 837, 0, 29}, - { 1619, 2, 30}, - { 2364, 0, 33}, - { 2494, 0, 34}, - { 2519, 0, 35}, - { 2878, 0, 36}, - { 2902, 1, 37}, - { 3006, 0, 39}, - { 3031, 0, 40}, - { 3158, 0, 41}, - { 3266, 0, 42}, - { 3285, 1, 43}, - { 3390, 0, 45}, - { 3415, 0, 46}, - { 3530, 0, 47}, - { 3535, 0, 48}, - { 3551, 0, 49}, - { 4142, 0, 50}, - { 6965, 0, 51}, - { 12441, 1, 52}, - { 69818, 0, 54}, - { 69927, 0, 55}, - { 70462, 0, 56}, - { 70487, 0, 57}, - { 70832, 0, 58}, - { 70842, 0, 59}, - { 70845, 0, 60}, - { 71087, 0, 61}, - {0,0,0} -}; - -#define UCDN_EAST_ASIAN_F 0 -#define UCDN_EAST_ASIAN_H 1 -#define UCDN_EAST_ASIAN_W 2 -#define UCDN_EAST_ASIAN_NA 3 -#define UCDN_EAST_ASIAN_A 4 -#define UCDN_EAST_ASIAN_N 5 - -#define UCDN_SCRIPT_COMMON 0 -#define UCDN_SCRIPT_LATIN 1 -#define UCDN_SCRIPT_GREEK 2 -#define UCDN_SCRIPT_CYRILLIC 3 -#define UCDN_SCRIPT_ARMENIAN 4 -#define UCDN_SCRIPT_HEBREW 5 -#define UCDN_SCRIPT_ARABIC 6 -#define UCDN_SCRIPT_SYRIAC 7 -#define UCDN_SCRIPT_THAANA 8 -#define UCDN_SCRIPT_DEVANAGARI 9 -#define UCDN_SCRIPT_BENGALI 10 -#define UCDN_SCRIPT_GURMUKHI 11 -#define UCDN_SCRIPT_GUJARATI 12 -#define UCDN_SCRIPT_ORIYA 13 -#define UCDN_SCRIPT_TAMIL 14 -#define UCDN_SCRIPT_TELUGU 15 -#define UCDN_SCRIPT_KANNADA 16 -#define UCDN_SCRIPT_MALAYALAM 17 -#define UCDN_SCRIPT_SINHALA 18 -#define UCDN_SCRIPT_THAI 19 -#define UCDN_SCRIPT_LAO 20 -#define UCDN_SCRIPT_TIBETAN 21 -#define UCDN_SCRIPT_MYANMAR 22 -#define UCDN_SCRIPT_GEORGIAN 23 -#define UCDN_SCRIPT_HANGUL 24 -#define UCDN_SCRIPT_ETHIOPIC 25 -#define UCDN_SCRIPT_CHEROKEE 26 -#define UCDN_SCRIPT_CANADIAN_ABORIGINAL 27 -#define UCDN_SCRIPT_OGHAM 28 -#define UCDN_SCRIPT_RUNIC 29 -#define UCDN_SCRIPT_KHMER 30 -#define UCDN_SCRIPT_MONGOLIAN 31 -#define UCDN_SCRIPT_HIRAGANA 32 -#define UCDN_SCRIPT_KATAKANA 33 -#define UCDN_SCRIPT_BOPOMOFO 34 -#define UCDN_SCRIPT_HAN 35 -#define UCDN_SCRIPT_YI 36 -#define UCDN_SCRIPT_OLD_ITALIC 37 -#define UCDN_SCRIPT_GOTHIC 38 -#define UCDN_SCRIPT_DESERET 39 -#define UCDN_SCRIPT_INHERITED 40 -#define UCDN_SCRIPT_TAGALOG 41 -#define UCDN_SCRIPT_HANUNOO 42 -#define UCDN_SCRIPT_BUHID 43 -#define UCDN_SCRIPT_TAGBANWA 44 -#define UCDN_SCRIPT_LIMBU 45 -#define UCDN_SCRIPT_TAI_LE 46 -#define UCDN_SCRIPT_LINEAR_B 47 -#define UCDN_SCRIPT_UGARITIC 48 -#define UCDN_SCRIPT_SHAVIAN 49 -#define UCDN_SCRIPT_OSMANYA 50 -#define UCDN_SCRIPT_CYPRIOT 51 -#define UCDN_SCRIPT_BRAILLE 52 -#define UCDN_SCRIPT_BUGINESE 53 -#define UCDN_SCRIPT_COPTIC 54 -#define UCDN_SCRIPT_NEW_TAI_LUE 55 -#define UCDN_SCRIPT_GLAGOLITIC 56 -#define UCDN_SCRIPT_TIFINAGH 57 -#define UCDN_SCRIPT_SYLOTI_NAGRI 58 -#define UCDN_SCRIPT_OLD_PERSIAN 59 -#define UCDN_SCRIPT_KHAROSHTHI 60 -#define UCDN_SCRIPT_BALINESE 61 -#define UCDN_SCRIPT_CUNEIFORM 62 -#define UCDN_SCRIPT_PHOENICIAN 63 -#define UCDN_SCRIPT_PHAGS_PA 64 -#define UCDN_SCRIPT_NKO 65 -#define UCDN_SCRIPT_SUNDANESE 66 -#define UCDN_SCRIPT_LEPCHA 67 -#define UCDN_SCRIPT_OL_CHIKI 68 -#define UCDN_SCRIPT_VAI 69 -#define UCDN_SCRIPT_SAURASHTRA 70 -#define UCDN_SCRIPT_KAYAH_LI 71 -#define UCDN_SCRIPT_REJANG 72 -#define UCDN_SCRIPT_LYCIAN 73 -#define UCDN_SCRIPT_CARIAN 74 -#define UCDN_SCRIPT_LYDIAN 75 -#define UCDN_SCRIPT_CHAM 76 -#define UCDN_SCRIPT_TAI_THAM 77 -#define UCDN_SCRIPT_TAI_VIET 78 -#define UCDN_SCRIPT_AVESTAN 79 -#define UCDN_SCRIPT_EGYPTIAN_HIEROGLYPHS 80 -#define UCDN_SCRIPT_SAMARITAN 81 -#define UCDN_SCRIPT_LISU 82 -#define UCDN_SCRIPT_BAMUM 83 -#define UCDN_SCRIPT_JAVANESE 84 -#define UCDN_SCRIPT_MEETEI_MAYEK 85 -#define UCDN_SCRIPT_IMPERIAL_ARAMAIC 86 -#define UCDN_SCRIPT_OLD_SOUTH_ARABIAN 87 -#define UCDN_SCRIPT_INSCRIPTIONAL_PARTHIAN 88 -#define UCDN_SCRIPT_INSCRIPTIONAL_PAHLAVI 89 -#define UCDN_SCRIPT_OLD_TURKIC 90 -#define UCDN_SCRIPT_KAITHI 91 -#define UCDN_SCRIPT_BATAK 92 -#define UCDN_SCRIPT_BRAHMI 93 -#define UCDN_SCRIPT_MANDAIC 94 -#define UCDN_SCRIPT_CHAKMA 95 -#define UCDN_SCRIPT_MEROITIC_CURSIVE 96 -#define UCDN_SCRIPT_MEROITIC_HIEROGLYPHS 97 -#define UCDN_SCRIPT_MIAO 98 -#define UCDN_SCRIPT_SHARADA 99 -#define UCDN_SCRIPT_SORA_SOMPENG 100 -#define UCDN_SCRIPT_TAKRI 101 -#define UCDN_SCRIPT_UNKNOWN 102 -#define UCDN_SCRIPT_BASSA_VAH 103 -#define UCDN_SCRIPT_CAUCASIAN_ALBANIAN 104 -#define UCDN_SCRIPT_DUPLOYAN 105 -#define UCDN_SCRIPT_ELBASAN 106 -#define UCDN_SCRIPT_GRANTHA 107 -#define UCDN_SCRIPT_KHOJKI 108 -#define UCDN_SCRIPT_KHUDAWADI 109 -#define UCDN_SCRIPT_LINEAR_A 110 -#define UCDN_SCRIPT_MAHAJANI 111 -#define UCDN_SCRIPT_MANICHAEAN 112 -#define UCDN_SCRIPT_MENDE_KIKAKUI 113 -#define UCDN_SCRIPT_MODI 114 -#define UCDN_SCRIPT_MRO 115 -#define UCDN_SCRIPT_NABATAEAN 116 -#define UCDN_SCRIPT_OLD_NORTH_ARABIAN 117 -#define UCDN_SCRIPT_OLD_PERMIC 118 -#define UCDN_SCRIPT_PAHAWH_HMONG 119 -#define UCDN_SCRIPT_PALMYRENE 120 -#define UCDN_SCRIPT_PAU_CIN_HAU 121 -#define UCDN_SCRIPT_PSALTER_PAHLAVI 122 -#define UCDN_SCRIPT_SIDDHAM 123 -#define UCDN_SCRIPT_TIRHUTA 124 -#define UCDN_SCRIPT_WARANG_CITI 125 -#define UCDN_SCRIPT_AHOM 126 -#define UCDN_SCRIPT_ANATOLIAN_HIEROGLYPHS 127 -#define UCDN_SCRIPT_HATRAN 128 -#define UCDN_SCRIPT_MULTANI 129 -#define UCDN_SCRIPT_OLD_HUNGARIAN 130 -#define UCDN_SCRIPT_SIGNWRITING 131 -#define UCDN_SCRIPT_ADLAM 132 -#define UCDN_SCRIPT_BHAIKSUKI 133 -#define UCDN_SCRIPT_MARCHEN 134 -#define UCDN_SCRIPT_NEWA 135 -#define UCDN_SCRIPT_OSAGE 136 -#define UCDN_SCRIPT_TANGUT 137 -#define UCDN_SCRIPT_MASARAM_GONDI 138 -#define UCDN_SCRIPT_NUSHU 139 -#define UCDN_SCRIPT_SOYOMBO 140 -#define UCDN_SCRIPT_ZANABAZAR_SQUARE 141 -#define UCDN_SCRIPT_DOGRA 142 -#define UCDN_SCRIPT_GUNJALA_GONDI 143 -#define UCDN_SCRIPT_HANIFI_ROHINGYA 144 -#define UCDN_SCRIPT_MAKASAR 145 -#define UCDN_SCRIPT_MEDEFAIDRIN 146 -#define UCDN_SCRIPT_OLD_SOGDIAN 147 -#define UCDN_SCRIPT_SOGDIAN 148 - -#define UCDN_GENERAL_CATEGORY_CC 0 -#define UCDN_GENERAL_CATEGORY_CF 1 -#define UCDN_GENERAL_CATEGORY_CN 2 -#define UCDN_GENERAL_CATEGORY_CO 3 -#define UCDN_GENERAL_CATEGORY_CS 4 -#define UCDN_GENERAL_CATEGORY_LL 5 -#define UCDN_GENERAL_CATEGORY_LM 6 -#define UCDN_GENERAL_CATEGORY_LO 7 -#define UCDN_GENERAL_CATEGORY_LT 8 -#define UCDN_GENERAL_CATEGORY_LU 9 -#define UCDN_GENERAL_CATEGORY_MC 10 -#define UCDN_GENERAL_CATEGORY_ME 11 -#define UCDN_GENERAL_CATEGORY_MN 12 -#define UCDN_GENERAL_CATEGORY_ND 13 -#define UCDN_GENERAL_CATEGORY_NL 14 -#define UCDN_GENERAL_CATEGORY_NO 15 -#define UCDN_GENERAL_CATEGORY_PC 16 -#define UCDN_GENERAL_CATEGORY_PD 17 -#define UCDN_GENERAL_CATEGORY_PE 18 -#define UCDN_GENERAL_CATEGORY_PF 19 -#define UCDN_GENERAL_CATEGORY_PI 20 -#define UCDN_GENERAL_CATEGORY_PO 21 -#define UCDN_GENERAL_CATEGORY_PS 22 -#define UCDN_GENERAL_CATEGORY_SC 23 -#define UCDN_GENERAL_CATEGORY_SK 24 -#define UCDN_GENERAL_CATEGORY_SM 25 -#define UCDN_GENERAL_CATEGORY_SO 26 -#define UCDN_GENERAL_CATEGORY_ZL 27 -#define UCDN_GENERAL_CATEGORY_ZP 28 -#define UCDN_GENERAL_CATEGORY_ZS 29 - -#define UCDN_BIDI_CLASS_L 0 -#define UCDN_BIDI_CLASS_LRE 1 -#define UCDN_BIDI_CLASS_LRO 2 -#define UCDN_BIDI_CLASS_R 3 -#define UCDN_BIDI_CLASS_AL 4 -#define UCDN_BIDI_CLASS_RLE 5 -#define UCDN_BIDI_CLASS_RLO 6 -#define UCDN_BIDI_CLASS_PDF 7 -#define UCDN_BIDI_CLASS_EN 8 -#define UCDN_BIDI_CLASS_ES 9 -#define UCDN_BIDI_CLASS_ET 10 -#define UCDN_BIDI_CLASS_AN 11 -#define UCDN_BIDI_CLASS_CS 12 -#define UCDN_BIDI_CLASS_NSM 13 -#define UCDN_BIDI_CLASS_BN 14 -#define UCDN_BIDI_CLASS_B 15 -#define UCDN_BIDI_CLASS_S 16 -#define UCDN_BIDI_CLASS_WS 17 -#define UCDN_BIDI_CLASS_ON 18 -#define UCDN_BIDI_CLASS_LRI 19 -#define UCDN_BIDI_CLASS_RLI 20 -#define UCDN_BIDI_CLASS_FSI 21 -#define UCDN_BIDI_CLASS_PDI 22 - -/* index tables for the database records */ -#define SHIFT1 5 -#define SHIFT2 3 -static const unsigned char index0[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 53, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 54, 55, 56, 56, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, - 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, - 66, 67, 68, 69, 70, 71, 65, 66, 67, 68, 69, 70, 71, 65, 72, 73, 73, 73, - 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 52, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 106, 108, 109, 110, 106, - 111, 111, 111, 112, 113, 114, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 115, 115, 116, 117, 118, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 119, 120, 121, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 122, 122, 123, 124, 106, 106, 125, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 128, 127, 127, 129, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 130, 131, 132, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 133, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 134, 135, 136, 137, 138, 139, - 140, 141, 142, 142, 143, 106, 106, 106, 106, 106, 144, 106, 106, 106, - 106, 106, 106, 106, 145, 146, 106, 106, 147, 106, 148, 106, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 160, 160, 160, 161, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 162, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 163, 164, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 165, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 52, 52, - 168, 167, 167, 167, 167, 169, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 169, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 170, 171, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 172, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 172, -}; - -static const unsigned short index1[] = { - 0, 1, 0, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 11, 12, 13, 0, 0, 0, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 29, 31, 32, - 33, 34, 35, 27, 30, 29, 27, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 27, 27, 49, 27, 27, 27, 27, 27, 27, 27, 50, 51, 52, 27, 53, 54, - 53, 54, 54, 54, 54, 54, 55, 54, 54, 54, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 65, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 97, 97, 97, 97, 98, 98, 98, 98, 99, 100, 101, 101, 101, 101, 102, 103, - 101, 101, 101, 101, 101, 101, 104, 105, 101, 101, 101, 101, 101, 101, - 101, 101, 101, 101, 101, 101, 106, 107, 107, 107, 108, 109, 110, 110, - 110, 110, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 120, - 120, 121, 122, 119, 123, 124, 125, 126, 127, 127, 127, 127, 128, 129, - 130, 131, 132, 133, 134, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 144, - 145, 146, 147, 148, 127, 127, 127, 127, 127, 127, 149, 149, 149, 149, - 150, 151, 152, 119, 153, 154, 155, 155, 155, 156, 157, 158, 159, 159, - 160, 161, 162, 163, 164, 165, 166, 166, 166, 167, 144, 168, 119, 119, - 119, 119, 119, 119, 127, 127, 169, 170, 119, 119, 171, 125, 172, 173, - 174, 175, 176, 177, 177, 177, 177, 177, 177, 178, 179, 180, 181, 177, - 182, 183, 184, 177, 185, 186, 187, 188, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, - 207, 208, 209, 210, 211, 212, 213, 119, 214, 215, 216, 217, 217, 218, - 219, 220, 221, 222, 223, 119, 224, 225, 226, 227, 228, 229, 230, 231, - 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 119, 242, 243, - 244, 245, 246, 243, 247, 248, 249, 250, 251, 119, 252, 253, 254, 255, - 256, 257, 258, 259, 259, 258, 259, 260, 261, 262, 263, 264, 265, 266, - 119, 267, 268, 269, 270, 271, 271, 270, 272, 273, 274, 275, 276, 277, - 278, 279, 280, 119, 281, 282, 283, 284, 284, 284, 284, 285, 286, 287, - 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 295, 295, 298, 299, - 296, 300, 301, 302, 303, 304, 305, 119, 306, 307, 307, 307, 307, 307, - 308, 309, 310, 311, 312, 313, 119, 119, 119, 119, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 325, 119, 119, 119, 119, 326, 327, - 328, 329, 330, 331, 332, 333, 334, 335, 334, 334, 334, 336, 337, 338, - 339, 340, 341, 342, 341, 341, 341, 343, 344, 345, 346, 347, 119, 119, - 119, 119, 348, 348, 348, 348, 348, 349, 350, 351, 352, 353, 354, 355, - 356, 357, 358, 348, 359, 360, 352, 361, 362, 362, 362, 362, 363, 364, - 365, 365, 365, 365, 365, 366, 367, 367, 367, 367, 367, 367, 367, 367, - 367, 367, 367, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, 369, - 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, - 370, 370, 370, 370, 370, 371, 372, 371, 370, 370, 370, 370, 370, 371, - 370, 370, 370, 370, 371, 372, 371, 370, 372, 370, 370, 370, 370, 370, - 370, 370, 371, 370, 370, 370, 370, 370, 370, 370, 370, 373, 374, 375, - 376, 377, 370, 370, 378, 379, 380, 380, 380, 380, 380, 380, 380, 380, - 380, 380, 381, 382, 383, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 384, 384, - 386, 387, 387, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, - 391, 392, 393, 394, 395, 119, 396, 396, 397, 119, 398, 398, 399, 119, - 400, 401, 402, 119, 403, 403, 403, 403, 403, 403, 404, 405, 406, 407, - 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 418, 418, 418, - 419, 418, 418, 418, 418, 418, 418, 420, 421, 418, 418, 418, 418, 422, - 384, 384, 384, 384, 384, 384, 384, 384, 423, 119, 424, 424, 424, 425, - 426, 427, 428, 429, 430, 431, 432, 432, 432, 433, 434, 119, 435, 435, - 435, 435, 435, 436, 435, 435, 435, 437, 438, 439, 440, 440, 440, 440, - 441, 441, 442, 443, 444, 444, 444, 444, 444, 444, 445, 446, 447, 448, - 449, 450, 451, 452, 451, 452, 453, 454, 455, 456, 119, 119, 119, 119, - 119, 119, 119, 119, 457, 458, 458, 458, 458, 458, 459, 460, 461, 462, - 463, 464, 465, 466, 467, 468, 469, 470, 470, 470, 471, 472, 473, 474, - 475, 475, 475, 475, 476, 477, 478, 479, 480, 480, 480, 480, 481, 482, - 483, 484, 485, 486, 487, 488, 489, 489, 489, 490, 100, 491, 362, 362, - 362, 362, 362, 492, 493, 119, 494, 495, 496, 497, 498, 499, 54, 54, 54, - 54, 500, 501, 56, 56, 56, 56, 56, 502, 503, 504, 54, 505, 54, 54, 54, - 506, 56, 56, 56, 507, 508, 509, 510, 511, 511, 511, 512, 513, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 514, 515, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 516, 517, 518, 519, 516, 517, - 516, 517, 518, 519, 516, 520, 516, 517, 516, 518, 516, 521, 516, 521, - 516, 521, 522, 523, 524, 525, 526, 527, 516, 528, 529, 530, 531, 532, - 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, - 547, 548, 56, 549, 550, 551, 552, 553, 554, 554, 555, 556, 557, 558, 559, - 119, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, - 573, 572, 574, 575, 576, 577, 578, 579, 580, 581, 582, 581, 583, 584, - 581, 585, 581, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 587, - 596, 597, 587, 598, 599, 587, 587, 599, 587, 600, 601, 600, 587, 587, - 602, 587, 587, 587, 587, 587, 603, 587, 587, 581, 604, 605, 606, 607, - 608, 609, 610, 610, 610, 610, 610, 610, 610, 610, 611, 581, 581, 612, - 613, 587, 587, 614, 581, 581, 581, 581, 586, 607, 615, 616, 581, 581, - 581, 581, 581, 617, 119, 119, 119, 581, 618, 119, 119, 619, 619, 619, - 619, 619, 620, 620, 621, 622, 622, 622, 622, 622, 622, 622, 622, 622, - 623, 619, 624, 625, 625, 625, 625, 625, 625, 625, 625, 625, 626, 625, - 625, 625, 625, 627, 581, 625, 625, 628, 581, 629, 630, 631, 632, 633, - 634, 630, 581, 628, 635, 581, 636, 637, 638, 639, 640, 581, 581, 581, - 641, 642, 643, 644, 581, 645, 646, 581, 647, 581, 581, 648, 649, 650, - 651, 581, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 581, - 581, 581, 663, 581, 664, 581, 665, 666, 667, 668, 669, 670, 619, 671, - 671, 672, 581, 581, 581, 663, 673, 674, 587, 587, 587, 675, 676, 587, - 587, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 678, 679, 679, 680, 587, 587, 587, - 587, 587, 587, 587, 681, 587, 587, 587, 682, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 581, - 581, 581, 683, 581, 581, 587, 587, 684, 685, 686, 630, 581, 581, 687, - 581, 581, 581, 688, 581, 581, 581, 581, 581, 581, 689, 581, 581, 581, - 581, 581, 617, 690, 690, 690, 690, 690, 691, 692, 692, 692, 692, 692, - 693, 694, 695, 696, 697, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 698, 699, 700, 701, 365, 365, 365, 365, 702, 703, 704, 704, 704, 704, - 704, 704, 704, 705, 706, 707, 370, 370, 372, 119, 372, 372, 372, 372, - 372, 372, 372, 372, 708, 708, 708, 708, 709, 710, 711, 712, 713, 714, - 715, 716, 717, 718, 119, 119, 119, 119, 119, 119, 719, 719, 719, 720, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 721, 119, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 722, 119, 119, 119, - 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 735, - 736, 735, 735, 735, 737, 738, 739, 740, 741, 742, 743, 743, 744, 743, - 743, 743, 745, 746, 747, 748, 749, 750, 750, 750, 750, 750, 751, 752, - 752, 752, 752, 752, 752, 752, 752, 752, 752, 753, 754, 755, 750, 750, - 750, 756, 723, 723, 723, 723, 724, 119, 757, 757, 758, 758, 758, 759, - 760, 761, 755, 755, 755, 762, 763, 764, 758, 758, 758, 765, 760, 761, - 755, 755, 755, 755, 766, 764, 755, 767, 768, 768, 768, 768, 768, 769, - 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 755, 755, 755, - 770, 771, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 772, - 755, 755, 755, 770, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 774, 775, 581, 581, 581, 581, 581, 581, 581, 581, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 775, 775, 776, 776, 777, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 778, - 779, 779, 779, 779, 779, 779, 780, 119, 781, 781, 781, 781, 781, 782, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 784, 783, 783, 785, 786, 119, 119, 101, 101, - 101, 101, 101, 787, 788, 789, 101, 101, 101, 790, 791, 791, 791, 791, - 791, 791, 791, 791, 792, 793, 794, 119, 64, 64, 795, 796, 797, 27, 798, - 27, 27, 27, 27, 27, 27, 27, 799, 800, 27, 801, 802, 27, 27, 803, 804, - 805, 119, 119, 119, 119, 119, 119, 806, 807, 808, 809, 810, 810, 811, - 812, 813, 814, 815, 815, 815, 815, 815, 815, 816, 119, 817, 818, 818, - 818, 818, 818, 819, 820, 821, 822, 823, 824, 825, 825, 826, 827, 828, - 829, 830, 830, 831, 832, 833, 833, 834, 835, 836, 837, 367, 367, 367, - 838, 839, 840, 840, 840, 840, 840, 841, 842, 843, 844, 845, 846, 847, - 348, 352, 848, 849, 849, 849, 849, 849, 850, 851, 119, 852, 853, 854, - 855, 348, 348, 856, 857, 858, 858, 858, 858, 858, 858, 859, 860, 861, - 119, 119, 862, 863, 864, 865, 119, 866, 866, 866, 119, 372, 372, 54, 54, - 54, 54, 54, 867, 868, 119, 869, 869, 869, 869, 869, 869, 869, 869, 869, - 869, 863, 863, 863, 863, 870, 871, 872, 873, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 875, 875, 875, 874, 875, - 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, - 875, 877, 119, 368, 368, 878, 879, 369, 369, 369, 369, 369, 880, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 883, 775, 775, 775, 775, 884, 119, 885, - 886, 120, 887, 888, 889, 890, 120, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 891, 892, 893, 119, 894, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 895, 119, - 119, 127, 127, 127, 127, 127, 127, 127, 127, 896, 127, 127, 127, 127, - 127, 127, 119, 119, 119, 119, 119, 127, 897, 898, 898, 899, 900, 901, - 902, 903, 904, 905, 906, 907, 908, 909, 910, 169, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 911, 912, - 913, 914, 915, 916, 917, 917, 918, 919, 920, 920, 921, 922, 923, 924, - 925, 925, 925, 925, 926, 927, 927, 927, 928, 929, 929, 929, 930, 931, - 932, 119, 933, 934, 935, 934, 934, 936, 934, 934, 937, 934, 938, 934, - 938, 119, 119, 119, 119, 934, 934, 934, 934, 934, 934, 934, 934, 934, - 934, 934, 934, 934, 934, 934, 939, 940, 941, 941, 941, 941, 941, 942, - 610, 943, 943, 943, 943, 943, 943, 944, 945, 946, 947, 581, 948, 949, - 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, 950, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 951, - 951, 951, 952, 953, 953, 953, 953, 953, 953, 954, 119, 955, 956, 956, - 957, 958, 958, 958, 958, 959, 960, 961, 961, 962, 963, 964, 964, 964, - 964, 965, 966, 967, 967, 967, 968, 969, 969, 969, 969, 970, 969, 971, - 119, 119, 119, 119, 119, 972, 972, 972, 972, 972, 973, 973, 973, 973, - 973, 974, 974, 974, 974, 974, 974, 975, 975, 975, 976, 977, 978, 979, - 979, 979, 979, 980, 981, 981, 981, 981, 982, 983, 983, 983, 983, 983, - 119, 984, 984, 984, 984, 984, 984, 985, 986, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 988, 119, 987, 987, 989, - 119, 987, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 990, 991, 992, 992, 992, 992, 993, - 994, 995, 995, 996, 997, 998, 998, 999, 1000, 1001, 1001, 1001, 1002, - 1003, 1004, 119, 119, 119, 119, 119, 119, 1005, 1005, 1006, 1007, 1008, - 1008, 1009, 1010, 1011, 1011, 1011, 1012, 119, 119, 119, 119, 119, 119, - 119, 119, 1013, 1013, 1013, 1013, 1014, 1014, 1014, 1015, 1016, 1016, - 1017, 1016, 1016, 1016, 1016, 1016, 1018, 1019, 1020, 1021, 1022, 1022, - 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1029, 1029, 1030, 1031, 1031, - 1031, 1032, 119, 119, 119, 119, 1033, 1034, 1033, 1033, 1035, 1036, 1037, - 119, 1038, 1038, 1038, 1038, 1038, 1038, 1039, 1040, 1041, 1041, 1042, - 1043, 1044, 1044, 1045, 1046, 1047, 1047, 1048, 1049, 119, 1050, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 1051, 1051, 1051, 1051, - 1051, 1051, 1051, 1051, 1051, 1052, 119, 119, 119, 119, 119, 119, 1053, - 1053, 1053, 1053, 1053, 1053, 1054, 119, 1055, 1055, 1055, 1055, 1055, - 1055, 1056, 1057, 1058, 1058, 1058, 1058, 1059, 119, 1060, 1061, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1062, 1062, 1062, 1063, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1064, - 1064, 1064, 1065, 1066, 119, 1067, 1067, 1068, 1069, 1070, 1071, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1072, 1073, 1073, 1073, 1073, 1073, 1073, 1074, - 1075, 1076, 1077, 1078, 1079, 1080, 119, 1081, 1082, 1083, 1083, 1083, - 1083, 1083, 1084, 1085, 1086, 1087, 1088, 1088, 1088, 1089, 1090, 1091, - 1092, 1093, 1093, 1093, 1094, 1095, 1096, 1097, 1098, 119, 1099, 1099, - 1099, 1099, 1100, 119, 1101, 1102, 1102, 1102, 1102, 1102, 1103, 1104, - 1105, 1106, 1107, 1108, 1109, 1110, 1111, 119, 1112, 1112, 1113, 1112, - 1112, 1114, 1115, 1116, 119, 119, 119, 119, 119, 119, 119, 119, 1117, - 1118, 1119, 1120, 1119, 1121, 1122, 1122, 1122, 1122, 1122, 1123, 1124, - 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1131, 1132, 1133, 1134, 1135, - 1136, 1137, 1138, 1139, 1140, 1140, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1141, 1141, 1141, 1141, - 1141, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 119, 119, 119, 119, 1148, - 1148, 1148, 1148, 1148, 1148, 1149, 1150, 1151, 119, 1152, 1153, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1154, 1154, 1154, 1154, 1154, 1155, 1156, 1157, - 1158, 1159, 1160, 1161, 119, 119, 119, 119, 1162, 1162, 1162, 1162, 1162, - 1162, 1163, 1164, 1165, 119, 1166, 1167, 1168, 1169, 119, 119, 1170, - 1170, 1170, 1170, 1170, 1171, 1172, 119, 1173, 1174, 119, 119, 119, 119, - 119, 119, 1175, 1175, 1175, 1176, 1177, 1178, 1179, 1180, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1181, 1181, 1181, 1181, 1181, 1182, - 1183, 1184, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1185, 1185, 1185, 1185, 1186, 1186, 1186, 1186, 1187, 1188, 1189, 1190, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 1191, 1192, 1193, 1193, 1193, 1193, 1194, 1195, 1196, - 119, 1197, 1198, 1199, 1199, 1199, 1199, 1200, 1201, 1202, 1203, 1204, - 119, 119, 119, 1205, 1205, 1205, 1205, 1205, 1205, 1205, 1206, 1207, - 1208, 1207, 1207, 1207, 1209, 1210, 1211, 1212, 119, 1213, 1214, 1215, - 1216, 1217, 1218, 1218, 1218, 1219, 1220, 1220, 1221, 1222, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1223, 1224, 1225, 1225, 1225, 1225, - 1226, 1227, 1228, 119, 1229, 1230, 1231, 1232, 1233, 1233, 1233, 1234, - 1235, 1236, 1237, 1238, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1239, 1239, 1240, 1241, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1243, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1244, 1244, 1244, 1244, 1244, 1244, - 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1245, 1246, 119, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1247, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1249, 1248, 1248, 1248, 1248, 1250, 1251, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1252, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1253, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1255, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1256, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 1257, 1258, 1258, 1258, 1259, 1260, 1261, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1262, 1262, - 1262, 1263, 1264, 119, 1265, 1265, 1265, 1265, 1265, 1265, 1266, 1267, - 1268, 119, 1269, 1270, 1271, 1265, 1265, 1272, 1265, 1265, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1273, 1273, 1273, 1273, 1274, 1274, 1274, 1274, - 1275, 1275, 1276, 1277, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1279, 119, - 1280, 1281, 1281, 1281, 1281, 1282, 119, 1283, 1284, 1285, 119, 119, 119, - 119, 119, 119, 119, 119, 1286, 119, 119, 119, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1288, 119, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1289, 119, 1290, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 1291, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1293, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, - 1294, 1294, 1295, 1294, 1296, 1294, 1297, 1294, 1298, 1299, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 1300, 119, 610, - 610, 610, 610, 1301, 1302, 610, 610, 610, 610, 610, 610, 1303, 1304, - 1305, 1306, 1307, 1308, 610, 610, 610, 1309, 610, 610, 610, 610, 610, - 610, 610, 1310, 119, 119, 946, 946, 946, 946, 946, 946, 946, 946, 1311, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 941, 941, 1312, 119, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 617, 119, 941, 941, 941, 1313, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1314, - 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, 1318, 1319, 1316, 1316, - 1314, 1314, 1314, 1315, 1316, 1316, 1320, 1321, 1322, 1318, 1323, 1324, - 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1325, 1326, 1327, 1328, 1316, - 1316, 1316, 1329, 1330, 1331, 1332, 1316, 1316, 1317, 1314, 1314, 1318, - 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, - 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, - 1314, 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1333, - 1314, 1314, 1314, 1334, 1316, 1316, 1335, 1336, 1314, 1314, 1337, 1316, - 1316, 1338, 1317, 1314, 1314, 1339, 1316, 1316, 1340, 1341, 1314, 1314, - 1342, 1316, 1316, 1316, 1343, 1314, 1314, 1314, 1334, 1316, 1316, 1335, - 1344, 1345, 1345, 1345, 1345, 1345, 1345, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1347, 1347, 1347, 1347, 1347, 1347, 1348, 1349, 1347, - 1347, 1347, 1347, 1347, 1350, 1351, 1346, 1352, 1353, 119, 1354, 1355, - 1347, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1356, 1357, 1357, - 1358, 1359, 1360, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1362, 1363, 1364, 119, 119, 119, 119, 119, 1365, 1365, 1365, 1365, - 1366, 1367, 1367, 1367, 1368, 1369, 1370, 1371, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1372, 1373, 1373, 1373, 1373, 1373, 1373, 1374, 1375, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 1376, 127, 127, 127, 1377, 1378, 1379, - 1380, 1381, 1382, 1377, 1383, 1377, 1379, 1379, 1384, 127, 1385, 127, - 1386, 1387, 1385, 127, 1386, 119, 119, 119, 119, 119, 119, 1388, 119, - 1389, 1390, 1390, 1390, 1390, 1391, 1390, 1390, 1390, 1390, 1390, 1390, - 1390, 1390, 1390, 1390, 1390, 1390, 1391, 1392, 1390, 1393, 1394, 1390, - 1394, 1395, 1394, 1390, 1390, 1390, 1396, 1392, 620, 1397, 622, 622, 622, - 1398, 622, 622, 622, 622, 622, 622, 622, 1399, 622, 622, 622, 1400, 1401, - 1402, 622, 1403, 1392, 1392, 1392, 1392, 1392, 1392, 1404, 1405, 1405, - 1405, 1406, 1392, 755, 755, 755, 755, 755, 1407, 755, 1408, 1409, 1392, - 1410, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 723, 723, 723, 723, 1411, - 1412, 1413, 723, 723, 723, 723, 723, 723, 723, 723, 1414, 1415, 723, - 1416, 1417, 723, 723, 1418, 1419, 1420, 1421, 1416, 1390, 723, 723, 1422, - 1423, 723, 723, 723, 723, 723, 723, 723, 1424, 1425, 1426, 1427, 723, - 1428, 1429, 1426, 1430, 1431, 723, 723, 723, 1432, 1433, 1434, 723, 723, - 723, 723, 723, 723, 723, 723, 1435, 1436, 723, 1437, 643, 1438, 723, - 1439, 1440, 581, 1441, 723, 723, 723, 1390, 1442, 1443, 1390, 1390, 1444, - 1390, 1389, 1390, 1390, 1390, 1390, 1390, 1445, 1446, 1390, 1390, 1445, - 1447, 723, 723, 723, 723, 723, 723, 723, 723, 1448, 1449, 581, 581, 581, - 581, 1450, 1451, 723, 723, 723, 723, 1452, 723, 1453, 723, 1454, 1455, - 1456, 1392, 1390, 1457, 1458, 1459, 581, 581, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 581, 581, 1460, 1392, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 1461, 1462, 1392, 1392, 1392, 1392, 581, 1460, - 581, 581, 581, 581, 581, 581, 581, 1392, 581, 1463, 581, 581, 581, 581, - 581, 1392, 581, 581, 581, 1464, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 581, 1460, 723, 1465, 1466, 723, 1426, 1467, 723, 723, - 723, 723, 723, 723, 1468, 1469, 723, 723, 723, 723, 1470, 1392, 1471, - 1472, 1470, 1392, 1473, 1474, 723, 723, 723, 723, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1390, 1396, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1475, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 1476, 775, 775, 775, 775, 775, 773, - 773, 773, 773, 773, 773, 1477, 775, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 883, - 775, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 1478, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 773, 773, 773, 774, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 1479, 1480, - 119, 119, 119, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - 1481, 1481, 1481, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 119, 119, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 1482, -}; - -static const unsigned short index2[] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 1, 1, 1, 1, 1, 1, 7, 7, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 11, 16, 17, 15, 18, 19, 20, 19, 21, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 19, 23, 24, 24, 24, 10, 15, 25, 25, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 16, 26, 17, - 27, 28, 27, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 16, 30, 31, 24, 1, 1, 1, 1, 1, 1, 32, 1, 1, 33, 34, 35, 13, - 36, 13, 37, 38, 39, 40, 41, 42, 24, 43, 44, 27, 45, 46, 47, 47, 48, 49, - 38, 38, 39, 47, 41, 50, 51, 51, 51, 34, 52, 52, 52, 52, 52, 52, 53, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 52, 52, 52, 52, 52, 54, 53, 52, - 52, 52, 52, 52, 53, 55, 55, 55, 56, 56, 56, 56, 55, 56, 55, 55, 55, 56, - 55, 55, 56, 56, 55, 56, 55, 55, 56, 56, 56, 54, 55, 55, 55, 56, 55, 56, - 55, 56, 52, 55, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, 52, 56, - 52, 55, 52, 55, 52, 56, 52, 56, 52, 56, 52, 55, 52, 56, 52, 56, 52, 56, - 52, 56, 52, 56, 53, 55, 52, 55, 53, 55, 52, 56, 52, 56, 55, 52, 56, 52, - 56, 52, 56, 53, 55, 53, 55, 52, 55, 52, 56, 52, 55, 55, 53, 55, 52, 55, - 52, 56, 52, 56, 53, 55, 52, 56, 52, 56, 52, 52, 56, 52, 56, 52, 56, 56, - 56, 52, 52, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 56, 52, 52, 52, 52, - 56, 52, 52, 56, 52, 52, 52, 56, 56, 56, 52, 52, 56, 52, 52, 56, 52, 56, - 52, 56, 52, 52, 56, 52, 56, 56, 52, 56, 52, 52, 56, 52, 52, 52, 56, 52, - 56, 52, 52, 56, 56, 57, 52, 56, 56, 56, 57, 57, 57, 57, 52, 58, 56, 52, - 58, 56, 52, 58, 56, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, 55, 52, - 55, 52, 55, 56, 52, 56, 56, 52, 58, 56, 52, 56, 52, 52, 52, 56, 52, 56, - 56, 56, 56, 56, 56, 56, 52, 52, 56, 52, 52, 56, 56, 52, 56, 52, 52, 52, - 52, 56, 56, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 57, 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 60, - 60, 61, 61, 61, 61, 61, 61, 61, 62, 62, 63, 62, 60, 64, 65, 64, 64, 64, - 65, 64, 60, 60, 66, 61, 62, 62, 62, 62, 62, 62, 39, 39, 39, 39, 62, 39, - 62, 48, 59, 59, 59, 59, 59, 62, 62, 62, 62, 62, 67, 67, 60, 62, 61, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 69, 70, 70, 70, 70, 69, 71, 70, 70, 70, 70, 70, 72, 72, 70, - 70, 70, 70, 72, 72, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 73, 73, - 73, 73, 73, 70, 70, 70, 70, 68, 68, 68, 68, 68, 68, 68, 68, 74, 68, 70, - 70, 70, 68, 68, 68, 70, 70, 75, 68, 68, 68, 70, 70, 70, 70, 68, 69, 70, - 70, 68, 76, 77, 77, 76, 77, 77, 76, 68, 68, 68, 68, 68, 78, 79, 78, 79, - 60, 80, 78, 79, 81, 81, 82, 79, 79, 79, 83, 78, 81, 81, 81, 81, 80, 62, - 78, 84, 78, 78, 78, 81, 78, 81, 78, 78, 79, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 81, 85, 85, 85, 85, 85, 85, 85, - 78, 78, 79, 79, 79, 79, 79, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 79, 86, 86, 86, 86, 86, 86, 86, 79, 79, 79, 79, - 79, 78, 79, 79, 78, 78, 78, 79, 79, 79, 78, 79, 78, 79, 78, 79, 78, 79, - 78, 79, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 87, 88, 79, 79, - 79, 79, 78, 79, 89, 78, 79, 78, 78, 79, 79, 78, 78, 78, 90, 91, 90, 90, - 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, - 91, 91, 92, 92, 92, 92, 92, 92, 92, 92, 93, 92, 93, 93, 93, 93, 93, 93, - 93, 93, 93, 93, 93, 93, 93, 93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, - 94, 95, 95, 96, 96, 95, 97, 97, 90, 93, 90, 93, 90, 93, 90, 90, 93, 90, - 93, 90, 93, 90, 93, 90, 93, 90, 93, 90, 93, 93, 81, 98, 98, 98, 98, 98, - 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 81, - 81, 99, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, - 101, 101, 102, 103, 81, 81, 104, 104, 105, 81, 106, 107, 107, 107, 107, - 106, 107, 107, 107, 108, 106, 107, 107, 107, 107, 107, 107, 106, 106, - 106, 106, 106, 106, 107, 107, 106, 107, 107, 108, 109, 107, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, 119, 120, 121, 122, 123, 124, - 125, 126, 127, 125, 107, 106, 128, 118, 81, 81, 81, 81, 81, 81, 81, 81, - 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 81, 81, 81, 81, - 129, 129, 129, 129, 125, 125, 81, 81, 81, 130, 130, 130, 130, 130, 131, - 132, 132, 133, 134, 134, 135, 136, 137, 138, 138, 139, 139, 139, 139, - 139, 139, 139, 139, 140, 141, 142, 143, 144, 81, 145, 143, 146, 146, 146, - 146, 146, 146, 146, 146, 147, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 148, 149, 150, 151, 152, 153, 154, 155, 96, 96, 156, 157, 139, - 139, 139, 139, 139, 157, 139, 139, 157, 158, 158, 158, 158, 158, 158, - 158, 158, 158, 158, 134, 159, 159, 160, 146, 146, 161, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 146, 145, 146, 139, 139, 139, 139, - 139, 139, 139, 131, 138, 139, 139, 139, 139, 157, 139, 162, 162, 139, - 139, 138, 157, 139, 139, 157, 146, 146, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 163, 146, 146, 146, 164, 164, 146, 165, 165, 165, 165, - 165, 165, 165, 165, 165, 165, 165, 165, 165, 165, 81, 166, 167, 168, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 169, - 170, 169, 169, 170, 169, 169, 170, 170, 170, 169, 170, 170, 169, 170, - 169, 169, 169, 170, 169, 170, 169, 170, 169, 170, 169, 169, 81, 81, 167, - 167, 167, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, - 171, 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 171, 81, - 81, 81, 81, 81, 81, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, - 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, - 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, 176, 175, 177, 177, - 178, 179, 180, 181, 177, 81, 81, 176, 182, 182, 183, 183, 183, 183, 183, - 183, 183, 183, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 185, - 184, 184, 184, 184, 184, 184, 184, 184, 184, 185, 184, 184, 184, 185, - 184, 184, 184, 184, 184, 81, 81, 186, 186, 186, 186, 186, 186, 186, 186, - 186, 186, 186, 186, 186, 186, 186, 81, 187, 187, 187, 187, 187, 187, 187, - 187, 187, 188, 188, 188, 81, 81, 189, 81, 167, 167, 167, 81, 81, 81, 81, - 81, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 81, 81, 81, 81, 81, 157, 139, 139, 139, 139, 139, 139, 131, 157, 139, - 139, 157, 139, 139, 157, 139, 139, 139, 157, 157, 157, 190, 191, 192, - 139, 139, 139, 157, 139, 139, 157, 157, 139, 139, 139, 139, 139, 193, - 193, 193, 194, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, - 195, 195, 195, 193, 194, 196, 195, 194, 194, 194, 193, 193, 193, 193, - 193, 193, 193, 193, 194, 194, 194, 194, 197, 194, 194, 195, 96, 156, 198, - 198, 193, 193, 193, 195, 195, 193, 193, 199, 199, 200, 200, 200, 200, - 200, 200, 200, 200, 200, 200, 201, 202, 195, 195, 195, 195, 195, 195, - 203, 204, 205, 205, 81, 203, 203, 203, 203, 203, 203, 203, 203, 81, 81, - 203, 203, 81, 81, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, - 203, 203, 203, 81, 203, 203, 203, 203, 203, 203, 203, 81, 203, 81, 81, - 81, 203, 203, 203, 203, 81, 81, 206, 203, 205, 205, 205, 204, 204, 204, - 204, 81, 81, 205, 205, 81, 81, 205, 205, 207, 203, 81, 81, 81, 81, 81, - 81, 81, 81, 205, 81, 81, 81, 81, 203, 203, 81, 203, 203, 203, 204, 204, - 81, 81, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 203, 203, 209, - 209, 210, 210, 210, 210, 210, 211, 212, 213, 203, 214, 215, 81, 81, 216, - 216, 217, 81, 218, 218, 218, 218, 218, 218, 81, 81, 81, 81, 218, 218, 81, - 81, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, - 81, 218, 218, 218, 218, 218, 218, 218, 81, 218, 218, 81, 218, 218, 81, - 218, 218, 81, 81, 219, 81, 217, 217, 217, 216, 216, 81, 81, 81, 81, 216, - 216, 81, 81, 216, 216, 220, 81, 81, 81, 216, 81, 81, 81, 81, 81, 81, 81, - 218, 218, 218, 218, 81, 218, 81, 81, 81, 81, 81, 81, 81, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 216, 216, 218, 218, 218, 216, 222, 81, - 81, 223, 223, 224, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 81, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 81, 225, 225, 81, - 225, 225, 225, 225, 225, 81, 81, 226, 225, 224, 224, 224, 223, 223, 223, - 223, 223, 81, 223, 223, 224, 81, 224, 224, 227, 81, 81, 225, 81, 81, 81, - 81, 81, 81, 81, 225, 225, 223, 223, 81, 81, 228, 228, 228, 228, 228, 228, - 228, 228, 228, 228, 229, 230, 81, 81, 81, 81, 81, 81, 81, 225, 223, 223, - 223, 223, 223, 223, 81, 231, 232, 232, 81, 233, 233, 233, 233, 233, 233, - 233, 233, 81, 81, 233, 233, 81, 81, 233, 233, 233, 233, 233, 233, 233, - 233, 233, 233, 233, 233, 233, 233, 81, 233, 233, 233, 233, 233, 233, 233, - 81, 233, 233, 81, 233, 233, 233, 233, 233, 81, 81, 234, 233, 232, 231, - 232, 231, 231, 231, 231, 81, 81, 232, 232, 81, 81, 232, 232, 235, 81, 81, - 81, 81, 81, 81, 81, 81, 231, 232, 81, 81, 81, 81, 233, 233, 81, 233, 233, - 233, 231, 231, 81, 81, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 237, 233, 238, 238, 238, 238, 238, 238, 81, 81, 239, 240, 81, 240, 240, - 240, 240, 240, 240, 81, 81, 81, 240, 240, 240, 81, 240, 240, 240, 240, - 81, 81, 81, 240, 240, 81, 240, 81, 240, 240, 81, 81, 81, 240, 240, 81, - 81, 81, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 81, 81, 81, 81, - 241, 241, 239, 241, 241, 81, 81, 81, 241, 241, 241, 81, 241, 241, 241, - 242, 81, 81, 240, 81, 81, 81, 81, 81, 81, 241, 81, 81, 81, 81, 81, 81, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 244, 244, 245, - 245, 245, 245, 245, 245, 246, 245, 81, 81, 81, 81, 81, 247, 248, 248, - 248, 247, 249, 249, 249, 249, 249, 249, 249, 249, 81, 249, 249, 249, 81, - 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, - 249, 249, 81, 81, 81, 249, 247, 247, 247, 248, 248, 248, 248, 81, 247, - 247, 247, 81, 247, 247, 247, 250, 81, 81, 81, 81, 81, 81, 81, 251, 252, - 81, 249, 249, 249, 81, 81, 81, 81, 81, 249, 249, 247, 247, 81, 81, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, - 254, 254, 255, 256, 257, 258, 258, 259, 256, 256, 256, 256, 256, 256, - 256, 256, 81, 256, 256, 256, 81, 256, 256, 256, 256, 256, 256, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 81, 256, 256, 256, 256, - 256, 81, 81, 260, 256, 258, 261, 258, 258, 258, 258, 258, 81, 261, 258, - 258, 81, 258, 258, 257, 262, 81, 81, 81, 81, 81, 81, 81, 258, 258, 81, - 81, 81, 81, 81, 81, 81, 256, 81, 256, 256, 257, 257, 81, 81, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 81, 256, 256, 81, 81, 81, 81, 81, - 264, 264, 265, 265, 81, 266, 266, 266, 266, 266, 266, 266, 266, 81, 266, - 266, 266, 81, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 267, 267, 266, 265, 265, 265, 264, 264, 264, - 264, 81, 265, 265, 265, 81, 265, 265, 265, 267, 266, 268, 81, 81, 81, 81, - 266, 266, 266, 265, 269, 269, 269, 269, 269, 269, 269, 266, 266, 266, - 264, 264, 81, 81, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 269, - 269, 269, 269, 269, 269, 269, 269, 269, 271, 266, 266, 266, 266, 266, - 266, 81, 81, 272, 272, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 273, 273, 273, 273, 273, 273, 273, 273, 273, 81, 81, 81, 273, 273, 273, - 273, 273, 273, 273, 273, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 81, 273, 81, 81, 81, 81, 274, 81, 81, 81, 81, 272, 272, 272, 275, 275, - 275, 81, 275, 81, 272, 272, 272, 272, 272, 272, 272, 272, 81, 81, 81, 81, - 81, 81, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 81, 81, 272, - 272, 277, 81, 81, 81, 81, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 279, 278, 278, 279, 279, 279, 279, - 280, 280, 281, 81, 81, 81, 81, 282, 278, 278, 278, 278, 278, 278, 283, - 279, 284, 284, 284, 284, 279, 279, 279, 285, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 287, 287, 81, 81, 81, 81, 81, 288, 288, 81, 288, - 81, 81, 288, 288, 81, 288, 81, 81, 288, 81, 81, 81, 81, 81, 81, 288, 288, - 288, 288, 81, 288, 288, 288, 288, 288, 288, 288, 81, 288, 288, 288, 81, - 288, 81, 288, 81, 81, 288, 288, 81, 288, 288, 288, 288, 289, 288, 288, - 289, 289, 289, 289, 290, 290, 81, 289, 289, 288, 81, 81, 288, 288, 288, - 288, 288, 81, 291, 81, 292, 292, 292, 292, 289, 289, 81, 81, 293, 293, - 293, 293, 293, 293, 293, 293, 293, 293, 81, 81, 288, 288, 288, 288, 294, - 295, 295, 295, 296, 297, 296, 296, 298, 296, 296, 299, 298, 300, 300, - 300, 300, 300, 298, 301, 300, 301, 301, 301, 302, 302, 301, 301, 301, - 301, 301, 301, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, - 304, 304, 304, 304, 304, 304, 304, 304, 304, 305, 302, 301, 302, 301, - 306, 307, 308, 307, 308, 309, 309, 294, 294, 294, 294, 294, 294, 294, - 294, 81, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 81, - 81, 81, 81, 310, 311, 312, 313, 312, 312, 312, 312, 312, 311, 311, 311, - 311, 312, 314, 311, 312, 315, 315, 316, 299, 315, 315, 294, 294, 294, - 294, 294, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 305, 305, 301, - 301, 301, 301, 301, 301, 302, 301, 301, 301, 301, 301, 301, 81, 301, 301, - 296, 296, 299, 296, 297, 317, 317, 317, 317, 298, 298, 81, 81, 81, 81, - 81, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 319, 319, 320, - 320, 320, 320, 319, 320, 320, 320, 320, 320, 321, 319, 322, 322, 319, - 319, 320, 320, 318, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - 324, 324, 325, 325, 325, 325, 318, 318, 318, 318, 318, 318, 319, 319, - 320, 320, 318, 318, 318, 318, 320, 320, 320, 318, 319, 319, 319, 318, - 318, 319, 319, 319, 319, 319, 319, 319, 318, 318, 318, 320, 320, 320, - 320, 318, 318, 318, 318, 318, 320, 319, 319, 320, 320, 319, 319, 319, - 319, 319, 319, 326, 318, 319, 323, 323, 319, 319, 319, 320, 327, 327, - 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 81, - 328, 81, 81, 81, 81, 81, 328, 81, 81, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 330, 331, 329, 329, 329, 332, 332, 332, 332, 332, - 332, 332, 332, 333, 333, 333, 333, 333, 333, 333, 333, 334, 334, 334, - 334, 334, 334, 334, 334, 335, 335, 335, 335, 335, 335, 335, 335, 335, 81, - 335, 335, 335, 335, 81, 81, 335, 335, 335, 335, 335, 335, 335, 81, 335, - 335, 335, 81, 81, 336, 336, 336, 337, 338, 337, 337, 337, 337, 337, 337, - 337, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, - 339, 339, 339, 339, 339, 339, 339, 81, 81, 81, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 81, 81, 81, 81, 81, 81, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 81, 81, 342, 342, 342, 342, - 342, 342, 81, 81, 343, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, - 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, 345, 345, 344, 346, - 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 348, 349, 81, 81, 81, 350, 350, 350, 350, 350, 350, - 350, 350, 350, 350, 350, 199, 199, 199, 351, 351, 351, 350, 350, 350, - 350, 350, 350, 350, 350, 81, 81, 81, 81, 81, 81, 81, 352, 352, 352, 352, - 352, 352, 352, 352, 352, 352, 352, 352, 352, 81, 352, 352, 352, 352, 353, - 353, 354, 81, 81, 81, 355, 355, 355, 355, 355, 355, 355, 355, 355, 355, - 356, 356, 357, 199, 199, 81, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 358, 359, 359, 81, 81, 81, 81, 360, 360, 360, 360, 360, 360, 360, 360, - 360, 360, 360, 360, 360, 81, 360, 360, 360, 81, 361, 361, 81, 81, 81, 81, - 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 363, 363, - 364, 363, 363, 363, 363, 363, 363, 363, 364, 364, 364, 364, 364, 364, - 364, 364, 363, 364, 364, 363, 363, 363, 363, 363, 363, 363, 363, 363, - 365, 363, 366, 366, 367, 368, 366, 369, 366, 370, 362, 371, 81, 81, 372, - 372, 372, 372, 372, 372, 372, 372, 372, 372, 81, 81, 81, 81, 81, 81, 373, - 373, 373, 373, 373, 373, 373, 373, 373, 373, 81, 81, 81, 81, 81, 81, 374, - 374, 375, 375, 376, 377, 378, 374, 379, 379, 374, 380, 380, 380, 381, 81, - 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 81, 81, 81, 81, 81, 81, - 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 383, 383, - 383, 383, 383, 81, 81, 81, 81, 81, 81, 81, 383, 383, 383, 383, 383, 380, - 380, 383, 383, 385, 383, 81, 81, 81, 81, 81, 344, 344, 344, 344, 344, - 344, 81, 81, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, - 386, 386, 386, 81, 387, 387, 387, 388, 388, 388, 388, 387, 387, 388, 388, - 388, 81, 81, 81, 81, 388, 388, 387, 388, 388, 388, 388, 388, 388, 389, - 390, 391, 81, 81, 81, 81, 392, 81, 81, 81, 393, 393, 394, 394, 394, 394, - 394, 394, 394, 394, 394, 394, 395, 395, 395, 395, 395, 395, 395, 395, - 395, 395, 395, 395, 395, 395, 81, 81, 395, 395, 395, 395, 395, 81, 81, - 81, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 81, 81, - 81, 81, 396, 396, 81, 81, 81, 81, 81, 81, 397, 397, 397, 397, 397, 397, - 397, 397, 397, 397, 398, 81, 81, 81, 399, 399, 400, 400, 400, 400, 400, - 400, 400, 400, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - 401, 401, 401, 401, 402, 403, 404, 404, 405, 81, 81, 406, 406, 407, 407, - 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 408, 409, 408, - 409, 409, 409, 409, 409, 409, 409, 81, 410, 408, 409, 408, 408, 409, 409, - 409, 409, 409, 409, 409, 409, 408, 408, 408, 408, 408, 408, 409, 409, - 411, 411, 411, 411, 411, 411, 411, 411, 81, 81, 412, 413, 413, 413, 413, - 413, 413, 413, 413, 413, 413, 81, 81, 81, 81, 81, 81, 414, 414, 414, 414, - 414, 414, 414, 415, 414, 414, 414, 414, 414, 414, 81, 81, 96, 96, 96, 96, - 96, 156, 156, 156, 156, 156, 156, 96, 96, 156, 416, 81, 417, 417, 417, - 417, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, - 419, 419, 419, 420, 418, 417, 417, 417, 417, 417, 418, 417, 418, 418, - 418, 418, 418, 417, 418, 421, 419, 419, 419, 419, 419, 419, 419, 81, 81, - 81, 81, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, 423, 423, 424, - 423, 423, 423, 423, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, - 426, 427, 426, 426, 426, 426, 426, 426, 426, 425, 425, 425, 425, 425, - 425, 425, 425, 425, 81, 81, 81, 428, 428, 429, 430, 430, 430, 430, 430, - 430, 430, 430, 430, 430, 430, 430, 430, 430, 429, 428, 428, 428, 428, - 429, 429, 428, 428, 431, 432, 428, 428, 430, 430, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 430, 430, 430, 430, 430, 430, 434, 434, - 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, 436, - 437, 437, 436, 436, 436, 437, 436, 437, 437, 437, 438, 438, 81, 81, 81, - 81, 81, 81, 81, 81, 439, 439, 439, 439, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 440, 440, 441, 441, 441, 441, 441, 441, 441, 441, - 442, 442, 442, 442, 442, 442, 442, 442, 441, 441, 442, 443, 81, 81, 81, - 444, 444, 444, 444, 444, 445, 445, 445, 445, 445, 445, 445, 445, 445, - 445, 81, 81, 81, 440, 440, 440, 446, 446, 446, 446, 446, 446, 446, 446, - 446, 446, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, - 447, 447, 448, 448, 448, 448, 448, 448, 449, 449, 93, 81, 81, 81, 81, 81, - 81, 81, 328, 328, 328, 81, 81, 328, 328, 328, 450, 450, 450, 450, 450, - 450, 450, 450, 96, 96, 96, 330, 451, 156, 156, 156, 156, 156, 96, 96, - 156, 156, 156, 156, 96, 452, 451, 451, 451, 451, 451, 451, 451, 453, 453, - 453, 453, 156, 453, 453, 453, 453, 452, 452, 96, 453, 453, 452, 96, 96, - 81, 81, 81, 81, 81, 81, 56, 56, 56, 56, 56, 56, 79, 79, 79, 79, 79, 93, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 82, 82, 82, 82, 59, 59, 59, 59, - 82, 82, 82, 82, 82, 56, 56, 56, 56, 56, 454, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 96, 96, - 156, 96, 96, 96, 96, 96, 96, 96, 156, 96, 96, 455, 456, 156, 457, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 458, 459, 459, 156, 81, 96, 460, 156, 96, 156, 52, 56, 52, 56, 52, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 79, 79, 79, 79, 79, 79, 79, - 79, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 81, 81, 78, - 78, 78, 78, 78, 78, 81, 81, 81, 78, 81, 78, 81, 78, 81, 78, 461, 461, - 461, 461, 461, 461, 461, 461, 79, 79, 79, 79, 79, 81, 79, 79, 78, 78, 78, - 78, 461, 80, 79, 80, 80, 80, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 461, - 80, 80, 80, 79, 79, 79, 79, 81, 81, 79, 79, 78, 78, 78, 78, 81, 80, 80, - 80, 78, 78, 78, 78, 78, 80, 80, 80, 81, 81, 79, 79, 79, 81, 79, 79, 78, - 78, 78, 78, 461, 462, 80, 81, 463, 463, 463, 463, 463, 463, 463, 464, - 463, 463, 463, 465, 466, 467, 468, 469, 470, 471, 472, 470, 473, 474, 38, - 84, 475, 476, 477, 42, 475, 476, 477, 42, 38, 38, 478, 84, 479, 479, 479, - 480, 481, 482, 483, 484, 485, 486, 487, 33, 488, 489, 488, 488, 489, 490, - 491, 491, 84, 42, 50, 38, 492, 492, 478, 493, 493, 84, 84, 84, 494, 477, - 495, 492, 492, 492, 84, 84, 84, 84, 84, 84, 84, 84, 496, 84, 493, 84, - 377, 84, 377, 377, 377, 377, 84, 377, 377, 463, 497, 498, 498, 498, 498, - 81, 499, 500, 501, 502, 503, 503, 503, 503, 503, 503, 504, 59, 81, 81, - 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 506, 504, 47, 47, - 47, 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 81, 59, 59, 59, - 59, 59, 81, 81, 81, 282, 282, 282, 282, 282, 282, 282, 507, 282, 508, - 282, 282, 36, 282, 282, 282, 282, 282, 282, 282, 282, 282, 507, 282, 282, - 282, 282, 507, 282, 282, 507, 282, 509, 509, 509, 509, 509, 509, 509, - 509, 96, 96, 451, 451, 96, 96, 96, 96, 451, 451, 451, 96, 96, 416, 416, - 416, 416, 96, 416, 416, 416, 451, 451, 96, 156, 96, 451, 451, 156, 156, - 156, 156, 96, 81, 81, 81, 81, 81, 81, 81, 40, 40, 510, 511, 40, 512, 40, - 510, 40, 511, 49, 510, 510, 510, 49, 49, 510, 510, 510, 513, 40, 510, - 514, 40, 496, 510, 510, 510, 510, 510, 40, 40, 40, 512, 512, 40, 510, 40, - 85, 40, 510, 40, 52, 515, 510, 510, 516, 49, 510, 510, 52, 510, 49, 453, - 453, 453, 453, 49, 40, 40, 49, 49, 510, 510, 496, 496, 496, 496, 496, - 510, 49, 49, 49, 49, 40, 496, 40, 40, 56, 317, 517, 517, 517, 518, 51, - 519, 517, 517, 517, 517, 517, 51, 518, 518, 51, 517, 520, 520, 520, 520, - 520, 520, 520, 520, 520, 520, 520, 520, 521, 521, 521, 521, 520, 520, - 521, 521, 521, 521, 521, 521, 521, 521, 521, 52, 56, 521, 521, 521, 521, - 51, 40, 40, 81, 81, 81, 81, 54, 54, 54, 54, 54, 512, 512, 512, 512, 512, - 496, 496, 40, 40, 40, 40, 496, 40, 40, 496, 40, 40, 496, 40, 40, 40, 40, - 40, 40, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 44, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 496, 496, 40, 40, 54, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, 40, 40, 40, 496, 496, 496, 496, - 496, 496, 496, 496, 496, 496, 496, 496, 54, 496, 54, 54, 496, 496, 496, - 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 54, 522, 523, 496, 54, 496, - 496, 496, 496, 54, 496, 496, 54, 54, 54, 54, 496, 496, 54, 496, 54, 496, - 54, 54, 54, 54, 54, 54, 496, 54, 496, 496, 496, 496, 496, 54, 54, 54, 54, - 496, 496, 496, 496, 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 496, - 496, 496, 496, 54, 496, 496, 496, 496, 496, 54, 54, 496, 496, 54, 54, 54, - 54, 496, 496, 54, 54, 496, 496, 54, 54, 496, 496, 496, 496, 496, 54, 496, - 496, 496, 54, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, - 496, 54, 496, 496, 496, 496, 496, 496, 496, 524, 477, 495, 477, 495, 40, - 40, 40, 40, 40, 40, 512, 40, 40, 40, 40, 40, 40, 40, 525, 525, 40, 40, - 40, 40, 496, 496, 40, 40, 40, 40, 40, 40, 40, 526, 527, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 317, 317, 317, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 317, 40, 40, - 40, 40, 40, 496, 496, 496, 496, 496, 496, 496, 496, 496, 40, 40, 40, 40, - 40, 528, 528, 528, 528, 40, 40, 40, 525, 529, 529, 525, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 81, 40, 40, 40, 81, 81, 81, 81, 81, 51, 51, - 51, 51, 51, 51, 51, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, - 519, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 518, 512, 512, 512, - 512, 512, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 512, 512, - 512, 512, 531, 40, 40, 40, 40, 40, 512, 512, 512, 512, 40, 40, 512, 512, - 40, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 40, 40, 40, 40, - 512, 512, 40, 40, 512, 54, 40, 40, 40, 40, 512, 512, 40, 40, 512, 54, 40, - 40, 40, 40, 512, 512, 512, 40, 40, 512, 40, 40, 512, 512, 40, 40, 40, 40, - 40, 40, 40, 512, 496, 496, 496, 496, 496, 532, 532, 496, 529, 529, 529, - 529, 40, 512, 512, 40, 40, 512, 40, 40, 40, 40, 512, 512, 40, 40, 40, 40, - 525, 525, 531, 531, 529, 40, 529, 529, 533, 534, 533, 529, 40, 529, 529, - 529, 40, 40, 40, 40, 512, 40, 512, 40, 40, 40, 40, 40, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 512, 512, - 40, 512, 512, 512, 40, 512, 533, 512, 512, 40, 512, 512, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 525, 40, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 512, 512, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 528, 528, 317, - 40, 40, 40, 40, 40, 40, 40, 40, 525, 525, 533, 529, 529, 529, 529, 525, - 525, 533, 533, 533, 512, 512, 512, 512, 533, 528, 533, 533, 533, 512, - 533, 525, 512, 512, 512, 533, 533, 512, 512, 533, 512, 512, 533, 533, - 533, 40, 512, 40, 40, 40, 40, 512, 512, 525, 512, 512, 512, 512, 512, - 512, 533, 525, 525, 533, 525, 512, 533, 533, 535, 525, 512, 512, 525, - 533, 533, 529, 529, 529, 529, 529, 528, 40, 40, 529, 529, 536, 536, 534, - 534, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, - 40, 40, 40, 40, 40, 528, 40, 528, 40, 40, 40, 40, 528, 528, 528, 40, 537, - 40, 40, 40, 538, 538, 538, 538, 538, 538, 40, 539, 539, 529, 40, 40, 40, - 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 51, - 51, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 40, 528, - 528, 528, 40, 40, 40, 40, 40, 40, 40, 528, 496, 496, 496, 496, 496, 477, - 495, 496, 496, 496, 496, 496, 496, 496, 16, 31, 16, 31, 16, 31, 16, 31, - 477, 495, 540, 540, 540, 540, 540, 540, 540, 540, 496, 496, 496, 477, - 495, 16, 31, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 496, 496, - 496, 496, 496, 496, 496, 477, 495, 477, 495, 496, 496, 496, 496, 496, - 496, 496, 496, 477, 495, 496, 496, 40, 40, 40, 528, 528, 40, 40, 40, 496, - 496, 496, 496, 496, 40, 40, 496, 496, 496, 496, 496, 496, 40, 40, 40, - 528, 40, 40, 40, 40, 537, 512, 512, 40, 40, 40, 40, 81, 81, 40, 40, 40, - 40, 40, 40, 40, 40, 81, 81, 40, 81, 40, 40, 40, 40, 40, 40, 541, 541, - 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 81, 542, - 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 81, - 52, 56, 52, 52, 52, 56, 56, 52, 56, 52, 56, 52, 56, 52, 52, 52, 52, 56, - 52, 56, 56, 52, 56, 56, 56, 56, 56, 56, 59, 59, 52, 52, 87, 88, 87, 88, - 88, 543, 543, 543, 543, 543, 543, 87, 88, 87, 88, 544, 544, 544, 87, 88, - 81, 81, 81, 81, 81, 545, 546, 546, 546, 547, 545, 546, 329, 329, 329, - 329, 329, 329, 81, 329, 81, 81, 81, 81, 81, 329, 81, 81, 548, 548, 548, - 548, 548, 548, 548, 548, 81, 81, 81, 81, 81, 81, 81, 549, 550, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 551, 95, 95, 95, 95, 95, - 95, 95, 95, 552, 552, 42, 50, 42, 50, 552, 552, 552, 42, 50, 552, 42, 50, - 377, 377, 377, 377, 377, 377, 377, 377, 84, 472, 553, 377, 554, 84, 42, - 50, 84, 84, 42, 50, 477, 495, 477, 495, 477, 495, 477, 495, 377, 377, - 377, 377, 375, 60, 377, 377, 84, 377, 377, 84, 84, 84, 84, 84, 555, 555, - 377, 377, 377, 84, 472, 377, 477, 377, 377, 377, 377, 377, 377, 377, 377, - 84, 377, 84, 377, 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, - 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 81, 81, 81, 81, 556, - 556, 556, 556, 556, 556, 81, 81, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 81, 81, 81, 81, 557, 558, 558, 559, 525, 560, 561, - 562, 526, 527, 526, 527, 526, 527, 526, 527, 526, 527, 525, 525, 526, - 527, 526, 527, 526, 527, 526, 527, 563, 526, 527, 527, 525, 562, 562, - 562, 562, 562, 562, 562, 562, 562, 564, 565, 566, 567, 568, 568, 569, - 570, 570, 570, 570, 571, 525, 525, 562, 562, 562, 560, 572, 559, 525, - 529, 81, 573, 574, 573, 574, 573, 574, 573, 574, 573, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 573, - 574, 574, 574, 574, 574, 574, 574, 573, 574, 573, 574, 573, 574, 574, - 574, 574, 574, 574, 573, 574, 574, 574, 574, 574, 574, 573, 573, 81, 81, - 575, 575, 576, 576, 577, 577, 574, 563, 578, 579, 578, 579, 578, 579, - 578, 579, 578, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, - 579, 579, 579, 579, 579, 579, 578, 579, 579, 579, 579, 579, 579, 579, - 578, 579, 578, 579, 578, 579, 579, 579, 579, 579, 579, 578, 579, 579, - 579, 579, 579, 579, 578, 578, 579, 579, 579, 579, 580, 581, 582, 582, - 579, 81, 81, 81, 81, 81, 583, 583, 583, 583, 583, 583, 583, 583, 583, - 583, 583, 81, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, - 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 81, 585, 585, 586, 586, - 586, 586, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 583, 583, - 583, 81, 81, 81, 81, 81, 578, 578, 578, 578, 578, 578, 578, 578, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 588, 588, 81, - 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 585, 585, 585, 585, - 585, 585, 589, 589, 589, 589, 589, 589, 589, 589, 525, 590, 590, 590, - 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 587, 587, - 587, 587, 588, 588, 588, 585, 585, 590, 590, 590, 590, 590, 590, 590, - 585, 585, 585, 585, 525, 525, 525, 525, 591, 591, 591, 591, 591, 591, - 591, 591, 591, 591, 591, 591, 591, 591, 591, 81, 585, 585, 585, 585, 585, - 585, 585, 525, 525, 525, 525, 585, 585, 585, 585, 585, 585, 585, 585, - 585, 585, 585, 525, 525, 592, 592, 592, 592, 592, 592, 592, 592, 592, - 592, 592, 592, 592, 592, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, - 595, 594, 594, 594, 594, 594, 594, 594, 81, 81, 81, 596, 596, 596, 596, - 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 81, 597, 597, 597, - 597, 597, 597, 597, 597, 598, 598, 598, 598, 598, 598, 599, 599, 600, - 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 602, 603, - 602, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 600, 600, 81, 81, - 81, 81, 90, 93, 90, 93, 90, 93, 605, 95, 97, 97, 97, 606, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 606, 607, 90, 93, 90, 93, 454, 454, 95, 95, 608, - 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 609, - 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 610, 611, 612, 612, - 612, 612, 612, 62, 62, 62, 62, 62, 62, 62, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 62, 62, 52, 56, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 56, - 59, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 52, 56, 52, 52, 56, 60, 613, - 613, 52, 56, 52, 56, 57, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 52, - 52, 52, 52, 56, 52, 52, 52, 52, 52, 56, 52, 56, 52, 56, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 57, 59, 59, 56, 57, 57, 57, 57, 57, - 614, 614, 615, 614, 614, 614, 616, 614, 614, 614, 614, 615, 614, 614, - 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 617, - 617, 615, 615, 617, 618, 618, 618, 618, 81, 81, 81, 81, 619, 619, 619, - 619, 619, 619, 317, 317, 507, 516, 81, 81, 81, 81, 81, 81, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 620, 620, 621, 621, 622, 622, 623, - 623, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, - 624, 624, 624, 624, 624, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 625, 626, 81, 81, 81, 81, 81, 81, 81, - 81, 627, 627, 628, 628, 628, 628, 628, 628, 628, 628, 628, 628, 81, 81, - 81, 81, 81, 81, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 195, - 195, 195, 195, 195, 195, 201, 201, 201, 195, 629, 195, 195, 193, 630, - 630, 630, 630, 630, 630, 630, 630, 630, 630, 631, 631, 631, 631, 631, - 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, - 631, 632, 632, 632, 632, 632, 633, 633, 633, 199, 634, 635, 635, 635, - 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 636, 636, - 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 638, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 639, 332, 332, 332, 332, 332, 81, 81, 81, - 640, 640, 640, 641, 642, 642, 642, 642, 642, 642, 642, 642, 642, 642, - 642, 642, 642, 642, 642, 643, 641, 641, 640, 640, 640, 640, 641, 641, - 640, 641, 641, 641, 644, 645, 645, 645, 645, 645, 645, 646, 646, 646, - 645, 645, 645, 645, 81, 61, 647, 647, 647, 647, 647, 647, 647, 647, 647, - 647, 81, 81, 81, 81, 645, 645, 318, 318, 318, 318, 318, 320, 648, 318, - 323, 323, 318, 318, 318, 318, 318, 81, 649, 649, 649, 649, 649, 649, 649, - 649, 649, 650, 650, 650, 650, 650, 650, 651, 651, 650, 650, 651, 651, - 650, 650, 81, 649, 649, 649, 650, 649, 649, 649, 649, 649, 649, 649, 649, - 650, 651, 81, 81, 652, 652, 652, 652, 652, 652, 652, 652, 652, 652, 81, - 81, 653, 654, 654, 654, 648, 318, 318, 318, 318, 318, 318, 327, 327, 327, - 318, 319, 320, 319, 318, 318, 655, 655, 655, 655, 655, 655, 655, 655, - 656, 655, 656, 656, 657, 655, 655, 656, 656, 655, 655, 655, 655, 655, - 656, 656, 655, 656, 655, 81, 81, 81, 81, 81, 81, 81, 81, 655, 655, 658, - 659, 659, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, - 662, 662, 661, 661, 663, 663, 660, 664, 664, 661, 665, 81, 81, 335, 335, - 335, 335, 335, 335, 81, 56, 56, 56, 613, 59, 59, 59, 59, 56, 56, 56, 56, - 56, 79, 81, 81, 342, 342, 342, 342, 342, 342, 342, 342, 660, 660, 660, - 661, 661, 662, 661, 661, 662, 661, 661, 663, 661, 665, 81, 81, 666, 666, - 666, 666, 666, 666, 666, 666, 666, 666, 81, 81, 81, 81, 81, 81, 667, 668, - 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, - 668, 668, 668, 668, 667, 668, 668, 668, 668, 668, 668, 668, 81, 81, 81, - 81, 333, 333, 333, 333, 333, 333, 333, 81, 81, 81, 81, 334, 334, 334, - 334, 334, 334, 334, 334, 334, 81, 81, 81, 81, 669, 669, 669, 669, 669, - 669, 669, 669, 670, 670, 670, 670, 670, 670, 670, 670, 592, 592, 593, - 593, 593, 593, 593, 593, 56, 56, 56, 56, 56, 56, 56, 81, 81, 81, 81, 101, - 101, 101, 101, 101, 81, 81, 81, 81, 81, 129, 671, 129, 129, 672, 129, - 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 81, 129, 129, - 129, 129, 129, 81, 129, 81, 129, 129, 81, 129, 129, 81, 129, 129, 146, - 146, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, - 673, 673, 673, 81, 81, 81, 81, 81, 81, 81, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 495, 477, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 135, 138, 81, 81, 674, 674, 674, 674, 674, - 674, 674, 674, 675, 558, 558, 675, 675, 676, 676, 526, 527, 677, 81, 81, - 81, 81, 81, 81, 96, 96, 96, 96, 96, 96, 96, 156, 156, 156, 156, 156, 156, - 156, 95, 95, 559, 569, 569, 678, 678, 526, 527, 526, 527, 526, 527, 526, - 527, 526, 527, 526, 527, 526, 527, 526, 527, 559, 559, 526, 527, 559, - 559, 559, 559, 678, 678, 678, 679, 559, 679, 81, 580, 680, 676, 676, 569, - 526, 527, 526, 527, 526, 527, 681, 559, 559, 682, 683, 684, 684, 684, 81, - 559, 685, 686, 559, 81, 81, 81, 81, 146, 146, 146, 146, 146, 81, 81, 497, - 81, 687, 688, 689, 690, 691, 688, 688, 692, 693, 688, 694, 695, 696, 695, - 697, 698, 698, 698, 698, 698, 698, 698, 698, 698, 698, 699, 700, 701, - 701, 701, 687, 688, 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - 702, 702, 702, 702, 702, 702, 702, 702, 692, 688, 693, 703, 704, 703, - 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, - 705, 705, 705, 705, 692, 701, 693, 701, 692, 693, 706, 707, 708, 706, - 709, 710, 711, 711, 711, 711, 711, 711, 711, 711, 711, 712, 710, 710, - 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, - 710, 710, 710, 710, 710, 713, 713, 714, 714, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 81, 81, 81, 714, 714, 714, 714, - 714, 714, 81, 81, 714, 714, 714, 81, 81, 81, 715, 690, 701, 703, 716, - 690, 690, 81, 717, 718, 718, 718, 718, 717, 717, 81, 81, 719, 719, 719, - 720, 512, 81, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, - 721, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 81, 721, 721, - 721, 81, 721, 721, 81, 721, 721, 721, 721, 721, 721, 721, 81, 81, 721, - 721, 721, 81, 81, 81, 81, 81, 199, 377, 199, 81, 81, 81, 81, 619, 619, - 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 81, 81, 81, 317, - 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 723, - 723, 723, 723, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, - 724, 724, 724, 724, 724, 724, 723, 723, 724, 725, 725, 81, 40, 40, 40, - 40, 81, 81, 81, 81, 724, 81, 81, 81, 81, 81, 81, 81, 317, 317, 317, 317, - 317, 156, 81, 81, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, - 726, 726, 81, 81, 81, 727, 727, 727, 727, 727, 727, 727, 727, 727, 81, - 81, 81, 81, 81, 81, 81, 156, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 81, 81, 81, 81, 728, - 728, 728, 728, 728, 728, 728, 728, 729, 729, 729, 729, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 728, 728, 728, 730, 730, 730, 730, 730, 730, 730, - 730, 730, 731, 730, 730, 730, 730, 730, 730, 730, 730, 731, 81, 81, 81, - 81, 81, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, - 732, 733, 733, 733, 733, 733, 81, 81, 81, 81, 81, 734, 734, 734, 734, - 734, 734, 734, 734, 734, 734, 734, 734, 734, 734, 81, 735, 736, 736, 736, - 736, 736, 736, 736, 736, 736, 736, 736, 736, 81, 81, 81, 81, 737, 738, - 738, 738, 738, 738, 81, 81, 739, 739, 739, 739, 739, 739, 739, 739, 740, - 740, 740, 740, 740, 740, 740, 740, 741, 741, 741, 741, 741, 741, 741, - 741, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, - 742, 81, 81, 743, 743, 743, 743, 743, 743, 743, 743, 743, 743, 81, 81, - 81, 81, 81, 81, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, - 744, 81, 81, 81, 81, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, - 745, 745, 81, 81, 81, 81, 746, 746, 746, 746, 746, 746, 746, 746, 747, - 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 748, 749, 749, 749, 749, 749, 749, 749, 749, - 749, 749, 749, 749, 749, 749, 749, 81, 749, 749, 749, 749, 749, 749, 81, - 81, 750, 750, 750, 750, 750, 750, 81, 81, 750, 81, 750, 750, 750, 750, - 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, - 750, 750, 81, 750, 750, 81, 81, 81, 750, 81, 81, 750, 751, 751, 751, 751, - 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 81, 752, 753, 753, 753, - 753, 753, 753, 753, 753, 754, 754, 754, 754, 754, 754, 754, 754, 754, - 754, 754, 754, 754, 754, 754, 755, 755, 756, 756, 756, 756, 756, 756, - 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, - 757, 757, 81, 81, 81, 81, 81, 81, 81, 81, 758, 758, 758, 758, 758, 758, - 758, 758, 758, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 81, - 759, 759, 81, 81, 81, 81, 81, 760, 760, 760, 760, 760, 761, 761, 761, - 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 762, 762, 762, - 762, 762, 762, 81, 81, 81, 763, 764, 764, 764, 764, 764, 764, 764, 764, - 764, 764, 81, 81, 81, 81, 81, 765, 766, 766, 766, 766, 766, 766, 766, - 766, 767, 767, 767, 767, 767, 767, 767, 767, 81, 81, 81, 81, 768, 768, - 767, 767, 768, 768, 768, 768, 768, 768, 768, 768, 81, 81, 768, 768, 768, - 768, 768, 768, 769, 770, 770, 770, 81, 770, 770, 81, 81, 81, 81, 81, 770, - 771, 770, 772, 769, 769, 769, 769, 81, 769, 769, 769, 81, 769, 769, 769, - 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, - 769, 769, 769, 769, 81, 81, 772, 773, 771, 81, 81, 81, 81, 774, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 81, 81, 81, 81, 81, 81, 81, 776, 776, - 776, 776, 776, 776, 776, 776, 777, 81, 81, 81, 81, 81, 81, 81, 778, 778, - 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 779, 779, 780, - 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 782, - 782, 782, 783, 783, 783, 783, 783, 783, 783, 783, 784, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 785, 786, 81, 81, 81, 81, - 787, 787, 787, 787, 787, 788, 788, 788, 788, 788, 788, 789, 81, 790, 790, - 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 81, 81, 81, - 791, 791, 791, 791, 791, 791, 791, 792, 792, 792, 792, 792, 792, 792, - 792, 792, 792, 792, 792, 792, 792, 81, 81, 793, 793, 793, 793, 793, 793, - 793, 793, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 81, 81, - 81, 81, 81, 795, 795, 795, 795, 795, 795, 795, 795, 796, 796, 796, 796, - 796, 796, 796, 796, 796, 796, 81, 81, 81, 81, 81, 81, 81, 797, 797, 797, - 797, 81, 81, 81, 81, 798, 798, 798, 798, 798, 798, 798, 799, 799, 799, - 799, 799, 799, 799, 799, 799, 81, 81, 81, 81, 81, 81, 81, 800, 800, 800, - 800, 800, 800, 800, 800, 800, 800, 800, 81, 81, 81, 81, 81, 801, 801, - 801, 801, 801, 801, 801, 801, 801, 801, 801, 81, 81, 81, 81, 81, 81, 81, - 802, 802, 802, 802, 802, 802, 803, 803, 803, 803, 803, 803, 803, 803, - 803, 803, 803, 803, 804, 804, 804, 804, 805, 805, 805, 805, 805, 805, - 805, 805, 805, 805, 81, 81, 81, 81, 81, 81, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 81, 807, 807, 807, 807, 807, - 807, 807, 807, 807, 807, 807, 807, 807, 808, 808, 808, 808, 808, 808, - 808, 808, 808, 808, 807, 809, 809, 809, 809, 809, 809, 809, 809, 809, - 809, 809, 809, 809, 809, 810, 810, 811, 811, 811, 810, 811, 810, 810, - 810, 810, 812, 812, 812, 812, 813, 813, 813, 813, 813, 81, 81, 81, 81, - 81, 81, 814, 815, 814, 816, 816, 816, 816, 816, 816, 816, 816, 816, 816, - 816, 816, 816, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, - 815, 815, 815, 817, 818, 818, 819, 819, 819, 819, 819, 81, 81, 81, 81, - 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, - 820, 820, 820, 820, 820, 820, 821, 821, 821, 821, 821, 821, 821, 821, - 821, 821, 81, 81, 81, 81, 81, 81, 81, 817, 822, 822, 823, 824, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 823, 823, 823, 822, - 822, 822, 822, 823, 823, 825, 826, 827, 827, 828, 829, 829, 829, 829, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 828, 81, 81, 830, 830, 830, 830, - 830, 830, 830, 830, 830, 81, 81, 81, 81, 81, 81, 81, 831, 831, 831, 831, - 831, 831, 831, 831, 831, 831, 81, 81, 81, 81, 81, 81, 832, 832, 832, 833, - 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, - 833, 833, 833, 833, 833, 834, 834, 834, 834, 834, 835, 834, 834, 834, - 834, 834, 834, 836, 836, 81, 837, 837, 837, 837, 837, 837, 837, 837, 837, - 837, 838, 838, 838, 838, 833, 835, 835, 81, 839, 839, 839, 839, 839, 839, - 839, 839, 839, 839, 839, 840, 841, 842, 839, 81, 843, 843, 844, 845, 845, - 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, - 844, 844, 844, 843, 843, 843, 843, 843, 843, 843, 843, 843, 844, 846, - 845, 845, 845, 845, 847, 847, 848, 847, 843, 849, 843, 843, 848, 81, 81, - 850, 850, 850, 850, 850, 850, 850, 850, 850, 850, 845, 851, 845, 847, - 847, 847, 81, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, - 852, 852, 852, 852, 852, 852, 852, 852, 81, 81, 81, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 81, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 854, 854, 854, 855, 855, 855, 854, 854, 855, 856, 857, 855, 858, - 858, 859, 858, 858, 859, 855, 81, 860, 860, 860, 860, 860, 860, 860, 81, - 860, 81, 860, 860, 860, 860, 81, 860, 860, 860, 860, 860, 860, 860, 860, - 860, 860, 860, 860, 860, 860, 860, 81, 860, 860, 861, 81, 81, 81, 81, 81, - 81, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, - 862, 863, 864, 864, 864, 863, 863, 863, 863, 863, 863, 865, 866, 81, 81, - 81, 81, 81, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 81, 81, 81, - 81, 81, 81, 868, 868, 869, 869, 81, 870, 870, 870, 870, 870, 870, 870, - 870, 81, 81, 870, 870, 81, 81, 870, 870, 870, 870, 870, 870, 870, 870, - 870, 870, 870, 870, 870, 870, 81, 870, 870, 870, 870, 870, 870, 870, 81, - 870, 870, 81, 870, 870, 870, 870, 870, 81, 871, 872, 870, 869, 869, 868, - 869, 869, 869, 869, 81, 81, 869, 869, 81, 81, 869, 869, 873, 81, 81, 870, - 81, 81, 81, 81, 81, 81, 869, 81, 81, 81, 81, 81, 870, 870, 870, 870, 870, - 869, 869, 81, 81, 874, 874, 874, 874, 874, 874, 874, 81, 81, 81, 875, - 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 876, 876, - 876, 877, 877, 877, 877, 877, 877, 877, 877, 876, 876, 878, 877, 877, - 876, 879, 875, 875, 875, 875, 880, 880, 880, 880, 881, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 81, 880, 81, 881, 883, 81, 884, 884, - 884, 884, 884, 884, 884, 884, 885, 885, 885, 886, 886, 886, 886, 886, - 886, 885, 886, 885, 885, 885, 885, 886, 886, 885, 887, 888, 884, 884, - 889, 884, 890, 890, 890, 890, 890, 890, 890, 890, 890, 890, 81, 81, 81, - 81, 81, 81, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, - 891, 891, 891, 892, 892, 892, 893, 893, 893, 893, 81, 81, 892, 892, 892, - 892, 893, 893, 892, 894, 895, 896, 897, 897, 898, 898, 899, 899, 899, - 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, - 897, 891, 891, 891, 891, 893, 893, 81, 81, 900, 900, 900, 900, 900, 900, - 900, 900, 901, 901, 901, 902, 902, 902, 902, 902, 902, 902, 902, 901, - 901, 902, 901, 903, 902, 904, 904, 905, 900, 81, 81, 81, 906, 906, 906, - 906, 906, 906, 906, 906, 906, 906, 81, 81, 81, 81, 81, 81, 907, 907, 907, - 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 81, 81, 81, 908, 908, - 908, 908, 908, 908, 908, 908, 908, 908, 908, 909, 910, 909, 910, 910, - 909, 909, 909, 909, 909, 909, 911, 912, 913, 913, 913, 913, 913, 913, - 913, 913, 913, 913, 81, 81, 81, 81, 81, 81, 914, 914, 914, 914, 914, 914, - 914, 914, 914, 914, 914, 81, 81, 915, 915, 915, 916, 916, 915, 915, 915, - 915, 916, 915, 915, 915, 915, 917, 81, 81, 81, 81, 918, 918, 918, 918, - 918, 918, 918, 918, 918, 918, 919, 919, 920, 920, 920, 921, 922, 922, - 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, 923, 923, 923, 924, - 924, 924, 924, 924, 924, 924, 924, 924, 923, 925, 926, 927, 81, 81, 81, - 81, 928, 928, 928, 928, 928, 928, 928, 928, 929, 929, 929, 929, 929, 929, - 929, 929, 930, 930, 930, 930, 930, 930, 930, 930, 930, 930, 931, 931, - 931, 931, 931, 931, 931, 931, 931, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 932, 933, 934, 934, 934, 934, 934, 934, 935, 935, 934, 934, - 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, - 933, 933, 934, 936, 934, 934, 934, 934, 937, 933, 934, 934, 934, 934, - 938, 939, 940, 940, 940, 940, 938, 939, 936, 941, 942, 942, 942, 942, - 942, 942, 943, 943, 942, 942, 942, 941, 941, 941, 941, 941, 941, 941, - 941, 941, 941, 941, 941, 941, 941, 941, 941, 81, 81, 941, 941, 941, 941, - 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 943, - 942, 944, 945, 945, 945, 941, 946, 946, 946, 945, 945, 81, 81, 81, 81, - 81, 947, 947, 947, 947, 947, 947, 947, 947, 947, 81, 81, 81, 81, 81, 81, - 81, 948, 948, 948, 948, 948, 948, 948, 948, 948, 81, 948, 948, 948, 948, - 948, 948, 948, 948, 948, 948, 948, 948, 948, 949, 950, 950, 950, 950, - 950, 950, 950, 81, 950, 950, 950, 950, 950, 950, 949, 951, 948, 952, 952, - 952, 952, 952, 81, 81, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, - 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - 954, 954, 954, 954, 954, 81, 81, 81, 955, 956, 957, 957, 957, 957, 957, - 957, 957, 957, 957, 957, 957, 957, 957, 957, 81, 81, 958, 958, 958, 958, - 958, 958, 958, 958, 958, 958, 958, 958, 958, 958, 81, 959, 958, 958, 958, - 958, 958, 958, 958, 959, 958, 958, 959, 958, 958, 81, 960, 960, 960, 960, - 960, 960, 960, 81, 960, 960, 81, 960, 960, 960, 960, 960, 960, 960, 960, - 960, 960, 960, 960, 960, 960, 961, 961, 961, 961, 961, 961, 81, 81, 81, - 961, 81, 961, 961, 81, 961, 961, 961, 962, 961, 963, 963, 960, 961, 964, - 964, 964, 964, 964, 964, 964, 964, 964, 964, 81, 81, 81, 81, 81, 81, 965, - 965, 965, 965, 965, 965, 81, 965, 965, 81, 965, 965, 965, 965, 965, 965, - 965, 965, 965, 965, 965, 965, 965, 965, 965, 965, 966, 966, 966, 966, - 966, 81, 967, 967, 81, 966, 966, 967, 966, 968, 965, 81, 81, 81, 81, 81, - 81, 81, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 81, 81, 81, 81, - 81, 81, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 971, 971, - 972, 972, 973, 973, 81, 81, 81, 81, 81, 81, 81, 974, 974, 974, 974, 974, - 974, 974, 974, 974, 974, 81, 81, 81, 81, 81, 81, 975, 975, 975, 975, 975, - 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 81, 976, 976, 976, 976, - 976, 81, 81, 81, 974, 974, 974, 974, 81, 81, 81, 81, 977, 977, 977, 977, - 977, 977, 977, 977, 978, 978, 978, 979, 979, 979, 977, 977, 977, 977, - 979, 977, 977, 977, 978, 979, 978, 979, 977, 977, 977, 977, 977, 977, - 977, 978, 979, 979, 977, 977, 977, 977, 977, 977, 977, 977, 977, 977, - 977, 81, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, - 980, 981, 982, 980, 980, 980, 980, 980, 980, 980, 81, 608, 81, 81, 81, - 81, 81, 81, 81, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, - 983, 983, 983, 983, 81, 984, 984, 984, 984, 984, 984, 984, 984, 984, 984, - 81, 81, 81, 81, 985, 985, 986, 986, 986, 986, 986, 986, 986, 986, 986, - 986, 986, 986, 986, 986, 81, 81, 987, 987, 987, 987, 987, 988, 81, 81, - 989, 989, 989, 989, 989, 989, 989, 989, 990, 990, 990, 990, 990, 990, - 990, 991, 991, 991, 992, 992, 993, 993, 993, 993, 994, 994, 994, 994, - 991, 993, 81, 81, 995, 995, 995, 995, 995, 995, 995, 995, 995, 995, 81, - 996, 996, 996, 996, 996, 996, 996, 81, 989, 989, 989, 989, 989, 81, 81, - 81, 81, 81, 989, 989, 989, 997, 997, 997, 997, 997, 997, 997, 997, 998, - 998, 998, 998, 998, 998, 998, 998, 999, 999, 999, 999, 999, 999, 999, - 999, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, 1001, 1001, 81, 81, - 81, 81, 81, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, - 1002, 1002, 1002, 81, 81, 81, 1002, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 81, 81, 81, 81, 81, 81, 81, 81, 1004, 1004, 1004, - 1004, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - 1005, 1005, 1006, 1007, 81, 81, 81, 81, 81, 81, 1008, 1008, 1008, 1008, - 1008, 1008, 1008, 1008, 1008, 1008, 81, 81, 81, 81, 81, 81, 1008, 1008, - 1008, 81, 81, 81, 81, 81, 579, 574, 574, 574, 574, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 81, 1009, 1009, 1009, 1009, 1009, 1009, - 1009, 1009, 1009, 1009, 1009, 1009, 81, 81, 81, 81, 1010, 1010, 1010, - 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 81, 81, 81, 81, 81, 1010, - 1010, 1010, 1010, 1010, 81, 81, 81, 1010, 81, 81, 81, 81, 81, 81, 81, - 1010, 1010, 81, 81, 1011, 1012, 1013, 1014, 503, 503, 503, 503, 81, 81, - 81, 81, 317, 317, 317, 317, 317, 317, 81, 81, 317, 317, 317, 317, 317, - 317, 317, 81, 81, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 1015, 1015, 451, 451, 451, 317, 317, 317, 1016, 1015, 1015, 1015, - 1015, 1015, 503, 503, 503, 503, 503, 503, 503, 503, 156, 156, 156, 156, - 156, 156, 156, 156, 317, 317, 96, 96, 96, 96, 96, 156, 156, 317, 317, - 317, 317, 317, 317, 96, 96, 96, 96, 317, 317, 317, 81, 81, 81, 81, 81, - 81, 81, 724, 724, 1017, 1017, 1017, 724, 81, 81, 619, 619, 619, 619, 81, - 81, 81, 81, 619, 81, 81, 81, 81, 81, 81, 81, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, 49, 49, 510, 81, 510, - 510, 81, 81, 510, 81, 81, 510, 510, 81, 81, 510, 510, 510, 510, 81, 510, - 510, 49, 49, 81, 49, 81, 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, - 49, 49, 49, 510, 510, 81, 510, 510, 510, 510, 81, 81, 510, 510, 510, 510, - 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, - 510, 510, 81, 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 81, 510, - 81, 81, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 81, 81, 510, 1018, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 496, 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, - 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, 49, 49, - 510, 510, 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 1018, 49, 496, 49, 49, 49, 49, 49, 49, 49, 49, 510, 49, 81, 81, 1019, - 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1020, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, 1020, 1020, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1020, 1020, 1020, 1020, 1020, - 1020, 1021, 1020, 1020, 1022, 1022, 1022, 1022, 1023, 81, 81, 81, 81, 81, - 81, 81, 1021, 1021, 1021, 1021, 1021, 81, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 1024, 1024, - 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 81, 1024, 1024, 1024, 1024, - 1024, 1024, 1024, 81, 1024, 1024, 81, 1024, 1024, 1024, 1024, 1024, 81, - 81, 81, 81, 81, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, - 1025, 1025, 1025, 1025, 81, 81, 1026, 1026, 1026, 1026, 1026, 1026, 1026, - 1026, 1026, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 81, 1028, 1028, - 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1029, 1029, 1029, 1029, - 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, - 1029, 1029, 1030, 1030, 1030, 1030, 1030, 1030, 1031, 81, 81, 81, 81, 81, - 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 81, 81, 81, - 81, 1033, 1033, 81, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, - 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1035, 1034, - 1034, 1034, 1036, 1034, 1034, 1034, 1034, 81, 81, 81, 146, 146, 146, 146, - 81, 146, 146, 146, 81, 146, 146, 81, 146, 81, 81, 146, 81, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 81, 146, 81, - 146, 81, 81, 81, 81, 81, 81, 146, 81, 81, 81, 81, 146, 81, 146, 81, 146, - 81, 146, 146, 146, 81, 146, 81, 146, 81, 146, 81, 146, 81, 146, 146, 146, - 146, 81, 146, 81, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 81, 81, 81, 81, 81, 146, 146, 146, 81, 146, 146, 146, 132, 132, 81, - 81, 81, 81, 81, 81, 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, 1037, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 529, 529, 529, 529, 529, - 529, 529, 1037, 1037, 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, 529, 1037, 1037, 47, - 47, 47, 519, 519, 1037, 1037, 1037, 530, 530, 530, 530, 530, 530, 317, - 40, 530, 530, 40, 40, 1037, 1037, 1037, 1037, 530, 530, 530, 530, 530, - 530, 1038, 530, 530, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, - 1038, 1038, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1039, 1039, 1039, 1039, 1039, - 1039, 1039, 1039, 1039, 1039, 1040, 585, 585, 1037, 1037, 1037, 1037, - 1037, 585, 585, 585, 585, 1037, 1037, 1037, 1037, 585, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 585, 585, 1037, 1037, 1037, 1037, 1037, 1037, - 525, 525, 525, 525, 525, 525, 1037, 1037, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 529, 525, 525, 525, 525, 525, 525, 529, 525, 525, 525, 525, - 525, 525, 525, 536, 525, 525, 525, 525, 525, 525, 529, 529, 529, 529, - 529, 529, 529, 529, 40, 40, 529, 529, 525, 525, 525, 525, 525, 528, 528, - 525, 525, 525, 525, 525, 528, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 536, 525, 525, 536, 534, 534, 529, 529, 525, 525, 529, 529, - 529, 525, 529, 529, 529, 525, 525, 525, 1041, 1041, 1041, 1041, 1041, - 525, 525, 525, 525, 525, 525, 525, 529, 525, 529, 536, 536, 525, 525, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, 536, - 525, 525, 525, 525, 536, 525, 536, 525, 525, 525, 536, 525, 525, 525, - 525, 536, 536, 536, 525, 536, 536, 536, 528, 525, 528, 525, 528, 525, - 525, 525, 525, 525, 536, 525, 525, 525, 525, 528, 525, 528, 528, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 529, 529, 525, 528, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 525, 525, 525, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 528, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 529, 525, 525, - 525, 525, 529, 529, 529, 529, 529, 534, 534, 529, 529, 529, 529, 536, - 529, 529, 529, 529, 529, 534, 529, 529, 529, 529, 536, 536, 529, 529, - 529, 529, 529, 40, 40, 40, 40, 40, 40, 40, 40, 529, 529, 529, 529, 40, - 40, 529, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 525, 536, 536, 536, 536, 536, 40, 40, 40, 40, 40, 40, 538, 538, - 538, 1042, 1042, 1042, 40, 40, 40, 40, 525, 525, 525, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 536, 536, 536, 525, 536, 525, 525, 525, 525, - 525, 529, 529, 529, 529, 529, 529, 536, 529, 529, 529, 525, 525, 525, - 529, 529, 1037, 1037, 1037, 529, 529, 529, 525, 525, 1037, 1037, 1037, - 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 1037, 1037, 1037, 1037, - 1037, 1037, 40, 40, 40, 40, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, - 529, 529, 529, 529, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, - 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, 40, 1037, 1037, - 536, 536, 536, 536, 536, 525, 536, 536, 525, 525, 525, 525, 525, 525, - 536, 525, 536, 536, 525, 525, 525, 536, 536, 1037, 525, 1037, 1037, 525, - 525, 525, 525, 1037, 1037, 1037, 525, 1037, 525, 525, 525, 525, 525, 525, - 525, 1037, 1037, 1037, 1037, 1037, 525, 525, 525, 525, 525, 536, 536, - 525, 536, 536, 1037, 1037, 1037, 1037, 1037, 1037, 525, 536, 536, 536, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 1037, 1037, - 1037, 1037, 1037, 1037, 81, 81, 592, 592, 592, 592, 592, 592, 592, 593, - 592, 592, 592, 592, 592, 593, 593, 593, 592, 593, 593, 593, 593, 593, - 593, 593, 593, 593, 593, 593, 593, 593, 81, 81, 81, 503, 81, 81, 81, 81, - 81, 81, 503, 503, 503, 503, 503, 503, 503, 503, 670, 670, 670, 670, 670, - 670, 81, 81, -}; - -/* decomposition data */ -static const unsigned short decomp_data[] = { - 0, 257, 32, 514, 32, 776, 259, 97, 514, 32, 772, 259, 50, 259, 51, 514, - 32, 769, 258, 956, 514, 32, 807, 259, 49, 259, 111, 772, 49, 8260, 52, - 772, 49, 8260, 50, 772, 51, 8260, 52, 512, 65, 768, 512, 65, 769, 512, - 65, 770, 512, 65, 771, 512, 65, 776, 512, 65, 778, 512, 67, 807, 512, 69, - 768, 512, 69, 769, 512, 69, 770, 512, 69, 776, 512, 73, 768, 512, 73, - 769, 512, 73, 770, 512, 73, 776, 512, 78, 771, 512, 79, 768, 512, 79, - 769, 512, 79, 770, 512, 79, 771, 512, 79, 776, 512, 85, 768, 512, 85, - 769, 512, 85, 770, 512, 85, 776, 512, 89, 769, 512, 97, 768, 512, 97, - 769, 512, 97, 770, 512, 97, 771, 512, 97, 776, 512, 97, 778, 512, 99, - 807, 512, 101, 768, 512, 101, 769, 512, 101, 770, 512, 101, 776, 512, - 105, 768, 512, 105, 769, 512, 105, 770, 512, 105, 776, 512, 110, 771, - 512, 111, 768, 512, 111, 769, 512, 111, 770, 512, 111, 771, 512, 111, - 776, 512, 117, 768, 512, 117, 769, 512, 117, 770, 512, 117, 776, 512, - 121, 769, 512, 121, 776, 512, 65, 772, 512, 97, 772, 512, 65, 774, 512, - 97, 774, 512, 65, 808, 512, 97, 808, 512, 67, 769, 512, 99, 769, 512, 67, - 770, 512, 99, 770, 512, 67, 775, 512, 99, 775, 512, 67, 780, 512, 99, - 780, 512, 68, 780, 512, 100, 780, 512, 69, 772, 512, 101, 772, 512, 69, - 774, 512, 101, 774, 512, 69, 775, 512, 101, 775, 512, 69, 808, 512, 101, - 808, 512, 69, 780, 512, 101, 780, 512, 71, 770, 512, 103, 770, 512, 71, - 774, 512, 103, 774, 512, 71, 775, 512, 103, 775, 512, 71, 807, 512, 103, - 807, 512, 72, 770, 512, 104, 770, 512, 73, 771, 512, 105, 771, 512, 73, - 772, 512, 105, 772, 512, 73, 774, 512, 105, 774, 512, 73, 808, 512, 105, - 808, 512, 73, 775, 514, 73, 74, 514, 105, 106, 512, 74, 770, 512, 106, - 770, 512, 75, 807, 512, 107, 807, 512, 76, 769, 512, 108, 769, 512, 76, - 807, 512, 108, 807, 512, 76, 780, 512, 108, 780, 514, 76, 183, 514, 108, - 183, 512, 78, 769, 512, 110, 769, 512, 78, 807, 512, 110, 807, 512, 78, - 780, 512, 110, 780, 514, 700, 110, 512, 79, 772, 512, 111, 772, 512, 79, - 774, 512, 111, 774, 512, 79, 779, 512, 111, 779, 512, 82, 769, 512, 114, - 769, 512, 82, 807, 512, 114, 807, 512, 82, 780, 512, 114, 780, 512, 83, - 769, 512, 115, 769, 512, 83, 770, 512, 115, 770, 512, 83, 807, 512, 115, - 807, 512, 83, 780, 512, 115, 780, 512, 84, 807, 512, 116, 807, 512, 84, - 780, 512, 116, 780, 512, 85, 771, 512, 117, 771, 512, 85, 772, 512, 117, - 772, 512, 85, 774, 512, 117, 774, 512, 85, 778, 512, 117, 778, 512, 85, - 779, 512, 117, 779, 512, 85, 808, 512, 117, 808, 512, 87, 770, 512, 119, - 770, 512, 89, 770, 512, 121, 770, 512, 89, 776, 512, 90, 769, 512, 122, - 769, 512, 90, 775, 512, 122, 775, 512, 90, 780, 512, 122, 780, 258, 115, - 512, 79, 795, 512, 111, 795, 512, 85, 795, 512, 117, 795, 514, 68, 381, - 514, 68, 382, 514, 100, 382, 514, 76, 74, 514, 76, 106, 514, 108, 106, - 514, 78, 74, 514, 78, 106, 514, 110, 106, 512, 65, 780, 512, 97, 780, - 512, 73, 780, 512, 105, 780, 512, 79, 780, 512, 111, 780, 512, 85, 780, - 512, 117, 780, 512, 220, 772, 512, 252, 772, 512, 220, 769, 512, 252, - 769, 512, 220, 780, 512, 252, 780, 512, 220, 768, 512, 252, 768, 512, - 196, 772, 512, 228, 772, 512, 550, 772, 512, 551, 772, 512, 198, 772, - 512, 230, 772, 512, 71, 780, 512, 103, 780, 512, 75, 780, 512, 107, 780, - 512, 79, 808, 512, 111, 808, 512, 490, 772, 512, 491, 772, 512, 439, 780, - 512, 658, 780, 512, 106, 780, 514, 68, 90, 514, 68, 122, 514, 100, 122, - 512, 71, 769, 512, 103, 769, 512, 78, 768, 512, 110, 768, 512, 197, 769, - 512, 229, 769, 512, 198, 769, 512, 230, 769, 512, 216, 769, 512, 248, - 769, 512, 65, 783, 512, 97, 783, 512, 65, 785, 512, 97, 785, 512, 69, - 783, 512, 101, 783, 512, 69, 785, 512, 101, 785, 512, 73, 783, 512, 105, - 783, 512, 73, 785, 512, 105, 785, 512, 79, 783, 512, 111, 783, 512, 79, - 785, 512, 111, 785, 512, 82, 783, 512, 114, 783, 512, 82, 785, 512, 114, - 785, 512, 85, 783, 512, 117, 783, 512, 85, 785, 512, 117, 785, 512, 83, - 806, 512, 115, 806, 512, 84, 806, 512, 116, 806, 512, 72, 780, 512, 104, - 780, 512, 65, 775, 512, 97, 775, 512, 69, 807, 512, 101, 807, 512, 214, - 772, 512, 246, 772, 512, 213, 772, 512, 245, 772, 512, 79, 775, 512, 111, - 775, 512, 558, 772, 512, 559, 772, 512, 89, 772, 512, 121, 772, 259, 104, - 259, 614, 259, 106, 259, 114, 259, 633, 259, 635, 259, 641, 259, 119, - 259, 121, 514, 32, 774, 514, 32, 775, 514, 32, 778, 514, 32, 808, 514, - 32, 771, 514, 32, 779, 259, 611, 259, 108, 259, 115, 259, 120, 259, 661, - 256, 768, 256, 769, 256, 787, 512, 776, 769, 256, 697, 514, 32, 837, 256, - 59, 514, 32, 769, 512, 168, 769, 512, 913, 769, 256, 183, 512, 917, 769, - 512, 919, 769, 512, 921, 769, 512, 927, 769, 512, 933, 769, 512, 937, - 769, 512, 970, 769, 512, 921, 776, 512, 933, 776, 512, 945, 769, 512, - 949, 769, 512, 951, 769, 512, 953, 769, 512, 971, 769, 512, 953, 776, - 512, 965, 776, 512, 959, 769, 512, 965, 769, 512, 969, 769, 258, 946, - 258, 952, 258, 933, 512, 978, 769, 512, 978, 776, 258, 966, 258, 960, - 258, 954, 258, 961, 258, 962, 258, 920, 258, 949, 258, 931, 512, 1045, - 768, 512, 1045, 776, 512, 1043, 769, 512, 1030, 776, 512, 1050, 769, 512, - 1048, 768, 512, 1059, 774, 512, 1048, 774, 512, 1080, 774, 512, 1077, - 768, 512, 1077, 776, 512, 1075, 769, 512, 1110, 776, 512, 1082, 769, 512, - 1080, 768, 512, 1091, 774, 512, 1140, 783, 512, 1141, 783, 512, 1046, - 774, 512, 1078, 774, 512, 1040, 774, 512, 1072, 774, 512, 1040, 776, 512, - 1072, 776, 512, 1045, 774, 512, 1077, 774, 512, 1240, 776, 512, 1241, - 776, 512, 1046, 776, 512, 1078, 776, 512, 1047, 776, 512, 1079, 776, 512, - 1048, 772, 512, 1080, 772, 512, 1048, 776, 512, 1080, 776, 512, 1054, - 776, 512, 1086, 776, 512, 1256, 776, 512, 1257, 776, 512, 1069, 776, 512, - 1101, 776, 512, 1059, 772, 512, 1091, 772, 512, 1059, 776, 512, 1091, - 776, 512, 1059, 779, 512, 1091, 779, 512, 1063, 776, 512, 1095, 776, 512, - 1067, 776, 512, 1099, 776, 514, 1381, 1410, 512, 1575, 1619, 512, 1575, - 1620, 512, 1608, 1620, 512, 1575, 1621, 512, 1610, 1620, 514, 1575, 1652, - 514, 1608, 1652, 514, 1735, 1652, 514, 1610, 1652, 512, 1749, 1620, 512, - 1729, 1620, 512, 1746, 1620, 512, 2344, 2364, 512, 2352, 2364, 512, 2355, - 2364, 512, 2325, 2364, 512, 2326, 2364, 512, 2327, 2364, 512, 2332, 2364, - 512, 2337, 2364, 512, 2338, 2364, 512, 2347, 2364, 512, 2351, 2364, 512, - 2503, 2494, 512, 2503, 2519, 512, 2465, 2492, 512, 2466, 2492, 512, 2479, - 2492, 512, 2610, 2620, 512, 2616, 2620, 512, 2582, 2620, 512, 2583, 2620, - 512, 2588, 2620, 512, 2603, 2620, 512, 2887, 2902, 512, 2887, 2878, 512, - 2887, 2903, 512, 2849, 2876, 512, 2850, 2876, 512, 2962, 3031, 512, 3014, - 3006, 512, 3015, 3006, 512, 3014, 3031, 512, 3142, 3158, 512, 3263, 3285, - 512, 3270, 3285, 512, 3270, 3286, 512, 3270, 3266, 512, 3274, 3285, 512, - 3398, 3390, 512, 3399, 3390, 512, 3398, 3415, 512, 3545, 3530, 512, 3545, - 3535, 512, 3548, 3530, 512, 3545, 3551, 514, 3661, 3634, 514, 3789, 3762, - 514, 3755, 3737, 514, 3755, 3745, 257, 3851, 512, 3906, 4023, 512, 3916, - 4023, 512, 3921, 4023, 512, 3926, 4023, 512, 3931, 4023, 512, 3904, 4021, - 512, 3953, 3954, 512, 3953, 3956, 512, 4018, 3968, 514, 4018, 3969, 512, - 4019, 3968, 514, 4019, 3969, 512, 3953, 3968, 512, 3986, 4023, 512, 3996, - 4023, 512, 4001, 4023, 512, 4006, 4023, 512, 4011, 4023, 512, 3984, 4021, - 512, 4133, 4142, 259, 4316, 512, 6917, 6965, 512, 6919, 6965, 512, 6921, - 6965, 512, 6923, 6965, 512, 6925, 6965, 512, 6929, 6965, 512, 6970, 6965, - 512, 6972, 6965, 512, 6974, 6965, 512, 6975, 6965, 512, 6978, 6965, 259, - 65, 259, 198, 259, 66, 259, 68, 259, 69, 259, 398, 259, 71, 259, 72, 259, - 73, 259, 74, 259, 75, 259, 76, 259, 77, 259, 78, 259, 79, 259, 546, 259, - 80, 259, 82, 259, 84, 259, 85, 259, 87, 259, 97, 259, 592, 259, 593, 259, - 7426, 259, 98, 259, 100, 259, 101, 259, 601, 259, 603, 259, 604, 259, - 103, 259, 107, 259, 109, 259, 331, 259, 111, 259, 596, 259, 7446, 259, - 7447, 259, 112, 259, 116, 259, 117, 259, 7453, 259, 623, 259, 118, 259, - 7461, 259, 946, 259, 947, 259, 948, 259, 966, 259, 967, 261, 105, 261, - 114, 261, 117, 261, 118, 261, 946, 261, 947, 261, 961, 261, 966, 261, - 967, 259, 1085, 259, 594, 259, 99, 259, 597, 259, 240, 259, 604, 259, - 102, 259, 607, 259, 609, 259, 613, 259, 616, 259, 617, 259, 618, 259, - 7547, 259, 669, 259, 621, 259, 7557, 259, 671, 259, 625, 259, 624, 259, - 626, 259, 627, 259, 628, 259, 629, 259, 632, 259, 642, 259, 643, 259, - 427, 259, 649, 259, 650, 259, 7452, 259, 651, 259, 652, 259, 122, 259, - 656, 259, 657, 259, 658, 259, 952, 512, 65, 805, 512, 97, 805, 512, 66, - 775, 512, 98, 775, 512, 66, 803, 512, 98, 803, 512, 66, 817, 512, 98, - 817, 512, 199, 769, 512, 231, 769, 512, 68, 775, 512, 100, 775, 512, 68, - 803, 512, 100, 803, 512, 68, 817, 512, 100, 817, 512, 68, 807, 512, 100, - 807, 512, 68, 813, 512, 100, 813, 512, 274, 768, 512, 275, 768, 512, 274, - 769, 512, 275, 769, 512, 69, 813, 512, 101, 813, 512, 69, 816, 512, 101, - 816, 512, 552, 774, 512, 553, 774, 512, 70, 775, 512, 102, 775, 512, 71, - 772, 512, 103, 772, 512, 72, 775, 512, 104, 775, 512, 72, 803, 512, 104, - 803, 512, 72, 776, 512, 104, 776, 512, 72, 807, 512, 104, 807, 512, 72, - 814, 512, 104, 814, 512, 73, 816, 512, 105, 816, 512, 207, 769, 512, 239, - 769, 512, 75, 769, 512, 107, 769, 512, 75, 803, 512, 107, 803, 512, 75, - 817, 512, 107, 817, 512, 76, 803, 512, 108, 803, 512, 7734, 772, 512, - 7735, 772, 512, 76, 817, 512, 108, 817, 512, 76, 813, 512, 108, 813, 512, - 77, 769, 512, 109, 769, 512, 77, 775, 512, 109, 775, 512, 77, 803, 512, - 109, 803, 512, 78, 775, 512, 110, 775, 512, 78, 803, 512, 110, 803, 512, - 78, 817, 512, 110, 817, 512, 78, 813, 512, 110, 813, 512, 213, 769, 512, - 245, 769, 512, 213, 776, 512, 245, 776, 512, 332, 768, 512, 333, 768, - 512, 332, 769, 512, 333, 769, 512, 80, 769, 512, 112, 769, 512, 80, 775, - 512, 112, 775, 512, 82, 775, 512, 114, 775, 512, 82, 803, 512, 114, 803, - 512, 7770, 772, 512, 7771, 772, 512, 82, 817, 512, 114, 817, 512, 83, - 775, 512, 115, 775, 512, 83, 803, 512, 115, 803, 512, 346, 775, 512, 347, - 775, 512, 352, 775, 512, 353, 775, 512, 7778, 775, 512, 7779, 775, 512, - 84, 775, 512, 116, 775, 512, 84, 803, 512, 116, 803, 512, 84, 817, 512, - 116, 817, 512, 84, 813, 512, 116, 813, 512, 85, 804, 512, 117, 804, 512, - 85, 816, 512, 117, 816, 512, 85, 813, 512, 117, 813, 512, 360, 769, 512, - 361, 769, 512, 362, 776, 512, 363, 776, 512, 86, 771, 512, 118, 771, 512, - 86, 803, 512, 118, 803, 512, 87, 768, 512, 119, 768, 512, 87, 769, 512, - 119, 769, 512, 87, 776, 512, 119, 776, 512, 87, 775, 512, 119, 775, 512, - 87, 803, 512, 119, 803, 512, 88, 775, 512, 120, 775, 512, 88, 776, 512, - 120, 776, 512, 89, 775, 512, 121, 775, 512, 90, 770, 512, 122, 770, 512, - 90, 803, 512, 122, 803, 512, 90, 817, 512, 122, 817, 512, 104, 817, 512, - 116, 776, 512, 119, 778, 512, 121, 778, 514, 97, 702, 512, 383, 775, 512, - 65, 803, 512, 97, 803, 512, 65, 777, 512, 97, 777, 512, 194, 769, 512, - 226, 769, 512, 194, 768, 512, 226, 768, 512, 194, 777, 512, 226, 777, - 512, 194, 771, 512, 226, 771, 512, 7840, 770, 512, 7841, 770, 512, 258, - 769, 512, 259, 769, 512, 258, 768, 512, 259, 768, 512, 258, 777, 512, - 259, 777, 512, 258, 771, 512, 259, 771, 512, 7840, 774, 512, 7841, 774, - 512, 69, 803, 512, 101, 803, 512, 69, 777, 512, 101, 777, 512, 69, 771, - 512, 101, 771, 512, 202, 769, 512, 234, 769, 512, 202, 768, 512, 234, - 768, 512, 202, 777, 512, 234, 777, 512, 202, 771, 512, 234, 771, 512, - 7864, 770, 512, 7865, 770, 512, 73, 777, 512, 105, 777, 512, 73, 803, - 512, 105, 803, 512, 79, 803, 512, 111, 803, 512, 79, 777, 512, 111, 777, - 512, 212, 769, 512, 244, 769, 512, 212, 768, 512, 244, 768, 512, 212, - 777, 512, 244, 777, 512, 212, 771, 512, 244, 771, 512, 7884, 770, 512, - 7885, 770, 512, 416, 769, 512, 417, 769, 512, 416, 768, 512, 417, 768, - 512, 416, 777, 512, 417, 777, 512, 416, 771, 512, 417, 771, 512, 416, - 803, 512, 417, 803, 512, 85, 803, 512, 117, 803, 512, 85, 777, 512, 117, - 777, 512, 431, 769, 512, 432, 769, 512, 431, 768, 512, 432, 768, 512, - 431, 777, 512, 432, 777, 512, 431, 771, 512, 432, 771, 512, 431, 803, - 512, 432, 803, 512, 89, 768, 512, 121, 768, 512, 89, 803, 512, 121, 803, - 512, 89, 777, 512, 121, 777, 512, 89, 771, 512, 121, 771, 512, 945, 787, - 512, 945, 788, 512, 7936, 768, 512, 7937, 768, 512, 7936, 769, 512, 7937, - 769, 512, 7936, 834, 512, 7937, 834, 512, 913, 787, 512, 913, 788, 512, - 7944, 768, 512, 7945, 768, 512, 7944, 769, 512, 7945, 769, 512, 7944, - 834, 512, 7945, 834, 512, 949, 787, 512, 949, 788, 512, 7952, 768, 512, - 7953, 768, 512, 7952, 769, 512, 7953, 769, 512, 917, 787, 512, 917, 788, - 512, 7960, 768, 512, 7961, 768, 512, 7960, 769, 512, 7961, 769, 512, 951, - 787, 512, 951, 788, 512, 7968, 768, 512, 7969, 768, 512, 7968, 769, 512, - 7969, 769, 512, 7968, 834, 512, 7969, 834, 512, 919, 787, 512, 919, 788, - 512, 7976, 768, 512, 7977, 768, 512, 7976, 769, 512, 7977, 769, 512, - 7976, 834, 512, 7977, 834, 512, 953, 787, 512, 953, 788, 512, 7984, 768, - 512, 7985, 768, 512, 7984, 769, 512, 7985, 769, 512, 7984, 834, 512, - 7985, 834, 512, 921, 787, 512, 921, 788, 512, 7992, 768, 512, 7993, 768, - 512, 7992, 769, 512, 7993, 769, 512, 7992, 834, 512, 7993, 834, 512, 959, - 787, 512, 959, 788, 512, 8000, 768, 512, 8001, 768, 512, 8000, 769, 512, - 8001, 769, 512, 927, 787, 512, 927, 788, 512, 8008, 768, 512, 8009, 768, - 512, 8008, 769, 512, 8009, 769, 512, 965, 787, 512, 965, 788, 512, 8016, - 768, 512, 8017, 768, 512, 8016, 769, 512, 8017, 769, 512, 8016, 834, 512, - 8017, 834, 512, 933, 788, 512, 8025, 768, 512, 8025, 769, 512, 8025, 834, - 512, 969, 787, 512, 969, 788, 512, 8032, 768, 512, 8033, 768, 512, 8032, - 769, 512, 8033, 769, 512, 8032, 834, 512, 8033, 834, 512, 937, 787, 512, - 937, 788, 512, 8040, 768, 512, 8041, 768, 512, 8040, 769, 512, 8041, 769, - 512, 8040, 834, 512, 8041, 834, 512, 945, 768, 256, 940, 512, 949, 768, - 256, 941, 512, 951, 768, 256, 942, 512, 953, 768, 256, 943, 512, 959, - 768, 256, 972, 512, 965, 768, 256, 973, 512, 969, 768, 256, 974, 512, - 7936, 837, 512, 7937, 837, 512, 7938, 837, 512, 7939, 837, 512, 7940, - 837, 512, 7941, 837, 512, 7942, 837, 512, 7943, 837, 512, 7944, 837, 512, - 7945, 837, 512, 7946, 837, 512, 7947, 837, 512, 7948, 837, 512, 7949, - 837, 512, 7950, 837, 512, 7951, 837, 512, 7968, 837, 512, 7969, 837, 512, - 7970, 837, 512, 7971, 837, 512, 7972, 837, 512, 7973, 837, 512, 7974, - 837, 512, 7975, 837, 512, 7976, 837, 512, 7977, 837, 512, 7978, 837, 512, - 7979, 837, 512, 7980, 837, 512, 7981, 837, 512, 7982, 837, 512, 7983, - 837, 512, 8032, 837, 512, 8033, 837, 512, 8034, 837, 512, 8035, 837, 512, - 8036, 837, 512, 8037, 837, 512, 8038, 837, 512, 8039, 837, 512, 8040, - 837, 512, 8041, 837, 512, 8042, 837, 512, 8043, 837, 512, 8044, 837, 512, - 8045, 837, 512, 8046, 837, 512, 8047, 837, 512, 945, 774, 512, 945, 772, - 512, 8048, 837, 512, 945, 837, 512, 940, 837, 512, 945, 834, 512, 8118, - 837, 512, 913, 774, 512, 913, 772, 512, 913, 768, 256, 902, 512, 913, - 837, 514, 32, 787, 256, 953, 514, 32, 787, 514, 32, 834, 512, 168, 834, - 512, 8052, 837, 512, 951, 837, 512, 942, 837, 512, 951, 834, 512, 8134, - 837, 512, 917, 768, 256, 904, 512, 919, 768, 256, 905, 512, 919, 837, - 512, 8127, 768, 512, 8127, 769, 512, 8127, 834, 512, 953, 774, 512, 953, - 772, 512, 970, 768, 256, 912, 512, 953, 834, 512, 970, 834, 512, 921, - 774, 512, 921, 772, 512, 921, 768, 256, 906, 512, 8190, 768, 512, 8190, - 769, 512, 8190, 834, 512, 965, 774, 512, 965, 772, 512, 971, 768, 256, - 944, 512, 961, 787, 512, 961, 788, 512, 965, 834, 512, 971, 834, 512, - 933, 774, 512, 933, 772, 512, 933, 768, 256, 910, 512, 929, 788, 512, - 168, 768, 256, 901, 256, 96, 512, 8060, 837, 512, 969, 837, 512, 974, - 837, 512, 969, 834, 512, 8182, 837, 512, 927, 768, 256, 908, 512, 937, - 768, 256, 911, 512, 937, 837, 256, 180, 514, 32, 788, 256, 8194, 256, - 8195, 258, 32, 258, 32, 258, 32, 258, 32, 258, 32, 257, 32, 258, 32, 258, - 32, 258, 32, 257, 8208, 514, 32, 819, 258, 46, 514, 46, 46, 770, 46, 46, - 46, 257, 32, 514, 8242, 8242, 770, 8242, 8242, 8242, 514, 8245, 8245, - 770, 8245, 8245, 8245, 514, 33, 33, 514, 32, 773, 514, 63, 63, 514, 63, - 33, 514, 33, 63, 1026, 8242, 8242, 8242, 8242, 258, 32, 259, 48, 259, - 105, 259, 52, 259, 53, 259, 54, 259, 55, 259, 56, 259, 57, 259, 43, 259, - 8722, 259, 61, 259, 40, 259, 41, 259, 110, 261, 48, 261, 49, 261, 50, - 261, 51, 261, 52, 261, 53, 261, 54, 261, 55, 261, 56, 261, 57, 261, 43, - 261, 8722, 261, 61, 261, 40, 261, 41, 261, 97, 261, 101, 261, 111, 261, - 120, 261, 601, 261, 104, 261, 107, 261, 108, 261, 109, 261, 110, 261, - 112, 261, 115, 261, 116, 514, 82, 115, 770, 97, 47, 99, 770, 97, 47, 115, - 262, 67, 514, 176, 67, 770, 99, 47, 111, 770, 99, 47, 117, 258, 400, 514, - 176, 70, 262, 103, 262, 72, 262, 72, 262, 72, 262, 104, 262, 295, 262, - 73, 262, 73, 262, 76, 262, 108, 262, 78, 514, 78, 111, 262, 80, 262, 81, - 262, 82, 262, 82, 262, 82, 515, 83, 77, 770, 84, 69, 76, 515, 84, 77, - 262, 90, 256, 937, 262, 90, 256, 75, 256, 197, 262, 66, 262, 67, 262, - 101, 262, 69, 262, 70, 262, 77, 262, 111, 258, 1488, 258, 1489, 258, - 1490, 258, 1491, 262, 105, 770, 70, 65, 88, 262, 960, 262, 947, 262, 915, - 262, 928, 262, 8721, 262, 68, 262, 100, 262, 101, 262, 105, 262, 106, - 772, 49, 8260, 55, 772, 49, 8260, 57, 1028, 49, 8260, 49, 48, 772, 49, - 8260, 51, 772, 50, 8260, 51, 772, 49, 8260, 53, 772, 50, 8260, 53, 772, - 51, 8260, 53, 772, 52, 8260, 53, 772, 49, 8260, 54, 772, 53, 8260, 54, - 772, 49, 8260, 56, 772, 51, 8260, 56, 772, 53, 8260, 56, 772, 55, 8260, - 56, 516, 49, 8260, 258, 73, 514, 73, 73, 770, 73, 73, 73, 514, 73, 86, - 258, 86, 514, 86, 73, 770, 86, 73, 73, 1026, 86, 73, 73, 73, 514, 73, 88, - 258, 88, 514, 88, 73, 770, 88, 73, 73, 258, 76, 258, 67, 258, 68, 258, - 77, 258, 105, 514, 105, 105, 770, 105, 105, 105, 514, 105, 118, 258, 118, - 514, 118, 105, 770, 118, 105, 105, 1026, 118, 105, 105, 105, 514, 105, - 120, 258, 120, 514, 120, 105, 770, 120, 105, 105, 258, 108, 258, 99, 258, - 100, 258, 109, 772, 48, 8260, 51, 512, 8592, 824, 512, 8594, 824, 512, - 8596, 824, 512, 8656, 824, 512, 8660, 824, 512, 8658, 824, 512, 8707, - 824, 512, 8712, 824, 512, 8715, 824, 512, 8739, 824, 512, 8741, 824, 514, - 8747, 8747, 770, 8747, 8747, 8747, 514, 8750, 8750, 770, 8750, 8750, - 8750, 512, 8764, 824, 512, 8771, 824, 512, 8773, 824, 512, 8776, 824, - 512, 61, 824, 512, 8801, 824, 512, 8781, 824, 512, 60, 824, 512, 62, 824, - 512, 8804, 824, 512, 8805, 824, 512, 8818, 824, 512, 8819, 824, 512, - 8822, 824, 512, 8823, 824, 512, 8826, 824, 512, 8827, 824, 512, 8834, - 824, 512, 8835, 824, 512, 8838, 824, 512, 8839, 824, 512, 8866, 824, 512, - 8872, 824, 512, 8873, 824, 512, 8875, 824, 512, 8828, 824, 512, 8829, - 824, 512, 8849, 824, 512, 8850, 824, 512, 8882, 824, 512, 8883, 824, 512, - 8884, 824, 512, 8885, 824, 256, 12296, 256, 12297, 263, 49, 263, 50, 263, - 51, 263, 52, 263, 53, 263, 54, 263, 55, 263, 56, 263, 57, 519, 49, 48, - 519, 49, 49, 519, 49, 50, 519, 49, 51, 519, 49, 52, 519, 49, 53, 519, 49, - 54, 519, 49, 55, 519, 49, 56, 519, 49, 57, 519, 50, 48, 770, 40, 49, 41, - 770, 40, 50, 41, 770, 40, 51, 41, 770, 40, 52, 41, 770, 40, 53, 41, 770, - 40, 54, 41, 770, 40, 55, 41, 770, 40, 56, 41, 770, 40, 57, 41, 1026, 40, - 49, 48, 41, 1026, 40, 49, 49, 41, 1026, 40, 49, 50, 41, 1026, 40, 49, 51, - 41, 1026, 40, 49, 52, 41, 1026, 40, 49, 53, 41, 1026, 40, 49, 54, 41, - 1026, 40, 49, 55, 41, 1026, 40, 49, 56, 41, 1026, 40, 49, 57, 41, 1026, - 40, 50, 48, 41, 514, 49, 46, 514, 50, 46, 514, 51, 46, 514, 52, 46, 514, - 53, 46, 514, 54, 46, 514, 55, 46, 514, 56, 46, 514, 57, 46, 770, 49, 48, - 46, 770, 49, 49, 46, 770, 49, 50, 46, 770, 49, 51, 46, 770, 49, 52, 46, - 770, 49, 53, 46, 770, 49, 54, 46, 770, 49, 55, 46, 770, 49, 56, 46, 770, - 49, 57, 46, 770, 50, 48, 46, 770, 40, 97, 41, 770, 40, 98, 41, 770, 40, - 99, 41, 770, 40, 100, 41, 770, 40, 101, 41, 770, 40, 102, 41, 770, 40, - 103, 41, 770, 40, 104, 41, 770, 40, 105, 41, 770, 40, 106, 41, 770, 40, - 107, 41, 770, 40, 108, 41, 770, 40, 109, 41, 770, 40, 110, 41, 770, 40, - 111, 41, 770, 40, 112, 41, 770, 40, 113, 41, 770, 40, 114, 41, 770, 40, - 115, 41, 770, 40, 116, 41, 770, 40, 117, 41, 770, 40, 118, 41, 770, 40, - 119, 41, 770, 40, 120, 41, 770, 40, 121, 41, 770, 40, 122, 41, 263, 65, - 263, 66, 263, 67, 263, 68, 263, 69, 263, 70, 263, 71, 263, 72, 263, 73, - 263, 74, 263, 75, 263, 76, 263, 77, 263, 78, 263, 79, 263, 80, 263, 81, - 263, 82, 263, 83, 263, 84, 263, 85, 263, 86, 263, 87, 263, 88, 263, 89, - 263, 90, 263, 97, 263, 98, 263, 99, 263, 100, 263, 101, 263, 102, 263, - 103, 263, 104, 263, 105, 263, 106, 263, 107, 263, 108, 263, 109, 263, - 110, 263, 111, 263, 112, 263, 113, 263, 114, 263, 115, 263, 116, 263, - 117, 263, 118, 263, 119, 263, 120, 263, 121, 263, 122, 263, 48, 1026, - 8747, 8747, 8747, 8747, 770, 58, 58, 61, 514, 61, 61, 770, 61, 61, 61, - 512, 10973, 824, 261, 106, 259, 86, 259, 11617, 258, 27597, 258, 40863, - 258, 19968, 258, 20008, 258, 20022, 258, 20031, 258, 20057, 258, 20101, - 258, 20108, 258, 20128, 258, 20154, 258, 20799, 258, 20837, 258, 20843, - 258, 20866, 258, 20886, 258, 20907, 258, 20960, 258, 20981, 258, 20992, - 258, 21147, 258, 21241, 258, 21269, 258, 21274, 258, 21304, 258, 21313, - 258, 21340, 258, 21353, 258, 21378, 258, 21430, 258, 21448, 258, 21475, - 258, 22231, 258, 22303, 258, 22763, 258, 22786, 258, 22794, 258, 22805, - 258, 22823, 258, 22899, 258, 23376, 258, 23424, 258, 23544, 258, 23567, - 258, 23586, 258, 23608, 258, 23662, 258, 23665, 258, 24027, 258, 24037, - 258, 24049, 258, 24062, 258, 24178, 258, 24186, 258, 24191, 258, 24308, - 258, 24318, 258, 24331, 258, 24339, 258, 24400, 258, 24417, 258, 24435, - 258, 24515, 258, 25096, 258, 25142, 258, 25163, 258, 25903, 258, 25908, - 258, 25991, 258, 26007, 258, 26020, 258, 26041, 258, 26080, 258, 26085, - 258, 26352, 258, 26376, 258, 26408, 258, 27424, 258, 27490, 258, 27513, - 258, 27571, 258, 27595, 258, 27604, 258, 27611, 258, 27663, 258, 27668, - 258, 27700, 258, 28779, 258, 29226, 258, 29238, 258, 29243, 258, 29247, - 258, 29255, 258, 29273, 258, 29275, 258, 29356, 258, 29572, 258, 29577, - 258, 29916, 258, 29926, 258, 29976, 258, 29983, 258, 29992, 258, 30000, - 258, 30091, 258, 30098, 258, 30326, 258, 30333, 258, 30382, 258, 30399, - 258, 30446, 258, 30683, 258, 30690, 258, 30707, 258, 31034, 258, 31160, - 258, 31166, 258, 31348, 258, 31435, 258, 31481, 258, 31859, 258, 31992, - 258, 32566, 258, 32593, 258, 32650, 258, 32701, 258, 32769, 258, 32780, - 258, 32786, 258, 32819, 258, 32895, 258, 32905, 258, 33251, 258, 33258, - 258, 33267, 258, 33276, 258, 33292, 258, 33307, 258, 33311, 258, 33390, - 258, 33394, 258, 33400, 258, 34381, 258, 34411, 258, 34880, 258, 34892, - 258, 34915, 258, 35198, 258, 35211, 258, 35282, 258, 35328, 258, 35895, - 258, 35910, 258, 35925, 258, 35960, 258, 35997, 258, 36196, 258, 36208, - 258, 36275, 258, 36523, 258, 36554, 258, 36763, 258, 36784, 258, 36789, - 258, 37009, 258, 37193, 258, 37318, 258, 37324, 258, 37329, 258, 38263, - 258, 38272, 258, 38428, 258, 38582, 258, 38585, 258, 38632, 258, 38737, - 258, 38750, 258, 38754, 258, 38761, 258, 38859, 258, 38893, 258, 38899, - 258, 38913, 258, 39080, 258, 39131, 258, 39135, 258, 39318, 258, 39321, - 258, 39340, 258, 39592, 258, 39640, 258, 39647, 258, 39717, 258, 39727, - 258, 39730, 258, 39740, 258, 39770, 258, 40165, 258, 40565, 258, 40575, - 258, 40613, 258, 40635, 258, 40643, 258, 40653, 258, 40657, 258, 40697, - 258, 40701, 258, 40718, 258, 40723, 258, 40736, 258, 40763, 258, 40778, - 258, 40786, 258, 40845, 258, 40860, 258, 40864, 264, 32, 258, 12306, 258, - 21313, 258, 21316, 258, 21317, 512, 12363, 12441, 512, 12365, 12441, 512, - 12367, 12441, 512, 12369, 12441, 512, 12371, 12441, 512, 12373, 12441, - 512, 12375, 12441, 512, 12377, 12441, 512, 12379, 12441, 512, 12381, - 12441, 512, 12383, 12441, 512, 12385, 12441, 512, 12388, 12441, 512, - 12390, 12441, 512, 12392, 12441, 512, 12399, 12441, 512, 12399, 12442, - 512, 12402, 12441, 512, 12402, 12442, 512, 12405, 12441, 512, 12405, - 12442, 512, 12408, 12441, 512, 12408, 12442, 512, 12411, 12441, 512, - 12411, 12442, 512, 12358, 12441, 514, 32, 12441, 514, 32, 12442, 512, - 12445, 12441, 521, 12424, 12426, 512, 12459, 12441, 512, 12461, 12441, - 512, 12463, 12441, 512, 12465, 12441, 512, 12467, 12441, 512, 12469, - 12441, 512, 12471, 12441, 512, 12473, 12441, 512, 12475, 12441, 512, - 12477, 12441, 512, 12479, 12441, 512, 12481, 12441, 512, 12484, 12441, - 512, 12486, 12441, 512, 12488, 12441, 512, 12495, 12441, 512, 12495, - 12442, 512, 12498, 12441, 512, 12498, 12442, 512, 12501, 12441, 512, - 12501, 12442, 512, 12504, 12441, 512, 12504, 12442, 512, 12507, 12441, - 512, 12507, 12442, 512, 12454, 12441, 512, 12527, 12441, 512, 12528, - 12441, 512, 12529, 12441, 512, 12530, 12441, 512, 12541, 12441, 521, - 12467, 12488, 258, 4352, 258, 4353, 258, 4522, 258, 4354, 258, 4524, 258, - 4525, 258, 4355, 258, 4356, 258, 4357, 258, 4528, 258, 4529, 258, 4530, - 258, 4531, 258, 4532, 258, 4533, 258, 4378, 258, 4358, 258, 4359, 258, - 4360, 258, 4385, 258, 4361, 258, 4362, 258, 4363, 258, 4364, 258, 4365, - 258, 4366, 258, 4367, 258, 4368, 258, 4369, 258, 4370, 258, 4449, 258, - 4450, 258, 4451, 258, 4452, 258, 4453, 258, 4454, 258, 4455, 258, 4456, - 258, 4457, 258, 4458, 258, 4459, 258, 4460, 258, 4461, 258, 4462, 258, - 4463, 258, 4464, 258, 4465, 258, 4466, 258, 4467, 258, 4468, 258, 4469, - 258, 4448, 258, 4372, 258, 4373, 258, 4551, 258, 4552, 258, 4556, 258, - 4558, 258, 4563, 258, 4567, 258, 4569, 258, 4380, 258, 4573, 258, 4575, - 258, 4381, 258, 4382, 258, 4384, 258, 4386, 258, 4387, 258, 4391, 258, - 4393, 258, 4395, 258, 4396, 258, 4397, 258, 4398, 258, 4399, 258, 4402, - 258, 4406, 258, 4416, 258, 4423, 258, 4428, 258, 4593, 258, 4594, 258, - 4439, 258, 4440, 258, 4441, 258, 4484, 258, 4485, 258, 4488, 258, 4497, - 258, 4498, 258, 4500, 258, 4510, 258, 4513, 259, 19968, 259, 20108, 259, - 19977, 259, 22235, 259, 19978, 259, 20013, 259, 19979, 259, 30002, 259, - 20057, 259, 19993, 259, 19969, 259, 22825, 259, 22320, 259, 20154, 770, - 40, 4352, 41, 770, 40, 4354, 41, 770, 40, 4355, 41, 770, 40, 4357, 41, - 770, 40, 4358, 41, 770, 40, 4359, 41, 770, 40, 4361, 41, 770, 40, 4363, - 41, 770, 40, 4364, 41, 770, 40, 4366, 41, 770, 40, 4367, 41, 770, 40, - 4368, 41, 770, 40, 4369, 41, 770, 40, 4370, 41, 1026, 40, 4352, 4449, 41, - 1026, 40, 4354, 4449, 41, 1026, 40, 4355, 4449, 41, 1026, 40, 4357, 4449, - 41, 1026, 40, 4358, 4449, 41, 1026, 40, 4359, 4449, 41, 1026, 40, 4361, - 4449, 41, 1026, 40, 4363, 4449, 41, 1026, 40, 4364, 4449, 41, 1026, 40, - 4366, 4449, 41, 1026, 40, 4367, 4449, 41, 1026, 40, 4368, 4449, 41, 1026, - 40, 4369, 4449, 41, 1026, 40, 4370, 4449, 41, 1026, 40, 4364, 4462, 41, - 1794, 40, 4363, 4457, 4364, 4453, 4523, 41, 1538, 40, 4363, 4457, 4370, - 4462, 41, 770, 40, 19968, 41, 770, 40, 20108, 41, 770, 40, 19977, 41, - 770, 40, 22235, 41, 770, 40, 20116, 41, 770, 40, 20845, 41, 770, 40, - 19971, 41, 770, 40, 20843, 41, 770, 40, 20061, 41, 770, 40, 21313, 41, - 770, 40, 26376, 41, 770, 40, 28779, 41, 770, 40, 27700, 41, 770, 40, - 26408, 41, 770, 40, 37329, 41, 770, 40, 22303, 41, 770, 40, 26085, 41, - 770, 40, 26666, 41, 770, 40, 26377, 41, 770, 40, 31038, 41, 770, 40, - 21517, 41, 770, 40, 29305, 41, 770, 40, 36001, 41, 770, 40, 31069, 41, - 770, 40, 21172, 41, 770, 40, 20195, 41, 770, 40, 21628, 41, 770, 40, - 23398, 41, 770, 40, 30435, 41, 770, 40, 20225, 41, 770, 40, 36039, 41, - 770, 40, 21332, 41, 770, 40, 31085, 41, 770, 40, 20241, 41, 770, 40, - 33258, 41, 770, 40, 33267, 41, 263, 21839, 263, 24188, 263, 25991, 263, - 31631, 778, 80, 84, 69, 519, 50, 49, 519, 50, 50, 519, 50, 51, 519, 50, - 52, 519, 50, 53, 519, 50, 54, 519, 50, 55, 519, 50, 56, 519, 50, 57, 519, - 51, 48, 519, 51, 49, 519, 51, 50, 519, 51, 51, 519, 51, 52, 519, 51, 53, - 263, 4352, 263, 4354, 263, 4355, 263, 4357, 263, 4358, 263, 4359, 263, - 4361, 263, 4363, 263, 4364, 263, 4366, 263, 4367, 263, 4368, 263, 4369, - 263, 4370, 519, 4352, 4449, 519, 4354, 4449, 519, 4355, 4449, 519, 4357, - 4449, 519, 4358, 4449, 519, 4359, 4449, 519, 4361, 4449, 519, 4363, 4449, - 519, 4364, 4449, 519, 4366, 4449, 519, 4367, 4449, 519, 4368, 4449, 519, - 4369, 4449, 519, 4370, 4449, 1287, 4366, 4449, 4535, 4352, 4457, 1031, - 4364, 4462, 4363, 4468, 519, 4363, 4462, 263, 19968, 263, 20108, 263, - 19977, 263, 22235, 263, 20116, 263, 20845, 263, 19971, 263, 20843, 263, - 20061, 263, 21313, 263, 26376, 263, 28779, 263, 27700, 263, 26408, 263, - 37329, 263, 22303, 263, 26085, 263, 26666, 263, 26377, 263, 31038, 263, - 21517, 263, 29305, 263, 36001, 263, 31069, 263, 21172, 263, 31192, 263, - 30007, 263, 22899, 263, 36969, 263, 20778, 263, 21360, 263, 27880, 263, - 38917, 263, 20241, 263, 20889, 263, 27491, 263, 19978, 263, 20013, 263, - 19979, 263, 24038, 263, 21491, 263, 21307, 263, 23447, 263, 23398, 263, - 30435, 263, 20225, 263, 36039, 263, 21332, 263, 22812, 519, 51, 54, 519, - 51, 55, 519, 51, 56, 519, 51, 57, 519, 52, 48, 519, 52, 49, 519, 52, 50, - 519, 52, 51, 519, 52, 52, 519, 52, 53, 519, 52, 54, 519, 52, 55, 519, 52, - 56, 519, 52, 57, 519, 53, 48, 514, 49, 26376, 514, 50, 26376, 514, 51, - 26376, 514, 52, 26376, 514, 53, 26376, 514, 54, 26376, 514, 55, 26376, - 514, 56, 26376, 514, 57, 26376, 770, 49, 48, 26376, 770, 49, 49, 26376, - 770, 49, 50, 26376, 522, 72, 103, 778, 101, 114, 103, 522, 101, 86, 778, - 76, 84, 68, 263, 12450, 263, 12452, 263, 12454, 263, 12456, 263, 12458, - 263, 12459, 263, 12461, 263, 12463, 263, 12465, 263, 12467, 263, 12469, - 263, 12471, 263, 12473, 263, 12475, 263, 12477, 263, 12479, 263, 12481, - 263, 12484, 263, 12486, 263, 12488, 263, 12490, 263, 12491, 263, 12492, - 263, 12493, 263, 12494, 263, 12495, 263, 12498, 263, 12501, 263, 12504, - 263, 12507, 263, 12510, 263, 12511, 263, 12512, 263, 12513, 263, 12514, - 263, 12516, 263, 12518, 263, 12520, 263, 12521, 263, 12522, 263, 12523, - 263, 12524, 263, 12525, 263, 12527, 263, 12528, 263, 12529, 263, 12530, - 1034, 12450, 12497, 12540, 12488, 1034, 12450, 12523, 12501, 12449, 1034, - 12450, 12531, 12506, 12450, 778, 12450, 12540, 12523, 1034, 12452, 12491, - 12531, 12464, 778, 12452, 12531, 12481, 778, 12454, 12457, 12531, 1290, - 12456, 12473, 12463, 12540, 12489, 1034, 12456, 12540, 12459, 12540, 778, - 12458, 12531, 12473, 778, 12458, 12540, 12512, 778, 12459, 12452, 12522, - 1034, 12459, 12521, 12483, 12488, 1034, 12459, 12525, 12522, 12540, 778, - 12460, 12525, 12531, 778, 12460, 12531, 12510, 522, 12462, 12460, 778, - 12462, 12491, 12540, 1034, 12461, 12517, 12522, 12540, 1034, 12462, - 12523, 12480, 12540, 522, 12461, 12525, 1290, 12461, 12525, 12464, 12521, - 12512, 1546, 12461, 12525, 12513, 12540, 12488, 12523, 1290, 12461, - 12525, 12527, 12483, 12488, 778, 12464, 12521, 12512, 1290, 12464, 12521, - 12512, 12488, 12531, 1290, 12463, 12523, 12476, 12452, 12525, 1034, - 12463, 12525, 12540, 12493, 778, 12465, 12540, 12473, 778, 12467, 12523, - 12490, 778, 12467, 12540, 12509, 1034, 12469, 12452, 12463, 12523, 1290, - 12469, 12531, 12481, 12540, 12512, 1034, 12471, 12522, 12531, 12464, 778, - 12475, 12531, 12481, 778, 12475, 12531, 12488, 778, 12480, 12540, 12473, - 522, 12487, 12471, 522, 12489, 12523, 522, 12488, 12531, 522, 12490, - 12494, 778, 12494, 12483, 12488, 778, 12495, 12452, 12484, 1290, 12497, - 12540, 12475, 12531, 12488, 778, 12497, 12540, 12484, 1034, 12496, 12540, - 12524, 12523, 1290, 12500, 12450, 12473, 12488, 12523, 778, 12500, 12463, - 12523, 522, 12500, 12467, 522, 12499, 12523, 1290, 12501, 12449, 12521, - 12483, 12489, 1034, 12501, 12451, 12540, 12488, 1290, 12502, 12483, - 12471, 12455, 12523, 778, 12501, 12521, 12531, 1290, 12504, 12463, 12479, - 12540, 12523, 522, 12506, 12477, 778, 12506, 12491, 12498, 778, 12504, - 12523, 12484, 778, 12506, 12531, 12473, 778, 12506, 12540, 12472, 778, - 12505, 12540, 12479, 1034, 12509, 12452, 12531, 12488, 778, 12508, 12523, - 12488, 522, 12507, 12531, 778, 12509, 12531, 12489, 778, 12507, 12540, - 12523, 778, 12507, 12540, 12531, 1034, 12510, 12452, 12463, 12525, 778, - 12510, 12452, 12523, 778, 12510, 12483, 12495, 778, 12510, 12523, 12463, - 1290, 12510, 12531, 12471, 12519, 12531, 1034, 12511, 12463, 12525, - 12531, 522, 12511, 12522, 1290, 12511, 12522, 12496, 12540, 12523, 522, - 12513, 12460, 1034, 12513, 12460, 12488, 12531, 1034, 12513, 12540, - 12488, 12523, 778, 12516, 12540, 12489, 778, 12516, 12540, 12523, 778, - 12518, 12450, 12531, 1034, 12522, 12483, 12488, 12523, 522, 12522, 12521, - 778, 12523, 12500, 12540, 1034, 12523, 12540, 12502, 12523, 522, 12524, - 12512, 1290, 12524, 12531, 12488, 12466, 12531, 778, 12527, 12483, 12488, - 514, 48, 28857, 514, 49, 28857, 514, 50, 28857, 514, 51, 28857, 514, 52, - 28857, 514, 53, 28857, 514, 54, 28857, 514, 55, 28857, 514, 56, 28857, - 514, 57, 28857, 770, 49, 48, 28857, 770, 49, 49, 28857, 770, 49, 50, - 28857, 770, 49, 51, 28857, 770, 49, 52, 28857, 770, 49, 53, 28857, 770, - 49, 54, 28857, 770, 49, 55, 28857, 770, 49, 56, 28857, 770, 49, 57, - 28857, 770, 50, 48, 28857, 770, 50, 49, 28857, 770, 50, 50, 28857, 770, - 50, 51, 28857, 770, 50, 52, 28857, 778, 104, 80, 97, 522, 100, 97, 522, - 65, 85, 778, 98, 97, 114, 522, 111, 86, 522, 112, 99, 522, 100, 109, 778, - 100, 109, 178, 778, 100, 109, 179, 522, 73, 85, 522, 24179, 25104, 522, - 26157, 21644, 522, 22823, 27491, 522, 26126, 27835, 1034, 26666, 24335, - 20250, 31038, 522, 112, 65, 522, 110, 65, 522, 956, 65, 522, 109, 65, - 522, 107, 65, 522, 75, 66, 522, 77, 66, 522, 71, 66, 778, 99, 97, 108, - 1034, 107, 99, 97, 108, 522, 112, 70, 522, 110, 70, 522, 956, 70, 522, - 956, 103, 522, 109, 103, 522, 107, 103, 522, 72, 122, 778, 107, 72, 122, - 778, 77, 72, 122, 778, 71, 72, 122, 778, 84, 72, 122, 522, 956, 8467, - 522, 109, 8467, 522, 100, 8467, 522, 107, 8467, 522, 102, 109, 522, 110, - 109, 522, 956, 109, 522, 109, 109, 522, 99, 109, 522, 107, 109, 778, 109, - 109, 178, 778, 99, 109, 178, 522, 109, 178, 778, 107, 109, 178, 778, 109, - 109, 179, 778, 99, 109, 179, 522, 109, 179, 778, 107, 109, 179, 778, 109, - 8725, 115, 1034, 109, 8725, 115, 178, 522, 80, 97, 778, 107, 80, 97, 778, - 77, 80, 97, 778, 71, 80, 97, 778, 114, 97, 100, 1290, 114, 97, 100, 8725, - 115, 1546, 114, 97, 100, 8725, 115, 178, 522, 112, 115, 522, 110, 115, - 522, 956, 115, 522, 109, 115, 522, 112, 86, 522, 110, 86, 522, 956, 86, - 522, 109, 86, 522, 107, 86, 522, 77, 86, 522, 112, 87, 522, 110, 87, 522, - 956, 87, 522, 109, 87, 522, 107, 87, 522, 77, 87, 522, 107, 937, 522, 77, - 937, 1034, 97, 46, 109, 46, 522, 66, 113, 522, 99, 99, 522, 99, 100, - 1034, 67, 8725, 107, 103, 778, 67, 111, 46, 522, 100, 66, 522, 71, 121, - 522, 104, 97, 522, 72, 80, 522, 105, 110, 522, 75, 75, 522, 75, 77, 522, - 107, 116, 522, 108, 109, 522, 108, 110, 778, 108, 111, 103, 522, 108, - 120, 522, 109, 98, 778, 109, 105, 108, 778, 109, 111, 108, 522, 80, 72, - 1034, 112, 46, 109, 46, 778, 80, 80, 77, 522, 80, 82, 522, 115, 114, 522, - 83, 118, 522, 87, 98, 778, 86, 8725, 109, 778, 65, 8725, 109, 514, 49, - 26085, 514, 50, 26085, 514, 51, 26085, 514, 52, 26085, 514, 53, 26085, - 514, 54, 26085, 514, 55, 26085, 514, 56, 26085, 514, 57, 26085, 770, 49, - 48, 26085, 770, 49, 49, 26085, 770, 49, 50, 26085, 770, 49, 51, 26085, - 770, 49, 52, 26085, 770, 49, 53, 26085, 770, 49, 54, 26085, 770, 49, 55, - 26085, 770, 49, 56, 26085, 770, 49, 57, 26085, 770, 50, 48, 26085, 770, - 50, 49, 26085, 770, 50, 50, 26085, 770, 50, 51, 26085, 770, 50, 52, - 26085, 770, 50, 53, 26085, 770, 50, 54, 26085, 770, 50, 55, 26085, 770, - 50, 56, 26085, 770, 50, 57, 26085, 770, 51, 48, 26085, 770, 51, 49, - 26085, 778, 103, 97, 108, 259, 1098, 259, 1100, 259, 42863, 259, 294, - 259, 339, 259, 42791, 259, 43831, 259, 619, 259, 43858, 256, 35912, 256, - 26356, 256, 36554, 256, 36040, 256, 28369, 256, 20018, 256, 21477, 256, - 40860, 256, 40860, 256, 22865, 256, 37329, 256, 21895, 256, 22856, 256, - 25078, 256, 30313, 256, 32645, 256, 34367, 256, 34746, 256, 35064, 256, - 37007, 256, 27138, 256, 27931, 256, 28889, 256, 29662, 256, 33853, 256, - 37226, 256, 39409, 256, 20098, 256, 21365, 256, 27396, 256, 29211, 256, - 34349, 256, 40478, 256, 23888, 256, 28651, 256, 34253, 256, 35172, 256, - 25289, 256, 33240, 256, 34847, 256, 24266, 256, 26391, 256, 28010, 256, - 29436, 256, 37070, 256, 20358, 256, 20919, 256, 21214, 256, 25796, 256, - 27347, 256, 29200, 256, 30439, 256, 32769, 256, 34310, 256, 34396, 256, - 36335, 256, 38706, 256, 39791, 256, 40442, 256, 30860, 256, 31103, 256, - 32160, 256, 33737, 256, 37636, 256, 40575, 256, 35542, 256, 22751, 256, - 24324, 256, 31840, 256, 32894, 256, 29282, 256, 30922, 256, 36034, 256, - 38647, 256, 22744, 256, 23650, 256, 27155, 256, 28122, 256, 28431, 256, - 32047, 256, 32311, 256, 38475, 256, 21202, 256, 32907, 256, 20956, 256, - 20940, 256, 31260, 256, 32190, 256, 33777, 256, 38517, 256, 35712, 256, - 25295, 256, 27138, 256, 35582, 256, 20025, 256, 23527, 256, 24594, 256, - 29575, 256, 30064, 256, 21271, 256, 30971, 256, 20415, 256, 24489, 256, - 19981, 256, 27852, 256, 25976, 256, 32034, 256, 21443, 256, 22622, 256, - 30465, 256, 33865, 256, 35498, 256, 27578, 256, 36784, 256, 27784, 256, - 25342, 256, 33509, 256, 25504, 256, 30053, 256, 20142, 256, 20841, 256, - 20937, 256, 26753, 256, 31975, 256, 33391, 256, 35538, 256, 37327, 256, - 21237, 256, 21570, 256, 22899, 256, 24300, 256, 26053, 256, 28670, 256, - 31018, 256, 38317, 256, 39530, 256, 40599, 256, 40654, 256, 21147, 256, - 26310, 256, 27511, 256, 36706, 256, 24180, 256, 24976, 256, 25088, 256, - 25754, 256, 28451, 256, 29001, 256, 29833, 256, 31178, 256, 32244, 256, - 32879, 256, 36646, 256, 34030, 256, 36899, 256, 37706, 256, 21015, 256, - 21155, 256, 21693, 256, 28872, 256, 35010, 256, 35498, 256, 24265, 256, - 24565, 256, 25467, 256, 27566, 256, 31806, 256, 29557, 256, 20196, 256, - 22265, 256, 23527, 256, 23994, 256, 24604, 256, 29618, 256, 29801, 256, - 32666, 256, 32838, 256, 37428, 256, 38646, 256, 38728, 256, 38936, 256, - 20363, 256, 31150, 256, 37300, 256, 38584, 256, 24801, 256, 20102, 256, - 20698, 256, 23534, 256, 23615, 256, 26009, 256, 27138, 256, 29134, 256, - 30274, 256, 34044, 256, 36988, 256, 40845, 256, 26248, 256, 38446, 256, - 21129, 256, 26491, 256, 26611, 256, 27969, 256, 28316, 256, 29705, 256, - 30041, 256, 30827, 256, 32016, 256, 39006, 256, 20845, 256, 25134, 256, - 38520, 256, 20523, 256, 23833, 256, 28138, 256, 36650, 256, 24459, 256, - 24900, 256, 26647, 256, 29575, 256, 38534, 256, 21033, 256, 21519, 256, - 23653, 256, 26131, 256, 26446, 256, 26792, 256, 27877, 256, 29702, 256, - 30178, 256, 32633, 256, 35023, 256, 35041, 256, 37324, 256, 38626, 256, - 21311, 256, 28346, 256, 21533, 256, 29136, 256, 29848, 256, 34298, 256, - 38563, 256, 40023, 256, 40607, 256, 26519, 256, 28107, 256, 33256, 256, - 31435, 256, 31520, 256, 31890, 256, 29376, 256, 28825, 256, 35672, 256, - 20160, 256, 33590, 256, 21050, 256, 20999, 256, 24230, 256, 25299, 256, - 31958, 256, 23429, 256, 27934, 256, 26292, 256, 36667, 256, 34892, 256, - 38477, 256, 35211, 256, 24275, 256, 20800, 256, 21952, 256, 22618, 256, - 26228, 256, 20958, 256, 29482, 256, 30410, 256, 31036, 256, 31070, 256, - 31077, 256, 31119, 256, 38742, 256, 31934, 256, 32701, 256, 34322, 256, - 35576, 256, 36920, 256, 37117, 256, 39151, 256, 39164, 256, 39208, 256, - 40372, 256, 37086, 256, 38583, 256, 20398, 256, 20711, 256, 20813, 256, - 21193, 256, 21220, 256, 21329, 256, 21917, 256, 22022, 256, 22120, 256, - 22592, 256, 22696, 256, 23652, 256, 23662, 256, 24724, 256, 24936, 256, - 24974, 256, 25074, 256, 25935, 256, 26082, 256, 26257, 256, 26757, 256, - 28023, 256, 28186, 256, 28450, 256, 29038, 256, 29227, 256, 29730, 256, - 30865, 256, 31038, 256, 31049, 256, 31048, 256, 31056, 256, 31062, 256, - 31069, 256, 31117, 256, 31118, 256, 31296, 256, 31361, 256, 31680, 256, - 32244, 256, 32265, 256, 32321, 256, 32626, 256, 32773, 256, 33261, 256, - 33401, 256, 33401, 256, 33879, 256, 35088, 256, 35222, 256, 35585, 256, - 35641, 256, 36051, 256, 36104, 256, 36790, 256, 36920, 256, 38627, 256, - 38911, 256, 38971, 256, 24693, 256, 55376, 57070, 256, 33304, 256, 20006, - 256, 20917, 256, 20840, 256, 20352, 256, 20805, 256, 20864, 256, 21191, - 256, 21242, 256, 21917, 256, 21845, 256, 21913, 256, 21986, 256, 22618, - 256, 22707, 256, 22852, 256, 22868, 256, 23138, 256, 23336, 256, 24274, - 256, 24281, 256, 24425, 256, 24493, 256, 24792, 256, 24910, 256, 24840, - 256, 24974, 256, 24928, 256, 25074, 256, 25140, 256, 25540, 256, 25628, - 256, 25682, 256, 25942, 256, 26228, 256, 26391, 256, 26395, 256, 26454, - 256, 27513, 256, 27578, 256, 27969, 256, 28379, 256, 28363, 256, 28450, - 256, 28702, 256, 29038, 256, 30631, 256, 29237, 256, 29359, 256, 29482, - 256, 29809, 256, 29958, 256, 30011, 256, 30237, 256, 30239, 256, 30410, - 256, 30427, 256, 30452, 256, 30538, 256, 30528, 256, 30924, 256, 31409, - 256, 31680, 256, 31867, 256, 32091, 256, 32244, 256, 32574, 256, 32773, - 256, 33618, 256, 33775, 256, 34681, 256, 35137, 256, 35206, 256, 35222, - 256, 35519, 256, 35576, 256, 35531, 256, 35585, 256, 35582, 256, 35565, - 256, 35641, 256, 35722, 256, 36104, 256, 36664, 256, 36978, 256, 37273, - 256, 37494, 256, 38524, 256, 38627, 256, 38742, 256, 38875, 256, 38911, - 256, 38923, 256, 38971, 256, 39698, 256, 40860, 256, 55370, 56394, 256, - 55370, 56388, 256, 55372, 57301, 256, 15261, 256, 16408, 256, 16441, 256, - 55380, 56905, 256, 55383, 56528, 256, 55391, 57043, 256, 40771, 256, - 40846, 514, 102, 102, 514, 102, 105, 514, 102, 108, 770, 102, 102, 105, - 770, 102, 102, 108, 514, 383, 116, 514, 115, 116, 514, 1396, 1398, 514, - 1396, 1381, 514, 1396, 1387, 514, 1406, 1398, 514, 1396, 1389, 512, 1497, - 1460, 512, 1522, 1463, 262, 1506, 262, 1488, 262, 1491, 262, 1492, 262, - 1499, 262, 1500, 262, 1501, 262, 1512, 262, 1514, 262, 43, 512, 1513, - 1473, 512, 1513, 1474, 512, 64329, 1473, 512, 64329, 1474, 512, 1488, - 1463, 512, 1488, 1464, 512, 1488, 1468, 512, 1489, 1468, 512, 1490, 1468, - 512, 1491, 1468, 512, 1492, 1468, 512, 1493, 1468, 512, 1494, 1468, 512, - 1496, 1468, 512, 1497, 1468, 512, 1498, 1468, 512, 1499, 1468, 512, 1500, - 1468, 512, 1502, 1468, 512, 1504, 1468, 512, 1505, 1468, 512, 1507, 1468, - 512, 1508, 1468, 512, 1510, 1468, 512, 1511, 1468, 512, 1512, 1468, 512, - 1513, 1468, 512, 1514, 1468, 512, 1493, 1465, 512, 1489, 1471, 512, 1499, - 1471, 512, 1508, 1471, 514, 1488, 1500, 267, 1649, 268, 1649, 267, 1659, - 268, 1659, 269, 1659, 270, 1659, 267, 1662, 268, 1662, 269, 1662, 270, - 1662, 267, 1664, 268, 1664, 269, 1664, 270, 1664, 267, 1658, 268, 1658, - 269, 1658, 270, 1658, 267, 1663, 268, 1663, 269, 1663, 270, 1663, 267, - 1657, 268, 1657, 269, 1657, 270, 1657, 267, 1700, 268, 1700, 269, 1700, - 270, 1700, 267, 1702, 268, 1702, 269, 1702, 270, 1702, 267, 1668, 268, - 1668, 269, 1668, 270, 1668, 267, 1667, 268, 1667, 269, 1667, 270, 1667, - 267, 1670, 268, 1670, 269, 1670, 270, 1670, 267, 1671, 268, 1671, 269, - 1671, 270, 1671, 267, 1677, 268, 1677, 267, 1676, 268, 1676, 267, 1678, - 268, 1678, 267, 1672, 268, 1672, 267, 1688, 268, 1688, 267, 1681, 268, - 1681, 267, 1705, 268, 1705, 269, 1705, 270, 1705, 267, 1711, 268, 1711, - 269, 1711, 270, 1711, 267, 1715, 268, 1715, 269, 1715, 270, 1715, 267, - 1713, 268, 1713, 269, 1713, 270, 1713, 267, 1722, 268, 1722, 267, 1723, - 268, 1723, 269, 1723, 270, 1723, 267, 1728, 268, 1728, 267, 1729, 268, - 1729, 269, 1729, 270, 1729, 267, 1726, 268, 1726, 269, 1726, 270, 1726, - 267, 1746, 268, 1746, 267, 1747, 268, 1747, 267, 1709, 268, 1709, 269, - 1709, 270, 1709, 267, 1735, 268, 1735, 267, 1734, 268, 1734, 267, 1736, - 268, 1736, 267, 1655, 267, 1739, 268, 1739, 267, 1733, 268, 1733, 267, - 1737, 268, 1737, 267, 1744, 268, 1744, 269, 1744, 270, 1744, 269, 1609, - 270, 1609, 523, 1574, 1575, 524, 1574, 1575, 523, 1574, 1749, 524, 1574, - 1749, 523, 1574, 1608, 524, 1574, 1608, 523, 1574, 1735, 524, 1574, 1735, - 523, 1574, 1734, 524, 1574, 1734, 523, 1574, 1736, 524, 1574, 1736, 523, - 1574, 1744, 524, 1574, 1744, 525, 1574, 1744, 523, 1574, 1609, 524, 1574, - 1609, 525, 1574, 1609, 267, 1740, 268, 1740, 269, 1740, 270, 1740, 523, - 1574, 1580, 523, 1574, 1581, 523, 1574, 1605, 523, 1574, 1609, 523, 1574, - 1610, 523, 1576, 1580, 523, 1576, 1581, 523, 1576, 1582, 523, 1576, 1605, - 523, 1576, 1609, 523, 1576, 1610, 523, 1578, 1580, 523, 1578, 1581, 523, - 1578, 1582, 523, 1578, 1605, 523, 1578, 1609, 523, 1578, 1610, 523, 1579, - 1580, 523, 1579, 1605, 523, 1579, 1609, 523, 1579, 1610, 523, 1580, 1581, - 523, 1580, 1605, 523, 1581, 1580, 523, 1581, 1605, 523, 1582, 1580, 523, - 1582, 1581, 523, 1582, 1605, 523, 1587, 1580, 523, 1587, 1581, 523, 1587, - 1582, 523, 1587, 1605, 523, 1589, 1581, 523, 1589, 1605, 523, 1590, 1580, - 523, 1590, 1581, 523, 1590, 1582, 523, 1590, 1605, 523, 1591, 1581, 523, - 1591, 1605, 523, 1592, 1605, 523, 1593, 1580, 523, 1593, 1605, 523, 1594, - 1580, 523, 1594, 1605, 523, 1601, 1580, 523, 1601, 1581, 523, 1601, 1582, - 523, 1601, 1605, 523, 1601, 1609, 523, 1601, 1610, 523, 1602, 1581, 523, - 1602, 1605, 523, 1602, 1609, 523, 1602, 1610, 523, 1603, 1575, 523, 1603, - 1580, 523, 1603, 1581, 523, 1603, 1582, 523, 1603, 1604, 523, 1603, 1605, - 523, 1603, 1609, 523, 1603, 1610, 523, 1604, 1580, 523, 1604, 1581, 523, - 1604, 1582, 523, 1604, 1605, 523, 1604, 1609, 523, 1604, 1610, 523, 1605, - 1580, 523, 1605, 1581, 523, 1605, 1582, 523, 1605, 1605, 523, 1605, 1609, - 523, 1605, 1610, 523, 1606, 1580, 523, 1606, 1581, 523, 1606, 1582, 523, - 1606, 1605, 523, 1606, 1609, 523, 1606, 1610, 523, 1607, 1580, 523, 1607, - 1605, 523, 1607, 1609, 523, 1607, 1610, 523, 1610, 1580, 523, 1610, 1581, - 523, 1610, 1582, 523, 1610, 1605, 523, 1610, 1609, 523, 1610, 1610, 523, - 1584, 1648, 523, 1585, 1648, 523, 1609, 1648, 779, 32, 1612, 1617, 779, - 32, 1613, 1617, 779, 32, 1614, 1617, 779, 32, 1615, 1617, 779, 32, 1616, - 1617, 779, 32, 1617, 1648, 524, 1574, 1585, 524, 1574, 1586, 524, 1574, - 1605, 524, 1574, 1606, 524, 1574, 1609, 524, 1574, 1610, 524, 1576, 1585, - 524, 1576, 1586, 524, 1576, 1605, 524, 1576, 1606, 524, 1576, 1609, 524, - 1576, 1610, 524, 1578, 1585, 524, 1578, 1586, 524, 1578, 1605, 524, 1578, - 1606, 524, 1578, 1609, 524, 1578, 1610, 524, 1579, 1585, 524, 1579, 1586, - 524, 1579, 1605, 524, 1579, 1606, 524, 1579, 1609, 524, 1579, 1610, 524, - 1601, 1609, 524, 1601, 1610, 524, 1602, 1609, 524, 1602, 1610, 524, 1603, - 1575, 524, 1603, 1604, 524, 1603, 1605, 524, 1603, 1609, 524, 1603, 1610, - 524, 1604, 1605, 524, 1604, 1609, 524, 1604, 1610, 524, 1605, 1575, 524, - 1605, 1605, 524, 1606, 1585, 524, 1606, 1586, 524, 1606, 1605, 524, 1606, - 1606, 524, 1606, 1609, 524, 1606, 1610, 524, 1609, 1648, 524, 1610, 1585, - 524, 1610, 1586, 524, 1610, 1605, 524, 1610, 1606, 524, 1610, 1609, 524, - 1610, 1610, 525, 1574, 1580, 525, 1574, 1581, 525, 1574, 1582, 525, 1574, - 1605, 525, 1574, 1607, 525, 1576, 1580, 525, 1576, 1581, 525, 1576, 1582, - 525, 1576, 1605, 525, 1576, 1607, 525, 1578, 1580, 525, 1578, 1581, 525, - 1578, 1582, 525, 1578, 1605, 525, 1578, 1607, 525, 1579, 1605, 525, 1580, - 1581, 525, 1580, 1605, 525, 1581, 1580, 525, 1581, 1605, 525, 1582, 1580, - 525, 1582, 1605, 525, 1587, 1580, 525, 1587, 1581, 525, 1587, 1582, 525, - 1587, 1605, 525, 1589, 1581, 525, 1589, 1582, 525, 1589, 1605, 525, 1590, - 1580, 525, 1590, 1581, 525, 1590, 1582, 525, 1590, 1605, 525, 1591, 1581, - 525, 1592, 1605, 525, 1593, 1580, 525, 1593, 1605, 525, 1594, 1580, 525, - 1594, 1605, 525, 1601, 1580, 525, 1601, 1581, 525, 1601, 1582, 525, 1601, - 1605, 525, 1602, 1581, 525, 1602, 1605, 525, 1603, 1580, 525, 1603, 1581, - 525, 1603, 1582, 525, 1603, 1604, 525, 1603, 1605, 525, 1604, 1580, 525, - 1604, 1581, 525, 1604, 1582, 525, 1604, 1605, 525, 1604, 1607, 525, 1605, - 1580, 525, 1605, 1581, 525, 1605, 1582, 525, 1605, 1605, 525, 1606, 1580, - 525, 1606, 1581, 525, 1606, 1582, 525, 1606, 1605, 525, 1606, 1607, 525, - 1607, 1580, 525, 1607, 1605, 525, 1607, 1648, 525, 1610, 1580, 525, 1610, - 1581, 525, 1610, 1582, 525, 1610, 1605, 525, 1610, 1607, 526, 1574, 1605, - 526, 1574, 1607, 526, 1576, 1605, 526, 1576, 1607, 526, 1578, 1605, 526, - 1578, 1607, 526, 1579, 1605, 526, 1579, 1607, 526, 1587, 1605, 526, 1587, - 1607, 526, 1588, 1605, 526, 1588, 1607, 526, 1603, 1604, 526, 1603, 1605, - 526, 1604, 1605, 526, 1606, 1605, 526, 1606, 1607, 526, 1610, 1605, 526, - 1610, 1607, 782, 1600, 1614, 1617, 782, 1600, 1615, 1617, 782, 1600, - 1616, 1617, 523, 1591, 1609, 523, 1591, 1610, 523, 1593, 1609, 523, 1593, - 1610, 523, 1594, 1609, 523, 1594, 1610, 523, 1587, 1609, 523, 1587, 1610, - 523, 1588, 1609, 523, 1588, 1610, 523, 1581, 1609, 523, 1581, 1610, 523, - 1580, 1609, 523, 1580, 1610, 523, 1582, 1609, 523, 1582, 1610, 523, 1589, - 1609, 523, 1589, 1610, 523, 1590, 1609, 523, 1590, 1610, 523, 1588, 1580, - 523, 1588, 1581, 523, 1588, 1582, 523, 1588, 1605, 523, 1588, 1585, 523, - 1587, 1585, 523, 1589, 1585, 523, 1590, 1585, 524, 1591, 1609, 524, 1591, - 1610, 524, 1593, 1609, 524, 1593, 1610, 524, 1594, 1609, 524, 1594, 1610, - 524, 1587, 1609, 524, 1587, 1610, 524, 1588, 1609, 524, 1588, 1610, 524, - 1581, 1609, 524, 1581, 1610, 524, 1580, 1609, 524, 1580, 1610, 524, 1582, - 1609, 524, 1582, 1610, 524, 1589, 1609, 524, 1589, 1610, 524, 1590, 1609, - 524, 1590, 1610, 524, 1588, 1580, 524, 1588, 1581, 524, 1588, 1582, 524, - 1588, 1605, 524, 1588, 1585, 524, 1587, 1585, 524, 1589, 1585, 524, 1590, - 1585, 525, 1588, 1580, 525, 1588, 1581, 525, 1588, 1582, 525, 1588, 1605, - 525, 1587, 1607, 525, 1588, 1607, 525, 1591, 1605, 526, 1587, 1580, 526, - 1587, 1581, 526, 1587, 1582, 526, 1588, 1580, 526, 1588, 1581, 526, 1588, - 1582, 526, 1591, 1605, 526, 1592, 1605, 524, 1575, 1611, 523, 1575, 1611, - 781, 1578, 1580, 1605, 780, 1578, 1581, 1580, 781, 1578, 1581, 1580, 781, - 1578, 1581, 1605, 781, 1578, 1582, 1605, 781, 1578, 1605, 1580, 781, - 1578, 1605, 1581, 781, 1578, 1605, 1582, 780, 1580, 1605, 1581, 781, - 1580, 1605, 1581, 780, 1581, 1605, 1610, 780, 1581, 1605, 1609, 781, - 1587, 1581, 1580, 781, 1587, 1580, 1581, 780, 1587, 1580, 1609, 780, - 1587, 1605, 1581, 781, 1587, 1605, 1581, 781, 1587, 1605, 1580, 780, - 1587, 1605, 1605, 781, 1587, 1605, 1605, 780, 1589, 1581, 1581, 781, - 1589, 1581, 1581, 780, 1589, 1605, 1605, 780, 1588, 1581, 1605, 781, - 1588, 1581, 1605, 780, 1588, 1580, 1610, 780, 1588, 1605, 1582, 781, - 1588, 1605, 1582, 780, 1588, 1605, 1605, 781, 1588, 1605, 1605, 780, - 1590, 1581, 1609, 780, 1590, 1582, 1605, 781, 1590, 1582, 1605, 780, - 1591, 1605, 1581, 781, 1591, 1605, 1581, 781, 1591, 1605, 1605, 780, - 1591, 1605, 1610, 780, 1593, 1580, 1605, 780, 1593, 1605, 1605, 781, - 1593, 1605, 1605, 780, 1593, 1605, 1609, 780, 1594, 1605, 1605, 780, - 1594, 1605, 1610, 780, 1594, 1605, 1609, 780, 1601, 1582, 1605, 781, - 1601, 1582, 1605, 780, 1602, 1605, 1581, 780, 1602, 1605, 1605, 780, - 1604, 1581, 1605, 780, 1604, 1581, 1610, 780, 1604, 1581, 1609, 781, - 1604, 1580, 1580, 780, 1604, 1580, 1580, 780, 1604, 1582, 1605, 781, - 1604, 1582, 1605, 780, 1604, 1605, 1581, 781, 1604, 1605, 1581, 781, - 1605, 1581, 1580, 781, 1605, 1581, 1605, 780, 1605, 1581, 1610, 781, - 1605, 1580, 1581, 781, 1605, 1580, 1605, 781, 1605, 1582, 1580, 781, - 1605, 1582, 1605, 781, 1605, 1580, 1582, 781, 1607, 1605, 1580, 781, - 1607, 1605, 1605, 781, 1606, 1581, 1605, 780, 1606, 1581, 1609, 780, - 1606, 1580, 1605, 781, 1606, 1580, 1605, 780, 1606, 1580, 1609, 780, - 1606, 1605, 1610, 780, 1606, 1605, 1609, 780, 1610, 1605, 1605, 781, - 1610, 1605, 1605, 780, 1576, 1582, 1610, 780, 1578, 1580, 1610, 780, - 1578, 1580, 1609, 780, 1578, 1582, 1610, 780, 1578, 1582, 1609, 780, - 1578, 1605, 1610, 780, 1578, 1605, 1609, 780, 1580, 1605, 1610, 780, - 1580, 1581, 1609, 780, 1580, 1605, 1609, 780, 1587, 1582, 1609, 780, - 1589, 1581, 1610, 780, 1588, 1581, 1610, 780, 1590, 1581, 1610, 780, - 1604, 1580, 1610, 780, 1604, 1605, 1610, 780, 1610, 1581, 1610, 780, - 1610, 1580, 1610, 780, 1610, 1605, 1610, 780, 1605, 1605, 1610, 780, - 1602, 1605, 1610, 780, 1606, 1581, 1610, 781, 1602, 1605, 1581, 781, - 1604, 1581, 1605, 780, 1593, 1605, 1610, 780, 1603, 1605, 1610, 781, - 1606, 1580, 1581, 780, 1605, 1582, 1610, 781, 1604, 1580, 1605, 780, - 1603, 1605, 1605, 780, 1604, 1580, 1605, 780, 1606, 1580, 1581, 780, - 1580, 1581, 1610, 780, 1581, 1580, 1610, 780, 1605, 1580, 1610, 780, - 1601, 1605, 1610, 780, 1576, 1581, 1610, 781, 1603, 1605, 1605, 781, - 1593, 1580, 1605, 781, 1589, 1605, 1605, 780, 1587, 1582, 1610, 780, - 1606, 1580, 1610, 779, 1589, 1604, 1746, 779, 1602, 1604, 1746, 1035, - 1575, 1604, 1604, 1607, 1035, 1575, 1603, 1576, 1585, 1035, 1605, 1581, - 1605, 1583, 1035, 1589, 1604, 1593, 1605, 1035, 1585, 1587, 1608, 1604, - 1035, 1593, 1604, 1610, 1607, 1035, 1608, 1587, 1604, 1605, 779, 1589, - 1604, 1609, 4619, 1589, 1604, 1609, 32, 1575, 1604, 1604, 1607, 32, 1593, - 1604, 1610, 1607, 32, 1608, 1587, 1604, 1605, 2059, 1580, 1604, 32, 1580, - 1604, 1575, 1604, 1607, 1035, 1585, 1740, 1575, 1604, 265, 44, 265, - 12289, 265, 12290, 265, 58, 265, 59, 265, 33, 265, 63, 265, 12310, 265, - 12311, 265, 8230, 265, 8229, 265, 8212, 265, 8211, 265, 95, 265, 95, 265, - 40, 265, 41, 265, 123, 265, 125, 265, 12308, 265, 12309, 265, 12304, 265, - 12305, 265, 12298, 265, 12299, 265, 12296, 265, 12297, 265, 12300, 265, - 12301, 265, 12302, 265, 12303, 265, 91, 265, 93, 258, 8254, 258, 8254, - 258, 8254, 258, 8254, 258, 95, 258, 95, 258, 95, 271, 44, 271, 12289, - 271, 46, 271, 59, 271, 58, 271, 63, 271, 33, 271, 8212, 271, 40, 271, 41, - 271, 123, 271, 125, 271, 12308, 271, 12309, 271, 35, 271, 38, 271, 42, - 271, 43, 271, 45, 271, 60, 271, 62, 271, 61, 271, 92, 271, 36, 271, 37, - 271, 64, 523, 32, 1611, 526, 1600, 1611, 523, 32, 1612, 523, 32, 1613, - 523, 32, 1614, 526, 1600, 1614, 523, 32, 1615, 526, 1600, 1615, 523, 32, - 1616, 526, 1600, 1616, 523, 32, 1617, 526, 1600, 1617, 523, 32, 1618, - 526, 1600, 1618, 267, 1569, 267, 1570, 268, 1570, 267, 1571, 268, 1571, - 267, 1572, 268, 1572, 267, 1573, 268, 1573, 267, 1574, 268, 1574, 269, - 1574, 270, 1574, 267, 1575, 268, 1575, 267, 1576, 268, 1576, 269, 1576, - 270, 1576, 267, 1577, 268, 1577, 267, 1578, 268, 1578, 269, 1578, 270, - 1578, 267, 1579, 268, 1579, 269, 1579, 270, 1579, 267, 1580, 268, 1580, - 269, 1580, 270, 1580, 267, 1581, 268, 1581, 269, 1581, 270, 1581, 267, - 1582, 268, 1582, 269, 1582, 270, 1582, 267, 1583, 268, 1583, 267, 1584, - 268, 1584, 267, 1585, 268, 1585, 267, 1586, 268, 1586, 267, 1587, 268, - 1587, 269, 1587, 270, 1587, 267, 1588, 268, 1588, 269, 1588, 270, 1588, - 267, 1589, 268, 1589, 269, 1589, 270, 1589, 267, 1590, 268, 1590, 269, - 1590, 270, 1590, 267, 1591, 268, 1591, 269, 1591, 270, 1591, 267, 1592, - 268, 1592, 269, 1592, 270, 1592, 267, 1593, 268, 1593, 269, 1593, 270, - 1593, 267, 1594, 268, 1594, 269, 1594, 270, 1594, 267, 1601, 268, 1601, - 269, 1601, 270, 1601, 267, 1602, 268, 1602, 269, 1602, 270, 1602, 267, - 1603, 268, 1603, 269, 1603, 270, 1603, 267, 1604, 268, 1604, 269, 1604, - 270, 1604, 267, 1605, 268, 1605, 269, 1605, 270, 1605, 267, 1606, 268, - 1606, 269, 1606, 270, 1606, 267, 1607, 268, 1607, 269, 1607, 270, 1607, - 267, 1608, 268, 1608, 267, 1609, 268, 1609, 267, 1610, 268, 1610, 269, - 1610, 270, 1610, 523, 1604, 1570, 524, 1604, 1570, 523, 1604, 1571, 524, - 1604, 1571, 523, 1604, 1573, 524, 1604, 1573, 523, 1604, 1575, 524, 1604, - 1575, 264, 33, 264, 34, 264, 35, 264, 36, 264, 37, 264, 38, 264, 39, 264, - 40, 264, 41, 264, 42, 264, 43, 264, 44, 264, 45, 264, 46, 264, 47, 264, - 48, 264, 49, 264, 50, 264, 51, 264, 52, 264, 53, 264, 54, 264, 55, 264, - 56, 264, 57, 264, 58, 264, 59, 264, 60, 264, 61, 264, 62, 264, 63, 264, - 64, 264, 65, 264, 66, 264, 67, 264, 68, 264, 69, 264, 70, 264, 71, 264, - 72, 264, 73, 264, 74, 264, 75, 264, 76, 264, 77, 264, 78, 264, 79, 264, - 80, 264, 81, 264, 82, 264, 83, 264, 84, 264, 85, 264, 86, 264, 87, 264, - 88, 264, 89, 264, 90, 264, 91, 264, 92, 264, 93, 264, 94, 264, 95, 264, - 96, 264, 97, 264, 98, 264, 99, 264, 100, 264, 101, 264, 102, 264, 103, - 264, 104, 264, 105, 264, 106, 264, 107, 264, 108, 264, 109, 264, 110, - 264, 111, 264, 112, 264, 113, 264, 114, 264, 115, 264, 116, 264, 117, - 264, 118, 264, 119, 264, 120, 264, 121, 264, 122, 264, 123, 264, 124, - 264, 125, 264, 126, 264, 10629, 264, 10630, 272, 12290, 272, 12300, 272, - 12301, 272, 12289, 272, 12539, 272, 12530, 272, 12449, 272, 12451, 272, - 12453, 272, 12455, 272, 12457, 272, 12515, 272, 12517, 272, 12519, 272, - 12483, 272, 12540, 272, 12450, 272, 12452, 272, 12454, 272, 12456, 272, - 12458, 272, 12459, 272, 12461, 272, 12463, 272, 12465, 272, 12467, 272, - 12469, 272, 12471, 272, 12473, 272, 12475, 272, 12477, 272, 12479, 272, - 12481, 272, 12484, 272, 12486, 272, 12488, 272, 12490, 272, 12491, 272, - 12492, 272, 12493, 272, 12494, 272, 12495, 272, 12498, 272, 12501, 272, - 12504, 272, 12507, 272, 12510, 272, 12511, 272, 12512, 272, 12513, 272, - 12514, 272, 12516, 272, 12518, 272, 12520, 272, 12521, 272, 12522, 272, - 12523, 272, 12524, 272, 12525, 272, 12527, 272, 12531, 272, 12441, 272, - 12442, 272, 12644, 272, 12593, 272, 12594, 272, 12595, 272, 12596, 272, - 12597, 272, 12598, 272, 12599, 272, 12600, 272, 12601, 272, 12602, 272, - 12603, 272, 12604, 272, 12605, 272, 12606, 272, 12607, 272, 12608, 272, - 12609, 272, 12610, 272, 12611, 272, 12612, 272, 12613, 272, 12614, 272, - 12615, 272, 12616, 272, 12617, 272, 12618, 272, 12619, 272, 12620, 272, - 12621, 272, 12622, 272, 12623, 272, 12624, 272, 12625, 272, 12626, 272, - 12627, 272, 12628, 272, 12629, 272, 12630, 272, 12631, 272, 12632, 272, - 12633, 272, 12634, 272, 12635, 272, 12636, 272, 12637, 272, 12638, 272, - 12639, 272, 12640, 272, 12641, 272, 12642, 272, 12643, 264, 162, 264, - 163, 264, 172, 264, 175, 264, 166, 264, 165, 264, 8361, 272, 9474, 272, - 8592, 272, 8593, 272, 8594, 272, 8595, 272, 9632, 272, 9675, 512, 55300, - 56473, 55300, 56506, 512, 55300, 56475, 55300, 56506, 512, 55300, 56485, - 55300, 56506, 512, 55300, 56625, 55300, 56615, 512, 55300, 56626, 55300, - 56615, 512, 55300, 57159, 55300, 57150, 512, 55300, 57159, 55300, 57175, - 512, 55301, 56505, 55301, 56506, 512, 55301, 56505, 55301, 56496, 512, - 55301, 56505, 55301, 56509, 512, 55301, 56760, 55301, 56751, 512, 55301, - 56761, 55301, 56751, 512, 55348, 56663, 55348, 56677, 512, 55348, 56664, - 55348, 56677, 512, 55348, 56671, 55348, 56686, 512, 55348, 56671, 55348, - 56687, 512, 55348, 56671, 55348, 56688, 512, 55348, 56671, 55348, 56689, - 512, 55348, 56671, 55348, 56690, 512, 55348, 56761, 55348, 56677, 512, - 55348, 56762, 55348, 56677, 512, 55348, 56763, 55348, 56686, 512, 55348, - 56764, 55348, 56686, 512, 55348, 56763, 55348, 56687, 512, 55348, 56764, - 55348, 56687, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, - 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, - 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, - 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, - 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, - 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, - 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, - 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, - 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, - 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, - 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, - 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, - 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, - 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, - 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, - 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, - 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, - 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, - 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, - 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 67, 262, 68, - 262, 71, 262, 74, 262, 75, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, - 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, - 262, 98, 262, 99, 262, 100, 262, 102, 262, 104, 262, 105, 262, 106, 262, - 107, 262, 108, 262, 109, 262, 110, 262, 112, 262, 113, 262, 114, 262, - 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, - 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, - 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, - 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, - 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, - 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, - 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, - 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 74, 262, 75, 262, 76, - 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 83, 262, 84, 262, 85, - 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, - 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, - 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, - 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, - 262, 122, 262, 65, 262, 66, 262, 68, 262, 69, 262, 70, 262, 71, 262, 73, - 262, 74, 262, 75, 262, 76, 262, 77, 262, 79, 262, 83, 262, 84, 262, 85, - 262, 86, 262, 87, 262, 88, 262, 89, 262, 97, 262, 98, 262, 99, 262, 100, - 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, - 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, - 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, - 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, - 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, - 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, - 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, - 262, 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, - 262, 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, - 262, 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, - 262, 65, 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, - 262, 73, 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, - 262, 81, 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, - 262, 89, 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, - 102, 262, 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, - 109, 262, 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, - 116, 262, 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, - 262, 66, 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, - 262, 74, 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, - 262, 82, 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, - 262, 90, 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, - 103, 262, 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, - 110, 262, 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, - 117, 262, 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, - 262, 67, 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, - 262, 75, 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, - 262, 83, 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, - 262, 97, 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, - 104, 262, 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, - 111, 262, 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, - 118, 262, 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, - 262, 68, 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, - 262, 76, 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, - 262, 84, 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, - 262, 98, 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, - 105, 262, 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, - 112, 262, 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, - 119, 262, 120, 262, 121, 262, 122, 262, 65, 262, 66, 262, 67, 262, 68, - 262, 69, 262, 70, 262, 71, 262, 72, 262, 73, 262, 74, 262, 75, 262, 76, - 262, 77, 262, 78, 262, 79, 262, 80, 262, 81, 262, 82, 262, 83, 262, 84, - 262, 85, 262, 86, 262, 87, 262, 88, 262, 89, 262, 90, 262, 97, 262, 98, - 262, 99, 262, 100, 262, 101, 262, 102, 262, 103, 262, 104, 262, 105, 262, - 106, 262, 107, 262, 108, 262, 109, 262, 110, 262, 111, 262, 112, 262, - 113, 262, 114, 262, 115, 262, 116, 262, 117, 262, 118, 262, 119, 262, - 120, 262, 121, 262, 122, 262, 305, 262, 567, 262, 913, 262, 914, 262, - 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, - 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, - 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, - 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, - 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, - 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, - 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, - 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, - 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, - 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, - 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, - 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, - 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, - 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, - 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, - 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, - 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, 916, 262, 917, 262, - 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, 923, 262, 924, 262, - 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, 1012, 262, 931, 262, - 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, 937, 262, 8711, 262, - 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, 950, 262, 951, 262, - 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, 957, 262, 958, 262, - 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, 964, 262, 965, 262, - 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, 1013, 262, 977, 262, - 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, 914, 262, 915, 262, - 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, 921, 262, 922, 262, - 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, 928, 262, 929, 262, - 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, 935, 262, 936, 262, - 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, 948, 262, 949, 262, - 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, 955, 262, 956, 262, - 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, 962, 262, 963, 262, - 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, 969, 262, 8706, 262, - 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, 982, 262, 913, 262, - 914, 262, 915, 262, 916, 262, 917, 262, 918, 262, 919, 262, 920, 262, - 921, 262, 922, 262, 923, 262, 924, 262, 925, 262, 926, 262, 927, 262, - 928, 262, 929, 262, 1012, 262, 931, 262, 932, 262, 933, 262, 934, 262, - 935, 262, 936, 262, 937, 262, 8711, 262, 945, 262, 946, 262, 947, 262, - 948, 262, 949, 262, 950, 262, 951, 262, 952, 262, 953, 262, 954, 262, - 955, 262, 956, 262, 957, 262, 958, 262, 959, 262, 960, 262, 961, 262, - 962, 262, 963, 262, 964, 262, 965, 262, 966, 262, 967, 262, 968, 262, - 969, 262, 8706, 262, 1013, 262, 977, 262, 1008, 262, 981, 262, 1009, 262, - 982, 262, 988, 262, 989, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, - 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, - 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 48, - 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, 262, 55, 262, 56, - 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, 262, 53, 262, 54, - 262, 55, 262, 56, 262, 57, 262, 48, 262, 49, 262, 50, 262, 51, 262, 52, - 262, 53, 262, 54, 262, 55, 262, 56, 262, 57, 262, 1575, 262, 1576, 262, - 1580, 262, 1583, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, - 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, - 1601, 262, 1589, 262, 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, - 262, 1582, 262, 1584, 262, 1590, 262, 1592, 262, 1594, 262, 1646, 262, - 1722, 262, 1697, 262, 1647, 262, 1576, 262, 1580, 262, 1607, 262, 1581, - 262, 1610, 262, 1603, 262, 1604, 262, 1605, 262, 1606, 262, 1587, 262, - 1593, 262, 1601, 262, 1589, 262, 1602, 262, 1588, 262, 1578, 262, 1579, - 262, 1582, 262, 1590, 262, 1594, 262, 1580, 262, 1581, 262, 1610, 262, - 1604, 262, 1606, 262, 1587, 262, 1593, 262, 1589, 262, 1602, 262, 1588, - 262, 1582, 262, 1590, 262, 1594, 262, 1722, 262, 1647, 262, 1576, 262, - 1580, 262, 1607, 262, 1581, 262, 1591, 262, 1610, 262, 1603, 262, 1605, - 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, - 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1590, 262, 1592, 262, 1594, - 262, 1646, 262, 1697, 262, 1575, 262, 1576, 262, 1580, 262, 1583, 262, - 1607, 262, 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, - 262, 1605, 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, - 1602, 262, 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, - 262, 1590, 262, 1592, 262, 1594, 262, 1576, 262, 1580, 262, 1583, 262, - 1608, 262, 1586, 262, 1581, 262, 1591, 262, 1610, 262, 1604, 262, 1605, - 262, 1606, 262, 1587, 262, 1593, 262, 1601, 262, 1589, 262, 1602, 262, - 1585, 262, 1588, 262, 1578, 262, 1579, 262, 1582, 262, 1584, 262, 1590, - 262, 1592, 262, 1594, 514, 48, 46, 514, 48, 44, 514, 49, 44, 514, 50, 44, - 514, 51, 44, 514, 52, 44, 514, 53, 44, 514, 54, 44, 514, 55, 44, 514, 56, - 44, 514, 57, 44, 770, 40, 65, 41, 770, 40, 66, 41, 770, 40, 67, 41, 770, - 40, 68, 41, 770, 40, 69, 41, 770, 40, 70, 41, 770, 40, 71, 41, 770, 40, - 72, 41, 770, 40, 73, 41, 770, 40, 74, 41, 770, 40, 75, 41, 770, 40, 76, - 41, 770, 40, 77, 41, 770, 40, 78, 41, 770, 40, 79, 41, 770, 40, 80, 41, - 770, 40, 81, 41, 770, 40, 82, 41, 770, 40, 83, 41, 770, 40, 84, 41, 770, - 40, 85, 41, 770, 40, 86, 41, 770, 40, 87, 41, 770, 40, 88, 41, 770, 40, - 89, 41, 770, 40, 90, 41, 770, 12308, 83, 12309, 263, 67, 263, 82, 519, - 67, 68, 519, 87, 90, 266, 65, 266, 66, 266, 67, 266, 68, 266, 69, 266, - 70, 266, 71, 266, 72, 266, 73, 266, 74, 266, 75, 266, 76, 266, 77, 266, - 78, 266, 79, 266, 80, 266, 81, 266, 82, 266, 83, 266, 84, 266, 85, 266, - 86, 266, 87, 266, 88, 266, 89, 266, 90, 522, 72, 86, 522, 77, 86, 522, - 83, 68, 522, 83, 83, 778, 80, 80, 86, 522, 87, 67, 515, 77, 67, 515, 77, - 68, 522, 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, 12469, 266, - 25163, 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, 22810, 266, - 35299, 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, 26009, 266, - 21069, 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, 32066, 266, - 29983, 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, 25237, 266, - 25429, 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, 20013, 266, - 21491, 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, 31354, 266, - 21512, 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, 21106, 266, - 21942, 266, 37197, 770, 12308, 26412, 12309, 770, 12308, 19977, 12309, - 770, 12308, 20108, 12309, 770, 12308, 23433, 12309, 770, 12308, 28857, - 12309, 770, 12308, 25171, 12309, 770, 12308, 30423, 12309, 770, 12308, - 21213, 12309, 770, 12308, 25943, 12309, 263, 24471, 263, 21487, 256, - 20029, 256, 20024, 256, 20033, 256, 55360, 56610, 256, 20320, 256, 20398, - 256, 20411, 256, 20482, 256, 20602, 256, 20633, 256, 20711, 256, 20687, - 256, 13470, 256, 55361, 56890, 256, 20813, 256, 20820, 256, 20836, 256, - 20855, 256, 55361, 56604, 256, 13497, 256, 20839, 256, 20877, 256, 55361, - 56651, 256, 20887, 256, 20900, 256, 20172, 256, 20908, 256, 20917, 256, - 55396, 56799, 256, 20981, 256, 20995, 256, 13535, 256, 21051, 256, 21062, - 256, 21106, 256, 21111, 256, 13589, 256, 21191, 256, 21193, 256, 21220, - 256, 21242, 256, 21253, 256, 21254, 256, 21271, 256, 21321, 256, 21329, - 256, 21338, 256, 21363, 256, 21373, 256, 21375, 256, 21375, 256, 21375, - 256, 55362, 56876, 256, 28784, 256, 21450, 256, 21471, 256, 55362, 57187, - 256, 21483, 256, 21489, 256, 21510, 256, 21662, 256, 21560, 256, 21576, - 256, 21608, 256, 21666, 256, 21750, 256, 21776, 256, 21843, 256, 21859, - 256, 21892, 256, 21892, 256, 21913, 256, 21931, 256, 21939, 256, 21954, - 256, 22294, 256, 22022, 256, 22295, 256, 22097, 256, 22132, 256, 20999, - 256, 22766, 256, 22478, 256, 22516, 256, 22541, 256, 22411, 256, 22578, - 256, 22577, 256, 22700, 256, 55365, 56548, 256, 22770, 256, 22775, 256, - 22790, 256, 22810, 256, 22818, 256, 22882, 256, 55365, 57000, 256, 55365, - 57066, 256, 23020, 256, 23067, 256, 23079, 256, 23000, 256, 23142, 256, - 14062, 256, 14076, 256, 23304, 256, 23358, 256, 23358, 256, 55366, 56776, - 256, 23491, 256, 23512, 256, 23527, 256, 23539, 256, 55366, 57112, 256, - 23551, 256, 23558, 256, 24403, 256, 23586, 256, 14209, 256, 23648, 256, - 23662, 256, 23744, 256, 23693, 256, 55367, 56804, 256, 23875, 256, 55367, - 56806, 256, 23918, 256, 23915, 256, 23932, 256, 24033, 256, 24034, 256, - 14383, 256, 24061, 256, 24104, 256, 24125, 256, 24169, 256, 14434, 256, - 55368, 56707, 256, 14460, 256, 24240, 256, 24243, 256, 24246, 256, 24266, - 256, 55400, 57234, 256, 24318, 256, 55368, 57137, 256, 55368, 57137, 256, - 33281, 256, 24354, 256, 24354, 256, 14535, 256, 55372, 57016, 256, 55384, - 56794, 256, 24418, 256, 24427, 256, 14563, 256, 24474, 256, 24525, 256, - 24535, 256, 24569, 256, 24705, 256, 14650, 256, 14620, 256, 24724, 256, - 55369, 57044, 256, 24775, 256, 24904, 256, 24908, 256, 24910, 256, 24908, - 256, 24954, 256, 24974, 256, 25010, 256, 24996, 256, 25007, 256, 25054, - 256, 25074, 256, 25078, 256, 25104, 256, 25115, 256, 25181, 256, 25265, - 256, 25300, 256, 25424, 256, 55370, 57100, 256, 25405, 256, 25340, 256, - 25448, 256, 25475, 256, 25572, 256, 55370, 57329, 256, 25634, 256, 25541, - 256, 25513, 256, 14894, 256, 25705, 256, 25726, 256, 25757, 256, 25719, - 256, 14956, 256, 25935, 256, 25964, 256, 55372, 56330, 256, 26083, 256, - 26360, 256, 26185, 256, 15129, 256, 26257, 256, 15112, 256, 15076, 256, - 20882, 256, 20885, 256, 26368, 256, 26268, 256, 32941, 256, 17369, 256, - 26391, 256, 26395, 256, 26401, 256, 26462, 256, 26451, 256, 55372, 57283, - 256, 15177, 256, 26618, 256, 26501, 256, 26706, 256, 26757, 256, 55373, - 56429, 256, 26766, 256, 26655, 256, 26900, 256, 15261, 256, 26946, 256, - 27043, 256, 27114, 256, 27304, 256, 55373, 56995, 256, 27355, 256, 15384, - 256, 27425, 256, 55374, 56487, 256, 27476, 256, 15438, 256, 27506, 256, - 27551, 256, 27578, 256, 27579, 256, 55374, 56973, 256, 55367, 56587, 256, - 55374, 57082, 256, 27726, 256, 55375, 56508, 256, 27839, 256, 27853, 256, - 27751, 256, 27926, 256, 27966, 256, 28023, 256, 27969, 256, 28009, 256, - 28024, 256, 28037, 256, 55375, 56606, 256, 27956, 256, 28207, 256, 28270, - 256, 15667, 256, 28363, 256, 28359, 256, 55375, 57041, 256, 28153, 256, - 28526, 256, 55375, 57182, 256, 55375, 57230, 256, 28614, 256, 28729, 256, - 28702, 256, 28699, 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, - 28845, 256, 55361, 56613, 256, 28997, 256, 55376, 56931, 256, 29084, 256, - 55376, 57259, 256, 29224, 256, 29237, 256, 29264, 256, 55377, 56840, 256, - 29312, 256, 29333, 256, 55377, 57141, 256, 55378, 56340, 256, 29562, 256, - 29579, 256, 16044, 256, 29605, 256, 16056, 256, 16056, 256, 29767, 256, - 29788, 256, 29809, 256, 29829, 256, 29898, 256, 16155, 256, 29988, 256, - 55379, 56374, 256, 30014, 256, 55379, 56466, 256, 30064, 256, 55368, - 56735, 256, 30224, 256, 55379, 57249, 256, 55379, 57272, 256, 55380, - 56388, 256, 16380, 256, 16392, 256, 30452, 256, 55380, 56563, 256, 55380, - 56562, 256, 55380, 56601, 256, 55380, 56627, 256, 30494, 256, 30495, 256, - 30495, 256, 30538, 256, 16441, 256, 30603, 256, 16454, 256, 16534, 256, - 55381, 56349, 256, 30798, 256, 30860, 256, 30924, 256, 16611, 256, 55381, - 56870, 256, 31062, 256, 55381, 56986, 256, 55381, 57029, 256, 31119, 256, - 31211, 256, 16687, 256, 31296, 256, 31306, 256, 31311, 256, 55382, 56700, - 256, 55382, 56999, 256, 55382, 56999, 256, 31470, 256, 16898, 256, 55382, - 57259, 256, 31686, 256, 31689, 256, 16935, 256, 55383, 56448, 256, 31954, - 256, 17056, 256, 31976, 256, 31971, 256, 32000, 256, 55383, 57222, 256, - 32099, 256, 17153, 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, - 55384, 56872, 256, 55384, 56903, 256, 17241, 256, 55384, 57049, 256, - 32634, 256, 55384, 57150, 256, 32661, 256, 32762, 256, 32773, 256, 55385, - 56538, 256, 55385, 56611, 256, 32864, 256, 55385, 56744, 256, 32880, 256, - 55372, 57183, 256, 17365, 256, 32946, 256, 33027, 256, 17419, 256, 33086, - 256, 23221, 256, 55385, 57255, 256, 55385, 57269, 256, 55372, 57235, 256, - 55372, 57244, 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, - 256, 33419, 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, - 256, 33510, 256, 55386, 57148, 256, 33509, 256, 33565, 256, 33635, 256, - 33709, 256, 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, - 33738, 256, 33740, 256, 33756, 256, 55387, 56374, 256, 55387, 56683, 256, - 55387, 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, 256, 55388, - 57290, 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, 256, 55387, - 57265, 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, 256, 34407, - 256, 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, 256, 34681, - 256, 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, 256, 34817, - 256, 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, 35031, 256, - 35038, 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, 256, 55390, - 56678, 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, 35722, 256, - 35925, 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, 256, 36215, - 256, 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, 256, 36336, - 256, 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, 256, 55393, - 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, 256, 37147, - 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, - 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, 55395, 56695, - 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, 56645, 256, - 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, 56330, 256, - 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, 256, 55397, - 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, 39362, 256, - 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, 256, 40189, - 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, 19704, 256, - 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, 40635, 256, - 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, - 40763, 256, 55401, 56832, -}; - -/* index tables for the decomposition data */ -#define DECOMP_SHIFT1 6 -#define DECOMP_SHIFT2 4 -static const unsigned char decomp_index0[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 13, 14, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 16, 5, 5, 5, 5, 17, 18, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 19, 20, - 5, 5, 5, 5, 5, 21, 22, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 23, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -}; - -static const unsigned short decomp_index1[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 0, 0, 0, 0, - 25, 0, 26, 27, 0, 0, 0, 0, 0, 28, 0, 0, 29, 30, 31, 32, 33, 34, 35, 0, - 36, 37, 38, 0, 39, 0, 40, 0, 41, 0, 0, 0, 0, 42, 43, 44, 45, 0, 0, 0, 0, - 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 48, 0, 0, 0, - 0, 49, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 0, 53, 0, 0, 0, 0, - 0, 0, 54, 55, 0, 0, 0, 0, 0, 56, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 58, 59, 0, 0, 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, - 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, - 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 70, - 71, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 0, - 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 84, 85, 86, 87, 88, 89, 0, 90, 91, 92, 0, 0, 0, 0, - 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, - 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, - 123, 124, 125, 126, 127, 128, 129, 130, 0, 131, 132, 133, 134, 0, 0, 0, - 0, 0, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 0, 146, 0, - 0, 0, 147, 0, 148, 149, 150, 0, 151, 152, 153, 0, 154, 0, 0, 0, 155, 0, - 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, - 158, 159, 160, 161, 162, 163, 164, 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 0, - 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 168, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 171, 0, 0, 0, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, - 182, 183, 184, 185, 186, 0, 0, 187, 0, 0, 188, 189, 190, 191, 192, 0, - 193, 194, 195, 196, 197, 0, 198, 0, 0, 0, 199, 200, 201, 202, 203, 204, - 205, 0, 0, 0, 0, 0, 0, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, - 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, - 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 0, 0, 272, 273, 274, - 275, 276, 277, 278, 279, 280, 281, 282, 283, 0, 284, 285, 286, 287, 288, - 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, - 303, 304, 305, 306, 0, 307, 308, 309, 310, 311, 312, 313, 314, 0, 0, 315, - 0, 316, 0, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, - 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, - 343, 344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 0, - 347, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 350, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 351, 352, 0, 0, 0, 0, 353, 354, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, - 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, - 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, - 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 431, 432, 433, 434, 435, 0, 436, 0, - 0, 437, 0, 0, 0, 0, 0, 0, 438, 439, 440, 441, 442, 443, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 444, 445, - 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, - 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, - 474, 475, 476, 477, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static const unsigned short decomp_index2[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 3, 0, 6, 0, 0, 0, 0, 8, 0, 0, 11, 13, 15, 18, 0, 0, 20, 23, 25, 0, 27, - 31, 35, 0, 39, 42, 45, 48, 51, 54, 0, 57, 60, 63, 66, 69, 72, 75, 78, 81, - 0, 84, 87, 90, 93, 96, 99, 0, 0, 102, 105, 108, 111, 114, 0, 0, 117, 120, - 123, 126, 129, 132, 0, 135, 138, 141, 144, 147, 150, 153, 156, 159, 0, - 162, 165, 168, 171, 174, 177, 0, 0, 180, 183, 186, 189, 192, 0, 195, 198, - 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, - 243, 0, 0, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, - 282, 285, 288, 291, 294, 297, 300, 303, 0, 0, 306, 309, 312, 315, 318, - 321, 324, 327, 330, 0, 333, 336, 339, 342, 345, 348, 0, 351, 354, 357, - 360, 363, 366, 369, 372, 0, 0, 375, 378, 381, 384, 387, 390, 393, 0, 0, - 396, 399, 402, 405, 408, 411, 0, 0, 414, 417, 420, 423, 426, 429, 432, - 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 0, 0, 468, 471, - 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, - 516, 519, 522, 525, 528, 531, 534, 537, 539, 542, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 545, 548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 551, 554, 557, 560, 563, 566, 569, 572, 575, 578, 581, 584, 587, - 590, 593, 596, 599, 602, 605, 608, 611, 614, 617, 620, 623, 0, 626, 629, - 632, 635, 638, 641, 0, 0, 644, 647, 650, 653, 656, 659, 662, 665, 668, - 671, 674, 677, 680, 683, 686, 689, 0, 0, 692, 695, 698, 701, 704, 707, - 710, 713, 716, 719, 722, 725, 728, 731, 734, 737, 740, 743, 746, 749, - 752, 755, 758, 761, 764, 767, 770, 773, 776, 779, 782, 785, 788, 791, - 794, 797, 0, 0, 800, 803, 0, 0, 0, 0, 0, 0, 806, 809, 812, 815, 818, 821, - 824, 827, 830, 833, 836, 839, 842, 845, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 848, 850, 852, 854, 856, 858, 860, 862, 864, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 866, 869, 872, 875, 878, 881, 0, 0, 884, 886, 888, - 890, 892, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 894, 896, 0, 898, 900, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 903, 0, 0, 0, 0, 0, 905, 0, 0, 0, - 908, 0, 0, 0, 0, 0, 910, 913, 916, 919, 921, 924, 927, 0, 930, 0, 933, - 936, 939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 942, 945, 948, 951, 954, 957, 960, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 963, 966, 969, 972, 975, - 0, 978, 980, 982, 984, 987, 990, 992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 994, - 996, 998, 0, 1000, 1002, 0, 0, 0, 1004, 0, 0, 0, 0, 0, 0, 1006, 1009, 0, - 1012, 0, 0, 0, 1015, 0, 0, 0, 0, 1018, 1021, 1024, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1027, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1030, 0, 0, - 0, 0, 0, 0, 1033, 1036, 0, 1039, 0, 0, 0, 1042, 0, 0, 0, 0, 1045, 1048, - 1051, 0, 0, 0, 0, 0, 0, 0, 1054, 1057, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1060, - 1063, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1066, 1069, 1072, 1075, 0, - 0, 1078, 1081, 0, 0, 1084, 1087, 1090, 1093, 1096, 1099, 0, 0, 1102, - 1105, 1108, 1111, 1114, 1117, 0, 0, 1120, 1123, 1126, 1129, 1132, 1135, - 1138, 1141, 1144, 1147, 1150, 1153, 0, 0, 1156, 1159, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1165, 1168, - 1171, 1174, 1177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1180, 1183, - 1186, 1189, 0, 0, 0, 0, 0, 0, 0, 1192, 0, 1195, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1201, 0, 0, 0, 0, 0, 0, 0, 1204, 0, 0, 1207, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1210, 1213, 1216, - 1219, 1222, 1225, 1228, 1231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1234, - 1237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1240, 1243, 0, 1246, - 0, 0, 0, 1249, 0, 0, 1252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1255, 1258, 1261, 0, 0, 1264, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1267, - 0, 0, 1270, 1273, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1276, - 1279, 0, 0, 0, 0, 0, 0, 1282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1285, 1288, 1291, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1294, 0, 0, 0, 0, 0, 0, 0, 1297, 0, 0, 0, 0, 0, 0, 1300, 1303, 0, 1306, - 1309, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1312, 1315, 1318, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1321, 0, 1324, 1327, 1330, 0, 0, 0, 0, - 1333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1336, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1339, 1342, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, 0, 0, 0, 0, 0, 0, 1347, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1350, 0, 0, 0, 0, 1353, 0, 0, 0, 0, 1356, 0, 0, - 0, 0, 1359, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1362, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1365, 0, 1368, 1371, 1374, 1377, 1380, 0, 0, 0, 0, 0, 0, 0, - 1383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1386, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1389, 0, 0, 0, 0, 1392, 0, 0, 0, 0, 1395, 0, 0, 0, 0, - 1398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1401, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1409, 0, 1412, 0, 1415, 0, - 1418, 0, 1421, 0, 0, 0, 1424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1427, 0, 1430, 0, 0, 1433, 1436, 0, 1439, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1442, 1444, 1446, 0, 1448, 1450, 1452, 1454, 1456, 1458, 1460, 1462, - 1464, 1466, 1468, 0, 1470, 1472, 1474, 1476, 1478, 1480, 1482, 1484, - 1486, 1488, 1490, 1492, 1494, 1496, 1498, 1500, 1502, 1504, 0, 1506, - 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1528, 1530, - 1532, 1534, 1536, 1538, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, - 1556, 1558, 1560, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1562, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1564, 1566, 1568, 1570, - 1572, 1574, 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, - 1596, 1598, 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, 1616, 1618, - 1620, 1622, 1624, 1626, 1628, 1630, 1632, 1634, 1636, 1638, 1641, 1644, - 1647, 1650, 1653, 1656, 1659, 1662, 1665, 1668, 1671, 1674, 1677, 1680, - 1683, 1686, 1689, 1692, 1695, 1698, 1701, 1704, 1707, 1710, 1713, 1716, - 1719, 1722, 1725, 1728, 1731, 1734, 1737, 1740, 1743, 1746, 1749, 1752, - 1755, 1758, 1761, 1764, 1767, 1770, 1773, 1776, 1779, 1782, 1785, 1788, - 1791, 1794, 1797, 1800, 1803, 1806, 1809, 1812, 1815, 1818, 1821, 1824, - 1827, 1830, 1833, 1836, 1839, 1842, 1845, 1848, 1851, 1854, 1857, 1860, - 1863, 1866, 1869, 1872, 1875, 1878, 1881, 1884, 1887, 1890, 1893, 1896, - 1899, 1902, 1905, 1908, 1911, 1914, 1917, 1920, 1923, 1926, 1929, 1932, - 1935, 1938, 1941, 1944, 1947, 1950, 1953, 1956, 1959, 1962, 1965, 1968, - 1971, 1974, 1977, 1980, 1983, 1986, 1989, 1992, 1995, 1998, 2001, 2004, - 2007, 2010, 2013, 2016, 2019, 2022, 2025, 2028, 2031, 2034, 2037, 2040, - 2043, 2046, 2049, 2052, 2055, 2058, 2061, 2064, 2067, 2070, 2073, 2076, - 2079, 2082, 2085, 2088, 2091, 2094, 2097, 2100, 2103, 0, 0, 0, 0, 2106, - 2109, 2112, 2115, 2118, 2121, 2124, 2127, 2130, 2133, 2136, 2139, 2142, - 2145, 2148, 2151, 2154, 2157, 2160, 2163, 2166, 2169, 2172, 2175, 2178, - 2181, 2184, 2187, 2190, 2193, 2196, 2199, 2202, 2205, 2208, 2211, 2214, - 2217, 2220, 2223, 2226, 2229, 2232, 2235, 2238, 2241, 2244, 2247, 2250, - 2253, 2256, 2259, 2262, 2265, 2268, 2271, 2274, 2277, 2280, 2283, 2286, - 2289, 2292, 2295, 2298, 2301, 2304, 2307, 2310, 2313, 2316, 2319, 2322, - 2325, 2328, 2331, 2334, 2337, 2340, 2343, 2346, 2349, 2352, 2355, 2358, - 2361, 2364, 2367, 2370, 2373, 0, 0, 0, 0, 0, 0, 2376, 2379, 2382, 2385, - 2388, 2391, 2394, 2397, 2400, 2403, 2406, 2409, 2412, 2415, 2418, 2421, - 2424, 2427, 2430, 2433, 2436, 2439, 0, 0, 2442, 2445, 2448, 2451, 2454, - 2457, 0, 0, 2460, 2463, 2466, 2469, 2472, 2475, 2478, 2481, 2484, 2487, - 2490, 2493, 2496, 2499, 2502, 2505, 2508, 2511, 2514, 2517, 2520, 2523, - 2526, 2529, 2532, 2535, 2538, 2541, 2544, 2547, 2550, 2553, 2556, 2559, - 2562, 2565, 2568, 2571, 0, 0, 2574, 2577, 2580, 2583, 2586, 2589, 0, 0, - 2592, 2595, 2598, 2601, 2604, 2607, 2610, 2613, 0, 2616, 0, 2619, 0, - 2622, 0, 2625, 2628, 2631, 2634, 2637, 2640, 2643, 2646, 2649, 2652, - 2655, 2658, 2661, 2664, 2667, 2670, 2673, 2676, 2679, 2681, 2684, 2686, - 2689, 2691, 2694, 2696, 2699, 2701, 2704, 2706, 2709, 0, 0, 2711, 2714, - 2717, 2720, 2723, 2726, 2729, 2732, 2735, 2738, 2741, 2744, 2747, 2750, - 2753, 2756, 2759, 2762, 2765, 2768, 2771, 2774, 2777, 2780, 2783, 2786, - 2789, 2792, 2795, 2798, 2801, 2804, 2807, 2810, 2813, 2816, 2819, 2822, - 2825, 2828, 2831, 2834, 2837, 2840, 2843, 2846, 2849, 2852, 2855, 2858, - 2861, 2864, 2867, 0, 2870, 2873, 2876, 2879, 2882, 2885, 2887, 2890, - 2893, 2895, 2898, 2901, 2904, 2907, 2910, 0, 2913, 2916, 2919, 2922, - 2924, 2927, 2929, 2932, 2935, 2938, 2941, 2944, 2947, 2950, 0, 0, 2952, - 2955, 2958, 2961, 2964, 2967, 0, 2969, 2972, 2975, 2978, 2981, 2984, - 2987, 2989, 2992, 2995, 2998, 3001, 3004, 3007, 3010, 3012, 3015, 3018, - 3020, 0, 0, 3022, 3025, 3028, 0, 3031, 3034, 3037, 3040, 3042, 3045, - 3047, 3050, 3052, 0, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, - 3071, 3073, 3075, 0, 0, 0, 0, 0, 0, 3077, 0, 0, 0, 0, 0, 3079, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3082, 3084, 3087, 0, 0, 0, 0, 0, 0, 0, 0, - 3091, 0, 0, 0, 3093, 3096, 0, 3100, 3103, 0, 0, 0, 0, 3107, 0, 3110, 0, - 0, 0, 0, 0, 0, 0, 0, 3113, 3116, 3119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 3122, 0, 0, 0, 0, 0, 0, 0, 3127, 3129, 3131, 0, 0, 3133, 3135, - 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, - 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, - 3185, 0, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, - 3207, 3209, 3211, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3213, 0, 0, 0, 0, 0, - 0, 0, 3216, 3220, 3224, 3226, 0, 3229, 3233, 3237, 0, 3239, 3242, 3244, - 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, 0, 3262, 3264, 0, 0, - 3267, 3269, 3271, 3273, 3275, 0, 0, 3277, 3280, 3284, 0, 3287, 0, 3289, - 0, 3291, 0, 3293, 3295, 3297, 3299, 0, 3301, 3303, 3305, 0, 3307, 3309, - 3311, 3313, 3315, 3317, 3319, 0, 3321, 3325, 3327, 3329, 3331, 3333, 0, - 0, 0, 0, 3335, 3337, 3339, 3341, 3343, 0, 0, 0, 0, 0, 0, 3345, 3349, - 3353, 3358, 3362, 3366, 3370, 3374, 3378, 3382, 3386, 3390, 3394, 3398, - 3402, 3406, 3409, 3411, 3414, 3418, 3421, 3423, 3426, 3430, 3435, 3438, - 3440, 3443, 3447, 3449, 3451, 3453, 3455, 3457, 3460, 3464, 3467, 3469, - 3472, 3476, 3481, 3484, 3486, 3489, 3493, 3495, 3497, 3499, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3505, 3508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3511, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3514, 3517, 3520, 0, 0, 0, 0, - 3523, 0, 0, 0, 0, 3526, 0, 0, 3529, 0, 0, 0, 0, 0, 0, 0, 3532, 0, 3535, - 0, 0, 0, 0, 0, 3538, 3541, 0, 3545, 3548, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3552, 0, 0, 3555, 0, 0, 3558, 0, 3561, 0, 0, 0, 0, 0, - 0, 3564, 0, 3567, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3570, 3573, 3576, 3579, - 3582, 0, 0, 3585, 3588, 0, 0, 3591, 3594, 0, 0, 0, 0, 0, 0, 3597, 3600, - 0, 0, 3603, 3606, 0, 0, 3609, 3612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 3615, 3618, 3621, 3624, 3627, 3630, 3633, 3636, 0, 0, - 0, 0, 0, 0, 3639, 3642, 3645, 3648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3651, 3653, 0, 0, 0, 0, 0, 3655, 3657, 3659, 3661, 3663, 3665, 3667, - 3669, 3671, 3673, 3676, 3679, 3682, 3685, 3688, 3691, 3694, 3697, 3700, - 3703, 3706, 3710, 3714, 3718, 3722, 3726, 3730, 3734, 3738, 3742, 3747, - 3752, 3757, 3762, 3767, 3772, 3777, 3782, 3787, 3792, 3797, 3800, 3803, - 3806, 3809, 3812, 3815, 3818, 3821, 3824, 3828, 3832, 3836, 3840, 3844, - 3848, 3852, 3856, 3860, 3864, 3868, 3872, 3876, 3880, 3884, 3888, 3892, - 3896, 3900, 3904, 3908, 3912, 3916, 3920, 3924, 3928, 3932, 3936, 3940, - 3944, 3948, 3952, 3956, 3960, 3964, 3968, 3972, 3974, 3976, 3978, 3980, - 3982, 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, - 4006, 4008, 4010, 4012, 4014, 4016, 4018, 4020, 4022, 4024, 4026, 4028, - 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046, 4048, 4050, 4052, - 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4078, 0, 0, 0, 0, 0, - 0, 0, 4083, 4087, 4090, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4094, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4097, - 4099, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4101, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4103, 0, 0, 0, 4105, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, - 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, - 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, - 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, - 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, - 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, - 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, - 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, - 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, - 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, - 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, - 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, - 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, - 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, - 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, - 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, - 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, - 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, - 4531, 4533, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4535, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4537, 0, 4539, 4541, 4543, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4545, 0, 4548, 0, 4551, 0, - 4554, 0, 4557, 0, 4560, 0, 4563, 0, 4566, 0, 4569, 0, 4572, 0, 4575, 0, - 4578, 0, 0, 4581, 0, 4584, 0, 4587, 0, 0, 0, 0, 0, 0, 4590, 4593, 0, - 4596, 4599, 0, 4602, 4605, 0, 4608, 4611, 0, 4614, 4617, 0, 0, 0, 0, 0, - 0, 4620, 0, 0, 0, 0, 0, 0, 4623, 4626, 0, 4629, 4632, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4635, 0, 4638, 0, 4641, 0, 4644, 0, 4647, 0, 4650, 0, - 4653, 0, 4656, 0, 4659, 0, 4662, 0, 4665, 0, 4668, 0, 0, 4671, 0, 4674, - 0, 4677, 0, 0, 0, 0, 0, 0, 4680, 4683, 0, 4686, 4689, 0, 4692, 4695, 0, - 4698, 4701, 0, 4704, 4707, 0, 0, 0, 0, 0, 0, 4710, 0, 0, 4713, 4716, - 4719, 4722, 0, 0, 0, 4725, 4728, 0, 4731, 4733, 4735, 4737, 4739, 4741, - 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, - 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, - 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, - 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, - 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, - 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, - 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, - 4911, 4913, 4915, 4917, 0, 0, 0, 4919, 4921, 4923, 4925, 4927, 4929, - 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4951, 4955, 4959, - 4963, 4967, 4971, 4975, 4979, 4983, 4987, 4991, 4995, 4999, 5003, 5008, - 5013, 5018, 5023, 5028, 5033, 5038, 5043, 5048, 5053, 5058, 5063, 5068, - 5073, 5078, 5086, 0, 5093, 5097, 5101, 5105, 5109, 5113, 5117, 5121, - 5125, 5129, 5133, 5137, 5141, 5145, 5149, 5153, 5157, 5161, 5165, 5169, - 5173, 5177, 5181, 5185, 5189, 5193, 5197, 5201, 5205, 5209, 5213, 5217, - 5221, 5225, 5229, 5233, 5237, 5239, 5241, 5243, 0, 0, 0, 0, 0, 0, 0, 0, - 5245, 5249, 5252, 5255, 5258, 5261, 5264, 5267, 5270, 5273, 5276, 5279, - 5282, 5285, 5288, 5291, 5294, 5296, 5298, 5300, 5302, 5304, 5306, 5308, - 5310, 5312, 5314, 5316, 5318, 5320, 5322, 5325, 5328, 5331, 5334, 5337, - 5340, 5343, 5346, 5349, 5352, 5355, 5358, 5361, 5364, 5370, 5375, 0, - 5378, 5380, 5382, 5384, 5386, 5388, 5390, 5392, 5394, 5396, 5398, 5400, - 5402, 5404, 5406, 5408, 5410, 5412, 5414, 5416, 5418, 5420, 5422, 5424, - 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, 5444, 5446, 5448, - 5450, 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, 5468, 5470, 5472, - 5474, 5476, 5479, 5482, 5485, 5488, 5491, 5494, 5497, 5500, 5503, 5506, - 5509, 5512, 5515, 5518, 5521, 5524, 5527, 5530, 5533, 5536, 5539, 5542, - 5545, 5548, 5552, 5556, 5560, 5563, 5567, 5570, 5574, 5576, 5578, 5580, - 5582, 5584, 5586, 5588, 5590, 5592, 5594, 5596, 5598, 5600, 5602, 5604, - 5606, 5608, 5610, 5612, 5614, 5616, 5618, 5620, 5622, 5624, 5626, 5628, - 5630, 5632, 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, 5650, 5652, - 5654, 5656, 5658, 5660, 5662, 5664, 5666, 0, 5668, 5673, 5678, 5683, - 5687, 5692, 5696, 5700, 5706, 5711, 5715, 5719, 5723, 5728, 5733, 5737, - 5741, 5744, 5748, 5753, 5758, 5761, 5767, 5774, 5780, 5784, 5790, 5796, - 5801, 5805, 5809, 5813, 5818, 5824, 5829, 5833, 5837, 5841, 5844, 5847, - 5850, 5853, 5857, 5861, 5867, 5871, 5876, 5882, 5886, 5889, 5892, 5898, - 5903, 5909, 5913, 5919, 5922, 5926, 5930, 5934, 5938, 5942, 5947, 5951, - 5954, 5958, 5962, 5966, 5971, 5975, 5979, 5983, 5989, 5994, 5997, 6003, - 6006, 6011, 6016, 6020, 6024, 6028, 6033, 6036, 6040, 6045, 6048, 6054, - 6058, 6061, 6064, 6067, 6070, 6073, 6076, 6079, 6082, 6085, 6088, 6092, - 6096, 6100, 6104, 6108, 6112, 6116, 6120, 6124, 6128, 6132, 6136, 6140, - 6144, 6148, 6152, 6155, 6158, 6162, 6165, 6168, 6171, 6175, 6179, 6182, - 6185, 6188, 6191, 6194, 6199, 6202, 6205, 6208, 6211, 6214, 6217, 6220, - 6223, 6227, 6232, 6235, 6238, 6241, 6244, 6247, 6250, 6253, 6257, 6261, - 6265, 6269, 6272, 6275, 6278, 6281, 6284, 6287, 6290, 6293, 6296, 6299, - 6303, 6307, 6310, 6314, 6318, 6322, 6325, 6329, 6333, 6338, 6341, 6345, - 6349, 6353, 6357, 6363, 6370, 6373, 6376, 6379, 6382, 6385, 6388, 6391, - 6394, 6397, 6400, 6403, 6406, 6409, 6412, 6415, 6418, 6421, 6424, 6429, - 6432, 6435, 6438, 6443, 6447, 6450, 6453, 6456, 6459, 6462, 6465, 6468, - 6471, 6474, 6477, 6481, 6484, 6487, 6491, 6495, 6498, 6503, 6507, 6510, - 6513, 6516, 6519, 6523, 6527, 6530, 6533, 6536, 6539, 6542, 6545, 6548, - 6551, 6554, 6558, 6562, 6566, 6570, 6574, 6578, 6582, 6586, 6590, 6594, - 6598, 6602, 6606, 6610, 6614, 6618, 6622, 6626, 6630, 6634, 6638, 6642, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6646, 6648, 0, 0, 6650, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6652, 6654, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6656, 6658, 6660, - 6662, 6664, 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, 6682, 6684, - 6686, 6688, 6690, 6692, 6694, 6696, 6698, 6700, 6702, 6704, 6706, 6708, - 6710, 6712, 6714, 6716, 6718, 6720, 6722, 6724, 6726, 6728, 6730, 6732, - 6734, 6736, 6738, 6740, 6742, 6744, 6746, 6748, 6750, 6752, 6754, 6756, - 6758, 6760, 6762, 6764, 6766, 6768, 6770, 6772, 6774, 6776, 6778, 6780, - 6782, 6784, 6786, 6788, 6790, 6792, 6794, 6796, 6798, 6800, 6802, 6804, - 6806, 6808, 6810, 6812, 6814, 6816, 6818, 6820, 6822, 6824, 6826, 6828, - 6830, 6832, 6834, 6836, 6838, 6840, 6842, 6844, 6846, 6848, 6850, 6852, - 6854, 6856, 6858, 6860, 6862, 6864, 6866, 6868, 6870, 6872, 6874, 6876, - 6878, 6880, 6882, 6884, 6886, 6888, 6890, 6892, 6894, 6896, 6898, 6900, - 6902, 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, 6920, 6922, 6924, - 6926, 6928, 6930, 6932, 6934, 6936, 6938, 6940, 6942, 6944, 6946, 6948, - 6950, 6952, 6954, 6956, 6958, 6960, 6962, 6964, 6966, 6968, 6970, 6972, - 6974, 6976, 6978, 6980, 6982, 6984, 6986, 6988, 6990, 6992, 6994, 6996, - 6998, 7000, 7002, 7004, 7006, 7008, 7010, 7012, 7014, 7016, 7018, 7020, - 7022, 7024, 7026, 7028, 7030, 7032, 7034, 7036, 7038, 7040, 7042, 7044, - 7046, 7048, 7050, 7052, 7054, 7056, 7058, 7060, 7062, 7064, 7066, 7068, - 7070, 7072, 7074, 7076, 7078, 7080, 7082, 7084, 7086, 7088, 7090, 7092, - 7094, 7096, 7098, 7100, 7102, 7104, 7106, 7108, 7110, 7112, 7114, 7116, - 7118, 7120, 7122, 7124, 7126, 7128, 7130, 7132, 7134, 7136, 7138, 7140, - 7142, 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, 7162, 7164, - 7166, 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, 7186, 7188, - 7190, 7192, 7194, 7196, 7198, 7200, 7202, 0, 0, 7204, 0, 7206, 0, 0, - 7208, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 0, 7228, 0, - 7230, 0, 0, 7232, 7234, 0, 0, 0, 7236, 7238, 7240, 7242, 7244, 7246, - 7248, 7250, 7252, 7254, 7256, 7258, 7260, 7262, 7264, 7266, 7268, 7270, - 7272, 7274, 7276, 7278, 7280, 7282, 7284, 7286, 7288, 7290, 7292, 7294, - 7296, 7298, 7300, 7302, 7304, 7306, 7308, 7310, 7312, 7314, 7316, 7318, - 7320, 7322, 7324, 7326, 7328, 7330, 7332, 7334, 7336, 7338, 7340, 7342, - 7344, 7346, 7348, 7350, 7352, 7354, 7356, 7358, 7360, 7362, 7364, 7366, - 7368, 7371, 0, 0, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, - 7391, 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, - 7415, 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, - 7439, 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, - 7463, 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, - 7487, 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, - 7511, 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, - 7535, 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7553, 7555, 7557, - 7559, 7561, 7563, 7566, 7569, 7572, 7574, 7576, 7578, 7581, 7584, 7587, - 7589, 0, 0, 0, 0, 0, 0, 7591, 7594, 7597, 7600, 7604, 7608, 7611, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7614, 7617, 7620, 7623, 7626, 0, 0, 0, 0, - 0, 7629, 0, 7632, 7635, 7637, 7639, 7641, 7643, 7645, 7647, 7649, 7651, - 7653, 7655, 7658, 7661, 7664, 7667, 7670, 7673, 7676, 7679, 7682, 7685, - 7688, 7691, 0, 7694, 7697, 7700, 7703, 7706, 0, 7709, 0, 7712, 7715, 0, - 7718, 7721, 0, 7724, 7727, 7730, 7733, 7736, 7739, 7742, 7745, 7748, - 7751, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, - 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, - 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, - 7824, 7826, 7828, 7830, 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, - 7848, 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, - 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892, 7894, - 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, - 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, 7936, 7938, 7940, 7942, - 7944, 7946, 7948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7950, 7952, 7954, 7956, 7958, 7960, 7962, 7964, 7966, 7968, 7970, 7972, - 7974, 7976, 7978, 7980, 7982, 7984, 7986, 7988, 7990, 7992, 7994, 7996, - 7999, 8002, 8005, 8008, 8011, 8014, 8017, 8020, 8023, 8026, 8029, 8032, - 8035, 8038, 8041, 8044, 8047, 8050, 8052, 8054, 8056, 8058, 8061, 8064, - 8067, 8070, 8073, 8076, 8079, 8082, 8085, 8088, 8091, 8094, 8097, 8100, - 8103, 8106, 8109, 8112, 8115, 8118, 8121, 8124, 8127, 8130, 8133, 8136, - 8139, 8142, 8145, 8148, 8151, 8154, 8157, 8160, 8163, 8166, 8169, 8172, - 8175, 8178, 8181, 8184, 8187, 8190, 8193, 8196, 8199, 8202, 8205, 8208, - 8211, 8214, 8217, 8220, 8223, 8226, 8229, 8232, 8235, 8238, 8241, 8244, - 8247, 8250, 8253, 8256, 8259, 8262, 8265, 8268, 8271, 8274, 8277, 8280, - 8283, 8286, 8289, 8292, 8295, 8298, 8301, 8304, 8307, 8310, 8313, 8316, - 8319, 8322, 8325, 8328, 8331, 8334, 8337, 8340, 8344, 8348, 8352, 8356, - 8360, 8364, 8367, 8370, 8373, 8376, 8379, 8382, 8385, 8388, 8391, 8394, - 8397, 8400, 8403, 8406, 8409, 8412, 8415, 8418, 8421, 8424, 8427, 8430, - 8433, 8436, 8439, 8442, 8445, 8448, 8451, 8454, 8457, 8460, 8463, 8466, - 8469, 8472, 8475, 8478, 8481, 8484, 8487, 8490, 8493, 8496, 8499, 8502, - 8505, 8508, 8511, 8514, 8517, 8520, 8523, 8526, 8529, 8532, 8535, 8538, - 8541, 8544, 8547, 8550, 8553, 8556, 8559, 8562, 8565, 8568, 8571, 8574, - 8577, 8580, 8583, 8586, 8589, 8592, 8595, 8598, 8601, 8604, 8607, 8610, - 8613, 8616, 8619, 8622, 8625, 8628, 8631, 8634, 8637, 8640, 8643, 8646, - 8649, 8652, 8655, 8658, 8661, 8664, 8667, 8670, 8673, 8676, 8679, 8682, - 8685, 8688, 8691, 8694, 8697, 8700, 8703, 8706, 8709, 8712, 8715, 8718, - 8721, 8724, 8727, 8730, 8733, 8736, 8739, 8742, 8745, 8748, 8751, 8754, - 8757, 8760, 8763, 8766, 8769, 8772, 8775, 8778, 8781, 8784, 8787, 8790, - 8794, 8798, 8802, 8805, 8808, 8811, 8814, 8817, 8820, 8823, 8826, 8829, - 8832, 8835, 8838, 8841, 8844, 8847, 8850, 8853, 8856, 8859, 8862, 8865, - 8868, 8871, 8874, 8877, 8880, 8883, 8886, 8889, 8892, 8895, 8898, 8901, - 8904, 8907, 8910, 8913, 8916, 8919, 8922, 8925, 8928, 8931, 8934, 8937, - 8940, 8943, 8946, 8949, 8952, 8955, 8958, 8961, 8964, 8967, 8970, 8973, - 8976, 8979, 8982, 8985, 8988, 8991, 8994, 8997, 9000, 9003, 9006, 9009, - 9012, 9015, 9018, 0, 0, 9021, 9025, 9029, 9033, 9037, 9041, 9045, 9049, - 9053, 9057, 9061, 9065, 9069, 9073, 9077, 9081, 9085, 9089, 9093, 9097, - 9101, 9105, 9109, 9113, 9117, 9121, 9125, 9129, 9133, 9137, 9141, 9145, - 9149, 9153, 9157, 9161, 9165, 9169, 9173, 9177, 9181, 9185, 9189, 9193, - 9197, 9201, 9205, 9209, 9213, 9217, 9221, 9225, 9229, 9233, 9237, 9241, - 9245, 9249, 9253, 9257, 9261, 9265, 9269, 9273, 0, 0, 9277, 9281, 9285, - 9289, 9293, 9297, 9301, 9305, 9309, 9313, 9317, 9321, 9325, 9329, 9333, - 9337, 9341, 9345, 9349, 9353, 9357, 9361, 9365, 9369, 9373, 9377, 9381, - 9385, 9389, 9393, 9397, 9401, 9405, 9409, 9413, 9417, 9421, 9425, 9429, - 9433, 9437, 9441, 9445, 9449, 9453, 9457, 9461, 9465, 9469, 9473, 9477, - 9481, 9485, 9489, 0, 0, 0, 0, 0, 0, 0, 0, 9493, 9497, 9501, 9506, 9511, - 9516, 9521, 9526, 9531, 9536, 9540, 9559, 9568, 0, 0, 0, 9573, 9575, - 9577, 9579, 9581, 9583, 9585, 9587, 9589, 9591, 0, 0, 0, 0, 0, 0, 9593, - 9595, 9597, 9599, 9601, 9603, 9605, 9607, 9609, 9611, 9613, 9615, 9617, - 9619, 9621, 9623, 9625, 9627, 9629, 9631, 9633, 0, 0, 9635, 9637, 9639, - 9641, 9643, 9645, 9647, 9649, 9651, 9653, 9655, 9657, 0, 9659, 9661, - 9663, 9665, 9667, 9669, 9671, 9673, 9675, 9677, 9679, 9681, 9683, 9685, - 9687, 9689, 9691, 9693, 9695, 0, 9697, 9699, 9701, 9703, 0, 0, 0, 0, - 9705, 9708, 9711, 0, 9714, 0, 9717, 9720, 9723, 9726, 9729, 9732, 9735, - 9738, 9741, 9744, 9747, 9749, 9751, 9753, 9755, 9757, 9759, 9761, 9763, - 9765, 9767, 9769, 9771, 9773, 9775, 9777, 9779, 9781, 9783, 9785, 9787, - 9789, 9791, 9793, 9795, 9797, 9799, 9801, 9803, 9805, 9807, 9809, 9811, - 9813, 9815, 9817, 9819, 9821, 9823, 9825, 9827, 9829, 9831, 9833, 9835, - 9837, 9839, 9841, 9843, 9845, 9847, 9849, 9851, 9853, 9855, 9857, 9859, - 9861, 9863, 9865, 9867, 9869, 9871, 9873, 9875, 9877, 9879, 9881, 9883, - 9885, 9887, 9889, 9891, 9893, 9895, 9897, 9899, 9901, 9903, 9905, 9907, - 9909, 9911, 9913, 9915, 9917, 9919, 9921, 9923, 9925, 9927, 9929, 9931, - 9933, 9935, 9937, 9939, 9941, 9943, 9945, 9947, 9949, 9951, 9953, 9955, - 9957, 9959, 9961, 9963, 9965, 9967, 9969, 9971, 9973, 9975, 9977, 9979, - 9981, 9984, 9987, 9990, 9993, 9996, 9999, 10002, 0, 0, 0, 0, 10005, - 10007, 10009, 10011, 10013, 10015, 10017, 10019, 10021, 10023, 10025, - 10027, 10029, 10031, 10033, 10035, 10037, 10039, 10041, 10043, 10045, - 10047, 10049, 10051, 10053, 10055, 10057, 10059, 10061, 10063, 10065, - 10067, 10069, 10071, 10073, 10075, 10077, 10079, 10081, 10083, 10085, - 10087, 10089, 10091, 10093, 10095, 10097, 10099, 10101, 10103, 10105, - 10107, 10109, 10111, 10113, 10115, 10117, 10119, 10121, 10123, 10125, - 10127, 10129, 10131, 10133, 10135, 10137, 10139, 10141, 10143, 10145, - 10147, 10149, 10151, 10153, 10155, 10157, 10159, 10161, 10163, 10165, - 10167, 10169, 10171, 10173, 10175, 10177, 10179, 10181, 10183, 10185, - 10187, 10189, 10191, 10193, 10195, 10197, 10199, 10201, 10203, 10205, - 10207, 10209, 10211, 10213, 10215, 10217, 10219, 10221, 10223, 10225, - 10227, 10229, 10231, 10233, 10235, 10237, 10239, 10241, 10243, 10245, - 10247, 10249, 10251, 10253, 10255, 10257, 10259, 10261, 10263, 10265, - 10267, 10269, 10271, 10273, 10275, 10277, 10279, 10281, 10283, 10285, - 10287, 10289, 10291, 10293, 10295, 10297, 10299, 10301, 10303, 10305, - 10307, 10309, 10311, 10313, 10315, 10317, 10319, 10321, 10323, 10325, - 10327, 10329, 10331, 10333, 10335, 10337, 10339, 10341, 10343, 10345, - 10347, 10349, 10351, 10353, 10355, 10357, 10359, 10361, 10363, 10365, - 10367, 10369, 10371, 10373, 10375, 10377, 10379, 10381, 10383, 0, 0, 0, - 10385, 10387, 10389, 10391, 10393, 10395, 0, 0, 10397, 10399, 10401, - 10403, 10405, 10407, 0, 0, 10409, 10411, 10413, 10415, 10417, 10419, 0, - 0, 10421, 10423, 10425, 0, 0, 0, 10427, 10429, 10431, 10433, 10435, - 10437, 10439, 0, 10441, 10443, 10445, 10447, 10449, 10451, 10453, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 10455, 0, 10460, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10465, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 10470, 10475, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10480, 10485, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10490, 10495, 0, 10500, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 10505, 10510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 10515, 10520, 10525, 10530, 10535, 10540, 10545, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10550, 10555, 10560, - 10565, 10570, 10575, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10580, - 10582, 10584, 10586, 10588, 10590, 10592, 10594, 10596, 10598, 10600, - 10602, 10604, 10606, 10608, 10610, 10612, 10614, 10616, 10618, 10620, - 10622, 10624, 10626, 10628, 10630, 10632, 10634, 10636, 10638, 10640, - 10642, 10644, 10646, 10648, 10650, 10652, 10654, 10656, 10658, 10660, - 10662, 10664, 10666, 10668, 10670, 10672, 10674, 10676, 10678, 10680, - 10682, 10684, 10686, 10688, 10690, 10692, 10694, 10696, 10698, 10700, - 10702, 10704, 10706, 10708, 10710, 10712, 10714, 10716, 10718, 10720, - 10722, 10724, 10726, 10728, 10730, 10732, 10734, 10736, 10738, 10740, - 10742, 10744, 10746, 10748, 0, 10750, 10752, 10754, 10756, 10758, 10760, - 10762, 10764, 10766, 10768, 10770, 10772, 10774, 10776, 10778, 10780, - 10782, 10784, 10786, 10788, 10790, 10792, 10794, 10796, 10798, 10800, - 10802, 10804, 10806, 10808, 10810, 10812, 10814, 10816, 10818, 10820, - 10822, 10824, 10826, 10828, 10830, 10832, 10834, 10836, 10838, 10840, - 10842, 10844, 10846, 10848, 10850, 10852, 10854, 10856, 10858, 10860, - 10862, 10864, 10866, 10868, 10870, 10872, 10874, 10876, 10878, 10880, - 10882, 10884, 10886, 10888, 10890, 0, 10892, 10894, 0, 0, 10896, 0, 0, - 10898, 10900, 0, 0, 10902, 10904, 10906, 10908, 0, 10910, 10912, 10914, - 10916, 10918, 10920, 10922, 10924, 10926, 10928, 10930, 10932, 0, 10934, - 0, 10936, 10938, 10940, 10942, 10944, 10946, 10948, 0, 10950, 10952, - 10954, 10956, 10958, 10960, 10962, 10964, 10966, 10968, 10970, 10972, - 10974, 10976, 10978, 10980, 10982, 10984, 10986, 10988, 10990, 10992, - 10994, 10996, 10998, 11000, 11002, 11004, 11006, 11008, 11010, 11012, - 11014, 11016, 11018, 11020, 11022, 11024, 11026, 11028, 11030, 11032, - 11034, 11036, 11038, 11040, 11042, 11044, 11046, 11048, 11050, 11052, - 11054, 11056, 11058, 11060, 11062, 11064, 11066, 11068, 11070, 11072, - 11074, 11076, 11078, 0, 11080, 11082, 11084, 11086, 0, 0, 11088, 11090, - 11092, 11094, 11096, 11098, 11100, 11102, 0, 11104, 11106, 11108, 11110, - 11112, 11114, 11116, 0, 11118, 11120, 11122, 11124, 11126, 11128, 11130, - 11132, 11134, 11136, 11138, 11140, 11142, 11144, 11146, 11148, 11150, - 11152, 11154, 11156, 11158, 11160, 11162, 11164, 11166, 11168, 11170, - 11172, 0, 11174, 11176, 11178, 11180, 0, 11182, 11184, 11186, 11188, - 11190, 0, 11192, 0, 0, 0, 11194, 11196, 11198, 11200, 11202, 11204, - 11206, 0, 11208, 11210, 11212, 11214, 11216, 11218, 11220, 11222, 11224, - 11226, 11228, 11230, 11232, 11234, 11236, 11238, 11240, 11242, 11244, - 11246, 11248, 11250, 11252, 11254, 11256, 11258, 11260, 11262, 11264, - 11266, 11268, 11270, 11272, 11274, 11276, 11278, 11280, 11282, 11284, - 11286, 11288, 11290, 11292, 11294, 11296, 11298, 11300, 11302, 11304, - 11306, 11308, 11310, 11312, 11314, 11316, 11318, 11320, 11322, 11324, - 11326, 11328, 11330, 11332, 11334, 11336, 11338, 11340, 11342, 11344, - 11346, 11348, 11350, 11352, 11354, 11356, 11358, 11360, 11362, 11364, - 11366, 11368, 11370, 11372, 11374, 11376, 11378, 11380, 11382, 11384, - 11386, 11388, 11390, 11392, 11394, 11396, 11398, 11400, 11402, 11404, - 11406, 11408, 11410, 11412, 11414, 11416, 11418, 11420, 11422, 11424, - 11426, 11428, 11430, 11432, 11434, 11436, 11438, 11440, 11442, 11444, - 11446, 11448, 11450, 11452, 11454, 11456, 11458, 11460, 11462, 11464, - 11466, 11468, 11470, 11472, 11474, 11476, 11478, 11480, 11482, 11484, - 11486, 11488, 11490, 11492, 11494, 11496, 11498, 11500, 11502, 11504, - 11506, 11508, 11510, 11512, 11514, 11516, 11518, 11520, 11522, 11524, - 11526, 11528, 11530, 11532, 11534, 11536, 11538, 11540, 11542, 11544, - 11546, 11548, 11550, 11552, 11554, 11556, 11558, 11560, 11562, 11564, - 11566, 11568, 11570, 11572, 11574, 11576, 11578, 11580, 11582, 11584, - 11586, 11588, 11590, 11592, 11594, 11596, 11598, 11600, 11602, 11604, - 11606, 11608, 11610, 11612, 11614, 11616, 11618, 11620, 11622, 11624, - 11626, 11628, 11630, 11632, 11634, 11636, 11638, 11640, 11642, 11644, - 11646, 11648, 11650, 11652, 11654, 11656, 11658, 11660, 11662, 11664, - 11666, 11668, 11670, 11672, 11674, 11676, 11678, 11680, 11682, 11684, - 11686, 11688, 11690, 11692, 11694, 11696, 11698, 11700, 11702, 11704, - 11706, 11708, 11710, 11712, 11714, 11716, 11718, 11720, 11722, 11724, - 11726, 11728, 11730, 11732, 11734, 11736, 11738, 11740, 11742, 11744, - 11746, 11748, 11750, 11752, 11754, 11756, 11758, 11760, 11762, 11764, - 11766, 11768, 11770, 11772, 11774, 11776, 11778, 11780, 11782, 11784, - 11786, 11788, 11790, 11792, 11794, 11796, 11798, 11800, 11802, 11804, - 11806, 11808, 11810, 11812, 11814, 11816, 11818, 11820, 11822, 11824, - 11826, 11828, 11830, 11832, 11834, 11836, 11838, 11840, 11842, 11844, - 11846, 11848, 11850, 11852, 11854, 11856, 11858, 11860, 11862, 11864, - 11866, 11868, 11870, 11872, 11874, 11876, 11878, 11880, 11882, 11884, - 11886, 0, 0, 11888, 11890, 11892, 11894, 11896, 11898, 11900, 11902, - 11904, 11906, 11908, 11910, 11912, 11914, 11916, 11918, 11920, 11922, - 11924, 11926, 11928, 11930, 11932, 11934, 11936, 11938, 11940, 11942, - 11944, 11946, 11948, 11950, 11952, 11954, 11956, 11958, 11960, 11962, - 11964, 11966, 11968, 11970, 11972, 11974, 11976, 11978, 11980, 11982, - 11984, 11986, 11988, 11990, 11992, 11994, 11996, 11998, 12000, 12002, - 12004, 12006, 12008, 12010, 12012, 12014, 12016, 12018, 12020, 12022, - 12024, 12026, 12028, 12030, 12032, 12034, 12036, 12038, 12040, 12042, - 12044, 12046, 12048, 12050, 12052, 12054, 12056, 12058, 12060, 12062, - 12064, 12066, 12068, 12070, 12072, 12074, 12076, 12078, 12080, 12082, - 12084, 12086, 12088, 12090, 12092, 12094, 12096, 12098, 12100, 12102, - 12104, 12106, 12108, 12110, 12112, 12114, 12116, 12118, 12120, 12122, - 12124, 12126, 12128, 12130, 12132, 12134, 12136, 12138, 12140, 12142, - 12144, 12146, 12148, 12150, 12152, 12154, 12156, 12158, 12160, 12162, - 12164, 12166, 12168, 12170, 12172, 12174, 12176, 12178, 12180, 12182, - 12184, 12186, 12188, 12190, 12192, 12194, 12196, 12198, 12200, 12202, - 12204, 12206, 12208, 12210, 12212, 12214, 12216, 12218, 12220, 12222, - 12224, 12226, 12228, 12230, 12232, 12234, 12236, 12238, 12240, 12242, - 12244, 12246, 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, - 12264, 12266, 12268, 12270, 12272, 12274, 12276, 12278, 12280, 12282, - 12284, 12286, 12288, 12290, 12292, 12294, 12296, 12298, 12300, 12302, - 12304, 12306, 12308, 12310, 12312, 12314, 12316, 12318, 12320, 12322, - 12324, 12326, 12328, 12330, 12332, 12334, 12336, 12338, 12340, 12342, - 12344, 12346, 12348, 12350, 12352, 12354, 12356, 12358, 12360, 12362, - 12364, 12366, 12368, 12370, 12372, 12374, 12376, 12378, 12380, 12382, - 12384, 12386, 12388, 12390, 12392, 12394, 12396, 12398, 12400, 12402, - 12404, 12406, 12408, 12410, 12412, 12414, 12416, 12418, 12420, 12422, - 12424, 12426, 12428, 12430, 12432, 12434, 12436, 12438, 12440, 12442, - 12444, 12446, 12448, 12450, 12452, 12454, 12456, 12458, 12460, 12462, - 12464, 12466, 12468, 12470, 0, 0, 12472, 12474, 12476, 12478, 12480, - 12482, 12484, 12486, 12488, 12490, 12492, 12494, 12496, 12498, 12500, - 12502, 12504, 12506, 12508, 12510, 12512, 12514, 12516, 12518, 12520, - 12522, 12524, 12526, 12528, 12530, 12532, 12534, 12536, 12538, 12540, - 12542, 12544, 12546, 12548, 12550, 12552, 12554, 12556, 12558, 12560, - 12562, 12564, 12566, 12568, 12570, 12572, 12574, 12576, 12578, 0, 12580, - 12582, 12584, 12586, 12588, 12590, 12592, 12594, 12596, 12598, 12600, - 12602, 12604, 12606, 12608, 12610, 12612, 12614, 12616, 12618, 12620, - 12622, 12624, 12626, 12628, 12630, 12632, 0, 12634, 12636, 0, 12638, 0, - 0, 12640, 0, 12642, 12644, 12646, 12648, 12650, 12652, 12654, 12656, - 12658, 12660, 0, 12662, 12664, 12666, 12668, 0, 12670, 0, 12672, 0, 0, 0, - 0, 0, 0, 12674, 0, 0, 0, 0, 12676, 0, 12678, 0, 12680, 0, 12682, 12684, - 12686, 0, 12688, 12690, 0, 12692, 0, 0, 12694, 0, 12696, 0, 12698, 0, - 12700, 0, 12702, 0, 12704, 12706, 0, 12708, 0, 0, 12710, 12712, 12714, - 12716, 0, 12718, 12720, 12722, 12724, 12726, 12728, 12730, 0, 12732, - 12734, 12736, 12738, 0, 12740, 12742, 12744, 12746, 0, 12748, 0, 12750, - 12752, 12754, 12756, 12758, 12760, 12762, 12764, 12766, 12768, 0, 12770, - 12772, 12774, 12776, 12778, 12780, 12782, 12784, 12786, 12788, 12790, - 12792, 12794, 12796, 12798, 12800, 12802, 0, 0, 0, 0, 0, 12804, 12806, - 12808, 0, 12810, 12812, 12814, 12816, 12818, 0, 12820, 12822, 12824, - 12826, 12828, 12830, 12832, 12834, 12836, 12838, 12840, 12842, 12844, - 12846, 12848, 12850, 12852, 0, 0, 0, 0, 12854, 12857, 12860, 12863, - 12866, 12869, 12872, 12875, 12878, 12881, 12884, 0, 0, 0, 0, 0, 12887, - 12891, 12895, 12899, 12903, 12907, 12911, 12915, 12919, 12923, 12927, - 12931, 12935, 12939, 12943, 12947, 12951, 12955, 12959, 12963, 12967, - 12971, 12975, 12979, 12983, 12987, 12991, 12995, 12997, 12999, 13002, 0, - 13005, 13007, 13009, 13011, 13013, 13015, 13017, 13019, 13021, 13023, - 13025, 13027, 13029, 13031, 13033, 13035, 13037, 13039, 13041, 13043, - 13045, 13047, 13049, 13051, 13053, 13055, 13057, 13060, 13063, 13066, - 13069, 13073, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13076, 13079, 0, 0, 0, 0, - 13082, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13085, 13088, 13091, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13093, 13095, 13097, 13099, 13101, - 13103, 13105, 13107, 13109, 13111, 13113, 13115, 13117, 13119, 13121, - 13123, 13125, 13127, 13129, 13131, 13133, 13135, 13137, 13139, 13141, - 13143, 13145, 13147, 13149, 13151, 13153, 13155, 13157, 13159, 13161, - 13163, 13165, 13167, 13169, 13171, 13173, 13175, 13177, 13179, 0, 0, 0, - 0, 13181, 13185, 13189, 13193, 13197, 13201, 13205, 13209, 13213, 0, 0, - 0, 0, 0, 0, 0, 13217, 13219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 13221, 13223, 13225, 13227, 13230, 13232, 13234, 13236, 13238, 13240, - 13242, 13244, 13246, 13248, 13251, 13253, 13255, 13257, 13259, 13262, - 13264, 13266, 13268, 13271, 13273, 13275, 13277, 13279, 13281, 13284, - 13286, 13288, 13290, 13292, 13294, 13296, 13298, 13300, 13302, 13304, - 13306, 13308, 13310, 13312, 13314, 13316, 13318, 13320, 13322, 13324, - 13326, 13328, 13330, 13333, 13335, 13337, 13339, 13342, 13344, 13346, - 13348, 13350, 13352, 13354, 13356, 13358, 13360, 13362, 13364, 13366, - 13368, 13370, 13372, 13374, 13376, 13378, 13380, 13382, 13384, 13386, - 13388, 13390, 13392, 13394, 13396, 13398, 13400, 13402, 13404, 13406, - 13409, 13411, 13413, 13415, 13417, 13419, 13421, 13424, 13427, 13429, - 13431, 13433, 13435, 13437, 13439, 13441, 13443, 13445, 13447, 13450, - 13452, 13454, 13456, 13458, 13461, 13463, 13465, 13467, 13469, 13471, - 13473, 13475, 13477, 13479, 13482, 13484, 13487, 13489, 13491, 13493, - 13495, 13497, 13499, 13501, 13503, 13505, 13507, 13509, 13512, 13514, - 13516, 13518, 13520, 13522, 13525, 13527, 13530, 13533, 13535, 13537, - 13539, 13541, 13544, 13547, 13549, 13551, 13553, 13555, 13557, 13559, - 13561, 13563, 13565, 13567, 13569, 13572, 13574, 13576, 13578, 13580, - 13582, 13584, 13586, 13588, 13590, 13592, 13594, 13596, 13598, 13600, - 13602, 13604, 13606, 13608, 13610, 13613, 13615, 13617, 13619, 13621, - 13623, 13626, 13628, 13630, 13632, 13634, 13636, 13638, 13640, 13642, - 13644, 13646, 13648, 13651, 13653, 13655, 13657, 13659, 13661, 13663, - 13665, 13667, 13669, 13671, 13673, 13675, 13677, 13679, 13681, 13683, - 13685, 13687, 13690, 13692, 13694, 13696, 13698, 13700, 13703, 13705, - 13707, 13709, 13711, 13713, 13715, 13717, 13719, 13722, 13724, 13726, - 13728, 13731, 13733, 13735, 13737, 13739, 13741, 13743, 13746, 13749, - 13752, 13754, 13757, 13759, 13761, 13763, 13765, 13767, 13769, 13771, - 13773, 13775, 13777, 13780, 13782, 13784, 13786, 13788, 13790, 13792, - 13795, 13797, 13799, 13802, 13805, 13807, 13809, 13811, 13813, 13815, - 13817, 13819, 13821, 13823, 13826, 13828, 13831, 13833, 13836, 13838, - 13840, 13842, 13845, 13847, 13849, 13852, 13855, 13857, 13859, 13861, - 13863, 13865, 13867, 13869, 13871, 13873, 13875, 13877, 13879, 13881, - 13884, 13886, 13889, 13891, 13894, 13896, 13899, 13902, 13905, 13907, - 13909, 13911, 13914, 13917, 13920, 13923, 13925, 13927, 13929, 13931, - 13933, 13935, 13937, 13939, 13942, 13944, 13946, 13948, 13950, 13953, - 13955, 13958, 13961, 13963, 13965, 13967, 13969, 13971, 13973, 13976, - 13979, 13982, 13984, 13986, 13989, 13991, 13993, 13995, 13998, 14000, - 14002, 14004, 14006, 14008, 14011, 14013, 14015, 14017, 14019, 14021, - 14023, 14026, 14029, 14031, 14034, 14036, 14039, 14041, 14043, 14045, - 14048, 14051, 14053, 14056, 14058, 14061, 14063, 14065, 14067, 14069, - 14071, 14073, 14076, 14079, 14082, 14085, 14087, 14089, 14091, 14093, - 14095, 14097, 14099, 14101, 14103, 14105, 14107, 14109, 14112, 14114, - 14116, 14118, 14120, 14122, 14124, 14126, 14128, 14130, 14132, 14134, - 14136, 14139, 14142, 14145, 14147, 14149, 14151, 14153, 14156, 14158, - 14161, 14163, 14165, 14168, 14171, 14173, 14175, 14177, 14179, 14181, - 14183, 14185, 14187, 14189, 14191, 14193, 14195, 14197, 14199, 14201, - 14203, 14205, 14207, 14209, 14212, 14214, 14216, 14218, 14220, 14222, - 14225, 14228, 14230, 14232, 14234, 14236, 14238, 14240, 14243, 14245, - 14247, 14249, 14251, 14254, 14257, 14259, 14261, 14263, 14266, 14268, - 14270, 14273, 14276, 14278, 14280, 14282, 14285, 14287, 14289, 14291, - 14293, 14295, 14297, 14299, 14302, 14304, 14306, 14308, 14311, 14313, - 14315, 14317, 14319, 14322, 14325, 14327, 14329, 14331, 14334, 14336, - 14339, 14341, 14343, 14345, 14348, 14350, 14352, 14354, 14356, 14358, - 14360, 14362, 14365, 14367, 14369, 14371, 14373, 14375, 14377, 14380, - 14382, 14385, 14388, 14391, 14393, 14395, 14397, 14399, 14401, 14403, - 14405, 14407, 0, 0, -}; - -/* NFC pairs */ -#define COMP_SHIFT1 2 -#define COMP_SHIFT2 1 -static const unsigned short comp_index0[] = { - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, - 5, 6, 7, 0, 0, 0, 0, 8, 0, 9, 10, 0, 0, 0, 11, 12, 13, 14, 0, 0, 0, 0, 0, - 15, 16, 17, 0, 0, 0, 0, 18, 19, 20, 21, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, - 23, 24, 25, 26, 0, 0, 0, 0, 27, 28, 29, 30, 0, 0, 0, 0, 31, 32, 33, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 36, 0, 37, 38, 39, 0, 0, 0, 40, 41, 42, - 43, 0, 0, 0, 0, 44, 45, 46, 0, 0, 0, 0, 0, 47, 48, 49, 50, 0, 0, 0, 51, - 52, 53, 54, 0, 0, 0, 0, 55, 56, 0, 0, 0, 0, 0, 0, 57, 58, 59, 60, 0, 0, - 0, 0, 61, 62, 63, 0, 0, 0, 0, 0, 64, 65, 66, 67, 0, 0, 0, 68, 69, 70, 71, - 0, 0, 0, 0, 72, 0, 73, 0, 0, 0, 0, 0, 74, 0, 75, 0, 0, 0, 0, 0, 76, 0, 0, - 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 80, 81, 82, 83, 0, 0, 0, 0, 84, - 85, 86, 0, 0, 0, 0, 0, 87, 88, 0, 89, 0, 0, 0, 90, 91, 0, 92, 0, 0, 0, 0, - 0, 93, 94, 95, 0, 0, 0, 0, 96, 97, 98, 99, 0, 0, 0, 0, 100, 0, 0, 0, 0, - 0, 0, 101, 102, 0, 103, 0, 0, 0, 0, 104, 105, 106, 107, 0, 0, 0, 0, 108, - 109, 110, 111, 0, 0, 0, 0, 112, 113, 0, 0, 0, 0, 0, 114, 115, 116, 117, - 0, 0, 0, 0, 118, 119, 120, 121, 0, 0, 0, 0, 122, 0, 123, 0, 0, 0, 0, 124, - 125, 126, 127, 128, 0, 0, 0, 129, 130, 131, 132, 0, 0, 0, 0, 133, 134, 0, - 0, 0, 0, 0, 0, 135, 136, 137, 138, 0, 0, 0, 139, 140, 141, 142, 0, 0, 0, - 0, 0, 143, 144, 145, 0, 0, 0, 0, 146, 147, 148, 149, 0, 0, 0, 0, 150, 0, - 151, 0, 0, 0, 0, 152, 153, 154, 0, 0, 0, 0, 0, 0, 155, 0, 0, 0, 0, 0, 0, - 156, 157, 158, 0, 0, 0, 0, 0, 159, 160, 161, 162, 0, 0, 0, 163, 0, 0, 0, - 164, 0, 0, 0, 165, 166, 0, 0, 0, 0, 0, 0, 167, 0, 0, 0, 0, 0, 0, 0, 168, - 0, 0, 0, 0, 0, 0, 169, 170, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, - 172, 173, 0, 0, 0, 0, 0, 0, 174, 0, 0, 0, 0, 0, 0, 175, 176, 0, 0, 0, 0, - 0, 0, 177, 178, 0, 0, 0, 0, 0, 0, 179, 0, 0, 0, 0, 0, 0, 0, 180, 0, 0, 0, - 0, 0, 0, 181, 182, 183, 0, 0, 0, 0, 0, 184, 185, 0, 0, 0, 0, 0, 0, 186, - 0, 0, 0, 0, 0, 0, 0, 187, 0, 0, 0, 0, 0, 0, 188, 189, 0, 0, 0, 0, 0, 0, - 190, 0, 0, 0, 0, 0, 0, 0, 191, 192, 0, 0, 0, 0, 0, 0, 193, 0, 0, 0, 0, 0, - 0, 194, 195, 0, 0, 0, 0, 0, 0, 196, 197, 0, 0, 0, 0, 0, 0, 198, 0, 0, 0, - 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, 0, 200, 201, 202, 0, 0, 0, 0, 0, 203, - 204, 0, 0, 0, 0, 0, 0, 205, 206, 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, - 208, 0, 0, 0, 0, 0, 0, 0, 209, 0, 0, 0, 0, 0, 0, 0, 210, 0, 0, 0, 0, 0, - 0, 0, 211, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 0, 0, 0, 0, 213, 0, 0, 0, - 0, 0, 0, 0, 214, 0, 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, 0, 0, 216, 0, 0, 0, - 0, 0, 0, 0, 0, 217, 0, 0, 0, 0, 0, 0, 0, 218, 0, 0, 0, 0, 0, 0, 219, 0, - 0, 0, 0, 0, 0, 220, 221, 222, 0, 0, 0, 0, 0, 223, 224, 225, 0, 0, 0, 0, - 0, 226, 227, 228, 0, 0, 0, 0, 0, 229, 230, 231, 0, 0, 0, 0, 0, 0, 232, 0, - 0, 0, 0, 0, 0, 233, 0, 0, 0, 0, 0, 0, 234, 0, 0, 0, 0, 0, 0, 0, 235, 0, - 0, 0, 0, 0, 0, 0, 236, 0, 0, 0, 0, 0, 0, 0, 237, 0, 0, 0, 0, 0, 0, 238, - 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, - 241, 0, 0, 0, 0, 0, 0, 242, 0, 243, 244, 0, 0, 0, 0, 245, 246, 0, 0, 0, - 0, 0, 247, 0, 248, 0, 249, 0, 0, 0, 250, 251, 252, 0, 0, 0, 0, 0, 253, 0, - 254, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 256, 257, 258, 0, 0, 0, 0, 0, - 259, 0, 260, 0, 261, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 0, 263, 0, - 0, 0, 264, 265, 266, 0, 267, 0, 0, 0, 268, 0, 269, 0, 0, 0, 0, 0, 270, 0, - 271, 272, 0, 0, 0, 0, 273, 274, 0, 275, 0, 0, 0, 276, 0, 277, 0, 0, 0, 0, - 0, 0, 0, 278, 0, 0, 0, 0, 0, 279, 280, 281, 282, 0, 0, 0, 0, 283, 284, 0, - 285, 0, 0, 0, 286, 0, 0, 0, 287, 0, 0, 0, 288, 0, 0, 0, 289, 0, 0, 0, 0, - 0, 0, 290, 0, 0, 0, 0, 291, 0, 0, 0, 0, 0, 0, 0, 292, 0, 0, 0, 0, 0, 0, - 0, 293, 0, 0, 0, 0, 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, 295, 0, 0, 0, 0, 0, - 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 297, 0, 0, 0, 0, 0, 0, 298, 299, 0, 0, 0, - 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 0, 301, 0, 0, 0, 0, 0, 0, 0, 302, 0, 0, - 0, 0, 0, 0, 0, 303, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 305, 0, - 0, 0, 0, 0, 0, 0, 306, 0, 0, 0, 0, 0, 0, 307, 0, 0, 0, 0, 0, 0, 0, 308, - 0, 0, 0, 0, 0, 0, 0, 309, 0, 0, 0, 0, 0, 0, 0, 310, 0, 0, 0, 0, 0, 0, - 311, 312, 0, 0, 0, 0, 0, 0, 313, 0, 0, 0, 0, 0, 0, 0, 314, 0, 0, 0, 0, 0, - 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, 316, 0, 0, 0, 0, 0, 0, 317, 0, 0, 0, 0, - 0, 0, 0, 318, 0, 0, 0, 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, 0, 0, 320, 0, 0, - 0, 0, 0, 0, 0, 321, 0, 0, 0, 0, 0, 0, 322, 0, 0, 0, 0, 0, 0, 0, 323, 0, - 0, 0, 0, 0, 0, 0, 324, 0, 0, 0, 0, 0, 0, 325, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 326, 0, 0, 0, 0, 0, 0, 0, 327, 0, 0, 0, 0, 0, 0, 0, 328, 0, 0, 0, 0, - 0, 0, 329, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 0, 0, 0, 0, 0, 331, 0, 0, 0, - 0, 0, 0, 0, 332, 0, 0, 0, 0, 0, 0, 0, 333, 0, 0, 0, 0, 0, 0, 334, 0, 0, - 0, 0, 0, 0, 0, 335, 0, 0, 0, 0, 0, 0, 0, 336, 337, 0, 0, 0, 0, 0, 0, 0, - 338, 0, 0, 0, 0, 0, 0, 339, 0, 0, 0, 0, 0, 0, 0, 340, 0, 0, 0, 0, 0, 0, - 0, 341, 0, 0, 0, 0, 0, 0, 0, 342, 0, 0, 0, 0, 0, 0, 0, 343, 0, 0, 0, 0, - 0, 0, 344, 0, 0, 0, 0, 0, 0, 0, 345, 346, 0, 0, 0, 0, 0, 0, 347, 0, 0, 0, - 0, 0, 0, 0, 348, 0, 0, 0, 0, 0, 0, 0, 349, 0, 0, 0, 0, 0, 0, 0, 350, 0, - 0, 0, 0, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 353, - 0, 0, 0, 0, 0, 0, 0, 354, 0, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 0, 0, 0, 0, - 356, 0, 0, 0, 0, 0, 0, 357, 0, 0, 0, 0, 0, 0, 0, 358, 0, 0, 0, 0, 0, 0, - 0, 359, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 361, 0, 362, 0, 0, 0, - 0, 0, 0, 0, 363, 0, 0, 0, 0, 0, 0, 0, 364, 0, 0, 0, 0, 0, 0, 0, 365, 0, - 0, 0, 0, 0, 0, 0, 366, 0, 0, 0, 0, 0, 0, 367, 0, 0, 0, 0, 0, 0, 0, 368, - 0, 0, 0, 0, 0, 0, 369, 370, 0, 0, 0, 0, 0, 0, 371, 0, 0, 0, 0, 0, 0, 0, - 372, 0, 0, 0, 0, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 0, 0, 0, 0, - 0, 375, 0, 0, 376, 0, 0, 0, 0, 377, 0, 0, 378, 0, 0, 0, 0, 0, 0, 0, 379, - 0, 0, 0, 0, 0, 0, 0, 380, 0, 0, 0, 0, 0, 0, 381, 0, 0, 0, 0, 0, 0, 0, - 382, 0, 0, 0, 0, 0, 0, 0, 383, 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, 0, 385, 0, - 0, 386, 0, 0, 0, 0, 387, 0, 0, 388, 0, 0, 0, 0, 0, 0, 0, 389, 0, 0, 0, 0, - 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, 391, 0, 0, 0, 0, 0, 0, 0, 392, 0, 0, 0, - 0, 0, 0, 0, 393, 0, 0, 0, 0, 0, 0, 0, 394, 0, 0, 0, 395, 0, 0, 0, 0, 0, - 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 0, 0, 0, 0, 398, 0, 0, 0, 0, - 0, 0, 0, 399, 0, 0, 400, 0, 0, 0, 0, 401, 0, 0, 402, 0, 0, 0, 0, 0, 0, 0, - 403, 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 405, 0, 0, 0, 0, 0, 0, - 0, 406, 0, 0, 0, 0, 0, 0, 0, 407, 0, 0, 0, 0, 0, 0, 0, 408, 0, 0, 0, 409, - 0, 0, 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 0, 0, 0, 0, 413, 0, 0, 0, - 0, 0, 0, 0, 414, 0, 0, 0, 0, 0, 0, 415, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, - 0, 0, 0, 0, 0, 417, 0, 0, 0, 0, 0, 0, 0, 418, 0, 0, 0, 419, 0, 0, 420, 0, - 0, 0, 0, 421, 0, 0, 422, 0, 0, 0, 423, 0, 0, 0, 424, 0, 0, 0, 425, 0, 0, - 0, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, 0, 0, 428, 0, 0, 0, 0, 0, 0, 429, 0, - 0, 0, 0, 0, 0, 0, 430, 0, 0, 0, 0, 0, 0, 0, 431, 0, 0, 432, 0, 0, 0, 0, - 433, 0, 0, 434, 0, 0, 0, 435, 0, 0, 0, 436, 0, 0, 0, 437, 0, 0, 0, 438, - 0, 0, 0, 439, 0, 0, 440, 0, 0, 0, 0, 0, 0, 0, 441, 0, 0, 0, 0, 0, 0, 0, - 442, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 444, 0, 0, 0, 0, 0, 0, - 0, 445, 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, 0, 447, 0, 0, 0, 448, 0, 0, 0, - 449, 0, 0, 450, 0, 0, 0, 0, 0, 0, 0, 451, 0, 0, 0, 0, 0, 0, 0, 452, 0, 0, - 0, 0, 0, 0, 0, 453, 0, 0, 0, 0, 0, 0, 454, 0, 0, 0, 0, 0, 0, 0, 455, 0, - 0, 0, 0, 0, 0, 0, 456, 0, 0, 0, 0, 0, 0, 0, 457, 0, 0, 0, 0, 0, 0, 458, - 0, 0, 0, 0, 0, 0, 0, 459, 0, 0, 0, 0, 0, 0, 0, 460, 0, 0, 0, 461, 0, 0, - 0, 462, 0, 0, 0, 0, 0, 0, 463, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 465, 0, - 0, 0, 466, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 0, 468, 0, 0, 0, 0, - 0, 0, 0, 469, 0, 0, 0, 0, 0, 0, 0, 470, 0, 0, 0, 0, 0, 0, 471, 0, 0, 0, - 0, 0, 0, 0, 472, 0, 0, 0, 0, 0, 0, 0, 473, 0, 0, 0, 0, 0, 0, 0, 474, 0, - 0, 0, 0, 0, 0, 475, 0, 0, 0, 0, 0, 0, 0, 476, 0, 0, 0, 0, 0, 0, 0, 477, - 0, 0, 0, 0, 0, 0, 0, 478, 0, 0, 0, 0, 0, 0, 479, 0, 0, 0, 0, 0, 0, 0, - 480, 0, 0, 0, 0, 0, 0, 0, 481, 0, 0, 0, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, - 0, 483, 0, 0, 0, 0, 0, 0, 0, 484, 0, 0, 0, 0, 0, 0, 0, 485, 0, 0, 0, 0, - 0, 0, 0, 486, 0, 0, 0, 0, 0, 0, 487, 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, 0, - 0, 0, 0, 0, 489, 0, 0, 0, 0, 0, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, - 0, 0, 0, 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, 493, 0, 0, 0, 0, 0, 0, 0, 494, - 0, 0, 0, 0, 0, 0, 495, 0, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, - 497, 0, 0, 0, 0, 0, 0, 0, 498, 0, 0, 0, 0, 0, 0, 499, 0, 0, 0, 0, 0, 0, - 0, 500, 0, 0, 0, 0, 0, 0, 0, 501, 0, 0, 0, 0, 0, 0, 0, 502, 0, 0, 0, 0, - 0, 0, 503, 0, 0, 0, 0, 0, 0, 0, 504, 0, 0, 0, 0, 0, 0, 0, 505, 0, 0, 0, - 0, 0, 0, 0, 506, 0, 0, 0, 0, 0, 0, 507, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 508, 0, 0, 0, 0, 0, 0, 0, 509, 0, 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, - 0, 0, 511, 0, 0, 0, 0, 0, 0, 512, 0, 0, 0, 0, 0, 0, 0, 513, 0, 0, 0, 0, - 0, 0, 0, 514, 0, 0, 0, 0, 0, 0, 0, 515, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, - 0, 0, 0, 0, 517, 0, 0, 0, 0, 0, 0, 0, 518, 0, 0, 0, 0, 0, 0, 0, 519, 0, - 0, 0, 0, 0, 0, 520, 0, 0, 0, 0, 0, 0, 0, 521, 0, 0, 0, 0, 0, 0, 0, 522, - 0, 0, 0, 0, 0, 0, 0, 523, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, - 525, 0, 0, 0, 0, 0, 0, 0, 526, 0, 0, 0, 0, 0, 0, 0, 527, 0, 0, 0, 0, 0, - 0, 528, 0, 0, 0, 0, 0, 0, 0, 529, 0, 0, 0, 0, 0, 0, 0, 530, 0, 0, 0, 0, - 0, 0, 0, 531, 0, 0, 0, 0, 0, 0, 532, 0, 0, 0, 0, 0, 0, 0, 533, 0, 0, 0, - 0, 0, 0, 0, 534, 0, 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, 0, 0, 0, 536, 0, 0, - 0, 0, 0, 0, 0, 537, 0, 0, 0, 0, 0, 0, 0, 538, 0, 0, 0, 0, 0, 0, 0, 539, - 0, 0, 0, 0, 0, 0, 540, 0, 0, 0, 0, 0, 0, 0, 541, 0, 0, 0, 0, 0, 0, 0, - 542, 0, 0, 0, 0, 0, 0, 0, 543, 0, 0, 0, 0, 0, 0, 544, 0, 0, 0, 0, 0, 0, - 0, 545, 0, 0, 0, 0, 0, 0, 0, 546, 0, 0, 0, 0, 0, 0, 0, 547, 0, 0, 0, 0, - 0, 0, 548, 0, 0, 0, 0, 0, 0, 0, 549, 0, 0, 0, 0, 0, 0, 0, 550, 0, 0, 0, - 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 0, 552, 0, 0, 0, 0, 0, 0, 0, 553, 0, 0, - 0, 0, 0, 0, 0, 554, 0, 0, 0, 0, 0, 0, 0, 555, 0, 0, 0, 0, 0, 0, 0, 556, - 0, 0, 0, 0, 0, 0, 557, 0, 0, 0, 0, 0, 0, 0, 558, 0, 0, 0, 0, 0, 0, 0, - 559, 0, 0, 0, 0, 0, 0, 0, 560, 0, 0, 0, 0, 0, 0, 0, 561, 0, 0, 0, 0, 0, - 0, 0, 562, 0, 0, 0, 0, 0, 0, 0, 563, 0, 0, 0, 0, 0, 0, 564, -}; - -static const unsigned short comp_index1[] = { - 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 0, 4, 5, 6, 7, 8, 9, 10, - 0, 11, 12, 0, 13, 0, 0, 0, 0, 0, 0, 14, 15, 0, 0, 0, 0, 16, 0, 0, 0, 0, - 0, 17, 18, 0, 19, 0, 20, 0, 0, 0, 0, 21, 0, 0, 0, 22, 0, 23, 0, 0, 24, 0, - 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 33, 34, 0, 35, 0, 36, 37, 38, 0, 0, - 0, 0, 0, 39, 0, 0, 0, 40, 41, 42, 43, 0, 44, 0, 0, 0, 0, 45, 0, 0, 0, 0, - 0, 46, 0, 47, 0, 48, 0, 0, 49, 0, 50, 0, 51, 0, 0, 52, 53, 54, 55, 56, - 57, 58, 0, 59, 0, 0, 60, 61, 0, 0, 0, 62, 0, 0, 0, 0, 0, 63, 64, 0, 0, - 65, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, 0, 70, 0, 71, - 72, 0, 73, 0, 74, 0, 0, 75, 0, 0, 0, 0, 76, 0, 0, 77, 78, 0, 79, 0, 80, - 0, 0, 81, 0, 82, 83, 0, 84, 0, 0, 0, 0, 0, 85, 86, 87, 88, 89, 90, 91, 0, - 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 95, 0, 0, 0, 96, 0, 0, 97, 0, 98, 99, 0, - 100, 0, 101, 0, 0, 102, 0, 103, 104, 0, 105, 0, 106, 0, 0, 107, 0, 108, - 0, 0, 0, 109, 0, 110, 0, 0, 111, 0, 112, 113, 0, 114, 0, 0, 0, 0, 0, 115, - 116, 117, 118, 119, 120, 121, 0, 122, 123, 0, 124, 125, 0, 0, 0, 126, 0, - 0, 127, 0, 0, 128, 129, 0, 130, 131, 0, 0, 0, 0, 0, 132, 0, 0, 0, 133, - 134, 135, 136, 137, 0, 0, 0, 138, 0, 0, 139, 140, 0, 141, 0, 142, 0, 0, - 143, 0, 0, 0, 0, 144, 0, 145, 146, 147, 148, 149, 150, 151, 0, 152, 153, - 0, 154, 0, 0, 155, 0, 0, 0, 0, 156, 157, 0, 0, 0, 0, 0, 158, 159, 0, 160, - 0, 161, 162, 0, 0, 0, 163, 0, 164, 0, 0, 165, 0, 166, 167, 0, 168, 0, - 169, 170, 171, 172, 173, 174, 175, 0, 176, 0, 177, 178, 179, 0, 0, 0, 0, - 0, 180, 0, 0, 0, 181, 182, 183, 184, 0, 185, 186, 0, 0, 0, 0, 0, 187, 0, - 188, 0, 189, 0, 0, 190, 0, 191, 0, 192, 193, 0, 194, 195, 196, 197, 198, - 199, 200, 0, 201, 0, 0, 202, 203, 0, 0, 0, 204, 0, 0, 0, 205, 0, 0, 0, 0, - 0, 206, 0, 0, 0, 0, 207, 0, 0, 208, 0, 209, 0, 0, 210, 0, 211, 0, 0, 0, - 0, 212, 0, 0, 213, 0, 214, 215, 0, 216, 0, 217, 0, 0, 218, 219, 0, 0, 0, - 0, 0, 0, 220, 221, 0, 222, 0, 223, 0, 0, 224, 0, 225, 226, 0, 227, 0, 0, - 0, 0, 0, 228, 229, 230, 231, 232, 233, 234, 0, 235, 0, 0, 236, 0, 0, 0, - 237, 0, 0, 238, 0, 0, 0, 239, 0, 0, 240, 0, 241, 242, 0, 243, 0, 244, 0, - 0, 245, 0, 0, 0, 0, 0, 246, 247, 0, 248, 0, 249, 0, 0, 250, 0, 251, 0, 0, - 0, 252, 0, 253, 0, 0, 254, 0, 255, 256, 0, 257, 0, 258, 259, 260, 261, - 262, 263, 264, 0, 265, 266, 0, 267, 268, 0, 0, 0, 269, 0, 0, 270, 0, 0, - 0, 0, 0, 0, 271, 272, 0, 273, 274, 0, 0, 0, 275, 0, 276, 0, 0, 0, 277, - 278, 279, 280, 281, 0, 0, 0, 282, 0, 0, 283, 284, 0, 285, 0, 286, 0, 0, - 287, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 289, 0, 290, 0, 0, 0, 0, 291, 292, - 0, 0, 293, 0, 0, 0, 0, 294, 295, 0, 0, 0, 0, 0, 0, 296, 0, 297, 0, 0, 0, - 0, 298, 0, 0, 299, 300, 0, 0, 301, 0, 0, 302, 0, 0, 0, 0, 0, 0, 303, 304, - 0, 0, 305, 0, 0, 306, 0, 307, 308, 0, 0, 0, 0, 0, 309, 310, 0, 0, 0, 0, - 0, 0, 311, 0, 312, 0, 0, 313, 0, 0, 0, 0, 0, 314, 315, 0, 0, 316, 0, 0, - 0, 0, 317, 318, 0, 0, 0, 0, 0, 0, 319, 0, 320, 0, 0, 0, 0, 321, 0, 0, - 322, 323, 0, 0, 324, 0, 0, 325, 0, 0, 0, 0, 0, 0, 326, 327, 0, 0, 328, 0, - 0, 329, 0, 330, 331, 0, 0, 0, 0, 0, 332, 333, 0, 0, 0, 0, 0, 0, 334, 0, - 335, 0, 0, 336, 0, 0, 0, 0, 0, 337, 338, 0, 0, 339, 0, 0, 340, 341, 0, 0, - 342, 0, 0, 343, 0, 0, 0, 0, 0, 0, 344, 0, 0, 345, 0, 0, 346, 0, 0, 0, 0, - 0, 347, 0, 0, 348, 0, 0, 349, 0, 0, 350, 0, 0, 0, 351, 0, 0, 0, 0, 0, 0, - 352, 0, 353, 0, 0, 354, 0, 0, 0, 0, 0, 0, 355, 0, 0, 0, 356, 357, 0, 0, - 358, 0, 0, 0, 359, 0, 0, 360, 361, 0, 0, 362, 0, 0, 0, 363, 0, 0, 364, - 365, 0, 0, 366, 0, 0, 0, 367, 0, 0, 368, 369, 0, 0, 370, 0, 0, 0, 371, 0, - 0, 0, 372, 0, 0, 0, 373, 0, 0, 0, 0, 0, 0, 374, 0, 0, 375, 0, 0, 376, 0, - 0, 377, 0, 0, 0, 0, 0, 0, 378, 0, 0, 379, 0, 0, 380, 0, 0, 0, 0, 0, 381, - 0, 382, 0, 383, 384, 0, 0, 0, 0, 0, 0, 385, 386, 0, 0, 0, 0, 0, 0, 387, - 0, 0, 0, 388, 0, 0, 389, 0, 0, 390, 0, 0, 0, 0, 391, 0, 392, 393, 0, 0, - 0, 394, 0, 0, 0, 395, 0, 0, 396, 0, 0, 0, 0, 0, 0, 397, 0, 0, 0, 398, 0, - 399, 400, 0, 0, 0, 401, 0, 0, 0, 402, 0, 0, 403, 0, 0, 404, 0, 0, 0, 0, - 0, 0, 405, 0, 0, 406, 0, 0, 0, 0, 407, 0, 408, 0, 0, 0, 0, 409, 0, 0, - 410, 0, 0, 0, 0, 411, 0, 0, 412, 0, 0, 0, 413, 0, 0, 414, 0, 0, 0, 0, 0, - 0, 415, 416, 0, 417, 418, 0, 0, 0, 419, 0, 0, 420, 0, 0, 0, 0, 421, 0, 0, - 422, 0, 0, 423, 0, 0, 0, 424, 0, 425, 426, 0, 0, 0, 427, 0, 0, 0, 0, 0, - 0, 428, 429, 0, 0, 0, 0, 0, 0, 430, 0, 0, 431, 0, 0, 0, 0, 432, 0, 433, - 0, 0, 0, 0, 434, 0, 435, 0, 0, 0, 0, 0, 0, 436, 437, 0, 0, 438, 0, 0, - 439, 0, 440, 441, 0, 0, 0, 442, 0, 0, 443, 0, 444, 445, 0, 446, 447, 0, - 0, 448, 0, 0, 0, 449, 0, 450, 451, 0, 0, 0, 452, 0, 0, 0, 0, 0, 453, 0, - 454, 455, 0, 456, 457, 0, 0, 0, 0, 0, 0, 458, 0, 0, 459, 0, 460, 461, 0, - 0, 0, 462, 0, 0, 463, 0, 464, 465, 0, 466, 467, 0, 0, 468, 0, 0, 0, 469, - 0, 470, 471, 0, 0, 0, 472, 0, 0, 0, 0, 0, 473, 0, 474, 475, 0, 476, 477, - 0, 0, 0, 0, 0, 0, 478, 0, 0, 479, 0, 0, 480, 0, 0, 0, 0, 0, 481, 0, 0, - 482, 0, 0, 0, 483, 0, 0, 484, 0, 0, 485, 0, 0, 0, 0, 0, 0, 486, 0, 0, - 487, 488, 0, 489, 0, 0, 490, 0, 0, 0, 0, 0, 0, 491, 0, 0, 492, 0, 0, 493, - 0, 0, 0, 494, 0, 0, 495, 0, 0, 0, 0, 0, 0, 496, 0, 0, 0, 497, 0, 0, 0, - 498, 499, 0, 0, 0, 500, 0, 0, 0, 0, 0, 501, 502, 0, 503, 0, 0, 0, 504, 0, - 0, 0, 505, 0, 0, 506, 507, 0, 0, 0, 0, 0, 508, 0, 0, 0, 509, 510, 0, 0, - 0, 0, 0, 511, 0, 0, 0, 512, 513, 0, 514, 0, 0, 0, 0, 515, 0, 0, 516, 0, - 0, 517, 0, 0, 0, 0, 0, 0, 518, 0, 0, 519, 0, 0, 520, 0, 0, 521, 0, 0, 0, - 0, 0, 0, 522, 0, 0, 523, 0, 0, 524, 0, 0, 525, 0, 0, 0, 0, 0, 0, 526, 0, - 0, 0, 527, 0, 0, 528, 0, 0, 529, 0, 0, 530, 0, 0, 0, 531, 0, 0, 0, 0, 0, - 0, 532, 533, 534, 0, 0, 0, 0, 0, 535, 536, 0, 0, 0, 0, 0, 537, 0, 0, 538, - 0, 0, 539, 0, 0, 0, 0, 0, 0, 540, 0, 541, 0, 0, 0, 0, 0, 542, 543, 0, 0, - 0, 0, 0, 544, 0, 0, 545, 0, 0, 546, 0, 0, 0, 0, 0, 0, 547, 0, 0, 548, 0, - 0, 549, 0, 0, 550, 0, 0, 0, 0, 551, 0, 0, 0, 0, 0, 552, 553, 0, 0, 0, 0, - 0, 554, 0, 0, 555, 0, 0, 556, 0, 0, 0, 0, 0, 0, 557, 0, 0, 558, 0, 0, - 559, 0, 0, 560, 0, 0, 0, 0, 561, 0, 0, 562, 0, 0, 0, 0, 0, 0, 563, 0, 0, - 564, 0, 0, 565, 0, 0, 0, 0, 0, 566, 567, 0, 0, 0, 0, 0, 568, 0, 0, 569, - 0, 0, 570, 0, 0, 0, 0, 0, 0, 571, 0, 0, 572, 0, 0, 573, 0, 0, 574, 0, 0, - 0, 0, 575, 0, 0, 0, 0, 0, 576, 577, 0, 0, 0, 0, 0, 578, 0, 0, 579, 0, 0, - 580, 0, 0, 0, 0, 0, 0, 581, 0, 0, 582, 0, 0, 583, 0, 0, 584, 0, 0, 0, 0, - 585, 0, 0, 0, 0, 0, 586, 587, 0, 0, 0, 0, 0, 588, 0, 0, 0, 0, 589, 0, - 590, 0, 0, 0, 0, 591, 0, 592, 0, 0, 0, 0, 593, 0, 0, 594, 0, 0, 0, 0, 0, - 0, 595, 0, 0, 596, 0, 0, 597, 0, 0, 0, 0, 0, 598, 599, 0, 0, 0, 0, 0, - 600, 0, 0, 0, 0, 601, 0, 602, 0, 0, 0, 0, 603, 0, 604, 0, 0, 0, 0, 605, - 0, 0, 0, 0, 0, 606, 0, 0, 607, 0, 0, 608, 0, 0, 609, 0, 0, 0, 0, 0, 0, - 610, 0, 0, 611, 0, 0, 612, 0, 0, 0, 0, 613, 0, 614, 0, 0, 0, 0, 615, 0, - 0, 0, 0, 0, 616, 0, 0, 617, 0, 0, 618, 0, 0, 619, 0, 0, 0, 0, 0, 0, 620, - 0, 0, 621, 0, 0, 622, 0, 0, 623, 0, 0, 0, 0, 0, 0, 624, 0, 0, 625, 0, 0, - 626, 0, 0, 0, 0, 627, 0, 628, 0, 0, 0, 0, 0, 0, 629, 0, 0, 630, 0, 0, 0, - 0, 631, 0, 632, 0, 0, 0, 0, 0, 633, 0, 0, 634, 0, 0, 635, 0, 0, 636, 0, - 0, 0, 0, 0, 0, 637, 0, 0, 638, 0, 0, 639, 0, 0, 640, 0, 0, 0, 0, 0, 0, - 641, 0, 0, 642, 0, 0, 643, 0, 0, 644, 0, 0, 0, 0, 0, 0, 645, 0, 0, 646, - 0, 0, 647, 0, 0, 648, 0, 0, 0, 0, 0, 0, 649, 0, 0, 650, 0, 0, 651, 0, 0, - 652, 0, 0, 0, 0, 0, 0, 653, 0, 0, 654, 0, 0, 655, 0, 0, 656, 0, 0, 0, 0, - 0, 0, 657, 0, 0, 658, 0, 0, 659, 0, 0, 660, 0, 0, 0, 0, 0, 0, 661, 0, 0, - 662, 0, 0, 663, 0, 0, 664, 0, 0, 0, 0, 0, 0, 665, 0, 0, 666, 0, 0, 667, - 0, 0, 668, 0, 0, 0, 0, 0, 0, 669, 0, 0, 670, 0, 0, 671, 0, 0, 672, 0, 0, - 0, 0, 0, 0, 673, 0, 0, 0, 674, 0, 0, 675, 0, 0, 676, 0, 0, 677, 0, 0, 0, - 0, 0, 0, 678, 0, 0, 679, 0, 0, 680, 0, 0, 681, 0, 0, 0, 0, 0, 0, 682, 0, - 0, 683, 0, 0, 684, 0, 0, 685, 0, 0, 0, 0, 0, 0, 686, 0, 0, 687, 0, 0, - 688, 0, 0, 689, 0, 0, 0, 0, 0, 0, 690, 0, 0, 691, 0, 0, 692, 0, 0, 693, - 0, 0, 0, 0, 0, 0, 694, 0, 0, 695, 0, 0, 696, 0, 0, 697, 0, 0, 0, 0, 0, 0, - 698, 0, 0, 699, 0, 0, 700, 0, 0, 701, 0, 0, 0, 0, 0, 0, 702, 0, 0, 703, - 0, 0, 704, 0, 0, 705, 0, 0, 0, 0, 0, 0, 706, 0, 0, 707, 0, 0, 708, 0, 0, - 709, 0, 0, 0, 0, 0, 0, 710, 0, 0, 711, 0, 0, 712, 0, 0, 713, 0, 0, 0, 0, - 0, 0, 714, 0, 0, 715, 0, 0, 716, 0, 0, 717, 0, 0, 0, 0, 0, 0, 718, 0, 0, - 719, 0, 0, 720, 0, 0, 721, 0, 0, 0, 722, 0, 0, 0, 0, 0, 0, 723, 0, 0, - 724, 0, 0, 725, 0, 0, 726, 0, 0, 0, 727, 0, 0, 0, 728, 729, 0, 0, 730, 0, - 0, 0, 0, 0, 0, 731, -}; - -static const unsigned int comp_data[] = { - 0, 0, 0, 8814, 0, 8800, 0, 8815, 192, 193, 194, 195, 256, 258, 550, 196, - 7842, 197, 0, 461, 512, 514, 0, 7840, 0, 7680, 260, 0, 7682, 0, 0, 7684, - 7686, 0, 0, 262, 264, 0, 266, 0, 0, 268, 0, 199, 7690, 0, 0, 270, 0, - 7692, 0, 7696, 0, 7698, 7694, 0, 200, 201, 202, 7868, 274, 276, 278, 203, - 7866, 0, 0, 282, 516, 518, 0, 7864, 0, 552, 280, 7704, 0, 7706, 7710, 0, - 0, 500, 284, 0, 7712, 286, 288, 0, 0, 486, 0, 290, 292, 0, 7714, 7718, 0, - 542, 0, 7716, 0, 7720, 7722, 0, 204, 205, 206, 296, 298, 300, 304, 207, - 7880, 0, 0, 463, 520, 522, 0, 7882, 302, 0, 0, 7724, 308, 0, 0, 7728, 0, - 488, 0, 7730, 0, 310, 7732, 0, 0, 313, 0, 317, 0, 7734, 0, 315, 0, 7740, - 7738, 0, 0, 7742, 7744, 0, 0, 7746, 504, 323, 0, 209, 7748, 0, 0, 327, 0, - 7750, 0, 325, 0, 7754, 7752, 0, 210, 211, 212, 213, 332, 334, 558, 214, - 7886, 0, 336, 465, 524, 526, 416, 7884, 490, 0, 0, 7764, 7766, 0, 0, 340, - 7768, 0, 0, 344, 528, 530, 0, 7770, 0, 342, 7774, 0, 0, 346, 348, 0, - 7776, 0, 0, 352, 0, 7778, 536, 350, 7786, 0, 0, 356, 0, 7788, 538, 354, - 0, 7792, 7790, 0, 217, 218, 219, 360, 362, 364, 0, 220, 7910, 366, 368, - 467, 532, 534, 431, 7908, 7794, 0, 370, 7798, 0, 7796, 0, 7804, 0, 7806, - 7808, 7810, 372, 0, 7814, 7812, 0, 7816, 7818, 7820, 7922, 221, 374, - 7928, 562, 0, 7822, 376, 7926, 0, 0, 7924, 0, 377, 7824, 0, 379, 0, 0, - 381, 0, 7826, 7828, 0, 224, 225, 226, 227, 257, 259, 551, 228, 7843, 229, - 0, 462, 513, 515, 0, 7841, 0, 7681, 261, 0, 7683, 0, 0, 7685, 7687, 0, 0, - 263, 265, 0, 267, 0, 0, 269, 0, 231, 7691, 0, 0, 271, 0, 7693, 0, 7697, - 0, 7699, 7695, 0, 232, 233, 234, 7869, 275, 277, 279, 235, 7867, 0, 0, - 283, 517, 519, 0, 7865, 0, 553, 281, 7705, 0, 7707, 7711, 0, 0, 501, 285, - 0, 7713, 287, 289, 0, 0, 487, 0, 291, 293, 0, 7715, 7719, 0, 543, 0, - 7717, 0, 7721, 7723, 0, 7830, 0, 236, 237, 238, 297, 299, 301, 0, 239, - 7881, 0, 0, 464, 521, 523, 0, 7883, 303, 0, 0, 7725, 309, 0, 0, 496, 0, - 7729, 0, 489, 0, 7731, 0, 311, 7733, 0, 0, 314, 0, 318, 0, 7735, 0, 316, - 0, 7741, 7739, 0, 0, 7743, 7745, 0, 0, 7747, 505, 324, 0, 241, 7749, 0, - 0, 328, 0, 7751, 0, 326, 0, 7755, 7753, 0, 242, 243, 244, 245, 333, 335, - 559, 246, 7887, 0, 337, 466, 525, 527, 417, 7885, 491, 0, 0, 7765, 7767, - 0, 0, 341, 7769, 0, 0, 345, 529, 531, 0, 7771, 0, 343, 7775, 0, 0, 347, - 349, 0, 7777, 0, 0, 353, 0, 7779, 537, 351, 7787, 7831, 0, 357, 0, 7789, - 539, 355, 0, 7793, 7791, 0, 249, 250, 251, 361, 363, 365, 0, 252, 7911, - 367, 369, 468, 533, 535, 432, 7909, 7795, 0, 371, 7799, 0, 7797, 0, 7805, - 0, 7807, 7809, 7811, 373, 0, 7815, 7813, 0, 7832, 0, 7817, 7819, 7821, - 7923, 253, 375, 7929, 563, 0, 7823, 255, 7927, 7833, 0, 7925, 0, 378, - 7825, 0, 380, 0, 0, 382, 0, 7827, 7829, 0, 8173, 901, 8129, 0, 7846, - 7844, 0, 7850, 7848, 0, 478, 0, 0, 506, 0, 508, 482, 0, 0, 7688, 7872, - 7870, 0, 7876, 7874, 0, 0, 7726, 7890, 7888, 0, 7894, 7892, 0, 0, 7756, - 556, 0, 0, 7758, 554, 0, 0, 510, 475, 471, 469, 0, 0, 473, 7847, 7845, 0, - 7851, 7849, 0, 479, 0, 0, 507, 0, 509, 483, 0, 0, 7689, 7873, 7871, 0, - 7877, 7875, 0, 0, 7727, 7891, 7889, 0, 7895, 7893, 0, 0, 7757, 557, 0, 0, - 7759, 555, 0, 0, 511, 476, 472, 470, 0, 0, 474, 7856, 7854, 0, 7860, - 7858, 0, 7857, 7855, 0, 7861, 7859, 0, 7700, 7702, 7701, 7703, 7760, - 7762, 7761, 7763, 7780, 0, 7781, 0, 7782, 0, 7783, 0, 0, 7800, 0, 7801, - 0, 7802, 0, 7803, 7835, 0, 7900, 7898, 0, 7904, 7902, 0, 0, 7906, 7901, - 7899, 0, 7905, 7903, 0, 0, 7907, 7914, 7912, 0, 7918, 7916, 0, 0, 7920, - 7915, 7913, 0, 7919, 7917, 0, 0, 7921, 0, 494, 492, 0, 493, 0, 480, 0, - 481, 0, 0, 7708, 0, 7709, 560, 0, 561, 0, 0, 495, 8122, 902, 8121, 8120, - 7944, 7945, 0, 8124, 8136, 904, 7960, 7961, 8138, 905, 7976, 7977, 0, - 8140, 8154, 906, 8153, 8152, 0, 938, 7992, 7993, 8184, 908, 8008, 8009, - 0, 8172, 8170, 910, 8169, 8168, 0, 939, 0, 8025, 8186, 911, 8040, 8041, - 0, 8188, 0, 8116, 0, 8132, 8048, 940, 8113, 8112, 7936, 7937, 8118, 8115, - 8050, 941, 7952, 7953, 8052, 942, 7968, 7969, 8134, 8131, 8054, 943, - 8145, 8144, 0, 970, 7984, 7985, 8150, 0, 8056, 972, 8000, 8001, 8164, - 8165, 8058, 973, 8161, 8160, 0, 971, 8016, 8017, 8166, 0, 8060, 974, - 8032, 8033, 8182, 8179, 8146, 912, 8151, 0, 8162, 944, 8167, 0, 0, 8180, - 0, 979, 0, 980, 0, 1031, 0, 1232, 0, 1234, 0, 1027, 1024, 0, 0, 1238, 0, - 1025, 0, 1217, 0, 1244, 0, 1246, 1037, 0, 1250, 1049, 0, 1252, 0, 1036, - 0, 1254, 1262, 1038, 0, 1264, 1266, 0, 0, 1268, 0, 1272, 0, 1260, 0, - 1233, 0, 1235, 0, 1107, 1104, 0, 0, 1239, 0, 1105, 0, 1218, 0, 1245, 0, - 1247, 1117, 0, 1251, 1081, 0, 1253, 0, 1116, 0, 1255, 1263, 1118, 0, - 1265, 1267, 0, 0, 1269, 0, 1273, 0, 1261, 0, 1111, 1142, 0, 1143, 0, 0, - 1242, 0, 1243, 0, 1258, 0, 1259, 1570, 1571, 1573, 0, 0, 1572, 0, 1574, - 0, 1730, 0, 1747, 0, 1728, 0, 2345, 0, 2353, 0, 2356, 2507, 2508, 2891, - 2888, 2892, 0, 2964, 0, 0, 3018, 3020, 0, 0, 3019, 0, 3144, 0, 3264, - 3274, 3271, 3272, 0, 0, 3275, 0, 3402, 3404, 0, 0, 3403, 0, 3546, 3548, - 3550, 0, 3549, 4134, 0, 0, 6918, 0, 6920, 0, 6922, 0, 6924, 0, 6926, 0, - 6930, 0, 6971, 0, 6973, 0, 6976, 0, 6977, 0, 6979, 7736, 0, 7737, 0, - 7772, 0, 7773, 0, 7784, 0, 7785, 0, 7852, 0, 0, 7862, 7853, 0, 0, 7863, - 7878, 0, 7879, 0, 7896, 0, 7897, 0, 7938, 7940, 7942, 8064, 7939, 7941, - 7943, 8065, 0, 8066, 0, 8067, 0, 8068, 0, 8069, 0, 8070, 0, 8071, 7946, - 7948, 7950, 8072, 7947, 7949, 7951, 8073, 0, 8074, 0, 8075, 0, 8076, 0, - 8077, 0, 8078, 0, 8079, 7954, 7956, 7955, 7957, 7962, 7964, 7963, 7965, - 7970, 7972, 7974, 8080, 7971, 7973, 7975, 8081, 0, 8082, 0, 8083, 0, - 8084, 0, 8085, 0, 8086, 0, 8087, 7978, 7980, 7982, 8088, 7979, 7981, - 7983, 8089, 0, 8090, 0, 8091, 0, 8092, 0, 8093, 0, 8094, 0, 8095, 7986, - 7988, 7990, 0, 7987, 7989, 7991, 0, 7994, 7996, 7998, 0, 7995, 7997, - 7999, 0, 8002, 8004, 8003, 8005, 8010, 8012, 8011, 8013, 8018, 8020, - 8022, 0, 8019, 8021, 8023, 0, 8027, 8029, 8031, 0, 8034, 8036, 8038, - 8096, 8035, 8037, 8039, 8097, 0, 8098, 0, 8099, 0, 8100, 0, 8101, 0, - 8102, 0, 8103, 8042, 8044, 8046, 8104, 8043, 8045, 8047, 8105, 0, 8106, - 0, 8107, 0, 8108, 0, 8109, 0, 8110, 0, 8111, 0, 8114, 0, 8130, 0, 8178, - 0, 8119, 8141, 8142, 8143, 0, 0, 8135, 0, 8183, 8157, 8158, 8159, 0, 0, - 8602, 0, 8603, 0, 8622, 0, 8653, 0, 8655, 0, 8654, 0, 8708, 0, 8713, 0, - 8716, 0, 8740, 0, 8742, 0, 8769, 0, 8772, 0, 8775, 0, 8777, 0, 8813, 0, - 8802, 0, 8816, 0, 8817, 0, 8820, 0, 8821, 0, 8824, 0, 8825, 0, 8832, 0, - 8833, 0, 8928, 0, 8929, 0, 8836, 0, 8837, 0, 8840, 0, 8841, 0, 8930, 0, - 8931, 0, 8876, 0, 8877, 0, 8878, 0, 8879, 0, 8938, 0, 8939, 0, 8940, 0, - 8941, 12436, 0, 12364, 0, 12366, 0, 12368, 0, 12370, 0, 12372, 0, 12374, - 0, 12376, 0, 12378, 0, 12380, 0, 12382, 0, 12384, 0, 12386, 0, 12389, 0, - 12391, 0, 12393, 0, 12400, 12401, 12403, 12404, 12406, 12407, 12409, - 12410, 12412, 12413, 12446, 0, 12532, 0, 12460, 0, 12462, 0, 12464, 0, - 12466, 0, 12468, 0, 12470, 0, 12472, 0, 12474, 0, 12476, 0, 12478, 0, - 12480, 0, 12482, 0, 12485, 0, 12487, 0, 12489, 0, 12496, 12497, 12499, - 12500, 12502, 12503, 12505, 12506, 12508, 12509, 12535, 0, 12536, 0, - 12537, 0, 12538, 0, 12542, 0, 69786, 0, 69788, 0, 69803, 0, 0, 69934, 0, - 69935, 70475, 70476, 70844, 70843, 70846, 0, 0, 71098, 0, 71099, -}; - diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh index 1dd0b3211e8de..eb7776eecbe3a 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2018-02-07, 07:55:18 GMT - * # © 2018 Unicode®, Inc. + * # Date: 2020-01-28, 20:52:38 GMT + * # © 2020 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. * # For terms of use, see http://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Version: 11.0 + * # Version: 13.0 * # * # For documentation and usage, see http://www.unicode.org/reports/tr51 */ @@ -23,88 +23,56 @@ #include "hb-unicode.hh" - -static const struct hb_unicode_range_t _hb_unicode_emoji_Extended_Pictographic_table[] = +static const uint8_t +_hb_emoji_u8[448] = { - {0x00A9, 0x00A9}, - {0x00AE, 0x00AE}, - {0x203C, 0x203C}, - {0x2049, 0x2049}, - {0x2122, 0x2122}, - {0x2139, 0x2139}, - {0x2194, 0x2199}, - {0x21A9, 0x21AA}, - {0x231A, 0x231B}, - {0x2328, 0x2328}, - {0x2388, 0x2388}, - {0x23CF, 0x23CF}, - {0x23E9, 0x23F3}, - {0x23F8, 0x23FA}, - {0x24C2, 0x24C2}, - {0x25AA, 0x25AB}, - {0x25B6, 0x25B6}, - {0x25C0, 0x25C0}, - {0x25FB, 0x25FE}, - {0x2600, 0x2605}, - {0x2607, 0x2612}, - {0x2614, 0x2685}, - {0x2690, 0x2705}, - {0x2708, 0x2712}, - {0x2714, 0x2714}, - {0x2716, 0x2716}, - {0x271D, 0x271D}, - {0x2721, 0x2721}, - {0x2728, 0x2728}, - {0x2733, 0x2734}, - {0x2744, 0x2744}, - {0x2747, 0x2747}, - {0x274C, 0x274C}, - {0x274E, 0x274E}, - {0x2753, 0x2755}, - {0x2757, 0x2757}, - {0x2763, 0x2767}, - {0x2795, 0x2797}, - {0x27A1, 0x27A1}, - {0x27B0, 0x27B0}, - {0x27BF, 0x27BF}, - {0x2934, 0x2935}, - {0x2B05, 0x2B07}, - {0x2B1B, 0x2B1C}, - {0x2B50, 0x2B50}, - {0x2B55, 0x2B55}, - {0x3030, 0x3030}, - {0x303D, 0x303D}, - {0x3297, 0x3297}, - {0x3299, 0x3299}, - {0x1F000, 0x1F0FF}, - {0x1F10D, 0x1F10F}, - {0x1F12F, 0x1F12F}, - {0x1F16C, 0x1F171}, - {0x1F17E, 0x1F17F}, - {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, - {0x1F1AD, 0x1F1E5}, - {0x1F201, 0x1F20F}, - {0x1F21A, 0x1F21A}, - {0x1F22F, 0x1F22F}, - {0x1F232, 0x1F23A}, - {0x1F23C, 0x1F23F}, - {0x1F249, 0x1F3FA}, - {0x1F400, 0x1F53D}, - {0x1F546, 0x1F64F}, - {0x1F680, 0x1F6FF}, - {0x1F774, 0x1F77F}, - {0x1F7D5, 0x1F7FF}, - {0x1F80C, 0x1F80F}, - {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, - {0x1F888, 0x1F88F}, - {0x1F8AE, 0x1F8FF}, - {0x1F90C, 0x1F93A}, - {0x1F93C, 0x1F945}, - {0x1F947, 0x1FFFD}, + 0, 0, 0, 0, 33, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84,118, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 7, 9, 10, 11, 0, + 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, + 7, 7, 7, 14, 15, 16, 17, 18, 19, 20, 7, 7, 7, 7, 7, 21, + 7, 7, 7, 7, 22, 23, 7, 7, 7, 24, 7, 14, 0, 25, 0, 26, + 27, 28, 29, 14, 30, 31, 7, 7, 7, 7, 7, 14, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 1, 0, 2, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 7, 3, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 159,255,243,255,255,255,255,255,255,255,255,255,255,255,255,255, + 31, 0,255,255,255,255,255,255, 31,255, 3, 0, 0, 0, 8, 0, + 0, 0, 24, 0,120, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 16, 0, 96, 0, 0, 8, 0, 0, 0, 0, + 255,255,255,255,255,255,255,127, 0, 96, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,240, 1, 64, 0, 0,254, 3, 0,224,255,255, + 255,255,255,255, 31, 0, 0, 0,254,127, 0, 0, 0, 0,252,115, + 0,254,255,255,255,255,255,255,255,255,255,255,255,255,255, 3, + 255,255,255,255,255,255,255, 31,192,255,255,255,255,255,255,255, + 255,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240,127, + 0, 0,224,255,255,255,255,127, 0,112, 0, 0, 0, 0, 0, 0, + 0,127, 0,124, 0, 0, 0, 0, 0,127, 0, 0, 0,192,255,255, + 0,240,255,255,255,255,255,243,159,255,255,255,255,255,255,255, }; +static inline unsigned +_hb_emoji_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline unsigned +_hb_emoji_b1 (const uint8_t* a, unsigned i) +{ + return (a[i>>3]>>((i&7u)<<0))&1u; +} +static inline uint_fast8_t +_hb_emoji_is_Extended_Pictographic (unsigned u) +{ + return u<131069u?_hb_emoji_b1(192+_hb_emoji_u8,((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>6>>4))<<4)+((u>>6)&15u))])<<6)+((u)&63u)):0; +} + + #endif /* HB_UNICODE_EMOJI_TABLE_HH */ /* == End of generated table == */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc b/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc index 47e6af647e30b..8530289cbad0e 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.cc @@ -60,6 +60,7 @@ hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; } +#ifndef HB_DISABLE_DEPRECATED static unsigned int hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t unicode HB_UNUSED, @@ -67,6 +68,7 @@ hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, { return 1; } +#endif static hb_unicode_general_category_t hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, @@ -113,6 +115,7 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, } +#ifndef HB_DISABLE_DEPRECATED static unsigned int hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, hb_codepoint_t u HB_UNUSED, @@ -121,20 +124,23 @@ hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED { return 0; } +#endif - -extern "C" hb_unicode_funcs_t *hb_glib_get_unicode_funcs (); -extern "C" hb_unicode_funcs_t *hb_icu_get_unicode_funcs (); -extern "C" hb_unicode_funcs_t *hb_ucdn_get_unicode_funcs (); +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) +#include "hb-glib.h" +#endif +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#include "hb-icu.h" +#endif hb_unicode_funcs_t * hb_unicode_funcs_get_default () { -#if defined(HAVE_UCDN) - return hb_ucdn_get_unicode_funcs (); -#elif defined(HAVE_GLIB) +#if !defined(HB_NO_UNICODE_FUNCS) && !defined(HB_NO_UCD) + return hb_ucd_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) return hb_glib_get_unicode_funcs (); -#elif defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) return hb_icu_get_unicode_funcs (); #else #define HB_UNICODE_FUNCS_NIL 1 @@ -144,7 +150,7 @@ hb_unicode_funcs_get_default () #if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) #error "Could not find any Unicode functions implementation, you have to provide your own" -#error "Consider building hb-ucdn.c. If you absolutely want to build without any, check the code." +#error "Consider building hb-ucd.cc. If you absolutely want to build without any, check the code." #endif /** @@ -206,7 +212,7 @@ DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) = hb_unicode_funcs_t * hb_unicode_funcs_get_empty () { - return const_cast (&Null(hb_unicode_funcs_t)); + return const_cast (&Null (hb_unicode_funcs_t)); } /** @@ -425,6 +431,7 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, return ufuncs->decompose (ab, a, b); } +#ifndef HB_DISABLE_DEPRECATED /** * hb_unicode_decompose_compatibility: * @ufuncs: Unicode functions. @@ -445,8 +452,10 @@ hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, { return ufuncs->decompose_compatibility (u, decomposed); } +#endif +#ifndef HB_NO_OT_SHAPE /* See hb-unicode.hh for details. */ const uint8_t _hb_modified_combining_class[256] = @@ -559,19 +568,19 @@ _hb_modified_combining_class[256] = 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, /* HB_UNICODE_COMBINING_CLASS_INVALID */ }; +#endif /* * Emoji */ +#ifndef HB_NO_EMOJI_SEQUENCES #include "hb-unicode-emoji-table.hh" bool _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp) { - return hb_bsearch (&cp, _hb_unicode_emoji_Extended_Pictographic_table, - ARRAY_LENGTH (_hb_unicode_emoji_Extended_Pictographic_table), - sizeof (hb_unicode_range_t), - hb_unicode_range_t::cmp); + return _hb_emoji_is_Extended_Pictographic (cp); } +#endif diff --git a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh index 24b03c20184f3..40f6a5a7f4346 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-unicode.hh @@ -42,19 +42,19 @@ extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; #define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \ HB_UNICODE_FUNC_IMPLEMENT (combining_class) \ - HB_UNICODE_FUNC_IMPLEMENT (eastasian_width) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (eastasian_width)) \ HB_UNICODE_FUNC_IMPLEMENT (general_category) \ HB_UNICODE_FUNC_IMPLEMENT (mirroring) \ HB_UNICODE_FUNC_IMPLEMENT (script) \ HB_UNICODE_FUNC_IMPLEMENT (compose) \ HB_UNICODE_FUNC_IMPLEMENT (decompose) \ - HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility)) \ /* ^--- Add new callbacks here */ /* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */ #define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \ HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \ - HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width)) \ HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \ HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \ HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \ @@ -89,7 +89,11 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE unsigned int decompose_compatibility (hb_codepoint_t u, hb_codepoint_t *decomposed) { +#ifdef HB_DISABLE_DEPRECATED + unsigned int ret = 0; +#else unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility); +#endif if (ret == 1 && u == decomposed[0]) { decomposed[0] = 0; return 0; @@ -101,9 +105,6 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE unsigned int modified_combining_class (hb_codepoint_t u) { - /* XXX This hack belongs to the Myanmar shaper. */ - if (unlikely (u == 0x1037u)) u = 0x103Au; - /* XXX This hack belongs to the USE shaper (for Tai Tham): * Reorder SAKOT to ensure it comes after any tone marks. */ if (unlikely (u == 0x1A60u)) return 254; @@ -322,11 +323,11 @@ DECLARE_NULL_INSTANCE (hb_unicode_funcs_t); * * Modify Telugu length marks (ccc=84, ccc=91). * These are the only matras in the main Indic scripts range that have - * a non-zero ccc. That makes them reorder with the Halant that is - * ccc=9. Just zero them, we don't need them in our Indic shaper. + * a non-zero ccc. That makes them reorder with the Halant (ccc=9). + * Assign 4 and 5, which are otherwise unassigned. */ -#define HB_MODIFIED_COMBINING_CLASS_CCC84 0 /* length mark */ -#define HB_MODIFIED_COMBINING_CLASS_CCC91 0 /* ai length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */ /* Thai * @@ -391,4 +392,7 @@ HB_INTERNAL bool _hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp); +extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs (); + + #endif /* HB_UNICODE_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh index b438675b9dc2a..4a7a7a734d1eb 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-vector.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb-vector.hh @@ -38,103 +38,141 @@ struct hb_vector_t typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_COPY_ASSIGN_TEMPLATE (hb_vector_t, Type); hb_vector_t () { init (); } + hb_vector_t (const hb_vector_t &o) + { + init (); + alloc (o.length); + hb_copy (o, *this); + } + hb_vector_t (hb_vector_t &&o) + { + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + } ~hb_vector_t () { fini (); } - unsigned int length; private: int allocated; /* == -1 means allocation failed. */ - Type *arrayZ_; public: + unsigned int length; + public: + Type *arrayZ; void init () { allocated = length = 0; - arrayZ_ = nullptr; + arrayZ = nullptr; } void fini () { - if (arrayZ_) - free (arrayZ_); + free (arrayZ); init (); } void fini_deep () { - Type *array = arrayZ(); unsigned int count = length; for (unsigned int i = 0; i < count; i++) - array[i].fini (); + arrayZ[i].fini (); + fini (); + } + + void reset () { resize (0); } + + hb_vector_t& operator = (const hb_vector_t &o) + { + reset (); + alloc (o.length); + hb_copy (o, *this); + return *this; + } + hb_vector_t& operator = (hb_vector_t &&o) + { fini (); + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + return *this; } - const Type * arrayZ () const { return arrayZ_; } - Type * arrayZ () { return arrayZ_; } + hb_bytes_t as_bytes () const + { return hb_bytes_t ((const char *) arrayZ, length * item_size); } + + bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); } + bool operator != (const hb_vector_t &o) const { return !(*this == o); } + uint32_t hash () const { return as_array ().hash (); } Type& operator [] (int i_) { unsigned int i = (unsigned int) i_; if (unlikely (i >= length)) return Crap (Type); - return arrayZ()[i]; + return arrayZ[i]; } const Type& operator [] (int i_) const { unsigned int i = (unsigned int) i_; if (unlikely (i >= length)) - return Null(Type); - return arrayZ()[i]; + return Null (Type); + return arrayZ[i]; } - explicit_operator bool () const { return length; } + Type& tail () { return (*this)[length - 1]; } + const Type& tail () const { return (*this)[length - 1]; } + + explicit operator bool () const { return length; } + unsigned get_size () const { return length * item_size; } + + /* Sink interface. */ + template + hb_vector_t& operator << (T&& v) { push (hb_forward (v)); return *this; } - hb_array_t as_array () - { return hb_array (arrayZ(), length); } - hb_array_t as_array () const - { return hb_array (arrayZ(), length); } + hb_array_t< Type> as_array () { return hb_array (arrayZ, length); } + hb_array_t as_array () const { return hb_array (arrayZ, length); } + + /* Iterator. */ + typedef hb_array_t iter_t; + typedef hb_array_t< Type> writer_t; + iter_t iter () const { return as_array (); } + writer_t writer () { return as_array (); } + operator iter_t () const { return iter (); } + operator writer_t () { return writer (); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int count) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_array_t sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_array ().sub_array (start_offset, count);} + { return as_array ().sub_array (start_offset, count); } hb_sorted_array_t as_sorted_array () - { return hb_sorted_array (arrayZ(), length); } + { return hb_sorted_array (arrayZ, length); } hb_sorted_array_t as_sorted_array () const - { return hb_sorted_array (arrayZ(), length); } - - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int count) const - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int count) - { return as_sorted_array ().sorted_sub_array (start_offset, count);} - hb_array_t sorted_sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) - { return as_sorted_array ().sorted_sub_array (start_offset, count);} + { return hb_sorted_array (arrayZ, length); } - template explicit_operator T * () { return arrayZ(); } - template explicit_operator const T * () const { return arrayZ(); } - operator hb_array_t () { return as_array (); } - operator hb_array_t () const { return as_array (); } + template explicit operator T * () { return arrayZ; } + template explicit operator const T * () const { return arrayZ; } - Type * operator + (unsigned int i) { return arrayZ() + i; } - const Type * operator + (unsigned int i) const { return arrayZ() + i; } + Type * operator + (unsigned int i) { return arrayZ + i; } + const Type * operator + (unsigned int i) const { return arrayZ + i; } Type *push () { if (unlikely (!resize (length + 1))) - return &Crap(Type); - return &arrayZ()[length - 1]; + return &Crap (Type); + return &arrayZ[length - 1]; } - Type *push (const Type& v) + template + Type *push (T&& v) { Type *p = push (); - *p = v; + *p = hb_forward (v); return p; } @@ -161,7 +199,7 @@ struct hb_vector_t (new_allocated < (unsigned) allocated) || hb_unsigned_mul_overflows (new_allocated, sizeof (Type)); if (likely (!overflows)) - new_array = (Type *) realloc (arrayZ_, new_allocated * sizeof (Type)); + new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type)); if (unlikely (!new_array)) { @@ -169,7 +207,7 @@ struct hb_vector_t return false; } - arrayZ_ = new_array; + arrayZ = new_array; allocated = new_allocated; return true; @@ -182,25 +220,24 @@ struct hb_vector_t return false; if (size > length) - memset (arrayZ() + length, 0, (size - length) * sizeof (*arrayZ())); + memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ)); length = size; return true; } - void pop () + Type pop () { - if (!length) return; - length--; + if (!length) return Null (Type); + return hb_move (arrayZ[--length]); /* Does this move actually work? */ } void remove (unsigned int i) { if (unlikely (i >= length)) return; - Type *array = arrayZ(); - memmove (static_cast (&array[i]), - static_cast (&array[i + 1]), + memmove (static_cast (&arrayZ[i]), + static_cast (&arrayZ[i + 1]), (length - i - 1) * sizeof (Type)); length--; } @@ -215,19 +252,17 @@ struct hb_vector_t template Type *find (T v) { - Type *array = arrayZ(); for (unsigned int i = 0; i < length; i++) - if (array[i] == v) - return &array[i]; + if (arrayZ[i] == v) + return &arrayZ[i]; return nullptr; } template const Type *find (T v) const { - const Type *array = arrayZ(); for (unsigned int i = 0; i < length; i++) - if (array[i] == v) - return &array[i]; + if (arrayZ[i] == v) + return &arrayZ[i]; return nullptr; } @@ -242,19 +277,37 @@ struct hb_vector_t template const Type *lsearch (const T &x, const Type *not_found = nullptr) const { return as_array ().lsearch (x, not_found); } + template + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } +}; + +template +struct hb_sorted_vector_t : hb_vector_t +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->length); } + hb_sorted_array_t as_array () const { return hb_sorted_array (this->arrayZ, this->length); } + + /* Iterator. */ + typedef hb_sorted_array_t const_iter_t; + typedef hb_sorted_array_t< Type> iter_t; + const_iter_t iter () const { return as_array (); } + const_iter_t citer () const { return as_array (); } + iter_t iter () { return as_array (); } + operator iter_t () { return iter (); } + operator const_iter_t () const { return iter (); } template Type *bsearch (const T &x, Type *not_found = nullptr) - { return as_sorted_array ().bsearch (x, not_found); } + { return as_array ().bsearch (x, not_found); } template const Type *bsearch (const T &x, const Type *not_found = nullptr) const - { return as_sorted_array ().bsearch (x, not_found); } + { return as_array ().bsearch (x, not_found); } template bool bfind (const T &x, unsigned int *i = nullptr, - hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, - unsigned int to_store = (unsigned int) -1) const - { return as_sorted_array ().bfind (x, i, not_found, to_store); } + hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE, + unsigned int to_store = (unsigned int) -1) const + { return as_array ().bfind (x, i, not_found, to_store); } }; - #endif /* HB_VECTOR_HH */ diff --git a/src/java.desktop/share/native/libharfbuzz/hb-version.h b/src/java.desktop/share/native/libharfbuzz/hb-version.h index 01248794ff87b..b9ab5f55f1bb4 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb-version.h +++ b/src/java.desktop/share/native/libharfbuzz/hb-version.h @@ -37,10 +37,10 @@ HB_BEGIN_DECLS #define HB_VERSION_MAJOR 2 -#define HB_VERSION_MINOR 3 -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MINOR 7 +#define HB_VERSION_MICRO 2 -#define HB_VERSION_STRING "2.3.1" +#define HB_VERSION_STRING "2.7.2" #define HB_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ diff --git a/src/java.desktop/share/native/libharfbuzz/hb.h b/src/java.desktop/share/native/libharfbuzz/hb.h index c5e7072fbacff..360686ca687e2 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.h +++ b/src/java.desktop/share/native/libharfbuzz/hb.h @@ -32,12 +32,14 @@ #include "hb-buffer.h" #include "hb-common.h" #include "hb-deprecated.h" +#include "hb-draw.h" #include "hb-face.h" #include "hb-font.h" #include "hb-map.h" #include "hb-set.h" #include "hb-shape.h" #include "hb-shape-plan.h" +#include "hb-style.h" #include "hb-unicode.h" #include "hb-version.h" diff --git a/src/java.desktop/share/native/libharfbuzz/hb.hh b/src/java.desktop/share/native/libharfbuzz/hb.hh index f2d3906866394..8f0ba2ee05477 100644 --- a/src/java.desktop/share/native/libharfbuzz/hb.hh +++ b/src/java.desktop/share/native/libharfbuzz/hb.hh @@ -29,8 +29,9 @@ #ifndef HB_HH #define HB_HH + #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC -#if defined(_MSC_VER) +#ifdef _MSC_VER #pragma warning( disable: 4068 ) /* Unknown pragma */ #endif #if defined(__GNUC__) || defined(__clang__) @@ -65,9 +66,12 @@ #pragma GCC diagnostic error "-Wcast-align" #pragma GCC diagnostic error "-Wcast-function-type" #pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" +#pragma GCC diagnostic error "-Wembedded-directive" +#pragma GCC diagnostic error "-Wextra-semi-stmt" #pragma GCC diagnostic error "-Wformat-security" #pragma GCC diagnostic error "-Wimplicit-function-declaration" #pragma GCC diagnostic error "-Winit-self" +#pragma GCC diagnostic error "-Winjected-class-name" #pragma GCC diagnostic error "-Wmissing-braces" #pragma GCC diagnostic error "-Wmissing-declarations" #pragma GCC diagnostic error "-Wmissing-prototypes" @@ -93,13 +97,17 @@ /* Warning. To be investigated if happens. */ #ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING #pragma GCC diagnostic warning "-Wbuiltin-macro-redefined" +#pragma GCC diagnostic warning "-Wdeprecated" +#pragma GCC diagnostic warning "-Wdeprecated-declarations" #pragma GCC diagnostic warning "-Wdisabled-optimization" +#pragma GCC diagnostic warning "-Wdouble-promotion" #pragma GCC diagnostic warning "-Wformat=2" #pragma GCC diagnostic warning "-Wignored-pragma-optimize" #pragma GCC diagnostic warning "-Wlogical-op" #pragma GCC diagnostic warning "-Wmaybe-uninitialized" #pragma GCC diagnostic warning "-Wmissing-format-attribute" #pragma GCC diagnostic warning "-Wundef" +#pragma GCC diagnostic warning "-Wunused-but-set-variable" #endif /* Ignored currently, but should be fixed at some point. */ @@ -120,14 +128,15 @@ #pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang #pragma GCC diagnostic ignored "-Wstrict-aliasing" #pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wc++11-compat" // only gcc raises it #endif #endif #endif -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif + +#include "hb-config.hh" + /* * Following added based on what AC_USE_SYSTEM_EXTENSIONS adds to @@ -166,20 +175,30 @@ #include "hb-aat.h" #define HB_AAT_H_IN -#include "hb-aat.h" - +#include #include +#include #include #include #include #include -#include #include #include #if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) +#ifdef __MINGW32_VERSION +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#else #include #endif +#endif + +#ifdef _WIN32 +#include +#include +#endif #define HB_PASTE1(a,b) a##b #define HB_PASTE(a,b) HB_PASTE1(a,b) @@ -187,10 +206,15 @@ /* Compile-time custom allocator support. */ -#if defined(hb_malloc_impl) \ - && defined(hb_calloc_impl) \ - && defined(hb_realloc_impl) \ - && defined(hb_free_impl) +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC extern "C" void* hb_malloc_impl(size_t size); extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); extern "C" void* hb_realloc_impl(void *ptr, size_t size); @@ -199,14 +223,6 @@ extern "C" void hb_free_impl(void *ptr); #define calloc hb_calloc_impl #define realloc hb_realloc_impl #define free hb_free_impl - -#if defined(hb_memalign_impl) -extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); -#define posix_memalign hb_memalign_impl -#else -#undef HAVE_POSIX_MEMALIGN -#endif - #endif @@ -214,58 +230,6 @@ extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); * Compiler attributes */ -#if __cplusplus < 201103L - -#ifndef nullptr -#define nullptr NULL -#endif - -#ifndef constexpr -#define constexpr const -#endif - -#ifndef static_assert -#define static_assert(e, msg) \ - HB_UNUSED typedef int HB_PASTE(static_assertion_failed_at_line_, __LINE__) [(e) ? 1 : -1] -#endif // static_assert - -#if defined(__GNUC__) -#if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) -#define thread_local __thread -#endif -#else -#define thread_local -#endif - -template -struct _hb_alignof -{ - struct s - { - char c; - T t; - }; - static constexpr size_t value = offsetof (s, t); -}; -#ifndef alignof -#define alignof(x) (_hb_alignof::value) -#endif - -/* https://github.com/harfbuzz/harfbuzz/issues/1127 */ -#ifndef explicit_operator -#define explicit_operator operator -#endif - -#else /* __cplusplus >= 201103L */ - -/* https://github.com/harfbuzz/harfbuzz/issues/1127 */ -#ifndef explicit_operator -#define explicit_operator explicit operator -#endif - -#endif /* __cplusplus < 201103L */ - - #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__) #define likely(expr) (__builtin_expect (!!(expr), 1)) #define unlikely(expr) (__builtin_expect (!!(expr), 0)) @@ -288,7 +252,7 @@ struct _hb_alignof #define HB_CONST_FUNC #define HB_PRINTF_FUNC(format_idx, arg_idx) #endif -#if defined(__GNUC__) && (__GNUC__ >= 4) +#if defined(__GNUC__) && (__GNUC__ >= 4) || (__clang__) #define HB_UNUSED __attribute__((unused)) #elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */ #define HB_UNUSED __pragma(warning(suppress: 4100 4101)) @@ -311,6 +275,13 @@ struct _hb_alignof # endif #endif +/* https://github.com/harfbuzz/harfbuzz/issues/1651 */ +#if defined(__clang__) && __clang_major__ < 10 +#define static_const static +#else +#define static_const static const +#endif + #if defined(__GNUC__) && (__GNUC__ >= 3) #define HB_FUNC __PRETTY_FUNCTION__ #elif defined(_MSC_VER) @@ -357,7 +328,20 @@ struct _hb_alignof # define HB_FALLTHROUGH /* FALLTHROUGH */ #endif -#if defined(__clang__) +/* A tag to enforce use of return value for a function */ +#if __cplusplus >= 201703L +# define HB_NODISCARD [[nodiscard]] +#elif defined(__GNUC__) || defined(__clang__) +# define HB_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +# define HB_NODISCARD _Check_return_ +#else +# define HB_NODISCARD +#endif +#define hb_success_t HB_NODISCARD bool + +/* https://github.com/harfbuzz/harfbuzz/issues/1852 */ +#if defined(__clang__) && !(defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))) /* Disable certain sanitizer errors. */ /* https://github.com/harfbuzz/harfbuzz/issues/1247 */ #define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow"))) @@ -374,7 +358,7 @@ struct _hb_alignof # undef _WIN32_WINNT # endif # ifndef _WIN32_WINNT -# if !defined(WINAPI_FAMILY) || !(WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) # define _WIN32_WINNT 0x0600 # endif # endif @@ -388,19 +372,35 @@ struct _hb_alignof # if defined(_WIN32_WCE) /* Some things not defined on Windows CE. */ # define vsnprintf _vsnprintf -# define getenv(Name) nullptr +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif # if _WIN32_WCE < 0x800 -# define setlocale(Category, Locale) "C" -static int errno = 0; /* Use something better? */ +# define HB_NO_SETLOCALE +# define HB_NO_ERRNO +# endif +# elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# ifndef HB_NO_GETENV +# define HB_NO_GETENV # endif -# elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) -# define getenv(Name) nullptr # endif # if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf _snprintf # endif #endif +#ifdef HB_NO_GETENV +#define getenv(Name) nullptr +#endif + +#ifndef HB_NO_ERRNO +# include +#else +static int HB_UNUSED _hb_errno = 0; +# undef errno +# define errno _hb_errno +#endif + #if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) /* atexit() is only safe to be called from shared libraries on certain * platforms. Whitelist. @@ -459,87 +459,13 @@ static_assert ((sizeof (hb_position_t) == 4), ""); static_assert ((sizeof (hb_mask_t) == 4), ""); static_assert ((sizeof (hb_var_int_t) == 4), ""); - -#if __cplusplus >= 201103L - -/* We only enable these with C++11 or later, since earlier language - * does not allow structs with constructors in unions, and we need - * those. */ - -#define HB_NO_COPY_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_COPY_ASSIGN_TEMPLATE(TypeName, T) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN(TypeName) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) \ - TypeName(); \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -#else /* __cpluspplus >= 201103L */ - -#define HB_NO_COPY_ASSIGN(TypeName) static_assert (true, "") -#define HB_NO_COPY_ASSIGN_TEMPLATE(TypeName, T) static_assert (true, "") -#define HB_NO_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN(TypeName) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE(TypeName, T) static_assert (true, "") -#define HB_NO_CREATE_COPY_ASSIGN_TEMPLATE2(TypeName, T1, T2) static_assert (true, "") - -#endif /* __cpluspplus >= 201103L */ - - -/* - * Compiler-assisted vectorization parameters. - */ - -/* - * Disable vectorization for now. To correctly use them, we should - * use posix_memalign() to allocate in hb_vector_t. Otherwise, can - * cause misaligned access. - * - * https://bugs.chromium.org/p/chromium/issues/detail?id=860184 - */ -#if !defined(HB_VECTOR_SIZE) -# define HB_VECTOR_SIZE 0 -#endif - -/* The `vector_size' attribute was introduced in gcc 3.1. */ -#if !defined(HB_VECTOR_SIZE) -# if defined( __GNUC__ ) && ( __GNUC__ >= 4 ) -# define HB_VECTOR_SIZE 128 -# else -# define HB_VECTOR_SIZE 0 -# endif -#endif -static_assert (0 == (HB_VECTOR_SIZE & (HB_VECTOR_SIZE - 1)), "HB_VECTOR_SIZE is not power of 2."); -static_assert (0 == (HB_VECTOR_SIZE % 64), "HB_VECTOR_SIZE is not multiple of 64."); -#if HB_VECTOR_SIZE -typedef uint64_t hb_vector_size_impl_t __attribute__((vector_size (HB_VECTOR_SIZE / 8))); -#else -typedef uint64_t hb_vector_size_impl_t; -#endif - - -/* HB_NDEBUG disables some sanity checks that are very safe to disable and - * should be disabled in production systems. If NDEBUG is defined, enable - * HB_NDEBUG; but if it's desirable that normal assert()s (which are very - * light-weight) to be enabled, then HB_DEBUG can be defined to disable - * the costlier checks. */ -#ifdef NDEBUG -#define HB_NDEBUG 1 -#endif +#define HB_DELETE_COPY_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete +#define HB_DELETE_CREATE_COPY_ASSIGN(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete /* Flags */ @@ -579,47 +505,107 @@ typedef uint64_t hb_vector_size_impl_t; /* Size signifying variable-sized array */ -#define VAR 1 - - -/* fallback for round() */ -static inline double -_hb_round (double x) -{ - if (x >= 0) - return floor (x + 0.5); - else - return ceil (x - 0.5); -} -#if !defined (HAVE_ROUND) && !defined (HAVE_DECL_ROUND) -#define round(x) _hb_round(x) +#ifndef HB_VAR_ARRAY +#define HB_VAR_ARRAY 1 #endif +static inline float +_hb_roundf (float x) { return floorf (x + .5f); } +#define roundf(x) _hb_roundf(x) -/* fallback for posix_memalign() */ -static inline int -_hb_memalign(void **memptr, size_t alignment, size_t size) -{ - if (unlikely (0 != (alignment & (alignment - 1)) || - !alignment || - 0 != (alignment & (sizeof (void *) - 1)))) - return EINVAL; - - char *p = (char *) malloc (size + alignment - 1); - if (unlikely (!p)) - return ENOMEM; +/* Endian swap, used in Windows related backends */ +static inline uint16_t hb_uint16_swap (const uint16_t v) +{ return (v >> 8) | (v << 8); } +static inline uint32_t hb_uint32_swap (const uint32_t v) +{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } - size_t off = (size_t) p & (alignment - 1); - if (off) - p += alignment - off; +/* + * Big-endian integers. Here because fundamental. + */ - *memptr = (void *) p; +template struct BEInt; - return 0; -} -#if !defined(posix_memalign) && !defined(HAVE_POSIX_MEMALIGN) -#define posix_memalign _hb_memalign -#endif +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v = V; + return *this; + } + operator Type () const { return v; } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 8) & 0xFF; + v[1] = (V ) & 0xFF; + return *this; + } + operator Type () const + { +#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ + defined(__BYTE_ORDER) && \ + (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) + /* Spoon-feed the compiler a big-endian integer with alignment 1. + * https://github.com/harfbuzz/harfbuzz/pull/1398 */ + struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; +#if __BYTE_ORDER == __LITTLE_ENDIAN + return __builtin_bswap16 (((packed_uint16_t *) this)->v); +#else /* __BYTE_ORDER == __BIG_ENDIAN */ + return ((packed_uint16_t *) this)->v; +#endif +#endif + return (v[0] << 8) + + (v[1] ); + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 16) & 0xFF; + v[1] = (V >> 8) & 0xFF; + v[2] = (V ) & 0xFF; + return *this; + } + operator Type () const + { + return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); + } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + BEInt& operator = (Type V) + { + v[0] = (V >> 24) & 0xFF; + v[1] = (V >> 16) & 0xFF; + v[2] = (V >> 8) & 0xFF; + v[3] = (V ) & 0xFF; + return *this; + } + operator Type () const + { + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); + } + private: uint8_t v[4]; +}; /* @@ -630,28 +616,18 @@ _hb_memalign(void **memptr, size_t alignment, size_t size) #define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g')) -/* Some really basic things everyone wants. */ -template struct hb_remove_const { typedef T value; }; -template struct hb_remove_const { typedef T value; }; -#define hb_remove_const(T) hb_remove_const::value -template struct hb_remove_reference { typedef T value; }; -template struct hb_remove_reference { typedef T value; }; -#define hb_remove_reference(T) hb_remove_reference::value -template struct hb_remove_pointer { typedef T value; }; -template struct hb_remove_pointer { typedef T value; }; -#define hb_remove_pointer(T) hb_remove_pointer::value - - /* Headers we include for everyone. Keep topologically sorted by dependency. * They express dependency amongst themselves, but no other file should include * them directly.*/ -#include "hb-atomic.hh" +#include "hb-meta.hh" #include "hb-mutex.hh" -#include "hb-null.hh" -#include "hb-dsalgs.hh" // Requires: hb-null -#include "hb-iter.hh" // Requires: hb-null -#include "hb-debug.hh" // Requires: hb-atomic hb-dsalgs -#include "hb-array.hh" // Requires: hb-dsalgs hb-iter hb-null +#include "hb-number.hh" +#include "hb-atomic.hh" // Requires: hb-meta +#include "hb-null.hh" // Requires: hb-meta +#include "hb-algs.hh" // Requires: hb-meta hb-null hb-number +#include "hb-iter.hh" // Requires: hb-algs hb-meta +#include "hb-debug.hh" // Requires: hb-algs hb-atomic +#include "hb-array.hh" // Requires: hb-algs hb-iter hb-null #include "hb-vector.hh" // Requires: hb-array hb-null #include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector From f39a2c89fcd44d5007fdd9372d2f38b16f0f7d89 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Sun, 8 Nov 2020 20:35:25 +0000 Subject: [PATCH 021/124] 8256015: Shenandoah: Add missing Shenandoah implementation in WB_isObjectInOldGen Reviewed-by: shade --- src/hotspot/share/prims/whitebox.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 223154ccc06c1..64734266ed61f 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -384,6 +384,11 @@ WB_ENTRY(jboolean, WB_isObjectInOldGen(JNIEnv* env, jobject o, jobject obj)) if (UseZGC) { return Universe::heap()->is_in(p); } +#endif +#if INCLUDE_SHENANDOAHGC + if (UseShenandoahGC) { + return Universe::heap()->is_in(p); + } #endif GenCollectedHeap* gch = GenCollectedHeap::heap(); return !gch->is_in_young(p); From a53b12df2d3a732a05ed79ed12e57f9f0d01fb49 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 01:12:46 +0000 Subject: [PATCH 022/124] 8255722: Create a new test for rotated blit Reviewed-by: prr --- test/jdk/ProblemList.txt | 1 + .../DrawImage/BlitRotateClippedArea.java | 142 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 test/jdk/java/awt/image/DrawImage/BlitRotateClippedArea.java diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 28ac3dfa96bbb..d1c155a7b9caa 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -254,6 +254,7 @@ java/awt/image/DrawImage/IncorrectAlphaSurface2SW.java 8056077 generic-all java/awt/image/DrawImage/IncorrectClipXorModeSW2Surface.java 8196025 windows-all java/awt/image/DrawImage/IncorrectClipXorModeSurface2Surface.java 8196025 windows-all java/awt/image/DrawImage/IncorrectSourceOffset.java 8196086 windows-all +java/awt/image/DrawImage/BlitRotateClippedArea.java 8255724 linux-all java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java 8198390 generic-all java/awt/image/multiresolution/MultiresolutionIconTest.java 8169187 macosx-all,windows-all java/awt/print/Headless/HeadlessPrinterJob.java 8196088 windows-all diff --git a/test/jdk/java/awt/image/DrawImage/BlitRotateClippedArea.java b/test/jdk/java/awt/image/DrawImage/BlitRotateClippedArea.java new file mode 100644 index 0000000000000..5db3fca9b602a --- /dev/null +++ b/test/jdk/java/awt/image/DrawImage/BlitRotateClippedArea.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import static java.awt.Transparency.TRANSLUCENT; + +/** + * @test + * @bug 8255722 + * @key headful + */ +public class BlitRotateClippedArea { + + /** + * The test use case: + * 1. The destination image is created of size 1000x1000 + * 2. The source image is created of size 2000x2000 + * 3. The source image is painted by the pattern outsize of 1000x1000 + * 4. If the source image is painted as-is to the destination then the + * pattern in the source will be ignored, but the test sets some + * specific rotation that the pattern will hit the source. + * Note that rotation is used not a scale/translate. + */ + public static void main(String[] args) throws Exception { + // the test check the exact pixels location + System.setProperty("sun.java2d.uiScale", "1"); + var ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + var gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + + var gold = gc.createCompatibleImage(1000, 1000, TRANSLUCENT); + var dstVI2BI = gc.createCompatibleImage(1000, 1000, TRANSLUCENT); + var dstVI2VI = gc.createCompatibleVolatileImage(1000, 1000, TRANSLUCENT); + var dstBI2VI = gc.createCompatibleVolatileImage(1000, 1000, TRANSLUCENT); + + var srcBI = gc.createCompatibleImage(2000, 2000, TRANSLUCENT); + var srcVI = gc.createCompatibleVolatileImage(2000, 2000, TRANSLUCENT); + + int attempt = 0; + BufferedImage snapshotVI2VI; + BufferedImage snapshotBI2VI; + do { + if (++attempt > 10) { + throw new RuntimeException("Too many attempts: " + attempt); + } + dstVI2VI.validate(gc); + dstBI2VI.validate(gc); + srcVI.validate(gc); + + fill(srcBI); + fill(srcVI); + + init(gold); + init(dstVI2BI); + init(dstVI2VI); + init(dstBI2VI); + + draw(gold, srcBI); + draw(dstVI2BI, srcVI); + draw(dstVI2VI, srcVI); + draw(dstBI2VI, srcBI); + + snapshotVI2VI = dstVI2VI.getSnapshot(); + snapshotBI2VI = dstBI2VI.getSnapshot(); + } while (dstVI2VI.contentsLost() || dstBI2VI.contentsLost() + || srcVI.contentsLost()); + + validate(gold, snapshotVI2VI); + validate(gold, snapshotBI2VI); + validate(gold, dstVI2BI); + } + + private static void validate(BufferedImage gold, BufferedImage img) + throws IOException { + for (int x = 0; x < gold.getWidth(); ++x) { + for (int y = 0; y < gold.getHeight(); ++y) { + if (gold.getRGB(x, y) != img.getRGB(x, y)) { + ImageIO.write(gold, "png", new File("gold.png")); + ImageIO.write(img, "png", new File("snapshot.png")); + throw new RuntimeException("Test failed."); + } + } + } + } + + private static void draw(Image dstBI, Image src) { + Graphics2D g = (Graphics2D) dstBI.getGraphics(); + g.rotate(Math.toRadians(180), 1250, 1150); + g.drawImage(src, 0, 0, null); + g.dispose(); + } + + private static void init(Image image) { + Graphics2D graphics = (Graphics2D) image.getGraphics(); + graphics.setComposite(AlphaComposite.Src); + graphics.setColor(Color.YELLOW); + graphics.fillRect(0, 0, image.getWidth(null), image.getHeight(null)); + graphics.dispose(); + } + + private static void fill(Image image) { + Graphics2D graphics = (Graphics2D) image.getGraphics(); + graphics.setComposite(AlphaComposite.Src); + for (int x = 1000; x < image.getWidth(null); ++x) { + for (int y = 1000; y < image.getHeight(null); ++y) { + graphics.setColor(new Color(x % 256, 0, y % 256, 125)); + graphics.fillRect(x, y, 1, 1); + } + } + graphics.dispose(); + } +} + From 2cad8368336bd9624f9400b81202bdd07f50c1dc Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 01:28:19 +0000 Subject: [PATCH 023/124] 8255575: java.awt.color.ICC_ColorSpace is not thread-safe Reviewed-by: prr --- .../java/awt/color/ICC_ColorSpace.java | 144 +++++++++--------- .../awt/color/MTICC_ColorSpaceToFrom.java | 102 +++++++++++++ 2 files changed, 178 insertions(+), 68 deletions(-) create mode 100644 test/jdk/java/awt/color/MTICC_ColorSpaceToFrom.java diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java index 73b5f1ebb922e..da571ab5db7db 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_ColorSpace.java @@ -115,10 +115,10 @@ public class ICC_ColorSpace extends ColorSpace { private boolean needScaleInit = true; // {to,from}{RGB,CIEXYZ} methods create and cache these when needed - private transient ColorTransform this2srgb; - private transient ColorTransform srgb2this; - private transient ColorTransform this2xyz; - private transient ColorTransform xyz2this; + private transient volatile ColorTransform this2srgb; + private transient volatile ColorTransform srgb2this; + private transient volatile ColorTransform this2xyz; + private transient volatile ColorTransform xyz2this; /** * Constructs a new {@code ICC_ColorSpace} from an {@code ICC_Profile} @@ -193,20 +193,22 @@ public ICC_Profile getProfile() { * @throws ArrayIndexOutOfBoundsException if array length is not at least * the number of components in this {@code ColorSpace} */ - public float[] toRGB (float[] colorvalue) { - + public float[] toRGB(float[] colorvalue) { if (this2srgb == null) { - ColorTransform[] transformList = new ColorTransform [2]; - ICC_ColorSpace srgbCS = - (ICC_ColorSpace) ColorSpace.getInstance (CS_sRGB); - PCMM mdl = CMSManager.getModule(); - transformList[0] = mdl.createTransform( - thisProfile, ColorTransform.Any, ColorTransform.In); - transformList[1] = mdl.createTransform( - srgbCS.getProfile(), ColorTransform.Any, ColorTransform.Out); - this2srgb = mdl.createTransform(transformList); - if (needScaleInit) { - setComponentScaling(); + synchronized (this) { + if (this2srgb == null) { + ColorTransform[] transforms = new ColorTransform[2]; + var srgb = (ICC_ColorSpace) getInstance(CS_sRGB); + PCMM mdl = CMSManager.getModule(); + transforms[0] = mdl.createTransform(thisProfile, + ColorTransform.Any, ColorTransform.In); + transforms[1] = mdl.createTransform(srgb.getProfile(), + ColorTransform.Any, ColorTransform.Out); + if (needScaleInit) { + setComponentScaling(); + } + this2srgb = mdl.createTransform(transforms); + } } } @@ -243,20 +245,22 @@ public float[] toRGB (float[] colorvalue) { * this {@code ColorSpace} * @throws ArrayIndexOutOfBoundsException if array length is not at least 3 */ - public float[] fromRGB(float[] rgbvalue) { - + public float[] fromRGB(float[] rgbvalue) { if (srgb2this == null) { - ColorTransform[] transformList = new ColorTransform [2]; - ICC_ColorSpace srgbCS = - (ICC_ColorSpace) ColorSpace.getInstance (CS_sRGB); - PCMM mdl = CMSManager.getModule(); - transformList[0] = mdl.createTransform( - srgbCS.getProfile(), ColorTransform.Any, ColorTransform.In); - transformList[1] = mdl.createTransform( - thisProfile, ColorTransform.Any, ColorTransform.Out); - srgb2this = mdl.createTransform(transformList); - if (needScaleInit) { - setComponentScaling(); + synchronized (this) { + if (srgb2this == null) { + ColorTransform[] transforms = new ColorTransform[2]; + var srgb = (ICC_ColorSpace) getInstance(CS_sRGB); + PCMM mdl = CMSManager.getModule(); + transforms[0] = mdl.createTransform(srgb.getProfile(), + ColorTransform.Any, ColorTransform.In); + transforms[1] = mdl.createTransform(thisProfile, + ColorTransform.Any, ColorTransform.Out); + if (needScaleInit) { + setComponentScaling(); + } + srgb2this = mdl.createTransform(transforms); + } } } @@ -373,26 +377,28 @@ public float[] fromRGB(float[] rgbvalue) { * @throws ArrayIndexOutOfBoundsException if array length is not at least * the number of components in this {@code ColorSpace} */ - public float[] toCIEXYZ(float[] colorvalue) { - + public float[] toCIEXYZ(float[] colorvalue) { if (this2xyz == null) { - ColorTransform[] transformList = new ColorTransform [2]; - ICC_ColorSpace xyzCS = - (ICC_ColorSpace) ColorSpace.getInstance (CS_CIEXYZ); - PCMM mdl = CMSManager.getModule(); - try { - transformList[0] = mdl.createTransform( - thisProfile, ICC_Profile.icRelativeColorimetric, - ColorTransform.In); - } catch (CMMException e) { - transformList[0] = mdl.createTransform( - thisProfile, ColorTransform.Any, ColorTransform.In); - } - transformList[1] = mdl.createTransform( - xyzCS.getProfile(), ColorTransform.Any, ColorTransform.Out); - this2xyz = mdl.createTransform (transformList); - if (needScaleInit) { - setComponentScaling(); + synchronized (this) { + if (this2xyz == null) { + ColorTransform[] transforms = new ColorTransform[2]; + var xyz = (ICC_ColorSpace) getInstance(CS_CIEXYZ); + PCMM mdl = CMSManager.getModule(); + try { + transforms[0] = mdl.createTransform(thisProfile, + ICC_Profile.icRelativeColorimetric, + ColorTransform.In); + } catch (CMMException e) { + transforms[0] = mdl.createTransform(thisProfile, + ColorTransform.Any, ColorTransform.In); + } + transforms[1] = mdl.createTransform(xyz.getProfile(), + ColorTransform.Any, ColorTransform.Out); + if (needScaleInit) { + setComponentScaling(); + } + this2xyz = mdl.createTransform(transforms); + } } } @@ -511,26 +517,28 @@ public float[] toCIEXYZ(float[] colorvalue) { * this {@code ColorSpace} * @throws ArrayIndexOutOfBoundsException if array length is not at least 3 */ - public float[] fromCIEXYZ(float[] colorvalue) { - + public float[] fromCIEXYZ(float[] colorvalue) { if (xyz2this == null) { - ColorTransform[] transformList = new ColorTransform [2]; - ICC_ColorSpace xyzCS = - (ICC_ColorSpace) ColorSpace.getInstance (CS_CIEXYZ); - PCMM mdl = CMSManager.getModule(); - transformList[0] = mdl.createTransform ( - xyzCS.getProfile(), ColorTransform.Any, ColorTransform.In); - try { - transformList[1] = mdl.createTransform( - thisProfile, ICC_Profile.icRelativeColorimetric, - ColorTransform.Out); - } catch (CMMException e) { - transformList[1] = CMSManager.getModule().createTransform( - thisProfile, ColorTransform.Any, ColorTransform.Out); - } - xyz2this = mdl.createTransform(transformList); - if (needScaleInit) { - setComponentScaling(); + synchronized (this) { + if (xyz2this == null) { + ColorTransform[] transforms = new ColorTransform[2]; + var xyz = (ICC_ColorSpace) getInstance(CS_CIEXYZ); + PCMM mdl = CMSManager.getModule(); + transforms[0] = mdl.createTransform(xyz.getProfile(), + ColorTransform.Any, ColorTransform.In); + try { + transforms[1] = mdl.createTransform(thisProfile, + ICC_Profile.icRelativeColorimetric, + ColorTransform.Out); + } catch (CMMException e) { + transforms[1] = mdl.createTransform(thisProfile, + ColorTransform.Any, ColorTransform.Out); + } + if (needScaleInit) { + setComponentScaling(); + } + xyz2this = mdl.createTransform(transforms); + } } } diff --git a/test/jdk/java/awt/color/MTICC_ColorSpaceToFrom.java b/test/jdk/java/awt/color/MTICC_ColorSpaceToFrom.java new file mode 100644 index 0000000000000..0a64e026d4e92 --- /dev/null +++ b/test/jdk/java/awt/color/MTICC_ColorSpaceToFrom.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.color.ColorSpace; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * @test + * @bug 8254370 + * @summary Verifies MT safety of ICC_ColorSpace#To/From methods + */ +public final class MTICC_ColorSpaceToFrom { + + private enum Method { + FROM_RGB, FROM_XYZ, TO_RGB, TO_XYZ; + } + + static volatile long endtime; + static volatile boolean failed; + + public static void main(String[] args) throws Exception { + ICC_Profile srgb = ICC_Profile.getInstance(ColorSpace.CS_sRGB); + ICC_Profile gray = ICC_Profile.getInstance(ColorSpace.CS_GRAY); + ICC_Profile xyz = ICC_Profile.getInstance(ColorSpace.CS_CIEXYZ); + ICC_Profile lrgb = ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB); + ICC_Profile pycc = ICC_Profile.getInstance(ColorSpace.CS_PYCC); + + // Will run the test no more than 15 seconds + endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); + for (int i = 0; i < 1000 && !isComplete(); i++) { + for (Method method : new Method[]{Method.FROM_RGB, Method.FROM_XYZ, + Method.TO_RGB, Method.TO_XYZ}) { + test(new ICC_ColorSpace(srgb), method); + test(new ICC_ColorSpace(gray), method); + test(new ICC_ColorSpace(xyz), method); + test(new ICC_ColorSpace(lrgb), method); + test(new ICC_ColorSpace(pycc), method); + } + } + if (failed) { + throw new RuntimeException(); + } + } + + private static void test(ColorSpace cs, Method method) throws Exception { + Thread[] ts = new Thread[10]; + CountDownLatch latch = new CountDownLatch(ts.length); + for (int i = 0; i < ts.length; i++) { + ts[i] = new Thread(() -> { + latch.countDown(); + try { + latch.await(); + } catch (InterruptedException ex) { + } + try { + switch (method) { + case TO_RGB -> cs.toRGB(new float[3]); + case FROM_RGB -> cs.fromRGB(new float[3]); + case TO_XYZ -> cs.toCIEXYZ(new float[3]); + case FROM_XYZ -> cs.fromCIEXYZ(new float[3]); + } + } catch (Throwable t) { + t.printStackTrace(); + failed = true; + } + }); + } + for (Thread t : ts) { + t.start(); + } + for (Thread t : ts) { + t.join(); + } + } + + private static boolean isComplete() { + return endtime - System.nanoTime() < 0 || failed; + } +} From 2c8f4e202bd7e5b78edb32580b8a836660493d9c Mon Sep 17 00:00:00 2001 From: Anton Kozlov Date: Mon, 9 Nov 2020 01:35:26 +0000 Subject: [PATCH 024/124] 8255799: AArch64: CPU_A53MAC feature may be set incorrectly Reviewed-by: ngasson, aph --- src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index 18f56a527ab91..f9d836ffb411f 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -181,10 +181,6 @@ void VM_Version::initialize() { } if (_cpu == CPU_ARM && (_model == 0xd07 || _model2 == 0xd07)) _features |= CPU_STXR_PREFETCH; - // If an olde style /proc/cpuinfo (cores == 1) then if _model is an A57 (0xd07) - // we assume the worst and assume we could be on a big little system and have - // undisclosed A53 cores which we could be swapped to at any stage - if (_cpu == CPU_ARM && os::processor_count() == 1 && _model == 0xd07) _features |= CPU_A53MAC; char buf[512]; sprintf(buf, "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision); From 3ce09c05c40f7435fcfa01260fcf8c2769202536 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 01:38:28 +0000 Subject: [PATCH 025/124] 8255920: J2DBench should support CS_PYCC color profile Reviewed-by: prr --- .../java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java b/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java index 210970f6469fc..3c0f936358c80 100644 --- a/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java +++ b/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java @@ -73,14 +73,16 @@ public static void init() { ColorSpace.CS_sRGB, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, - ColorSpace.CS_CIEXYZ + ColorSpace.CS_CIEXYZ, + ColorSpace.CS_PYCC }; String[] csNames = new String[]{ "CS_sRGB", "CS_GRAY", "CS_LINEAR_RGB", - "CS_CIEXYZ" + "CS_CIEXYZ", + "CS_PYCC" }; csList = new Option.IntList(cmmOptRoot, From 2d6c28db2404ce764fcdd8420e2fa73db6969b9c Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 06:35:50 +0000 Subject: [PATCH 026/124] 6847157: java.lang.NullPointerException: HDC for component at sun.java2d.loops.Blit.Blit Reviewed-by: prr --- .../java2d/windows/GDIWindowSurfaceData.cpp | 7 +- .../java/awt/Paint/RepaintOnAWTShutdown.java | 88 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/jdk/java/awt/Paint/RepaintOnAWTShutdown.java diff --git a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp index bada561fe8c53..804a8efb8e926 100644 --- a/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp +++ b/src/java.desktop/windows/native/libawt/java2d/windows/GDIWindowSurfaceData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -125,9 +125,14 @@ void SetupThreadGraphicsInfo(JNIEnv *env, GDIWinSDOps *wsdo) { // First, init the HDC object AwtComponent *comp = GDIWindowSurfaceData_GetComp(env, wsdo); if (comp == NULL) { + // wsdo->invalid is set by GDIWindowSurfaceData_GetComp return; } hDC = comp->GetDCFromComponent(); + if (hDC == NULL) { + wsdo->invalid = JNI_TRUE; + return; + } if (hDC != NULL && wsdo->device != NULL) { ::SelectObject(hDC, nullbrush); ::SelectObject(hDC, nullpen); diff --git a/test/jdk/java/awt/Paint/RepaintOnAWTShutdown.java b/test/jdk/java/awt/Paint/RepaintOnAWTShutdown.java new file mode 100644 index 0000000000000..6c94ee29a7f10 --- /dev/null +++ b/test/jdk/java/awt/Paint/RepaintOnAWTShutdown.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.swing.JFrame; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.SwingUtilities; + +/** + * @test + * @bug 6847157 + * @key headful + * @summary the java2D/AWT should die silently without exceptions + * @run main/othervm RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=1 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=1.2 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=1.25 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=1.5 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=1.75 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=2 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=2.25 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=5 RepaintOnAWTShutdown + * @run main/othervm -Dsun.java2d.uiScale=10 RepaintOnAWTShutdown + */ +public final class RepaintOnAWTShutdown implements Runnable { + + private static final CountDownLatch go = new CountDownLatch(1); + + public static void main(String[] args) throws Exception { + SwingUtilities.invokeLater(new RepaintOnAWTShutdown()); + go.await(5, TimeUnit.SECONDS); + // The test will check that no exception is thrown when the jtreg will + // kill this test at the moment the frame will be painted + } + + public void run() { + JFrame frame = new JFrame(); + JPanel panel = new MyPanel(); + panel.setPreferredSize(new Dimension(100, 100)); + panel.setLayout(new FlowLayout()); + panel.add(new JTree()); + panel.add(new JList(new String[]{"one", "two"})); + panel.add(new JTable(new String[][]{{"one", "two"}}, + new String[]{"one", "two"})); + frame.add(panel); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + // the frame is not disposed intentionally + } + + + private final class MyPanel extends JPanel { + + public void paint(Graphics g) { + super.paint(g); + go.countDown(); + } + } +} From c7551c37c7e9be7112371c351a4cc0d0d817cb46 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 06:38:53 +0000 Subject: [PATCH 027/124] 8256014: Eliminate the warning about serialization in non-public API of Swing Reviewed-by: prr, psadhukhan --- .../java/swing/plaf/motif/MotifBorders.java | 27 +++---- .../java/swing/plaf/motif/MotifButtonUI.java | 33 +++++---- .../swing/plaf/motif/MotifCheckBoxUI.java | 20 ++--- .../swing/plaf/motif/MotifComboBoxUI.java | 9 +-- .../swing/plaf/motif/MotifDesktopPaneUI.java | 29 ++++---- .../swing/plaf/motif/MotifEditorPaneUI.java | 16 ++-- .../swing/plaf/motif/MotifIconFactory.java | 24 ++---- .../plaf/motif/MotifInternalFrameUI.java | 40 +++++----- .../java/swing/plaf/motif/MotifLabelUI.java | 17 ++--- .../swing/plaf/motif/MotifLookAndFeel.java | 29 +++----- .../java/swing/plaf/motif/MotifMenuBarUI.java | 28 +------ .../swing/plaf/motif/MotifOptionPaneUI.java | 23 +++--- .../plaf/motif/MotifPasswordFieldUI.java | 16 ++-- .../swing/plaf/motif/MotifPopupMenuUI.java | 33 +++------ .../swing/plaf/motif/MotifProgressBarUI.java | 18 +---- .../motif/MotifRadioButtonMenuItemUI.java | 28 ++++--- .../swing/plaf/motif/MotifRadioButtonUI.java | 24 +++--- .../plaf/motif/MotifScrollBarButton.java | 20 ++--- .../swing/plaf/motif/MotifScrollBarUI.java | 11 +-- .../swing/plaf/motif/MotifScrollPaneUI.java | 23 +++--- .../swing/plaf/motif/MotifSeparatorUI.java | 18 +---- .../java/swing/plaf/motif/MotifSliderUI.java | 11 +-- .../plaf/motif/MotifSplitPaneDivider.java | 21 +++--- .../swing/plaf/motif/MotifSplitPaneUI.java | 16 +--- .../swing/plaf/motif/MotifTabbedPaneUI.java | 22 ++---- .../swing/plaf/motif/MotifTextAreaUI.java | 16 ++-- .../swing/plaf/motif/MotifTextFieldUI.java | 14 +--- .../swing/plaf/motif/MotifTextPaneUI.java | 16 ++-- .../java/swing/plaf/motif/MotifTextUI.java | 37 +++++----- .../swing/plaf/motif/MotifToggleButtonUI.java | 32 ++++---- .../plaf/motif/MotifTreeCellRenderer.java | 33 +++------ .../java/swing/plaf/motif/MotifTreeUI.java | 42 +++-------- .../plaf/windows/WindowsButtonListener.java | 18 +---- .../swing/plaf/windows/WindowsButtonUI.java | 43 ++++++----- .../windows/WindowsCheckBoxMenuItemUI.java | 23 +++--- .../swing/plaf/windows/WindowsCheckBoxUI.java | 20 ++--- .../windows/WindowsClassicLookAndFeel.java | 9 +-- .../swing/plaf/windows/WindowsComboBoxUI.java | 58 ++++++++++----- .../plaf/windows/WindowsDesktopIconUI.java | 21 ++---- .../plaf/windows/WindowsDesktopPaneUI.java | 14 +--- .../plaf/windows/WindowsEditorPaneUI.java | 16 +--- .../plaf/windows/WindowsIconFactory.java | 41 +++++++---- .../plaf/windows/WindowsInternalFrameUI.java | 28 +++---- .../swing/plaf/windows/WindowsLabelUI.java | 17 +---- .../plaf/windows/WindowsLookAndFeel.java | 73 ++++++++++++------- .../swing/plaf/windows/WindowsMenuBarUI.java | 35 +++++---- .../swing/plaf/windows/WindowsMenuItemUI.java | 33 +++++---- .../swing/plaf/windows/WindowsMenuUI.java | 26 ++++--- .../plaf/windows/WindowsOptionPaneUI.java | 17 +---- .../plaf/windows/WindowsPasswordFieldUI.java | 17 +---- .../plaf/windows/WindowsPopupMenuUI.java | 32 ++++---- .../plaf/windows/WindowsPopupWindow.java | 15 ++-- .../plaf/windows/WindowsProgressBarUI.java | 34 +++++---- .../windows/WindowsRadioButtonMenuItemUI.java | 22 +++--- .../plaf/windows/WindowsRadioButtonUI.java | 27 ++++--- .../plaf/windows/WindowsScrollBarUI.java | 41 ++++++----- .../plaf/windows/WindowsScrollPaneUI.java | 14 +--- .../swing/plaf/windows/WindowsSliderUI.java | 26 +++---- .../plaf/windows/WindowsSplitPaneDivider.java | 18 ++--- .../plaf/windows/WindowsSplitPaneUI.java | 18 +---- .../plaf/windows/WindowsTabbedPaneUI.java | 34 ++++----- .../swing/plaf/windows/WindowsTextAreaUI.java | 17 +---- .../plaf/windows/WindowsTextFieldUI.java | 35 +++++---- .../swing/plaf/windows/WindowsTextPaneUI.java | 16 +--- .../swing/plaf/windows/WindowsTextUI.java | 23 +++--- .../plaf/windows/WindowsToggleButtonUI.java | 30 ++++---- .../swing/plaf/windows/WindowsTreeUI.java | 55 +++++--------- 67 files changed, 708 insertions(+), 1004 deletions(-) diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java index 2b1b9b89f7ab3..c61b12588949b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,11 +25,6 @@ package com.sun.java.swing.plaf.motif; -import sun.swing.SwingUtilities2; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.*; - import java.awt.Color; import java.awt.Component; import java.awt.Dimension; @@ -40,15 +35,21 @@ import java.awt.Point; import java.awt.Rectangle; +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.JMenuBar; +import javax.swing.JPopupMenu; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.UIResource; + +import sun.swing.SwingUtilities2; + /** * Factory object that can vend Icons appropriate for the basic {@literal L & F}. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Amy Fowler */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java index eb2520cd2b1ea..08017a0b2c88a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,27 @@ package com.sun.java.swing.plaf.motif; -import sun.awt.AppContext; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonListener; +import javax.swing.plaf.basic.BasicButtonUI; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.basic.*; -import java.awt.*; -import java.awt.event.*; -import javax.swing.plaf.*; +import sun.awt.AppContext; /** * MotifButton implementation - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Rich Schiavi */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java index 11dc92d3a8d35..fa06aa6bd02a4 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,15 @@ package com.sun.java.swing.plaf.motif; -import sun.awt.AppContext; - -import javax.swing.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.*; - -import java.awt.*; +import sun.awt.AppContext; /** * MotifCheckBox implementation - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Rich Schiavi */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java index fcd12ac20d4e6..254b63181b385 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifComboBoxUI.java @@ -52,14 +52,7 @@ import javax.swing.plaf.basic.ComboPopup; /** - * ComboBox motif look and feel - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * ComboBox motif look and feel. * * @author Arnaud Weber */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java index c3e1f85993b6d..60552b8c780ba 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,26 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import java.awt.Rectangle; -import java.awt.Dimension; -import java.awt.Insets; import java.awt.Color; -import java.awt.Graphics; import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; import java.awt.Point; -import javax.swing.plaf.*; +import java.awt.Rectangle; import java.io.Serializable; +import javax.swing.DefaultDesktopManager; +import javax.swing.JComponent; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; + /** - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * Motif desktop pane. * * @author David Kloba */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java index c7943dff79f8c..3367b36f0fd4a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicEditorPaneUI; +import javax.swing.text.Caret; /** * Provides the look and feel for an pluggable content-type text editor. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java index c7cf812dbf5f3..dac6514f0c43d 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,29 +25,21 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; - -import javax.swing.plaf.UIResource; - import java.awt.Color; import java.awt.Component; -import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Polygon; - import java.io.Serializable; +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.UIManager; +import javax.swing.plaf.UIResource; + /** * Icon factory for the CDE/Motif Look and Feel - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * - * 1.20 04/27/99 * @author Georges Saab */ @SuppressWarnings("serial") // Same-version serialization only diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java index 8d9e5122ef60f..cba71847fa43c 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,27 +25,29 @@ package com.sun.java.swing.plaf.motif; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.*; - -import java.util.EventListener; - -import javax.swing.plaf.basic.*; -import javax.swing.border.*; -import javax.swing.plaf.*; - +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.KeyStroke; +import javax.swing.LookAndFeel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicInternalFrameUI; /** * A Motif {@literal L&F} implementation of InternalFrame. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Tom Ball */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java index 1da0036e3fae3..f19a3c23cb59e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,15 @@ package com.sun.java.swing.plaf.motif; -import sun.awt.AppContext; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicLabelUI; +import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicLabelUI; + +import sun.awt.AppContext; /** * A Motif {@literal L&F} implementation of LabelUI. * This merely sets up new default values in MotifLookAndFeel. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Amy Fowler */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index 455c521493da9..d4993c11eb989 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java @@ -27,37 +27,26 @@ import java.awt.Color; import java.awt.Font; -import java.awt.Insets; import java.awt.event.KeyEvent; -import java.awt.event.InputEvent; -import java.util.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.border.*; -import javax.swing.text.JTextComponent; -import javax.swing.text.DefaultEditorKit; - -import javax.swing.plaf.basic.BasicLookAndFeel; +import javax.swing.JTextField; +import javax.swing.UIDefaults; +import javax.swing.border.Border; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.InsetsUIResource; import javax.swing.plaf.basic.BasicBorders; -import javax.swing.plaf.basic.BasicComboBoxRenderer; -import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.basic.BasicLookAndFeel; +import javax.swing.text.DefaultEditorKit; import sun.swing.SwingAccessor; import sun.swing.SwingUtilities2; -import sun.awt.OSInfo; /** * Implements the Motif Look and Feel. * UI classes not implemented specifically for Motif will * default to those implemented in Basic. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @deprecated The Motif Look and Feel is deprecated with the intent to remove * it in some future release. It is recommended to use diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuBarUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuBarUI.java index cdc05400fe2ac..4e9beaa8ab4b3 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuBarUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,35 +25,13 @@ package com.sun.java.swing.plaf.motif; -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Color; -import java.awt.Insets; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.*; -import java.io.Serializable; - -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.border.*; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuBarUI; -//REMIND -import javax.swing.plaf.basic.*; /** * A Windows {@literal L&F} implementation of MenuBarUI. This implementation * is a "combined" view/controller. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Georges Saab * @author Rich Schiavi diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java index ddba46c9cc9d4..7da390bad230b 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,21 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.plaf.basic.BasicOptionPaneUI; -import javax.swing.plaf.ComponentUI; import java.awt.Color; -import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.Insets; -import java.awt.Rectangle; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicOptionPaneUI; /** * Provides the CDE/Motif look and feel for a JOptionPane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Scott Violet */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java index 3b0159c8d7b4b..8e1369d7e9643 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPasswordFieldUI; +import javax.swing.text.Caret; /** * Provides the Motif look and feel for a password field. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java index a0b182b8019c2..0986f81f0fffb 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,36 +25,27 @@ package com.sun.java.swing.plaf.motif; -import sun.swing.SwingUtilities2; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.border.*; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; -import java.awt.Frame; -import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.*; -import javax.swing.plaf.*; +import java.awt.event.MouseEvent; + +import javax.swing.JComponent; +import javax.swing.JPopupMenu; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicPopupMenuUI; +import sun.swing.SwingUtilities2; /** * A Motif {@literal L&F} implementation of PopupMenuUI. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Georges Saab * @author Rich Schiavi diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifProgressBarUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifProgressBarUI.java index 66d99e007d47c..2c53d741397d9 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifProgressBarUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,12 @@ package com.sun.java.swing.plaf.motif; -import java.awt.*; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; -import java.io.Serializable; - +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicProgressBarUI; - /** * A Motif ProgressBarUI. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Michael C. Albers */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java index 3cc793fccae8f..1a9d7fa1e9153 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,24 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.io.Serializable; + +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.LookAndFeel; +import javax.swing.MenuSelectionManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.MouseInputListener; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; -import java.awt.*; -import java.awt.event.*; -import java.io.Serializable; import sun.swing.SwingUtilities2; - /** * MotifRadioButtonMenuItem implementation - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Georges Saab * @author Rich Schiavi diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java index 328ca4844ed4b..5861928049303 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,21 @@ package com.sun.java.swing.plaf.motif; -import sun.awt.AppContext; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; -import javax.swing.*; -import javax.swing.border.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicRadioButtonUI; -import javax.swing.plaf.*; - -import java.awt.*; +import sun.awt.AppContext; /** * RadioButtonUI implementation for MotifRadioButtonUI - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Rich Schiavi */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java index cb586512c82e2..5b812ca4c8c52 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarButton.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,15 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicArrowButton; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; -import java.awt.*; -import java.awt.event.*; +import javax.swing.UIManager; +import javax.swing.plaf.basic.BasicArrowButton; /** * Motif scroll bar button. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class MotifScrollBarButton extends BasicArrowButton diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java index 294a1c374ab82..08a283f5e5201 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; import java.awt.Dimension; @@ -38,16 +39,8 @@ import static sun.swing.SwingUtilities2.drawHLine; import static sun.swing.SwingUtilities2.drawVLine; - /** * Implementation of ScrollBarUI for the Motif Look and Feel - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Rich Schiavi * @author Hans Muller diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollPaneUI.java index af65909567246..833cf732b0a4e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,20 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicScrollPaneUI; - import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicScrollPaneUI; + /** * A CDE/Motif {@code L&F} implementation of ScrollPaneUI. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Hans Muller */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSeparatorUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSeparatorUI.java index bc457b01b1371..dac713eda7ba0 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSeparatorUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSeparatorUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,13 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Insets; -import java.awt.Rectangle; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSeparatorUI; /** * A Motif {@literal L&F} implementation of SeparatorUI. * This implementation is a "combined" view/controller. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Georges Saab * @author Jeff Shapiro diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java index e22db5e05cd06..c8820de17c4cc 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,14 +38,7 @@ import static sun.swing.SwingUtilities2.drawVLine; /** - * Motif Slider - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * Motif Slider. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java index 6040eb3132372..7dceae4930d41 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,20 @@ package com.sun.java.swing.plaf.motif; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.event.MouseEvent; + import javax.swing.JSplitPane; import javax.swing.UIManager; -import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; - +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Divider used for Motif split pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java index 6ee670812c3a5..c61a1713c9b45 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,13 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.plaf.basic.BasicSplitPaneUI; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; -import javax.swing.plaf.*; -import javax.swing.*; -import java.awt.*; +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Motif rendition of a split pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java index e7bae098a5519..1c53fc4a17989 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,17 @@ package com.sun.java.swing.plaf.motif; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTabbedPaneUI; -import java.io.Serializable; /** * A Motif {@literal L&F} implementation of TabbedPaneUI. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Amy Fowler * @author Philip Milne diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java index c6f4cc54b3a2e..baddbf3a75d07 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,24 +22,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTextAreaUI; +import javax.swing.text.Caret; /** * Provides the look and feel for a plain text editor. In this * implementation the default UI is extended to act as a simple * view factory. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java index 895183c1e6a0e..08437687ebc4c 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTextFieldUI; -import javax.swing.plaf.*; import javax.swing.text.Caret; /** * Provides the Motif look and feel for a text field. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java index 3e1590a883155..e47ad4c60bf0e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,22 +22,16 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.plaf.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTextPaneUI; +import javax.swing.text.Caret; /** * Provides the look and feel for a styled text editor. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java index a2a85e9cbff0b..b79ef19df8e4a 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,25 +22,29 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.motif; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; -import javax.swing.*; -import javax.swing.text.*; -import javax.swing.plaf.*; +import javax.swing.KeyStroke; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.UIResource; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.JTextComponent; /** * Provides the look and feel features that are common across * the Motif/CDE text LAF implementations. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ @@ -58,13 +62,6 @@ public static Caret createCaret() { /** * The motif caret is rendered as an I beam. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Superclass is not serializable across versions public static class MotifCaret extends DefaultCaret implements UIResource { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java index b13c87e392bb6..eef60ade9c9ce 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,24 @@ package com.sun.java.swing.plaf.motif; -import sun.awt.AppContext; - -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicToggleButtonUI; +import sun.awt.AppContext; /** - * BasicToggleButton implementation - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * BasicToggleButton implementation. * * @author Rich Schiavi */ diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java index fbddca818418b..78ec251f05dc1 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,18 @@ package com.sun.java.swing.plaf.motif; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.tree.*; -import java.awt.*; -import java.awt.event.*; -import java.beans.*; -import java.io.*; -import java.util.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.io.Serializable; + +import javax.swing.Icon; +import javax.swing.UIManager; +import javax.swing.plaf.IconUIResource; +import javax.swing.tree.DefaultTreeCellRenderer; /** * Motif rendered to display a tree cell. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ @@ -62,13 +56,6 @@ public static Icon loadLeafIcon() { /** * Icon for a node with no children. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Same-version serialization only public static class TreeLeafIcon implements Icon, Serializable { diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java index 5977b80730016..468ef4eb56532 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/motif/MotifTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,20 @@ package com.sun.java.swing.plaf.motif; -import java.awt.*; -import java.awt.event.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.io.Serializable; -import java.io.*; -import java.util.*; - -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.tree.*; -import javax.swing.plaf.basic.*; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.TreeCellRenderer; /** * Motif rendition of the tree component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ @@ -83,13 +77,6 @@ protected void paintHorizontalLine( Graphics g, JComponent c, int y, int left, i /** * The minus sign button icon. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Same-version serialization only public static class MotifExpandedIcon implements Icon, Serializable { @@ -132,13 +119,6 @@ public void paintIcon(Component c, Graphics g, int x, int y) { /** * The plus sign button icon. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Same-version serialization only public static class MotifCollapsedIcon extends MotifExpandedIcon { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java index a78298cada2ac..11b0a8de24e1c 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,23 +23,13 @@ * questions. */ - package com.sun.java.swing.plaf.windows; -import java.beans.PropertyChangeEvent; - -import javax.swing.*; -import javax.swing.plaf.basic.*; +import javax.swing.AbstractButton; +import javax.swing.plaf.basic.BasicButtonListener; /** - * Button Listener - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * Button Listener. * * @author Rich Schiavi */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java index 89a2a18e115cb..6b3d9b004bfde 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,38 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JRadioButton; +import javax.swing.JToolBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicButtonUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; -import java.awt.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.TMSchema.Part.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; import sun.awt.AppContext; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows button. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins - * */ public class WindowsButtonUI extends BasicButtonUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java index 99966cb210e14..c7007bec3db8e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,21 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicCheckBoxMenuItemUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; - /** * Windows check box menu item. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsCheckBoxMenuItemUI extends BasicCheckBoxMenuItemUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java index 91c919c871130..65b1131a351d9 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsCheckBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,15 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; -import java.awt.*; +import sun.awt.AppContext; /** * Windows check box. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java index 5141fa399dacd..6a2c9eeae3181 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsClassicLookAndFeel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,6 @@ /** * Implements the Windows95/98/ME/NT/2000 Look and Feel. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @since 1.5 */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java index 0de8f38d43e80..13f7e6e7d47ac 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,38 +25,56 @@ package com.sun.java.swing.plaf.windows; -import java.beans.PropertyChangeListener; +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.LayoutManager; +import java.awt.Rectangle; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.border.*; -import javax.swing.*; -import java.awt.event.*; -import java.awt.*; +import java.beans.PropertyChangeListener; -import static com.sun.java.swing.plaf.windows.TMSchema.Part; -import static com.sun.java.swing.plaf.windows.TMSchema.State; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.ButtonModel; +import javax.swing.ComboBoxEditor; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicComboBoxEditor; +import javax.swing.plaf.basic.BasicComboBoxRenderer; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; +import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder; import sun.swing.DefaultLookup; import sun.swing.StringUIClientPropertyKey; -import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows combo box. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Tom Santos * @author Igor Kushnirskiy */ - public class WindowsComboBoxUI extends BasicComboBoxUI { private static final MouseListener rolloverListener = diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java index 38618a8215010..564ae5b9f712e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopIconUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,16 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.border.*; - +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JComponent; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicDesktopIconUI; /** * Windows icon for a minimized window on the desktop. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsDesktopIconUI extends BasicDesktopIconUI { private int width; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java index f9949fce4cab2..3ceea2d31dca4 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsDesktopPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,12 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.basic.*; +import javax.swing.JComponent; import javax.swing.plaf.ComponentUI; -import java.awt.event.*; +import javax.swing.plaf.basic.BasicDesktopPaneUI; /** * Windows desktop pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author David Kloba */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java index 0e2a1bcdc1bf5..2f5c45633d4c5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsEditorPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicEditorPaneUI; import javax.swing.text.Caret; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsEditorPaneUI extends BasicEditorPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java index 3d8b523a23835..86a587aabd3b6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsIconFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,39 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.ButtonUI; -import javax.swing.plaf.UIResource; - -import java.awt.*; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; import java.io.Serializable; -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JCheckBox; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JInternalFrame; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.plaf.ButtonUI; +import javax.swing.plaf.UIResource; import sun.swing.MenuItemCheckIconFactory; import sun.swing.SwingUtilities2; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; + /** * Factory object that can vend Icons appropriate for the Windows {@literal L & F}. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author David Kloba * @author Georges Saab diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java index 66b038d2ec575..19169f6e4241e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsInternalFrameUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,25 +25,25 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.beans.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.plaf.basic.*; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.DesktopManager; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.AbstractBorder; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicInternalFrameUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsInternalFrameUI extends BasicInternalFrameUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java index 0f3c4562a551b..93f69f49c63f6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLabelUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,20 @@ package com.sun.java.swing.plaf.windows; -import sun.swing.SwingUtilities2; -import sun.awt.AppContext; - import java.awt.Color; import java.awt.Graphics; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.UIManager; - import javax.swing.plaf.ComponentUI; - import javax.swing.plaf.basic.BasicLabelUI; - +import sun.awt.AppContext; +import sun.swing.SwingUtilities2; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsLabelUI extends BasicLabelUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index 554da44b67087..91a5732fc9e72 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -40,56 +40,75 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.HeadlessException; +import java.awt.Image; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; +import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; -import java.awt.image.FilteredImageSource; import java.awt.image.RGBImageFilter; +import java.security.AccessController; -import javax.swing.plaf.*; -import javax.swing.*; -import javax.swing.plaf.basic.*; -import javax.swing.border.*; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JRootPane; +import javax.swing.JTextField; +import javax.swing.LayoutStyle; +import javax.swing.LookAndFeel; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingConstants; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.BorderUIResource; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.FontUIResource; +import javax.swing.plaf.InsetsUIResource; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicBorders; +import javax.swing.plaf.basic.BasicLookAndFeel; import javax.swing.text.DefaultEditorKit; -import static javax.swing.UIDefaults.LazyValue; - -import java.awt.Font; -import java.awt.Color; -import java.awt.event.ActionEvent; - -import java.security.AccessController; -import sun.awt.SunToolkit; +import com.sun.java.swing.plaf.windows.WindowsIconFactory.VistaMenuItemCheckIconFactory; import sun.awt.OSInfo; +import sun.awt.SunToolkit; import sun.awt.shell.ShellFolder; import sun.font.FontUtilities; import sun.security.action.GetPropertyAction; - import sun.swing.DefaultLayoutStyle; import sun.swing.ImageIconUIResource; +import sun.swing.StringUIClientPropertyKey; import sun.swing.SwingAccessor; -import sun.swing.icon.SortArrowIcon; import sun.swing.SwingUtilities2; -import sun.swing.StringUIClientPropertyKey; +import sun.swing.icon.SortArrowIcon; import sun.swing.plaf.windows.ClassicSortArrowIcon; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - -import com.sun.java.swing.plaf.windows.WindowsIconFactory.VistaMenuItemCheckIconFactory; +import static javax.swing.UIDefaults.LazyValue; /** * Implements the Windows95/98/NT/2000 Look and Feel. * UI classes not implemented specifically for Windows will * default to those implemented in Basic. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Superclass is not serializable across versions public class WindowsLookAndFeel extends BasicLookAndFeel diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java index af73b1948f063..23df6b67849ec 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,32 +25,35 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.ActionMapUIResource; -import javax.swing.plaf.ComponentUI; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; -import java.awt.event.WindowStateListener; -import java.awt.*; +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JRootPane; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicMenuBarUI; -import com.sun.java.swing.plaf.windows.TMSchema.*; -import com.sun.java.swing.plaf.windows.XPStyle.*; +import com.sun.java.swing.plaf.windows.TMSchema.Part; +import com.sun.java.swing.plaf.windows.TMSchema.State; +import com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsMenuBarUI extends BasicMenuBarUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java index 3b895ba4d4fef..09fad9c98a6de 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,33 +25,34 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicMenuItemUI; + +import com.sun.java.swing.plaf.windows.TMSchema.Part; +import com.sun.java.swing.plaf.windows.TMSchema.State; +import com.sun.java.swing.plaf.windows.XPStyle.Skin; import sun.swing.MenuItemCheckIconFactory; import sun.swing.MenuItemLayoutHelper; import sun.swing.SwingUtilities2; -import com.sun.java.swing.plaf.windows.TMSchema.*; -import com.sun.java.swing.plaf.windows.XPStyle.*; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Igor Kushnirskiy */ - public class WindowsMenuItemUI extends BasicMenuItemUI { /** * The instance of {@code PropertyChangeListener}. diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java index 803952b80114c..4195b4e85cace 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,30 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.event.MouseEvent; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.event.MouseInputListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicMenuUI; -import javax.swing.event.MouseInputListener; -import javax.swing.*; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsMenuUI extends BasicMenuUI { protected Integer menuBarHeight; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java index b163d88d57778..2755cc76c96c5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsOptionPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2000, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,10 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - - +import javax.swing.plaf.basic.BasicOptionPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsOptionPaneUI extends BasicOptionPaneUI { } diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java index aef2a21832350..baf0997ceff30 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPasswordFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPasswordFieldUI; import javax.swing.text.Caret; - - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsPasswordFieldUI extends BasicPasswordFieldUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index 06dde67d7a95c..d85880d47e75d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,31 +28,31 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; -import java.awt.KeyEventPostProcessor; -import java.awt.KeyboardFocusManager; import java.awt.Window; -import java.awt.event.KeyEvent; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import sun.swing.StringUIClientPropertyKey; +import javax.swing.JComponent; +import javax.swing.JPopupMenu; +import javax.swing.JRootPane; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPopupMenuUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; import com.sun.java.swing.plaf.windows.XPStyle.Skin; +import sun.swing.StringUIClientPropertyKey; + import static sun.swing.SwingUtilities2.BASICMENUITEMUI_MAX_TEXT_OFFSET; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Igor Kushnirskiy */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java index 89ac13b66c4b8..bf01850297b51 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,11 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.sun.java.swing.plaf.windows; -import javax.swing.JWindow; -import java.awt.Window; import java.awt.Graphics; +import java.awt.Window; + +import javax.swing.JWindow; /** * A class which tags a window with a particular semantic usage, @@ -39,13 +41,6 @@ * Note that support for transition effects may be supported with a * different mechanism in the future and so this class is * package-private and targeted for Swing implementation use only. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Amy Fowler */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java index 45947fc2b7e3d..e53cc798e61d0 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsProgressBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,28 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.*; -import java.awt.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.JComponent; +import javax.swing.JProgressBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicProgressBarUI; + +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Michael C. Albers */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java index cce34eb9a6547..584a0f1622b1a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonMenuItemUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,21 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.*; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicRadioButtonMenuItemUI; import com.sun.java.swing.plaf.windows.TMSchema.Part; import com.sun.java.swing.plaf.windows.TMSchema.State; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsRadioButtonMenuItemUI extends BasicRadioButtonMenuItemUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java index 36b5413e9bbd0..dd0a93c95a1f6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsRadioButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,24 +25,23 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; -import java.awt.*; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.plaf.basic.BasicRadioButtonUI; +import sun.awt.AppContext; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsRadioButtonUI extends BasicRadioButtonUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java index f6f1bcf15b9d3..a3dd04362b9cd 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,35 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import java.lang.ref.*; -import java.util.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.lang.ref.WeakReference; +import java.util.HashMap; + +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicArrowButton; +import javax.swing.plaf.basic.BasicScrollBarUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsScrollBarUI extends BasicScrollBarUI { private Grid thumbGrid; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java index 86e5a0c9c8689..6c89eabc89455 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsScrollPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,20 +25,10 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; - - +import javax.swing.plaf.basic.BasicScrollPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsScrollPaneUI extends BasicScrollPaneUI {} diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java index 1bae3f8f44166..ceaf878cbca5f 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSliderUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,26 +25,24 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.event.MouseEvent; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.JSlider; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicSliderUI; -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.Prop; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsSliderUI extends BasicSliderUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java index 327162e62bfed..04eeb726f9042 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneDivider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,16 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import javax.swing.JSplitPane; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; + import javax.swing.UIManager; -import javax.swing.plaf.basic.BasicSplitPaneUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; - +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Divider used for Windows split pane. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java index 3b85bbe660c97..9305e46c36ff9 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsSplitPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,23 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.basic.*; -import javax.swing.*; - -import javax.swing.plaf.basic.BasicSplitPaneUI; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicSplitPaneDivider; -import javax.swing.plaf.*; - +import javax.swing.plaf.basic.BasicSplitPaneUI; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsSplitPaneUI extends BasicSplitPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java index eb8080ba3c933..9b4e22102eb1d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTabbedPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,28 +25,28 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; - -import javax.swing.plaf.basic.*; -import javax.swing.plaf.*; -import javax.swing.*; -import java.util.Set; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.Rectangle; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; import java.util.HashSet; -import java.awt.event.*; +import java.util.Set; -import static com.sun.java.swing.plaf.windows.TMSchema.*; -import static com.sun.java.swing.plaf.windows.XPStyle.Skin; +import javax.swing.JComponent; +import javax.swing.KeyStroke; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; +import static com.sun.java.swing.plaf.windows.XPStyle.Skin; /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTabbedPaneUI extends BasicTabbedPaneUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java index ed8f90d984733..6aca81225ea15 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextAreaUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,22 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTextAreaUI; import javax.swing.text.Caret; - - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTextAreaUI extends BasicTextAreaUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java index 4e5b3d6c1ae83..be3e6276161c5 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextFieldUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,23 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; -import java.beans.PropertyChangeEvent; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.BasicTextFieldUI; -import javax.swing.text.*; -import javax.swing.*; -import javax.swing.plaf.UIResource; -import sun.swing.DefaultLookup; - +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import javax.swing.BoundedRangeModel; +import javax.swing.JComponent; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.TextUI; +import javax.swing.plaf.UIResource; +import javax.swing.plaf.basic.BasicTextFieldUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.Highlighter; +import javax.swing.text.Position; /** * Provides the Windows look and feel for a text field. This @@ -53,13 +59,6 @@ *

  • Ctrl-left-arrow and ctrl-right-arrow act like home and * end respectively. * - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Timothy Prinzing */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java index f17b2c495990a..372563f74f46e 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextPaneUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 1998, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,21 +25,13 @@ package com.sun.java.swing.plaf.windows; -import javax.swing.plaf.*; -import javax.swing.plaf.basic.*; -import javax.swing.*; +import javax.swing.JComponent; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTextPaneUI; import javax.swing.text.Caret; - /** * Windows rendition of the component. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public class WindowsTextPaneUI extends BasicTextPaneUI { diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java index 58e9dfe5dbfdf..b73b38385f22d 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTextUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,21 +29,22 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.plaf.basic.*; -import javax.swing.*; + import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; -import javax.swing.text.*; +import javax.swing.plaf.basic.BasicTextUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.DefaultCaret; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.LayeredHighlighter; +import javax.swing.text.Position; +import javax.swing.text.View; /** * Windows text rendering. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ public abstract class WindowsTextUI extends BasicTextUI { /** diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java index afbc825fb2061..feee0c0aaefec 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsToggleButtonUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,27 +25,23 @@ package com.sun.java.swing.plaf.windows; -import sun.awt.AppContext; - -import javax.swing.plaf.basic.*; -import javax.swing.border.*; -import javax.swing.plaf.*; -import javax.swing.*; - -import java.awt.*; -import java.beans.PropertyChangeEvent; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import javax.swing.AbstractButton; +import javax.swing.JComponent; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicGraphicsUtils; +import javax.swing.plaf.basic.BasicToggleButtonUI; +import sun.awt.AppContext; /** * A Windows toggle button. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Jeff Dinkins */ diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java index 8849b786f6fa1..5bef0e075a44a 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsTreeUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,31 +25,26 @@ package com.sun.java.swing.plaf.windows; -import java.awt.*; -import java.awt.event.*; - -import java.io.*; -import java.util.*; - -import javax.swing.plaf.basic.*; -import javax.swing.*; -import javax.swing.plaf.*; - -import javax.swing.tree.*; - -import static com.sun.java.swing.plaf.windows.TMSchema.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.io.Serializable; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JTree; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicTreeUI; +import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreeCellRenderer; + +import static com.sun.java.swing.plaf.windows.TMSchema.Part; +import static com.sun.java.swing.plaf.windows.TMSchema.State; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; - /** * A Windows tree. - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. * * @author Scott Violet */ @@ -118,14 +113,7 @@ protected TreeCellRenderer createDefaultCellRenderer() { } /** - * The minus sign button icon - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. + * The minus sign button icon. */ @SuppressWarnings("serial") // Same-version serialization only public static class ExpandedIcon implements Icon, Serializable { @@ -172,13 +160,6 @@ public int getIconHeight() { /** * The plus sign button icon - *

    - * Warning: - * Serialized objects of this class will not be compatible with - * future Swing releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same - * version of Swing. A future release of Swing will provide support for - * long term persistence. */ @SuppressWarnings("serial") // Superclass is not serializable across versions public static class CollapsedIcon extends ExpandedIcon { From d99e1f6c296c23b8714fc94fe4f3c7bece000f9c Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Mon, 9 Nov 2020 09:19:13 +0000 Subject: [PATCH 028/124] 8255991: Shenandoah: Apply 'weak' LRB on cmpxchg and xchg Reviewed-by: shade --- .../cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp | 3 ++- .../share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp | 6 ++++-- .../share/gc/shenandoah/shenandoahBarrierSet.inline.hpp | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 01b53c5266a1e..2aac06082079f 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -111,7 +111,8 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt __ xchg(access.resolved_addr(), result, result, LIR_OprFact::illegalOpr); if (access.is_oop()) { - result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), ShenandoahBarrierSet::AccessKind::NORMAL); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), kind); LIR_Opr tmp = gen->new_register(type); __ move(result, tmp); result = tmp; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index d5ba63ce898b1..e13065e56c2be 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -644,7 +644,8 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess load_store = kit->gvn().transform(new DecodeNNode(load_store, load_store->get_ptr_type())); } #endif - load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, ShenandoahBarrierSet::AccessKind::NORMAL)); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, kind)); return load_store; } return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); @@ -712,7 +713,8 @@ Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& acces } Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type); if (access.is_oop()) { - result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, ShenandoahBarrierSet::AccessKind::NORMAL)); + ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); + result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, kind)); shenandoah_write_barrier_pre(kit, false /* do_load */, NULL, NULL, max_juint, NULL, NULL, result /* pre_val */, T_OBJECT); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 861ca705754e8..e9cd4a2b54f82 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -255,7 +255,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. - res = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(res); + res = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(res, NULL); bs->satb_enqueue(res); return res; } @@ -281,7 +281,7 @@ inline oop ShenandoahBarrierSet::AccessBarrier::oop_ato // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. - previous = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(previous); + previous = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(previous, NULL); bs->satb_enqueue(previous); return previous; } From dd8e4ffbe559568222fc21c09d162a867df2e805 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 9 Nov 2020 12:03:06 +0000 Subject: [PATCH 029/124] 8255711: Fix and unify hotspot signal handlers Reviewed-by: coleenp, gziemski, dholmes --- make/hotspot/symbols/symbols-aix | 2 +- src/hotspot/os/posix/signals_posix.cpp | 353 +++++++++++------- src/hotspot/os/posix/signals_posix.hpp | 6 +- src/hotspot/os/windows/os_windows.cpp | 2 + src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp | 93 +---- src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp | 73 +--- src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp | 73 +--- .../os_cpu/linux_aarch64/os_linux_aarch64.cpp | 85 +---- src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp | 81 +--- src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp | 91 +---- .../os_cpu/linux_s390/os_linux_s390.cpp | 89 +---- src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp | 83 +--- .../os_cpu/linux_zero/os_linux_zero.cpp | 82 +--- src/hotspot/share/runtime/globals.hpp | 2 +- src/hotspot/share/utilities/vmError.hpp | 6 +- 15 files changed, 269 insertions(+), 852 deletions(-) diff --git a/make/hotspot/symbols/symbols-aix b/make/hotspot/symbols/symbols-aix index 0efd2dba97f21..92703573a5f52 100644 --- a/make/hotspot/symbols/symbols-aix +++ b/make/hotspot/symbols/symbols-aix @@ -21,7 +21,7 @@ # questions. # -JVM_handle_linux_signal +JVM_handle_aix_signal numa_error numa_warn sysThreadAvailableStackWithSlack diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp index 6b248a969813d..94251da145dba 100644 --- a/src/hotspot/os/posix/signals_posix.cpp +++ b/src/hotspot/os/posix/signals_posix.cpp @@ -26,6 +26,7 @@ #include "jvm.h" #include "logging/log.hpp" +#include "runtime/atomic.hpp" #include "runtime/globals.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/os.hpp" @@ -71,10 +72,6 @@ extern "C" { static sigset_t check_signal_done; static bool check_signals = true; -// This boolean allows users to forward their own non-matching signals -// to JVM_handle_bsd_signal/JVM_handle_linux_signal, harmlessly. -static bool signal_handlers_are_installed = false; - debug_only(static bool signal_sets_initialized = false); static sigset_t unblocked_sigs, vm_sigs, preinstalled_sigs; struct sigaction sigact[NSIG]; @@ -261,6 +258,8 @@ static const struct { { -1, NULL } }; +static const char* get_signal_name(int sig, char* out, size_t outlen); + //////////////////////////////////////////////////////////////////////////////// // sun.misc.Signal support @@ -313,6 +312,8 @@ static int check_pending_signals() { } } while (threadIsSuspended); } + ShouldNotReachHere(); + return 0; // Satisfy compiler } int os::signal_wait() { @@ -408,50 +409,6 @@ bool PosixSignals::chained_handler(int sig, siginfo_t* siginfo, void* context) { return chained; } -//////////////////////////////////////////////////////////////////////////////// -// signal handling (except suspend/resume) - -// This routine may be used by user applications as a "hook" to catch signals. -// The user-defined signal handler must pass unrecognized signals to this -// routine, and if it returns true (non-zero), then the signal handler must -// return immediately. If the flag "abort_if_unrecognized" is true, then this -// routine will never retun false (zero), but instead will execute a VM panic -// routine kill the process. -// -// If this routine returns false, it is OK to call it again. This allows -// the user-defined signal handler to perform checks either before or after -// the VM performs its own checks. Naturally, the user code would be making -// a serious error if it tried to handle an exception (such as a null check -// or breakpoint) that the VM was generating for its own correct operation. -// -// This routine may recognize any of the following kinds of signals: -// SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGPIPE, SIGXFSZ, SIGUSR1. -// It should be consulted by handlers for any of those signals. -// -// The caller of this routine must pass in the three arguments supplied -// to the function referred to in the "sa_sigaction" (not the "sa_handler") -// field of the structure passed to sigaction(). This routine assumes that -// the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. -// -// Note that the VM will print warnings if it detects conflicting signal -// handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". -// - -#if defined(BSD) -extern "C" JNIEXPORT int JVM_handle_bsd_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); -#elif defined(AIX) -extern "C" JNIEXPORT int JVM_handle_aix_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); -#else -extern "C" JNIEXPORT int JVM_handle_linux_signal(int signo, siginfo_t* siginfo, - void* ucontext, - int abort_if_unrecognized); -#endif - - ///// Synchronous (non-deferrable) error signals (ILL, SEGV, FPE, BUS, TRAP): // These signals are special because they cannot be deferred and, if they @@ -502,21 +459,151 @@ void PosixSignals::unblock_error_signals() { ::pthread_sigmask(SIG_UNBLOCK, &set, NULL); } -// Renamed from 'signalHandler' to avoid collision with other shared libs. -static void javaSignalHandler(int sig, siginfo_t* info, void* uc) { - assert(info != NULL && uc != NULL, "it must be old kernel"); +class ErrnoPreserver: public StackObj { + const int _saved; +public: + ErrnoPreserver() : _saved(errno) {} + ~ErrnoPreserver() { errno = _saved; } +}; - PosixSignals::unblock_error_signals(); +//////////////////////////////////////////////////////////////////////////////// +// JVM_handle_(linux|aix|bsd)_signal() + +// This routine is the shared part of the central hotspot signal handler. It can +// also be called by a user application, if a user application prefers to do +// signal handling itself - in that case it needs to pass signals the VM +// internally uses on to the VM first. +// +// The user-defined signal handler must pass unrecognized signals to this +// routine, and if it returns true (non-zero), then the signal handler must +// return immediately. If the flag "abort_if_unrecognized" is true, then this +// routine will never return false (zero), but instead will execute a VM panic +// routine to kill the process. +// +// If this routine returns false, it is OK to call it again. This allows +// the user-defined signal handler to perform checks either before or after +// the VM performs its own checks. Naturally, the user code would be making +// a serious error if it tried to handle an exception (such as a null check +// or breakpoint) that the VM was generating for its own correct operation. +// +// This routine may recognize any of the following kinds of signals: +// SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGQUIT, SIGPIPE, SIGXFSZ, SIGUSR1. +// It should be consulted by handlers for any of those signals. +// +// The caller of this routine must pass in the three arguments supplied +// to the function referred to in the "sa_sigaction" (not the "sa_handler") +// field of the structure passed to sigaction(). This routine assumes that +// the sa_flags field passed to sigaction() includes SA_SIGINFO and SA_RESTART. +// +// Note that the VM will print warnings if it detects conflicting signal +// handlers, unless invoked with the option "-XX:+AllowUserSignalHandlers". +// - int orig_errno = errno; // Preserve errno value over signal handler. #if defined(BSD) - JVM_handle_bsd_signal(sig, info, uc, true); +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_bsd_signal #elif defined(AIX) - JVM_handle_aix_signal(sig, info, uc, true); +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_aix_signal +#elif defined(LINUX) +#define JVM_HANDLE_XXX_SIGNAL JVM_handle_linux_signal +#else +#error who are you? +#endif + +extern "C" JNIEXPORT +int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info, + void* ucVoid, int abort_if_unrecognized) +{ + assert(info != NULL && ucVoid != NULL, "sanity"); + + // Note: it's not uncommon that JNI code uses signal/sigset to install, + // then restore certain signal handler (e.g. to temporarily block SIGPIPE, + // or have a SIGILL handler when detecting CPU type). When that happens, + // this handler might be invoked with junk info/ucVoid. To avoid unnecessary + // crash when libjsig is not preloaded, try handle signals that do not require + // siginfo/ucontext first. + + // Preserve errno value over signal handler. + // (note: RAII ok here, even with JFR thread crash protection, see below). + ErrnoPreserver ep; + + // Unblock all synchronous error signals (see JDK-8252533) + PosixSignals::unblock_error_signals(); + + ucontext_t* const uc = (ucontext_t*) ucVoid; + Thread* const t = Thread::current_or_null_safe(); + + // Handle JFR thread crash protection. + // Note: this may cause us to longjmp away. Do not use any code before this + // point which really needs any form of epilogue code running, eg RAII objects. + os::ThreadCrashProtection::check_crash_protection(sig, t); + + bool signal_was_handled = false; + + // Handle assertion poison page accesses. +#ifdef CAN_SHOW_REGISTERS_ON_ASSERT + if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { + signal_was_handled = handle_assert_poison_fault(ucVoid, info->si_addr); + } +#endif + + // Ignore SIGPIPE and SIGXFSZ (4229104, 6499219). + if (sig == SIGPIPE || sig == SIGXFSZ) { + PosixSignals::chained_handler(sig, info, ucVoid); + signal_was_handled = true; // unconditionally. + } + + // Call platform dependent signal handler. + if (!signal_was_handled) { + JavaThread* const jt = (t != NULL && t->is_Java_thread()) ? (JavaThread*) t : NULL; + signal_was_handled = PosixSignals::pd_hotspot_signal_handler(sig, info, uc, jt); + } + + // From here on, if the signal had not been handled, it is a fatal error. + + // Give the chained signal handler - should it exist - a shot. + if (!signal_was_handled) { + signal_was_handled = PosixSignals::chained_handler(sig, info, ucVoid); + } + + // Invoke fatal error handling. + if (!signal_was_handled && abort_if_unrecognized) { + // Extract pc from context for the error handler to display. + address pc = NULL; + if (uc != NULL) { + // prepare fault pc address for error reporting. + if (S390_ONLY(sig == SIGILL || sig == SIGFPE) NOT_S390(false)) { + pc = (address)info->si_addr; + } else { + pc = PosixSignals::ucontext_get_pc(uc); + } + } +#if defined(ZERO) && !defined(PRODUCT) + char buf[64]; + VMError::report_and_die(t, sig, pc, info, ucVoid, + "\n#" + "\n# /--------------------\\" + "\n# | %-7s |" + "\n# \\---\\ /--------------/" + "\n# /" + "\n# [-] |\\_/| " + "\n# (+)=C |o o|__ " + "\n# | | =-*-=__\\ " + "\n# OOO c_c_(___)", + get_signal_name(sig, buf, sizeof(buf))); #else - JVM_handle_linux_signal(sig, info, uc, true); + VMError::report_and_die(t, sig, pc, info, ucVoid); #endif - errno = orig_errno; + // VMError should not return. + ShouldNotReachHere(); + } + return signal_was_handled; +} + +// Entry point for the hotspot signal handler. +static void javaSignalHandler(int sig, siginfo_t* info, void* ucVoid) { + // Do not add any code here! + // Only add code to either JVM_HANDLE_XXX_SIGNAL or PosixSignals::pd_hotspot_signal_handler. + (void)JVM_HANDLE_XXX_SIGNAL(sig, info, ucVoid, true); } static void UserHandler(int sig, void *siginfo, void *context) { @@ -766,9 +853,7 @@ void os::run_periodic_checks() { do_signal_check(SIGBUS); do_signal_check(SIGPIPE); do_signal_check(SIGXFSZ); -#if defined(PPC64) - do_signal_check(SIGTRAP); -#endif + PPC64_ONLY(do_signal_check(SIGTRAP);) // ReduceSignalUsage allows the user to override these handlers // see comments at the very top and jvm_md.h @@ -935,7 +1020,6 @@ static bool is_valid_signal(int sig) { #endif } -// Returned string is a constant. For unknown signals "UNKNOWN" is returned. static const char* get_signal_name(int sig, char* out, size_t outlen) { const char* ret = NULL; @@ -1075,7 +1159,7 @@ int os::get_signal_number(const char* signal_name) { return -1; } -void set_signal_handler(int sig, bool set_installed) { +void set_signal_handler(int sig) { // Check for overwrite. struct sigaction oldAct; sigaction(sig, (struct sigaction*)NULL, &oldAct); @@ -1086,7 +1170,7 @@ void set_signal_handler(int sig, bool set_installed) { if (oldhand != CAST_FROM_FN_PTR(void*, SIG_DFL) && oldhand != CAST_FROM_FN_PTR(void*, SIG_IGN) && oldhand != CAST_FROM_FN_PTR(void*, (sa_sigaction_t)javaSignalHandler)) { - if (AllowUserSignalHandlers || !set_installed) { + if (AllowUserSignalHandlers) { // Do not overwrite; user takes responsibility to forward to us. return; } else if (UseSignalChaining) { @@ -1103,13 +1187,8 @@ void set_signal_handler(int sig, bool set_installed) { struct sigaction sigAct; sigfillset(&(sigAct.sa_mask)); remove_error_signals_from_set(&(sigAct.sa_mask)); - sigAct.sa_handler = SIG_DFL; - if (!set_installed) { - sigAct.sa_flags = SA_SIGINFO|SA_RESTART; - } else { - sigAct.sa_sigaction = javaSignalHandler; - sigAct.sa_flags = SA_SIGINFO|SA_RESTART; - } + sigAct.sa_sigaction = javaSignalHandler; + sigAct.sa_flags = SA_SIGINFO|SA_RESTART; #if defined(__APPLE__) // Needed for main thread as XNU (Mac OS X kernel) will only deliver SIGSEGV // (which starts as SIGBUS) on main thread with faulting address inside "stack+guard pages" @@ -1136,87 +1215,75 @@ void set_signal_handler(int sig, bool set_installed) { assert(oldhand2 == oldhand, "no concurrent signal handler installation"); } -// install signal handlers for signals that HotSpot needs to -// handle in order to support Java-level exception handling. - -bool PosixSignals::are_signal_handlers_installed() { - return signal_handlers_are_installed; -} - // install signal handlers for signals that HotSpot needs to // handle in order to support Java-level exception handling. void PosixSignals::install_signal_handlers() { - if (!signal_handlers_are_installed) { - signal_handlers_are_installed = true; - - // signal-chaining - typedef void (*signal_setting_t)(); - signal_setting_t begin_signal_setting = NULL; - signal_setting_t end_signal_setting = NULL; - begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, - dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); - if (begin_signal_setting != NULL) { - end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, - dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); - get_signal_action = CAST_TO_FN_PTR(get_signal_t, - dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); - libjsig_is_loaded = true; - assert(UseSignalChaining, "should enable signal-chaining"); - } - if (libjsig_is_loaded) { - // Tell libjsig jvm is setting signal handlers - (*begin_signal_setting)(); - } - set_signal_handler(SIGSEGV, true); - set_signal_handler(SIGPIPE, true); - set_signal_handler(SIGBUS, true); - set_signal_handler(SIGILL, true); - set_signal_handler(SIGFPE, true); -#if defined(PPC64) || defined(AIX) - set_signal_handler(SIGTRAP, true); -#endif - set_signal_handler(SIGXFSZ, true); + // signal-chaining + typedef void (*signal_setting_t)(); + signal_setting_t begin_signal_setting = NULL; + signal_setting_t end_signal_setting = NULL; + begin_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_begin_signal_setting")); + if (begin_signal_setting != NULL) { + end_signal_setting = CAST_TO_FN_PTR(signal_setting_t, + dlsym(RTLD_DEFAULT, "JVM_end_signal_setting")); + get_signal_action = CAST_TO_FN_PTR(get_signal_t, + dlsym(RTLD_DEFAULT, "JVM_get_signal_action")); + libjsig_is_loaded = true; + assert(UseSignalChaining, "should enable signal-chaining"); + } + if (libjsig_is_loaded) { + // Tell libjsig jvm is setting signal handlers + (*begin_signal_setting)(); + } + + set_signal_handler(SIGSEGV); + set_signal_handler(SIGPIPE); + set_signal_handler(SIGBUS); + set_signal_handler(SIGILL); + set_signal_handler(SIGFPE); + PPC64_ONLY(set_signal_handler(SIGTRAP);) + set_signal_handler(SIGXFSZ); #if defined(__APPLE__) - // In Mac OS X 10.4, CrashReporter will write a crash log for all 'fatal' signals, including - // signals caught and handled by the JVM. To work around this, we reset the mach task - // signal handler that's placed on our process by CrashReporter. This disables - // CrashReporter-based reporting. - // - // This work-around is not necessary for 10.5+, as CrashReporter no longer intercedes - // on caught fatal signals. - // - // Additionally, gdb installs both standard BSD signal handlers, and mach exception - // handlers. By replacing the existing task exception handler, we disable gdb's mach - // exception handling, while leaving the standard BSD signal handlers functional. - kern_return_t kr; - kr = task_set_exception_ports(mach_task_self(), - EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, - MACH_PORT_NULL, - EXCEPTION_STATE_IDENTITY, - MACHINE_THREAD_STATE); - - assert(kr == KERN_SUCCESS, "could not set mach task signal handler"); + // In Mac OS X 10.4, CrashReporter will write a crash log for all 'fatal' signals, including + // signals caught and handled by the JVM. To work around this, we reset the mach task + // signal handler that's placed on our process by CrashReporter. This disables + // CrashReporter-based reporting. + // + // This work-around is not necessary for 10.5+, as CrashReporter no longer intercedes + // on caught fatal signals. + // + // Additionally, gdb installs both standard BSD signal handlers, and mach exception + // handlers. By replacing the existing task exception handler, we disable gdb's mach + // exception handling, while leaving the standard BSD signal handlers functional. + kern_return_t kr; + kr = task_set_exception_ports(mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, + MACH_PORT_NULL, + EXCEPTION_STATE_IDENTITY, + MACHINE_THREAD_STATE); + + assert(kr == KERN_SUCCESS, "could not set mach task signal handler"); #endif + if (libjsig_is_loaded) { + // Tell libjsig jvm finishes setting signal handlers + (*end_signal_setting)(); + } + + // We don't activate signal checker if libjsig is in place, we trust ourselves + // and if UserSignalHandler is installed all bets are off. + // Log that signal checking is off only if -verbose:jni is specified. + if (CheckJNICalls) { if (libjsig_is_loaded) { - // Tell libjsig jvm finishes setting signal handlers - (*end_signal_setting)(); + log_debug(jni, resolve)("Info: libjsig is activated, all active signal checking is disabled"); + check_signals = false; } - - // We don't activate signal checker if libjsig is in place, we trust ourselves - // and if UserSignalHandler is installed all bets are off. - // Log that signal checking is off only if -verbose:jni is specified. - if (CheckJNICalls) { - if (libjsig_is_loaded) { - log_debug(jni, resolve)("Info: libjsig is activated, all active signal checking is disabled"); - check_signals = false; - } - if (AllowUserSignalHandlers) { - log_debug(jni, resolve)("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); - check_signals = false; - } + if (AllowUserSignalHandlers) { + log_debug(jni, resolve)("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); + check_signals = false; } } } @@ -1354,9 +1421,7 @@ void PosixSignals::signal_sets_init() { sigaddset(&unblocked_sigs, SIGSEGV); sigaddset(&unblocked_sigs, SIGBUS); sigaddset(&unblocked_sigs, SIGFPE); - #if defined(PPC64) || defined(AIX) - sigaddset(&unblocked_sigs, SIGTRAP); - #endif + PPC64_ONLY(sigaddset(&unblocked_sigs, SIGTRAP);) sigaddset(&unblocked_sigs, SR_signum); if (!ReduceSignalUsage) { diff --git a/src/hotspot/os/posix/signals_posix.hpp b/src/hotspot/os/posix/signals_posix.hpp index 7194bd6c57397..7f0f1b55884fc 100644 --- a/src/hotspot/os/posix/signals_posix.hpp +++ b/src/hotspot/os/posix/signals_posix.hpp @@ -38,7 +38,11 @@ class PosixSignals : public AllStatic { public: - static bool are_signal_handlers_installed(); + // The platform dependent parts of the central hotspot signal handler. + // Returns true if the signal had been recognized and handled, false if not. If true, caller should + // return from signal handling. + static bool pd_hotspot_signal_handler(int sig, siginfo_t* info, ucontext_t* uc, JavaThread* thread); + static void install_signal_handlers(); static bool is_sig_ignored(int sig); diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 88428ca5ce3c3..3b829cddac7a7 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -2144,6 +2144,8 @@ static int check_pending_signals() { } } while (threadIsSuspended); } + ShouldNotReachHere(); + return 0; // Satisfy compiler } int os::signal_wait() { diff --git a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp index ca1c74aeee245..f214eee454a44 100644 --- a/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp +++ b/src/hotspot/os_cpu/aix_ppc/os_aix_ppc.cpp @@ -168,43 +168,8 @@ frame os::current_frame() { return os::get_sender_for_C_frame(&tmp); } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrecognized) { - - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_aix_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return 1; - } else { - // Ignoring SIGPIPE - see bugs 4229104 - return 1; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Decide if this trap can be handled by a stub. address stub = NULL; @@ -226,8 +191,8 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } } - if (info == NULL || uc == NULL || thread == NULL && vmthread == NULL) { - goto run_chained_handler; + if (info == NULL || uc == NULL) { + return false; // Fatal error } // If we are a java thread... @@ -237,11 +202,11 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec if (sig == SIGSEGV && thread->is_in_full_stack(addr)) { // stack overflow if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } else if (stub != NULL) { goto run_stub; } else { - goto report_and_die; + return false; // Fatal error } } // end handle SIGSEGV inside stack boundaries @@ -281,17 +246,6 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec // happens rarely. In heap based and disjoint base compressd oop modes also loads // are used for null checks. - // A VM-related SIGILL may only occur if we are not in the zero page. - // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else - // in the zero page, because it is filled with 0x0. We ignore - // explicit SIGILLs in the zero page. - if (sig == SIGILL && (pc < (address) 0x200)) { - if (TraceTraps) { - tty->print_raw_cr("SIGILL happened inside zero page."); - } - goto report_and_die; - } - int stop_type = -1; // Handle signal from NativeJump::patch_verified_entry(). if (sig == SIGILL && nativeInstruction_at(pc)->is_sigill_zombie_not_entrant()) { @@ -384,10 +338,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec tty->print_cr("trap: %s: %s (SIGTRAP, stop type %d)", msg, detail_msg, stop_type); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else if (sig == SIGBUS) { @@ -403,7 +354,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc); os::Aix::ucontext_set_pc(uc, next_pc); - return 1; + return true; } } } @@ -428,7 +379,7 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec } next_pc = SharedRuntime::handle_unsafe_access(thread, next_pc); os::Aix::ucontext_set_pc(uc, next_pc); - return 1; + return true; } } @@ -450,32 +401,10 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec // Save all thread context in case we need to restore it. if (thread != NULL) thread->set_saved_exception_pc(pc); os::Aix::ucontext_set_pc(uc, stub); - return 1; - } - -run_chained_handler: - - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return 1; + return true; } - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return 0; - } - -report_and_die: - // Use sigthreadmask instead of sigprocmask on AIX and unmask current signal. - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigthreadmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); - return 0; + return false; // Fatal error } void os::Aix::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp index c714640f73ba0..1faa5e80730df 100644 --- a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp +++ b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp @@ -385,56 +385,14 @@ frame os::current_frame() { } } -// Utility functions - // From IA32 System Programming Guide enum { trap_page_fault = 0xE }; -extern "C" JNIEXPORT int -JVM_handle_bsd_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // If crash protection is installed we may longjmp away and no destructors - // for objects in this scope will be run. - // So don't use any RAII utilities before crash protection is checked. - os::ThreadCrashProtection::check_crash_protection(sig, t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_bsd_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } /* NOTE: does not seem to work on bsd. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { @@ -455,7 +413,7 @@ JVM_handle_bsd_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Bsd::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } // Handle ALL stack overflow variations here @@ -466,7 +424,7 @@ JVM_handle_bsd_signal(int sig, if (thread->is_in_full_stack(addr)) { // stack overflow if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } } } @@ -678,29 +636,6 @@ JVM_handle_bsd_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Bsd::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; } diff --git a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp index ec7d8e789702f..1ab2001f5ab33 100644 --- a/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp +++ b/src/hotspot/os_cpu/bsd_zero/os_bsd_zero.cpp @@ -115,16 +115,10 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(); } -extern "C" JNIEXPORT int -JVM_handle_bsd_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - Thread* t = Thread::current_or_null_safe(); - - // handle SafeFetch faults + // handle SafeFetch faults the zero way if (sig == SIGSEGV || sig == SIGBUS) { sigjmp_buf* const pjb = get_jmp_buf_for_continuation(); if (pjb) { @@ -132,37 +126,6 @@ JVM_handle_bsd_signal(int sig, } } - // Note: it's not uncommon that JNI code uses signal/sigset to - // install then restore certain signal handler (e.g. to temporarily - // block SIGPIPE, or have a SIGILL handler when detecting CPU - // type). When that happens, JVM_handle_bsd_signal() might be - // invoked with junk info/ucVoid. To avoid unnecessary crash when - // libjsig is not preloaded, try handle signals that do not require - // siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - if (info != NULL && thread != NULL) { // Handle ALL stack overflow variations here if (sig == SIGSEGV || sig == SIGBUS) { @@ -202,36 +165,6 @@ JVM_handle_bsd_signal(int sig, }*/ } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - -#ifndef PRODUCT - if (sig == SIGSEGV) { - fatal("\n#" - "\n# /--------------------\\" - "\n# | segmentation fault |" - "\n# \\---\\ /--------------/" - "\n# /" - "\n# [-] |\\_/| " - "\n# (+)=C |o o|__ " - "\n# | | =-*-=__\\ " - "\n# OOO c_c_(___)"); - } -#endif // !PRODUCT - - const char *fmt = - "caught unhandled signal " INT32_FORMAT " at address " PTR_FORMAT; - char buf[128]; - - sprintf(buf, fmt, sig, info->si_addr); - fatal(buf); return false; } diff --git a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp index 935ab39ad863d..9ba2179a80c8f 100644 --- a/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp @@ -164,57 +164,9 @@ NOINLINE frame os::current_frame() { } } -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // If crash protection is installed we may longjmp away and no destructors - // for objects in this scope will be run. - // So don't use any RAII utilities before crash protection is checked. - os::ThreadCrashProtection::check_crash_protection(sig, t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } /* NOTE: does not seem to work on linux. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { @@ -235,7 +187,7 @@ JVM_handle_linux_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } address addr = (address) info->si_addr; @@ -250,7 +202,7 @@ JVM_handle_linux_signal(int sig, // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } } } @@ -293,10 +245,7 @@ JVM_handle_linux_signal(int sig, tty->print_cr("trap: %s: (SIGILL)", msg); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else @@ -342,30 +291,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); + return false; // Mute compiler - ShouldNotReachHere(); - return true; // Mute compiler } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp index 271d1194720fa..41733be6ca3f7 100644 --- a/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp +++ b/src/hotspot/os_cpu/linux_arm/os_linux_arm.cpp @@ -241,18 +241,9 @@ address check_vfp3_32_fault_instr = NULL; address check_simd_fault_instr = NULL; address check_mp_ext_fault_instr = NULL; -// Utility functions -extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, - void* ucVoid, int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // If crash protection is installed we may longjmp away and no destructors - // for objects in this scope will be run. - // So don't use any RAII utilities before crash protection is checked. - os::ThreadCrashProtection::check_crash_protection(sig, t); +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { if (sig == SIGILL && ((info->si_addr == (caddr_t)check_simd_fault_instr) @@ -266,44 +257,6 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, return true; } - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - address stub = NULL; address pc = NULL; bool unsafe_access = false; @@ -317,7 +270,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } // check if fault address is within thread stack if (thread->is_in_full_stack(addr)) { @@ -331,7 +284,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW); } else { // Thread was in the vm or native code. Return and try to finish. - return 1; + return true; } } else if (overflow_state->in_stack_red_zone(addr)) { // Fatal red zone violation. Disable the guard pages and fall through @@ -347,7 +300,7 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, thread->osthread()->set_expanding_stack(); if (os::Linux::manually_expand_stack(thread, addr)) { thread->osthread()->clear_expanding_stack(); - return 1; + return true; } thread->osthread()->clear_expanding_stack(); } else { @@ -440,30 +393,8 @@ extern "C" int JVM_handle_linux_signal(int sig, siginfo_t* info, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp index 288e2a3ece63e..ac4d1b72eb3ae 100644 --- a/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp +++ b/src/hotspot/os_cpu/linux_ppc/os_linux_ppc.cpp @@ -188,32 +188,8 @@ frame os::current_frame() { return os::get_sender_for_C_frame(&tmp); } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE - see bugs 4229104 - return true; - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Make the signal handler transaction-aware by checking the existence of a // second (transactional) context with MSR TS bits active. If the signal is @@ -237,26 +213,6 @@ JVM_handle_linux_signal(int sig, } } -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } - // Moved SafeFetch32 handling outside thread!=NULL conditional block to make // it work if no associated JavaThread object exists. if (uc) { @@ -297,7 +253,7 @@ JVM_handle_linux_signal(int sig, if (thread->is_in_full_stack(addr)) { // stack overflow if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } } } @@ -306,17 +262,6 @@ JVM_handle_linux_signal(int sig, // Java thread running in Java code => find exception handler if any // a fault inside compiled code, the interpreter, or a stub - // A VM-related SIGILL may only occur if we are not in the zero page. - // On AIX, we get a SIGILL if we jump to 0x0 or to somewhere else - // in the zero page, because it is filled with 0x0. We ignore - // explicit SIGILLs in the zero page. - if (sig == SIGILL && (pc < (address) 0x200)) { - if (TraceTraps) { - tty->print_raw_cr("SIGILL happened inside zero page."); - } - goto report_and_die; - } - CodeBlob *cb = NULL; int stop_type = -1; // Handle signal from NativeJump::patch_verified_entry(). @@ -404,10 +349,7 @@ JVM_handle_linux_signal(int sig, tty->print_cr("trap: %s: %s (SIGTRAP, stop type %d)", msg, detail_msg, stop_type); } - va_list detail_args; - VMError::report_and_die(INTERNAL_ERROR, msg, detail_msg, detail_args, thread, - pc, info, ucVoid, NULL, 0, 0); - va_end(detail_args); + return false; // Fatal error } else if (sig == SIGBUS) { @@ -465,31 +407,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - -report_and_die: - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp index 64d5fd8d5123c..d3d730530896d 100644 --- a/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp +++ b/src/hotspot/os_cpu/linux_s390/os_linux_s390.cpp @@ -204,59 +204,8 @@ frame os::current_frame() { } } -// Utility functions - -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // If crash protection is installed we may longjmp away and no destructors - // for objects in this scope will be run. - // So don't use any RAII utilities before crash protection is checked. - os::ThreadCrashProtection::check_crash_protection(sig, t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - if (PrintMiscellaneous && (WizardMode || Verbose)) { - warning("Ignoring SIGPIPE - see bug 4229104"); - } - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL) { - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } else if(t->is_VM_thread()) { - vmthread = (VMThread *)t; - } - } - } +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // Moved SafeFetch32 handling outside thread!=NULL conditional block to make // it work if no associated JavaThread object exists. @@ -294,7 +243,7 @@ JVM_handle_linux_signal(int sig, if (thread->is_in_full_stack(addr)) { // stack overflow if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } } } @@ -418,38 +367,8 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - // Hand down correct pc for SIGILL, SIGFPE. pc from context - // usually points to the instruction after the failing instruction. - // Note: this should be combined with the trap_pc handling above, - // because it handles the same issue. - if (sig == SIGILL || sig == SIGFPE) { - pc = (address)info->si_addr; - } - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); return false; + } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index 4204cbc50bdce..b25c22d721bfa 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -200,58 +200,10 @@ enum { trap_page_fault = 0xE }; -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); - - // If crash protection is installed we may longjmp away and no destructors - // for objects in this scope will be run. - // So don't use any RAII utilities before crash protection is checked. - os::ThreadCrashProtection::check_crash_protection(sig, t); - - // Note: it's not uncommon that JNI code uses signal/sigset to install - // then restore certain signal handler (e.g. to temporarily block SIGPIPE, - // or have a SIGILL handler when detecting CPU type). When that happens, - // JVM_handle_linux_signal() might be invoked with junk info/ucVoid. To - // avoid unnecessary crash when libjsig is not preloaded, try handle signals - // that do not require siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - -#ifdef CAN_SHOW_REGISTERS_ON_ASSERT - if ((sig == SIGSEGV || sig == SIGBUS) && info != NULL && info->si_addr == g_assert_poison) { - if (handle_assert_poison_fault(ucVoid, info->si_addr)) { - return 1; - } - } -#endif +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } -/* + /* NOTE: does not seem to work on linux. if (info == NULL || info->si_code <= 0 || info->si_code == SI_NOINFO) { // can't decode this kind of signal @@ -271,7 +223,7 @@ JVM_handle_linux_signal(int sig, if (StubRoutines::is_safefetch_fault(pc)) { os::Linux::ucontext_set_pc(uc, StubRoutines::continuation_for_safefetch_fault(pc)); - return 1; + return true; } #ifndef AMD64 @@ -292,7 +244,7 @@ JVM_handle_linux_signal(int sig, if (thread->is_in_full_stack(addr)) { // stack overflow if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) { - return 1; // continue + return true; // continue } } } @@ -469,30 +421,7 @@ JVM_handle_linux_signal(int sig, return true; } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - - if (pc == NULL && uc != NULL) { - pc = os::Linux::ucontext_get_pc(uc); - } - - // unmask current signal - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset, sig); - sigprocmask(SIG_UNBLOCK, &newset, NULL); - - VMError::report_and_die(t, sig, pc, info, ucVoid); - - ShouldNotReachHere(); - return true; // Mute compiler + return false; } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp index 8f2c8b8bc162f..8ff5ac61c5ceb 100644 --- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp +++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.cpp @@ -111,14 +111,8 @@ frame os::fetch_frame_from_context(const void* ucVoid) { return frame(NULL, NULL); // silence compile warnings } -extern "C" JNIEXPORT int -JVM_handle_linux_signal(int sig, - siginfo_t* info, - void* ucVoid, - int abort_if_unrecognized) { - ucontext_t* uc = (ucontext_t*) ucVoid; - - Thread* t = Thread::current_or_null_safe(); +bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, + ucontext_t* uc, JavaThread* thread) { // handle SafeFetch faults if (sig == SIGSEGV || sig == SIGBUS) { @@ -128,37 +122,6 @@ JVM_handle_linux_signal(int sig, } } - // Note: it's not uncommon that JNI code uses signal/sigset to - // install then restore certain signal handler (e.g. to temporarily - // block SIGPIPE, or have a SIGILL handler when detecting CPU - // type). When that happens, JVM_handle_linux_signal() might be - // invoked with junk info/ucVoid. To avoid unnecessary crash when - // libjsig is not preloaded, try handle signals that do not require - // siginfo/ucontext first. - - if (sig == SIGPIPE || sig == SIGXFSZ) { - // allow chained handler to go first - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } else { - // Ignoring SIGPIPE/SIGXFSZ - see bugs 4229104 or 6499219 - return true; - } - } - - JavaThread* thread = NULL; - VMThread* vmthread = NULL; - if (PosixSignals::are_signal_handlers_installed()) { - if (t != NULL ){ - if(t->is_Java_thread()) { - thread = t->as_Java_thread(); - } - else if(t->is_VM_thread()){ - vmthread = (VMThread *)t; - } - } - } - if (info != NULL && thread != NULL) { // Handle ALL stack overflow variations here if (sig == SIGSEGV) { @@ -216,47 +179,8 @@ JVM_handle_linux_signal(int sig, }*/ } - // signal-chaining - if (PosixSignals::chained_handler(sig, info, ucVoid)) { - return true; - } - - if (!abort_if_unrecognized) { - // caller wants another chance, so give it to him - return false; - } - -#ifndef PRODUCT - if (sig == SIGSEGV) { - fatal("\n#" - "\n# /--------------------\\" - "\n# | segmentation fault |" - "\n# \\---\\ /--------------/" - "\n# /" - "\n# [-] |\\_/| " - "\n# (+)=C |o o|__ " - "\n# | | =-*-=__\\ " - "\n# OOO c_c_(___)"); - } -#endif // !PRODUCT - - char buf[128]; - char exc_buf[32]; - - if (os::exception_name(sig, exc_buf, sizeof(exc_buf))) { - bool sent_by_kill = (info != NULL && os::signal_sent_by_kill(info)); - snprintf(buf, sizeof(buf), "caught unhandled signal: %s %s", - exc_buf, sent_by_kill ? "(sent by kill)" : ""); - } else { - snprintf(buf, sizeof(buf), "caught unhandled signal: %d", sig); - } + return false; // Fatal error -// Silence -Wformat-security warning for fatal() -PRAGMA_DIAG_PUSH -PRAGMA_FORMAT_NONLITERAL_IGNORED - fatal(buf); -PRAGMA_DIAG_POP - return true; // silence compiler warnings } void os::Linux::init_thread_fpu_state(void) { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index e5636525eff3a..aff2c78050cf1 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -755,7 +755,7 @@ const intx ObjectAlignmentInBytes = 8; "tables") \ \ product(bool, AllowUserSignalHandlers, false, \ - "Do not complain if the application installs signal handlers " \ + "Application will install primary signal handlers for the JVM " \ "(Unix only)") \ \ product(bool, UseSignalChaining, true, \ diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp index 172ddb52fb80c..84baf8fec62c8 100644 --- a/src/hotspot/share/utilities/vmError.hpp +++ b/src/hotspot/share/utilities/vmError.hpp @@ -116,9 +116,6 @@ class VMError : public AllStatic { // and the offending address points into CDS store. static void check_failing_cds_access(outputStream* st, const void* siginfo); - static void report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, - void* context, const char* detail_fmt, ...) ATTRIBUTE_PRINTF(6, 7); - // Timeout handling. // Hook functions for platform dependend functionality: static void reporting_started(); @@ -146,6 +143,9 @@ class VMError : public AllStatic { static void print_vm_info(outputStream* st); // main error reporting function + static void report_and_die(Thread* thread, unsigned int sig, address pc, void* siginfo, + void* context, const char* detail_fmt, ...) ATTRIBUTE_PRINTF(6, 7); + static void report_and_die(int id, const char* message, const char* detail_fmt, va_list detail_args, Thread* thread, address pc, void* siginfo, void* context, const char* filename, int lineno, size_t size) ATTRIBUTE_PRINTF(3, 0); From 79b790950795bb8e4c85bf99f7b5b3e70680f752 Mon Sep 17 00:00:00 2001 From: Stefan Johansson Date: Mon, 9 Nov 2020 14:24:24 +0000 Subject: [PATCH 030/124] 8255980: G1 Service thread register_task can be used after shutdown Reviewed-by: tschatzl, ayang --- src/hotspot/share/gc/g1/g1ServiceThread.cpp | 27 +++++++++++++++------ src/hotspot/share/gc/g1/g1ServiceThread.hpp | 7 ++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.cpp b/src/hotspot/share/gc/g1/g1ServiceThread.cpp index 3c3e561a6b438..02762b375a043 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.cpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.cpp @@ -176,15 +176,30 @@ G1ServiceThread::G1ServiceThread() : true, Monitor::_safepoint_check_never), _task_queue(), + _remset_task(new G1RemSetSamplingTask("Remembered Set Sampling Task")), + _periodic_gc_task(new G1PeriodicGCTask("Periodic GC Task")), _vtime_accum(0) { set_name("G1 Service"); create_and_start(); } +G1ServiceThread::~G1ServiceThread() { + delete _remset_task; + delete _periodic_gc_task; +} + void G1ServiceThread::register_task(G1ServiceTask* task, jlong delay) { guarantee(!task->is_registered(), "Task already registered"); guarantee(task->next() == NULL, "Task already in queue"); + // Make sure the service thread is still up and running, there is a race + // during shutdown where the service thread has been stopped, but other + // GC threads might still be running and trying to add tasks. + if (has_terminated()) { + log_debug(gc, task)("G1 Service Thread (%s) (terminated)", task->name()); + return; + } + log_debug(gc, task)("G1 Service Thread (%s) (register)", task->name()); // Associate the task with the service thread. @@ -272,13 +287,9 @@ void G1ServiceThread::run_task(G1ServiceTask* task) { void G1ServiceThread::run_service() { double vtime_start = os::elapsedVTime(); - // Setup the tasks handeled by the service thread and - // add them to the task list. - G1PeriodicGCTask gc_task("Periodic GC Task"); - register_task(&gc_task); - - G1RemSetSamplingTask remset_task("Remembered Set Sampling Task"); - register_task(&remset_task); + // Register the tasks handled by the service thread. + register_task(_periodic_gc_task); + register_task(_remset_task); while (!should_terminate()) { G1ServiceTask* task = pop_due_task(); @@ -293,6 +304,8 @@ void G1ServiceThread::run_service() { } sleep_before_next_cycle(); } + + log_debug(gc, task)("G1 Service Thread (stopping)"); } void G1ServiceThread::stop_service() { diff --git a/src/hotspot/share/gc/g1/g1ServiceThread.hpp b/src/hotspot/share/gc/g1/g1ServiceThread.hpp index 331e254012e9d..ec86dead87981 100644 --- a/src/hotspot/share/gc/g1/g1ServiceThread.hpp +++ b/src/hotspot/share/gc/g1/g1ServiceThread.hpp @@ -28,6 +28,8 @@ #include "gc/shared/concurrentGCThread.hpp" #include "runtime/mutex.hpp" +class G1PeriodicGCTask; +class G1RemSetSamplingTask; class G1ServiceTaskQueue; class G1ServiceThread; @@ -103,6 +105,9 @@ class G1ServiceThread: public ConcurrentGCThread { Monitor _monitor; G1ServiceTaskQueue _task_queue; + G1RemSetSamplingTask* _remset_task; + G1PeriodicGCTask* _periodic_gc_task; + double _vtime_accum; // Accumulated virtual time. void run_service(); @@ -122,6 +127,8 @@ class G1ServiceThread: public ConcurrentGCThread { public: G1ServiceThread(); + ~G1ServiceThread(); + double vtime_accum() { return _vtime_accum; } // Register a task with the service thread and schedule it. If // no delay is specified the task is scheduled to run directly. From 17f04fc9e755a83727ac9d13a39b487f99311efd Mon Sep 17 00:00:00 2001 From: Andrew Haley Date: Mon, 9 Nov 2020 16:02:30 +0000 Subject: [PATCH 031/124] 8254078: DataOutputStream is very slow post-disabling of Biased Locking Reviewed-by: rriggs, shade, alanb --- .../classes/java/io/DataInputStream.java | 7 +- .../classes/java/io/DataOutputStream.java | 24 ++-- .../bench/java/io/DataOutputStreamTest.java | 123 ++++++++++++++++++ 3 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java diff --git a/src/java.base/share/classes/java/io/DataInputStream.java b/src/java.base/share/classes/java/io/DataInputStream.java index 401a9764c6206..b784a5ab50e99 100644 --- a/src/java.base/share/classes/java/io/DataInputStream.java +++ b/src/java.base/share/classes/java/io/DataInputStream.java @@ -33,9 +33,10 @@ * way. An application uses a data output stream to write data that * can later be read by a data input stream. *

    - * DataInputStream is not necessarily safe for multithreaded access. - * Thread safety is optional and is the responsibility of users of - * methods in this class. + * A DataInputStream is not safe for use by multiple concurrent + * threads. If a DataInputStream is to be used by more than one + * thread then access to the data input stream should be controlled + * by appropriate synchronization. * * @author Arthur van Hoff * @see java.io.DataOutputStream diff --git a/src/java.base/share/classes/java/io/DataOutputStream.java b/src/java.base/share/classes/java/io/DataOutputStream.java index 94ba13a320f1a..e5c90a257f975 100644 --- a/src/java.base/share/classes/java/io/DataOutputStream.java +++ b/src/java.base/share/classes/java/io/DataOutputStream.java @@ -29,6 +29,11 @@ * A data output stream lets an application write primitive Java data * types to an output stream in a portable way. An application can * then use a data input stream to read the data back in. + *

    + * A DataOutputStream is not safe for use by multiple concurrent + * threads. If a DataOutputStream is to be used by more than one + * thread then access to the data output stream should be controlled + * by appropriate synchronization. * * @see java.io.DataInputStream * @since 1.0 @@ -162,8 +167,9 @@ public final void writeByte(int v) throws IOException { * @see java.io.FilterOutputStream#out */ public final void writeShort(int v) throws IOException { - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + writeBuffer[0] = (byte)(v >>> 8); + writeBuffer[1] = (byte)(v >>> 0); + out.write(writeBuffer, 0, 2); incCount(2); } @@ -177,8 +183,9 @@ public final void writeShort(int v) throws IOException { * @see java.io.FilterOutputStream#out */ public final void writeChar(int v) throws IOException { - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + writeBuffer[0] = (byte)(v >>> 8); + writeBuffer[1] = (byte)(v >>> 0); + out.write(writeBuffer, 0, 2); incCount(2); } @@ -192,10 +199,11 @@ public final void writeChar(int v) throws IOException { * @see java.io.FilterOutputStream#out */ public final void writeInt(int v) throws IOException { - out.write((v >>> 24) & 0xFF); - out.write((v >>> 16) & 0xFF); - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + writeBuffer[0] = (byte)(v >>> 24); + writeBuffer[1] = (byte)(v >>> 16); + writeBuffer[2] = (byte)(v >>> 8); + writeBuffer[3] = (byte)(v >>> 0); + out.write(writeBuffer, 0, 4); incCount(4); } diff --git a/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java b/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java new file mode 100644 index 0000000000000..2e63268f12d05 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/io/DataOutputStreamTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020, Red Hat Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.java.io; + +import org.openjdk.jmh.annotations.*; + +import java.io.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Fork(value = 1, warmups = 0) +@Measurement(iterations = 6, time = 1) +@Warmup(iterations=2, time = 2) +@State(Scope.Benchmark) +public class DataOutputStreamTest { + + public enum BasicType {CHAR, SHORT, INT, STRING} + @Param({"CHAR", "SHORT", "INT", /* "STRING"*/}) BasicType basicType; + + @Param({"4096"}) int size; + final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(size); + File f; + String outputString; + FileOutputStream fileOutputStream; + DataOutput bufferedFileStream, rawFileStream, byteArrayStream; + + @Setup(Level.Trial) + public void setup() throws Exception { + f = File.createTempFile("DataOutputStreamTest","out"); + fileOutputStream = new FileOutputStream(f); + byteArrayStream = new DataOutputStream(byteArrayOutputStream); + rawFileStream = new DataOutputStream(fileOutputStream); + bufferedFileStream = new DataOutputStream(new BufferedOutputStream(fileOutputStream)); + outputString = new String(new byte[size]); + } + + public void writeChars(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 2) { + dataOutput.writeChar(i); + } + } + + public void writeShorts(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 2) { + dataOutput.writeShort(i); + } + } + + public void writeInts(DataOutput dataOutput) + throws Exception { + for (int i = 0; i < size; i += 4) { + dataOutput.writeInt(i); + } + } + + public void writeString(DataOutput dataOutput) + throws Exception { + dataOutput.writeChars(outputString); + } + + public void write(DataOutput dataOutput) + throws Exception { + switch (basicType) { + case CHAR: + writeChars(dataOutput); + break; + case SHORT: + writeShorts(dataOutput); + break; + case INT: + writeInts(dataOutput); + break; + case STRING: + writeString(dataOutput); + break; + } + } + + @Benchmark + public void dataOutputStreamOverByteArray() throws Exception { + byteArrayOutputStream.reset(); + write(byteArrayStream); + byteArrayOutputStream.flush(); + } + + @Benchmark + public void dataOutputStreamOverRawFileStream() throws Exception { + fileOutputStream.getChannel().position(0); + write(rawFileStream); + fileOutputStream.flush(); + } + + @Benchmark + public void dataOutputStreamOverBufferedFileStream() throws Exception{ + fileOutputStream.getChannel().position(0); + write(bufferedFileStream); + fileOutputStream.flush(); + } +} From 11431b16e00ee21d5c909b99ea5ed4a9f6f8e288 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Mon, 9 Nov 2020 23:40:04 +0000 Subject: [PATCH 032/124] 4619330: All built-in java.awt.color.ColorSpace fields should be specified as such Reviewed-by: prr --- .../share/classes/java/awt/color/ColorSpace.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/color/ColorSpace.java b/src/java.desktop/share/classes/java/awt/color/ColorSpace.java index ecb40c2844f28..f88bde99dad47 100644 --- a/src/java.desktop/share/classes/java/awt/color/ColorSpace.java +++ b/src/java.desktop/share/classes/java/awt/color/ColorSpace.java @@ -244,25 +244,25 @@ public abstract class ColorSpace implements Serializable { /** - * The sRGB color space defined at + * The built-in sRGB color space defined at * * http://www.w3.org/pub/WWW/Graphics/Color/sRGB.html. */ @Native public static final int CS_sRGB = 1000; /** - * A built-in linear RGB color space. This space is based on the same RGB + * The built-in linear RGB color space. This space is based on the same RGB * primaries as {@code CS_sRGB}, but has a linear tone reproduction curve. */ @Native public static final int CS_LINEAR_RGB = 1004; /** - * The CIEXYZ conversion color space defined above. + * The built-in CIEXYZ conversion color space defined above. */ @Native public static final int CS_CIEXYZ = 1001; /** - * The Photo YCC conversion color space. + * The built-in Photo YCC conversion color space. */ @Native public static final int CS_PYCC = 1002; From 1332ba3c3c168d9fa368338e451c8c20a4c47ccc Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 10 Nov 2020 00:05:20 +0000 Subject: [PATCH 033/124] 8256039: Shenandoah: runtime/stringtable/StringTableCleaningTest.java fails Reviewed-by: shade, rkennke --- .../share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp index 09f0e92d6fb45..61e5b4b7705d7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahParallelCleaning.inline.hpp @@ -53,7 +53,9 @@ ShenandoahParallelWeakRootsCleaningTask::~ShenandoahParallel if (StringDedup::is_enabled()) { StringDedup::gc_epilogue(); } - _weak_processing_task.report_num_dead(); + if (_include_concurrent_roots) { + _weak_processing_task.report_num_dead(); + } } template From f71f9dc93a6963ce36953e0ad770edd761afd0b4 Mon Sep 17 00:00:00 2001 From: Dong Bo Date: Tue, 10 Nov 2020 01:24:25 +0000 Subject: [PATCH 034/124] 8255949: AArch64: Add support for vectorized shift right and accumulate Reviewed-by: aph --- src/hotspot/cpu/aarch64/aarch64.ad | 210 ++++++++++++++++++ src/hotspot/cpu/aarch64/assembler_aarch64.hpp | 2 + .../vm/compiler/VectorShiftAccumulate.java | 137 ++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 4297944eb8d10..4c209d8698120 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -18922,6 +18922,216 @@ instruct vsrl2L_imm(vecX dst, vecX src, immI shift) %{ ins_pipe(vshift128_imm); %} +instruct vsraa8B_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVB dst (RShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (8B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) sh = 7; + __ ssra(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa16B_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 16); + match(Set dst (AddVB dst (RShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (16B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) sh = 7; + __ ssra(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa4S_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVS dst (RShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (4H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) sh = 15; + __ ssra(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa8S_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVS dst (RShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (8H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) sh = 15; + __ ssra(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src$$reg), sh); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa2I_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVI dst (RShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (2S)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsraa4I_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVI dst (RShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (4S)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsraa2L_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVL dst (RShiftVL src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "ssra $dst, $src, $shift\t# vector (2D)" %} + ins_encode %{ + __ ssra(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla8B_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVB dst (URShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (8B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) { + __ eor(as_FloatRegister($src$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T8B, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla16B_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 16); + match(Set dst (AddVB dst (URShiftVB src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (16B)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 8) { + __ eor(as_FloatRegister($src$$reg), __ T16B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T16B, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla4S_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVS dst (URShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (4H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) { + __ eor(as_FloatRegister($src$$reg), __ T8B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ ushr(as_FloatRegister($dst$$reg), __ T4H, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla8S_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (AddVS dst (URShiftVS src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (8H)" %} + ins_encode %{ + int sh = (int)$shift$$constant; + if (sh >= 16) { + __ eor(as_FloatRegister($src$$reg), __ T16B, + as_FloatRegister($src$$reg), + as_FloatRegister($src$$reg)); + } else { + __ usra(as_FloatRegister($dst$$reg), __ T8H, + as_FloatRegister($src$$reg), sh); + } + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla2I_imm(vecD dst, vecD src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVI dst (URShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (2S)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T2S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift64_imm); +%} + +instruct vsrla4I_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (AddVI dst (URShiftVI src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (4S)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T4S, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + +instruct vsrla2L_imm(vecX dst, vecX src, immI shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (AddVL dst (URShiftVL src (RShiftCntV shift)))); + ins_cost(INSN_COST); + format %{ "usra $dst, $src, $shift\t# vector (2D)" %} + ins_encode %{ + __ usra(as_FloatRegister($dst$$reg), __ T2D, + as_FloatRegister($src$$reg), + (int)$shift$$constant); + %} + ins_pipe(vshift128_imm); +%} + instruct vmax2F(vecD dst, vecD src1, vecD src2) %{ predicate(n->as_Vector()->length() == 2 && n->bottom_type()->is_vect()->element_basic_type() == T_FLOAT); diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp index c1af5d1ef14c1..7ff9c018bef04 100644 --- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp @@ -2688,6 +2688,8 @@ void mvnw(Register Rd, Register Rm, INSN(shl, 0, 0b010101, /* isSHR = */ false); INSN(sshr, 0, 0b000001, /* isSHR = */ true); INSN(ushr, 1, 0b000001, /* isSHR = */ true); + INSN(usra, 1, 0b000101, /* isSHR = */ true); + INSN(ssra, 0, 0b000101, /* isSHAR =*/ true); #undef INSN diff --git a/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java b/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java new file mode 100644 index 0000000000000..d9c729f56a0a2 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/VectorShiftAccumulate.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2020, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.*; + +import java.util.concurrent.TimeUnit; +import java.util.Random; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class VectorShiftAccumulate { + @Param({"1028"}) + public int count; + + private byte[] bytesA, bytesB, bytesD; + private short[] shortsA, shortsB, shortsD; + private char[] charsA, charsB, charsD; + private int[] intsA, intsB, intsD; + private long[] longsA, longsB, longsD; + + @Param("0") + private int seed; + private Random r = new Random(seed); + + @Setup + public void init() { + bytesA = new byte[count]; + shortsA = new short[count]; + charsA = new char[count]; + intsA = new int[count]; + longsA = new long[count]; + + bytesB = new byte[count]; + shortsB = new short[count]; + charsB = new char[count]; + intsB = new int[count]; + longsB = new long[count]; + + bytesD = new byte[count]; + shortsD = new short[count]; + charsD = new char[count]; + intsD = new int[count]; + longsD = new long[count]; + + for (int i = 0; i < count; i++) { + bytesA[i] = (byte) r.nextInt(); + shortsA[i] = (short) r.nextInt(); + intsA[i] = r.nextInt(); + longsA[i] = r.nextLong(); + + bytesB[i] = (byte) r.nextInt(); + shortsB[i] = (short) r.nextInt(); + intsB[i] = r.nextInt(); + longsB[i] = r.nextLong(); + } + } + + @Benchmark + public void shiftRightAccumulateByte() { + for (int i = 0; i < count; i++) { + bytesD[i] = (byte) (bytesA[i] + (bytesB[i] >> 1)); + } + } + + @Benchmark + public void shiftURightAccumulateByte() { + for (int i = 0; i < count; i++) { + bytesD[i] = (byte) (bytesA[i] + (((byte) (bytesB[i] >>> 3)))); + } + } + + @Benchmark + public void shiftRightAccumulateShort() { + for (int i = 0; i < count; i++) { + shortsD[i] = (short) (shortsA[i] + (shortsB[i] >> 5)); + } + } + + @Benchmark + public void shiftURightAccumulateChar() { + for (int i = 0; i < count; i++) { + charsD[i] = (char) (charsA[i] + (charsB[i] >>> 4)); + } + } + + @Benchmark + public void shiftRightAccumulateInt() { + for (int i = 0; i < count; i++) { + intsD[i] = intsA[i] + (intsB[i] >> 2); + } + } + + @Benchmark + public void shiftURightAccumulateInt() { + for (int i = 0; i < count; i++) { + intsD[i] = (intsB[i] >>> 2) + intsA[i]; + } + } + + @Benchmark + public void shiftRightAccumulateLong() { + for (int i = 0; i < count; i++) { + longsD[i] = longsA[i] + (longsB[i] >> 5); + } + } + + @Benchmark + public void shiftURightAccumulateLong() { + for (int i = 0; i < count; i++) { + longsD[i] = (longsB[i] >>> 2) + longsA[i]; + } + } +} + From 8066b33c40fc594938e532d4e81bc3554173a3ef Mon Sep 17 00:00:00 2001 From: amresh-sahu <72060147+amresh-sahu@users.noreply.github.com> Date: Tue, 10 Nov 2020 05:27:16 +0000 Subject: [PATCH 035/124] 8253905: Update sanity test suite to not place windows at (0,0) Reviewed-by: shurailine, serb --- .../SwingSet3/src/com/sun/swingset3/demos/dialog/DialogDemo.java | 1 + .../SwingSet3/src/com/sun/swingset3/demos/frame/FrameDemo.java | 1 + .../SwingSet3/src/com/sun/swingset3/demos/table/TableDemo.java | 1 + .../SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java | 1 + 4 files changed, 4 insertions(+) diff --git a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/dialog/DialogDemo.java b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/dialog/DialogDemo.java index 4ff166cfd659f..9a8b92c1e9c20 100644 --- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/dialog/DialogDemo.java +++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/dialog/DialogDemo.java @@ -154,6 +154,7 @@ public void run() { frame.add(demo); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); + frame.setLocationRelativeTo(null); frame.setVisible(true); demo.start(); } diff --git a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/frame/FrameDemo.java b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/frame/FrameDemo.java index 518ef207a10ef..9f86dc2ad543a 100644 --- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/frame/FrameDemo.java +++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/frame/FrameDemo.java @@ -264,6 +264,7 @@ public void run() { frame.add(demo); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); +frame.setLocationRelativeTo(null); frame.setVisible(true); demo.start(); } diff --git a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/table/TableDemo.java b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/table/TableDemo.java index 86deeac797431..a2a26cc9bd3ec 100644 --- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/table/TableDemo.java +++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/table/TableDemo.java @@ -615,6 +615,7 @@ public void run() { TableDemo demo = new TableDemo(); frame.add(demo); frame.setSize(700, 400); + frame.setLocationRelativeTo(null); frame.setVisible(true); demo.start(); } diff --git a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java index bbb06a1182aea..5aefeed90c01c 100644 --- a/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java +++ b/test/jdk/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java @@ -152,6 +152,7 @@ public static void main(String args[]) throws InterruptedException, InvocationTa WindowDemo demo = new WindowDemo(); frame.add(demo); frame.pack(); + frame.setLocationRelativeTo(null); frame.setVisible(true); demo.start(); }); From b5d78afe20d98c11ba7d51a5a4c17eaee22fc82f Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Tue, 10 Nov 2020 06:17:19 +0000 Subject: [PATCH 036/124] 8254863: Delete code leftover from old fixes Reviewed-by: azeemj, kizune, prr --- .../classes/java/awt/color/ICC_Profile.java | 29 ++++--------------- .../classes/sun/java2d/cmm/CMSManager.java | 14 ++------- .../share/classes/sun/java2d/cmm/PCMM.java | 3 +- .../sun/java2d/cmm/ProfileDeferralMgr.java | 19 +----------- .../classes/sun/java2d/cmm/lcms/LCMS.java | 9 ++---- 5 files changed, 12 insertions(+), 62 deletions(-) diff --git a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java index 1ccfa05818eae..b4e38a25c7ea6 100644 --- a/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java +++ b/src/java.desktop/share/classes/java/awt/color/ICC_Profile.java @@ -831,8 +831,6 @@ else if ((getColorSpaceType (p) == ColorSpace.TYPE_RGB) && */ public static ICC_Profile getInstance (int cspace) { ICC_Profile thisProfile = null; - String fileName; - switch (cspace) { case ColorSpace.CS_sRGB: synchronized(ICC_Profile.class) { @@ -870,17 +868,11 @@ public static ICC_Profile getInstance (int cspace) { case ColorSpace.CS_PYCC: synchronized(ICC_Profile.class) { if (PYCCprofile == null) { - if (standardProfileExists("PYCC.pf")) - { - ProfileDeferralInfo pInfo = - new ProfileDeferralInfo("PYCC.pf", - ColorSpace.TYPE_3CLR, 3, - CLASS_DISPLAY); - PYCCprofile = getDeferredInstance(pInfo); - } else { - throw new IllegalArgumentException( - "Can't load standard profile: PYCC.pf"); - } + ProfileDeferralInfo pInfo = + new ProfileDeferralInfo("PYCC.pf", + ColorSpace.TYPE_3CLR, 3, + CLASS_DISPLAY); + PYCCprofile = getDeferredInstance(pInfo); } thisProfile = PYCCprofile; } @@ -1851,17 +1843,6 @@ private static boolean isChildOf(File f, String dirName) { } } - /** - * Checks whether built-in profile specified by fileName exists. - */ - private static boolean standardProfileExists(final String fileName) { - return AccessController.doPrivileged(new PrivilegedAction() { - public Boolean run() { - return PCMM.class.getResource("profiles/"+fileName) != null; - } - }); - } - /* * Serialization support. * diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java b/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java index fea14af51ed23..4d9f09c590597 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/CMSManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,14 +25,11 @@ package sun.java2d.cmm; +import java.awt.color.CMMException; import java.awt.color.ColorSpace; import java.awt.color.ICC_Profile; -import java.awt.color.CMMException; -import java.awt.image.BufferedImage; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; import java.security.AccessController; -import java.security.PrivilegedAction; + import sun.security.action.GetPropertyAction; public class CMSManager { @@ -103,11 +100,6 @@ public Profile loadProfile(byte[] data) { return p; } - public void freeProfile(Profile p) { - System.err.printf(cName + ".freeProfile(ID=%s)\n", p.toString()); - tcmm.freeProfile(p); - } - public int getProfileSize(Profile p) { System.err.print(cName + ".getProfileSize(ID=" + p + ")"); int size = tcmm.getProfileSize(p); diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java b/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java index d147f0a42dcef..0526abafecabf 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/PCMM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,6 @@ public interface PCMM { /* methods invoked from ICC_Profile */ public Profile loadProfile(byte[] data); - public void freeProfile(Profile p); public int getProfileSize(Profile p); public void getProfileData(Profile p, byte[] data); public void getTagData(Profile p, int tagSignature, byte[] data); diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java b/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java index 49b6808baa445..d19b6f6da68b2 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/ProfileDeferralMgr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,23 +59,6 @@ public static void registerDeferral(ProfileActivator pa) { } - /** - * Removes a ProfileActivator object from the vector of ProfileActivator - * objects whose activate method will be called if the CMM needs to be - * activated. - */ - public static void unregisterDeferral(ProfileActivator pa) { - - if (!deferring) { - return; - } - if (aVector == null) { - return; - } - aVector.removeElement(pa); - return; - } - /** * Removes a ProfileActivator object from the vector of ProfileActivator * objects whose activate method will be called if the CMM needs to be diff --git a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java index a9e6adbbed95d..32232d027988d 100644 --- a/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java +++ b/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.awt.color.CMMException; import java.awt.color.ICC_Profile; + import sun.java2d.cmm.ColorTransform; import sun.java2d.cmm.PCMM; import sun.java2d.cmm.Profile; @@ -56,12 +57,6 @@ private LCMSProfile getLcmsProfile(Profile p) { throw new CMMException("Invalid profile: " + p); } - - @Override - public void freeProfile(Profile p) { - // we use disposer, so this method does nothing - } - @Override public int getProfileSize(final Profile p) { synchronized (p) { From 4bc065cf9e93563439c3a7a3cd884fd801b6c9cd Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 10 Nov 2020 06:29:53 +0000 Subject: [PATCH 037/124] 8255782: Turn UseTLAB and ResizeTLAB from product_pd to product, defaulting to "true" Reviewed-by: stuefe, stefank, tschatzl --- src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp | 2 -- src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp | 2 -- src/hotspot/cpu/arm/c1_globals_arm.hpp | 2 -- src/hotspot/cpu/arm/c2_globals_arm.hpp | 2 -- src/hotspot/cpu/ppc/c1_globals_ppc.hpp | 2 -- src/hotspot/cpu/ppc/c2_globals_ppc.hpp | 2 -- src/hotspot/cpu/s390/c1_globals_s390.hpp | 2 -- src/hotspot/cpu/s390/c2_globals_s390.hpp | 2 -- src/hotspot/cpu/x86/c1_globals_x86.hpp | 2 -- src/hotspot/cpu/x86/c2_globals_x86.hpp | 2 -- src/hotspot/share/compiler/compiler_globals.hpp | 2 -- src/hotspot/share/gc/shared/gc_globals.hpp | 5 +++-- 12 files changed, 3 insertions(+), 24 deletions(-) diff --git a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp index 4e4262d5d6db6..d2520014ed173 100644 --- a/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c1_globals_aarch64.hpp @@ -34,8 +34,6 @@ #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, true ); define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp index 973cbe740bdb3..5a019eba6aeb4 100644 --- a/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/c2_globals_aarch64.hpp @@ -33,8 +33,6 @@ // (see c2_globals.hpp). Alpha-sorted. define_pd_global(bool, BackgroundCompilation, true); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, CICompileOSR, true); define_pd_global(bool, InlineIntrinsics, true); define_pd_global(bool, PreferInterpreterNativeStubs, false); diff --git a/src/hotspot/cpu/arm/c1_globals_arm.hpp b/src/hotspot/cpu/arm/c1_globals_arm.hpp index 8141870536b4a..7077a87092c28 100644 --- a/src/hotspot/cpu/arm/c1_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c1_globals_arm.hpp @@ -35,8 +35,6 @@ #ifndef COMPILER2 // avoid duplicated definitions, favoring C2 version define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, false); // TODO: ARM define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/arm/c2_globals_arm.hpp b/src/hotspot/cpu/arm/c2_globals_arm.hpp index 3708e38da2e09..525af8b1edc9e 100644 --- a/src/hotspot/cpu/arm/c2_globals_arm.hpp +++ b/src/hotspot/cpu/arm/c2_globals_arm.hpp @@ -54,8 +54,6 @@ define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); // (For _228_jack 16/16 is 2% better than 4/4, 16/4, 32/32, 32/16, or 16/32.) //define_pd_global(intx, OptoLoopAlignment, 16); // = 4*wordSize define_pd_global(intx, RegisterCostAreaRatio, 16000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); // Design center runs on 1.3.1 define_pd_global(intx, LoopPercentProfileLimit, 10); define_pd_global(intx, MinJumpTableSize, 16); diff --git a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp index 60b0005e03464..f90c1e8b1d204 100644 --- a/src/hotspot/cpu/ppc/c1_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c1_globals_ppc.hpp @@ -43,9 +43,7 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 1000); define_pd_global(intx, OnStackReplacePercentage, 1400); -define_pd_global(bool, UseTLAB, true); define_pd_global(bool, ProfileInterpreter, false); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(uintx, ReservedCodeCacheSize, 32*M); define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M ); define_pd_global(uintx, ProfiledCodeHeapSize, 14*M ); diff --git a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp index 7a0c311e7192e..c576ddc95c466 100644 --- a/src/hotspot/cpu/ppc/c2_globals_ppc.hpp +++ b/src/hotspot/cpu/ppc/c2_globals_ppc.hpp @@ -51,8 +51,6 @@ define_pd_global(intx, INTPRESSURE, 26); define_pd_global(intx, InteriorEntryAlignment, 16); define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); define_pd_global(intx, RegisterCostAreaRatio, 16000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); define_pd_global(intx, LoopPercentProfileLimit, 10); diff --git a/src/hotspot/cpu/s390/c1_globals_s390.hpp b/src/hotspot/cpu/s390/c1_globals_s390.hpp index 99e26e5e3f8d5..7fcb1ee0617e2 100644 --- a/src/hotspot/cpu/s390/c1_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c1_globals_s390.hpp @@ -43,9 +43,7 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 1000); define_pd_global(intx, OnStackReplacePercentage, 1400); -define_pd_global(bool, UseTLAB, true); define_pd_global(bool, ProfileInterpreter, false); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(uintx, ReservedCodeCacheSize, 32*M); define_pd_global(uintx, NonProfiledCodeHeapSize, 13*M); define_pd_global(uintx, ProfiledCodeHeapSize, 14*M); diff --git a/src/hotspot/cpu/s390/c2_globals_s390.hpp b/src/hotspot/cpu/s390/c2_globals_s390.hpp index 2f44fa73a2ef9..64d5585d616ca 100644 --- a/src/hotspot/cpu/s390/c2_globals_s390.hpp +++ b/src/hotspot/cpu/s390/c2_globals_s390.hpp @@ -51,8 +51,6 @@ define_pd_global(intx, INTPRESSURE, 10); // Medium size registe define_pd_global(intx, InteriorEntryAlignment, 2); define_pd_global(size_t, NewSizeThreadIncrease, ScaleForWordSize(4*K)); define_pd_global(intx, RegisterCostAreaRatio, 12000); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(intx, LoopUnrollLimit, 60); define_pd_global(intx, LoopPercentProfileLimit, 10); define_pd_global(intx, MinJumpTableSize, 18); diff --git a/src/hotspot/cpu/x86/c1_globals_x86.hpp b/src/hotspot/cpu/x86/c1_globals_x86.hpp index fbf538c2cec0e..afd2a65cb89c1 100644 --- a/src/hotspot/cpu/x86/c1_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c1_globals_x86.hpp @@ -33,8 +33,6 @@ #ifndef TIERED define_pd_global(bool, BackgroundCompilation, true ); -define_pd_global(bool, UseTLAB, true ); -define_pd_global(bool, ResizeTLAB, true ); define_pd_global(bool, InlineIntrinsics, true ); define_pd_global(bool, PreferInterpreterNativeStubs, false); define_pd_global(bool, ProfileTraps, false); diff --git a/src/hotspot/cpu/x86/c2_globals_x86.hpp b/src/hotspot/cpu/x86/c2_globals_x86.hpp index 6513be7b53ea7..31e77b52568e2 100644 --- a/src/hotspot/cpu/x86/c2_globals_x86.hpp +++ b/src/hotspot/cpu/x86/c2_globals_x86.hpp @@ -31,8 +31,6 @@ // Sets the default values for platform dependent flags used by the server compiler. // (see c2_globals.hpp). Alpha-sorted. define_pd_global(bool, BackgroundCompilation, true); -define_pd_global(bool, UseTLAB, true); -define_pd_global(bool, ResizeTLAB, true); define_pd_global(bool, CICompileOSR, true); define_pd_global(bool, InlineIntrinsics, true); define_pd_global(bool, PreferInterpreterNativeStubs, false); diff --git a/src/hotspot/share/compiler/compiler_globals.hpp b/src/hotspot/share/compiler/compiler_globals.hpp index 51ac7179bb07d..c28c0e86eb57c 100644 --- a/src/hotspot/share/compiler/compiler_globals.hpp +++ b/src/hotspot/share/compiler/compiler_globals.hpp @@ -38,7 +38,6 @@ #if !defined(COMPILER1) && !defined(COMPILER2) && !INCLUDE_JVMCI define_pd_global(bool, BackgroundCompilation, false); -define_pd_global(bool, UseTLAB, false); define_pd_global(bool, CICompileOSR, false); define_pd_global(bool, UseTypeProfile, false); define_pd_global(bool, UseOnStackReplacement, false); @@ -51,7 +50,6 @@ define_pd_global(bool, TieredCompilation, false); define_pd_global(intx, CompileThreshold, 0); define_pd_global(intx, OnStackReplacePercentage, 0); -define_pd_global(bool, ResizeTLAB, false); define_pd_global(size_t, NewSizeThreadIncrease, 4*K); define_pd_global(bool, InlineClassNatives, true); define_pd_global(bool, InlineUnsafeOps, true); diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 956634b2066a7..064e0da22a11b 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -293,9 +293,10 @@ product(bool, ExecutingUnitTests, false, \ "Whether the JVM is running unit tests or not") \ \ - product_pd(bool, UseTLAB, "Use thread-local object allocation") \ + product(bool, UseTLAB, true, \ + "Use thread-local object allocation") \ \ - product_pd(bool, ResizeTLAB, \ + product(bool, ResizeTLAB, true, \ "Dynamically resize TLAB size for threads") \ \ product(bool, ZeroTLAB, false, \ From 01567b51dc5149e906a2cbdf050272fdc647781e Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 10 Nov 2020 06:30:26 +0000 Subject: [PATCH 038/124] 8256036: Shenandoah: MethodHandles adapters section overflows after JDK-8255762 Reviewed-by: jiefu, redestad --- src/hotspot/cpu/x86/methodHandles_x86.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/x86/methodHandles_x86.hpp b/src/hotspot/cpu/x86/methodHandles_x86.hpp index bf2c8d72d05a6..444d049566637 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.hpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.hpp @@ -27,7 +27,7 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = 3000 DEBUG_ONLY(+ 6000) + adapter_code_size = 4000 DEBUG_ONLY(+ 6000) }; // Additional helper methods for MethodHandles code generation: From a38dd53411f896c4c307d2d55727e5d3915819e4 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Tue, 10 Nov 2020 06:56:58 +0000 Subject: [PATCH 039/124] 8256040: Shenandoah: Allow NULL referent in ShenandoahReferenceProcessor::should_discover() Reviewed-by: shade --- .../share/gc/shenandoah/shenandoahReferenceProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp index 9cdfa5d6e6560..8bbecdc0e2c38 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.cpp @@ -265,7 +265,7 @@ template bool ShenandoahReferenceProcessor::should_discover(oop reference, ReferenceType type) const { T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(reference); T heap_oop = RawAccess<>::oop_load(referent_addr); - oop referent = CompressedOops::decode_not_null(heap_oop); + oop referent = CompressedOops::decode(heap_oop); if (is_inactive(reference, referent, type)) { log_trace(gc,ref)("Reference inactive: " PTR_FORMAT, p2i(reference)); From c601849e00d08f8f7e2e92e288b2061bf06eb8be Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 10 Nov 2020 09:12:29 +0000 Subject: [PATCH 040/124] 8256038: G1: Improve comment about mark word handling of displaced mark words Reviewed-by: sjohanss, kbarrett --- src/hotspot/share/gc/g1/g1ParScanThreadState.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp index ad8f76e33a706..c9ca7daed3204 100644 --- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp +++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp @@ -490,12 +490,11 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio age++; } if (old_mark.has_displaced_mark_helper()) { - // In this case, we have to install the mark word first, - // otherwise obj looks to be forwarded (the old mark word, - // which contains the forward pointer, was copied) - obj->set_mark(old_mark); + // In this case, we have to install the old mark word containing the + // displacement tag, and update the age in the displaced mark word. markWord new_mark = old_mark.displaced_mark_helper().set_age(age); old_mark.set_displaced_mark_helper(new_mark); + obj->set_mark(old_mark); } else { obj->set_mark(old_mark.set_age(age)); } From e281b135975dd5294a81bdd6567b2346e1847343 Mon Sep 17 00:00:00 2001 From: Nils Eliasson Date: Tue, 10 Nov 2020 11:25:53 +0000 Subject: [PATCH 041/124] 8255011: [TESTBUG] compiler/codecache/stress/UnexpectedDeoptimizationAllTest.java timed out Change CodeCacheStressRunner to have a 60 second test time Reviewed-by: iignatyev --- .../compiler/codecache/stress/CodeCacheStressRunner.java | 7 +++---- .../codecache/stress/UnexpectedDeoptimizationTest.java | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/codecache/stress/CodeCacheStressRunner.java b/test/hotspot/jtreg/compiler/codecache/stress/CodeCacheStressRunner.java index 24ec37b89d4a0..7e9c14b801ec5 100644 --- a/test/hotspot/jtreg/compiler/codecache/stress/CodeCacheStressRunner.java +++ b/test/hotspot/jtreg/compiler/codecache/stress/CodeCacheStressRunner.java @@ -28,6 +28,7 @@ public class CodeCacheStressRunner { private final Runnable action; + public CodeCacheStressRunner(Runnable action) { this.action = action; } @@ -35,10 +36,8 @@ public CodeCacheStressRunner(Runnable action) { protected final void runTest() { Helper.startInfiniteLoopThread(action); try { - // adjust timeout and substract vm init and exit time - long timeout = Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT); - timeout *= 0.8; - new TimeLimitedRunner(timeout, 2.0d, this::test).call(); + // Adjust timeout and substract vm init and exit time + new TimeLimitedRunner(60 * 1000, 2.0d, this::test).call(); } catch (Exception e) { throw new Error("Exception occurred during test execution", e); } diff --git a/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTest.java b/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTest.java index e4d2a83bf0bc9..45974011efeab 100644 --- a/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTest.java +++ b/test/hotspot/jtreg/compiler/codecache/stress/UnexpectedDeoptimizationTest.java @@ -60,6 +60,13 @@ public static void main(String[] args) { @Override public void run() { Helper.WHITE_BOX.deoptimizeFrames(rng.nextBoolean()); + // Sleep a short while to allow the stacks to grow - otherwise + // we end up running almost all code in the interpreter + try { + Thread.sleep(10); + } catch (Exception e) { + } + } } From 9d07259f16ed5088dce4be8e6ef8b1208400725b Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Tue, 10 Nov 2020 11:48:23 +0000 Subject: [PATCH 042/124] 8255598: [PPC64] assert(Universe::heap()->is_in(result)) failed: object not in heap Reviewed-by: ayang, tschatzl --- src/hotspot/cpu/ppc/nativeInst_ppc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp index fbe956322a6b2..1134ed0366b29 100644 --- a/src/hotspot/cpu/ppc/nativeInst_ppc.cpp +++ b/src/hotspot/cpu/ppc/nativeInst_ppc.cpp @@ -197,7 +197,11 @@ intptr_t NativeMovConstReg::data() const { CodeBlob* cb = CodeCache::find_blob_unsafe(addr); if (MacroAssembler::is_set_narrow_oop(addr, cb->content_begin())) { narrowOop no = MacroAssembler::get_narrow_oop(addr, cb->content_begin()); - return cast_from_oop(CompressedOops::decode(no)); + // We can reach here during GC with 'no' pointing to new object location + // while 'heap()->is_in' still reports false (e.g. with SerialGC). + // Therefore we use raw decoding. + if (CompressedOops::is_null(no)) return 0; + return cast_from_oop(CompressedOops::decode_raw(no)); } else { assert(MacroAssembler::is_load_const_from_method_toc_at(addr), "must be load_const_from_pool"); From 52805f526b8ebf598c3d03ed2bed8b7dd49083f9 Mon Sep 17 00:00:00 2001 From: Jie Fu Date: Tue, 10 Nov 2020 12:16:00 +0000 Subject: [PATCH 043/124] 8256048: Incomplete gitignore setting for netbeans project Reviewed-by: erikj --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 668747f8ce90a..cf21c8919cd26 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /dist/ /.idea/ /.vscode/ +/nbproject/ nbproject/private/ /webrev /.src-rev From e6df13e6e09d2887a5297a64ea89921723243868 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Tue, 10 Nov 2020 12:39:33 +0000 Subject: [PATCH 044/124] 8256054: C2: Floating-point min/max operations on vectors intermittently produce wrong results for NaN values Reviewed-by: redestad, psandoz, dlong --- src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 7 +++++++ src/hotspot/cpu/x86/x86.ad | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp index f930a99e2dceb..3aef6446f7820 100644 --- a/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp @@ -878,6 +878,7 @@ void C2_MacroAssembler::vabsnegf(int opcode, XMMRegister dst, XMMRegister src, i void C2_MacroAssembler::pminmax(int opcode, BasicType elem_bt, XMMRegister dst, XMMRegister src, XMMRegister tmp) { assert(opcode == Op_MinV || opcode == Op_MaxV, "sanity"); + assert(tmp == xnoreg || elem_bt == T_LONG, "unused"); if (opcode == Op_MinV) { if (elem_bt == T_BYTE) { @@ -889,6 +890,7 @@ void C2_MacroAssembler::pminmax(int opcode, BasicType elem_bt, XMMRegister dst, } else { assert(elem_bt == T_LONG, "required"); assert(tmp == xmm0, "required"); + assert_different_registers(dst, src, tmp); movdqu(xmm0, dst); pcmpgtq(xmm0, src); blendvpd(dst, src); // xmm0 as mask @@ -903,6 +905,7 @@ void C2_MacroAssembler::pminmax(int opcode, BasicType elem_bt, XMMRegister dst, } else { assert(elem_bt == T_LONG, "required"); assert(tmp == xmm0, "required"); + assert_different_registers(dst, src, tmp); movdqu(xmm0, src); pcmpgtq(xmm0, dst); blendvpd(dst, src); // xmm0 as mask @@ -927,6 +930,7 @@ void C2_MacroAssembler::vpminmax(int opcode, BasicType elem_bt, if (UseAVX > 2 && (vlen_enc == Assembler::AVX_512bit || VM_Version::supports_avx512vl())) { vpminsq(dst, src1, src2, vlen_enc); } else { + assert_different_registers(dst, src1, src2); vpcmpgtq(dst, src1, src2, vlen_enc); vblendvpd(dst, src1, src2, dst, vlen_enc); } @@ -943,6 +947,7 @@ void C2_MacroAssembler::vpminmax(int opcode, BasicType elem_bt, if (UseAVX > 2 && (vlen_enc == Assembler::AVX_512bit || VM_Version::supports_avx512vl())) { vpmaxsq(dst, src1, src2, vlen_enc); } else { + assert_different_registers(dst, src1, src2); vpcmpgtq(dst, src1, src2, vlen_enc); vblendvpd(dst, src2, src1, dst, vlen_enc); } @@ -960,6 +965,7 @@ void C2_MacroAssembler::vminmax_fp(int opcode, BasicType elem_bt, assert(opcode == Op_MinV || opcode == Op_MinReductionV || opcode == Op_MaxV || opcode == Op_MaxReductionV, "sanity"); assert(elem_bt == T_FLOAT || elem_bt == T_DOUBLE, "sanity"); + assert_different_registers(a, b, tmp, atmp, btmp); bool is_min = (opcode == Op_MinV || opcode == Op_MinReductionV); bool is_double_word = is_double_word_type(elem_bt); @@ -1000,6 +1006,7 @@ void C2_MacroAssembler::evminmax_fp(int opcode, BasicType elem_bt, assert(opcode == Op_MinV || opcode == Op_MinReductionV || opcode == Op_MaxV || opcode == Op_MaxReductionV, "sanity"); assert(elem_bt == T_FLOAT || elem_bt == T_DOUBLE, "sanity"); + assert_different_registers(dst, a, b, atmp, btmp); bool is_min = (opcode == Op_MinV || opcode == Op_MinReductionV); bool is_double_word = is_double_word_type(elem_bt); diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index c6c8c172b0a6e..7551dfaa0fc5f 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -5787,7 +5787,7 @@ instruct evminmaxFP_reg_eavx(vec dst, vec a, vec b, vec atmp, vec btmp) %{ is_floating_point_type(vector_element_basic_type(n))); // T_FLOAT, T_DOUBLE match(Set dst (MinV a b)); match(Set dst (MaxV a b)); - effect(USE a, USE b, TEMP atmp, TEMP btmp); + effect(TEMP dst, USE a, USE b, TEMP atmp, TEMP btmp); format %{ "vector_minmaxFP $dst,$a,$b\t!using $atmp, $btmp as TEMP" %} ins_encode %{ assert(UseAVX > 2, "required"); From 3455fa9bfd85e66812230ce6c2e010130435c049 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Tue, 10 Nov 2020 12:41:11 +0000 Subject: [PATCH 045/124] 8256050: JVM crashes with -XX:+PrintDeoptimizationDetails Reviewed-by: kvn, dcubed --- src/hotspot/share/oops/instanceKlass.cpp | 14 ++++++++++++-- src/hotspot/share/oops/markWord.cpp | 14 ++++++++------ src/hotspot/share/oops/markWord.hpp | 2 +- src/hotspot/share/runtime/basicLock.cpp | 10 +++++++--- src/hotspot/share/runtime/basicLock.hpp | 2 +- src/hotspot/share/runtime/frame.cpp | 2 +- src/hotspot/share/runtime/vframe.cpp | 2 +- 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 046df25f0266b..a026fca809e04 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -3607,9 +3607,19 @@ void InstanceKlass::oop_print_value_on(oop obj, outputStream* st) { st->print(" = "); vmtarget->print_value_on(st); } else { - java_lang_invoke_MemberName::clazz(obj)->print_value_on(st); + oop clazz = java_lang_invoke_MemberName::clazz(obj); + oop name = java_lang_invoke_MemberName::name(obj); + if (clazz != NULL) { + clazz->print_value_on(st); + } else { + st->print("NULL"); + } st->print("."); - java_lang_invoke_MemberName::name(obj)->print_value_on(st); + if (name != NULL) { + name->print_value_on(st); + } else { + st->print("NULL"); + } } } } diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index 2c8ad2414dbdf..5104ff975ad6d 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -28,17 +28,19 @@ #include "runtime/objectMonitor.hpp" #include "utilities/ostream.hpp" -void markWord::print_on(outputStream* st) const { +void markWord::print_on(outputStream* st, bool print_monitor_info) const { if (is_marked()) { // last bits = 11 st->print(" marked(" INTPTR_FORMAT ")", value()); } else if (has_monitor()) { // last bits = 10 // have to check has_monitor() before is_locked() st->print(" monitor(" INTPTR_FORMAT ")=", value()); - ObjectMonitor* mon = monitor(); - if (mon == NULL) { - st->print("NULL (this should never be seen!)"); - } else { - mon->print_on(st); + if (print_monitor_info) { + ObjectMonitor* mon = monitor(); + if (mon == NULL) { + st->print("NULL (this should never be seen!)"); + } else { + mon->print_on(st); + } } } else if (is_locked()) { // last bits != 01 => 00 // thin locked diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 2846e1ae1123b..5e5cae12a2c6a 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -355,7 +355,7 @@ class markWord { static inline markWord prototype_for_klass(const Klass* klass); // Debugging - void print_on(outputStream* st) const; + void print_on(outputStream* st, bool print_monitor_info = true) const; // Prepare address of oop for placement into mark inline static markWord encode_pointer_as_mark(void* p) { return from_pointer(p).set_marked(); } diff --git a/src/hotspot/share/runtime/basicLock.cpp b/src/hotspot/share/runtime/basicLock.cpp index 30017fc03037b..623c3d1f7e8eb 100644 --- a/src/hotspot/share/runtime/basicLock.cpp +++ b/src/hotspot/share/runtime/basicLock.cpp @@ -23,14 +23,18 @@ */ #include "precompiled.hpp" +#include "oops/oop.inline.hpp" #include "runtime/basicLock.hpp" #include "runtime/synchronizer.hpp" -void BasicLock::print_on(outputStream* st) const { +void BasicLock::print_on(outputStream* st, oop owner) const { st->print("monitor"); markWord mark_word = displaced_header(); - if (mark_word.value() != 0) - mark_word.print_on(st); + if (mark_word.value() != 0) { + // Print monitor info if there's an owning oop and it refers to this BasicLock. + bool print_monitor_info = (owner != NULL) && (owner->mark() == markWord::from_pointer((void*)this)); + mark_word.print_on(st, print_monitor_info); + } } void BasicLock::move_to(oop obj, BasicLock* dest) { diff --git a/src/hotspot/share/runtime/basicLock.hpp b/src/hotspot/share/runtime/basicLock.hpp index a292a742d8d87..18236b9e2a368 100644 --- a/src/hotspot/share/runtime/basicLock.hpp +++ b/src/hotspot/share/runtime/basicLock.hpp @@ -43,7 +43,7 @@ class BasicLock { Atomic::store(&_displaced_header, header); } - void print_on(outputStream* st) const; + void print_on(outputStream* st, oop owner) const; // move a basic lock (used during deoptimization void move_to(oop obj, BasicLock* dest); diff --git a/src/hotspot/share/runtime/frame.cpp b/src/hotspot/share/runtime/frame.cpp index 7ee194e3533d3..5b9e46f3cb52d 100644 --- a/src/hotspot/share/runtime/frame.cpp +++ b/src/hotspot/share/runtime/frame.cpp @@ -509,7 +509,7 @@ void frame::interpreter_frame_print_on(outputStream* st) const { current->obj()->print_value_on(st); st->print_cr("]"); st->print(" - lock ["); - current->lock()->print_on(st); + current->lock()->print_on(st, current->obj()); st->print_cr("]"); } // monitor diff --git a/src/hotspot/share/runtime/vframe.cpp b/src/hotspot/share/runtime/vframe.cpp index 226bb4642eb11..773069913efdd 100644 --- a/src/hotspot/share/runtime/vframe.cpp +++ b/src/hotspot/share/runtime/vframe.cpp @@ -669,7 +669,7 @@ void javaVFrame::print() { } tty->cr(); tty->print("\t "); - monitor->lock()->print_on(tty); + monitor->lock()->print_on(tty, monitor->owner()); tty->cr(); } } From a1d4b9f35b446f21b1f52bff736aa9068e8d7574 Mon Sep 17 00:00:00 2001 From: Jie Fu Date: Tue, 10 Nov 2020 13:49:01 +0000 Subject: [PATCH 046/124] 8256009: Remove src/hotspot/share/adlc/Test/i486.ad Reviewed-by: shade, thartmann --- src/hotspot/cpu/ppc/ppc.ad | 4 ++-- src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp | 2 +- src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp | 2 +- src/hotspot/share/adlc/Test/i486.ad | 0 src/hotspot/share/adlc/output_c.cpp | 2 +- src/hotspot/share/opto/machnode.cpp | 2 +- src/hotspot/share/opto/machnode.hpp | 2 +- src/hotspot/share/opto/parse2.cpp | 4 ++-- src/hotspot/share/runtime/synchronizer.cpp | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 src/hotspot/share/adlc/Test/i486.ad diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 2588f7a211c5f..b8f4f26995f8a 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3888,7 +3888,7 @@ frame %{ // The `sig' array is to be updated. sig[j] represents the location // of the j-th argument, either a register or a stack slot. - // Comment taken from i486.ad: + // Comment taken from x86_32.ad: // Body of function which returns an integer array locating // arguments either in registers or in stack slots. Passed an array // of ideal registers called "sig" and a "length" count. Stack-slot @@ -3900,7 +3900,7 @@ frame %{ SharedRuntime::java_calling_convention(sig_bt, regs, length, false); %} - // Comment taken from i486.ad: + // Comment taken from x86_32.ad: // Body of function which returns an integer array locating // arguments either in registers or in stack slots. Passed an array // of ideal registers called "sig" and a "length" count. Stack-slot diff --git a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp index 1faa5e80730df..0b1a2424d2b18 100644 --- a/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp +++ b/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp @@ -507,7 +507,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, int op = pc[0]; if (op == 0xDB) { // FIST - // TODO: The encoding of D2I in i486.ad can cause an exception + // TODO: The encoding of D2I in x86_32.ad can cause an exception // prior to the fist instruction if there was an invalid operation // pending. We want to dismiss that exception. From the win_32 // side it also seems that if it really was the fist causing diff --git a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp index b25c22d721bfa..6c977fc96f19e 100644 --- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp +++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp @@ -292,7 +292,7 @@ bool PosixSignals::pd_hotspot_signal_handler(int sig, siginfo_t* info, int op = pc[0]; if (op == 0xDB) { // FIST - // TODO: The encoding of D2I in i486.ad can cause an exception + // TODO: The encoding of D2I in x86_32.ad can cause an exception // prior to the fist instruction if there was an invalid operation // pending. We want to dismiss that exception. From the win_32 // side it also seems that if it really was the fist causing diff --git a/src/hotspot/share/adlc/Test/i486.ad b/src/hotspot/share/adlc/Test/i486.ad deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index f4e6e2956039e..c6083533b58b1 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -3000,7 +3000,7 @@ void ArchDesc::define_oper_interface(FILE *fp, OperandForm &oper, FormDict &glob // Provide a non-NULL return for disp_as_type() that will allow adr_type() // to correctly compute the access type for alias analysis. // - // See BugId 4796752, operand indOffset32X in i486.ad + // See BugId 4796752, operand indOffset32X in x86_32.ad int idx = rep_var_to_constant_index(disp, oper, globals); fprintf(fp," virtual const TypePtr *disp_as_type() const { return _c%d; }\n", idx); } diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index c51d6117cfb32..2f8bfb51abd0e 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -295,7 +295,7 @@ const Node* MachNode::get_base_and_disp(intptr_t &offset, const TypePtr* &adr_ty } offset = disp; - // In i486.ad, indOffset32X uses base==RegI and disp==RegP, + // In x86_32.ad, indOffset32X uses base==RegI and disp==RegP, // this will prevent alias analysis without the following support: // Lookup the TypePtr used by indOffset32X, a compile-time constant oop, // Add the offset determined by the "base", or use Type::OffsetBot. diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index badb35e5ce5dd..ec375abc37a78 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -151,7 +151,7 @@ class MachOper : public ResourceObj { virtual int index_position() const; // index edge position, or -1 // Access the TypeKlassPtr of operands with a base==RegI and disp==RegP - // Only returns non-null value for i486.ad's indOffset32X + // Only returns non-null value for x86_32.ad's indOffset32X virtual const TypePtr *disp_as_type() const { return NULL; } // Return the label diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index d39e0456388d3..6f2ae1af88a6d 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -2337,7 +2337,7 @@ void Parse::do_one_bytecode() { if (Matcher::convL2FSupported()) { a = pop_pair(); b = _gvn.transform( new ConvL2FNode(a)); - // For i486.ad, FILD doesn't restrict precision to 24 or 53 bits. + // For x86_32.ad, FILD doesn't restrict precision to 24 or 53 bits. // Rather than storing the result into an FP register then pushing // out to memory to round, the machine instruction that implements // ConvL2D is responsible for rounding. @@ -2352,7 +2352,7 @@ void Parse::do_one_bytecode() { case Bytecodes::_l2d: a = pop_pair(); b = _gvn.transform( new ConvL2DNode(a)); - // For i486.ad, rounding is always necessary (see _l2f above). + // For x86_32.ad, rounding is always necessary (see _l2f above). // c = dprecision_rounding(b); c = _gvn.transform(b); push_pair(c); diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index b0d238cfeeaf4..a0c90ce66a7b0 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -59,8 +59,8 @@ // The "core" versions of monitor enter and exit reside in this file. // The interpreter and compilers contain specialized transliterated -// variants of the enter-exit fast-path operations. See i486.ad fast_lock(), -// for instance. If you make changes here, make sure to modify the +// variants of the enter-exit fast-path operations. See c2_MacroAssembler_x86.cpp +// fast_lock(...) for instance. If you make changes here, make sure to modify the // interpreter, and both C1 and C2 fast-path inline locking code emission. // // ----------------------------------------------------------------------------- From 97d6e4aed766f4109ada03ed5b2d38cca9a08dac Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Tue, 10 Nov 2020 15:25:44 +0000 Subject: [PATCH 047/124] 8256046: Shenandoah: Mix-in NULL_PTR in non-strong ShLRBNode's type Reviewed-by: roland, shade --- .../share/gc/shenandoah/c2/shenandoahSupport.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 5ea607ccb272a..ed9c9b545cbf5 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -2945,7 +2945,12 @@ const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const { if (t == TypePtr::NULL_PTR) { return t; } - return t->is_oopptr(); + + if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + return t; + } + + return t->meet(TypePtr::NULL_PTR); } const Type* ShenandoahLoadReferenceBarrierNode::Value(PhaseGVN* phase) const { @@ -2957,8 +2962,11 @@ const Type* ShenandoahLoadReferenceBarrierNode::Value(PhaseGVN* phase) const { return t2; } - const Type* type = t2->is_oopptr(); - return type; + if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + return t2; + } + + return t2->meet(TypePtr::NULL_PTR); } Node* ShenandoahLoadReferenceBarrierNode::Identity(PhaseGVN* phase) { From 6555996f92adb98efc41cd415180713bc8b0d780 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 10 Nov 2020 16:36:06 +0000 Subject: [PATCH 048/124] 8253600: G1: Fully support pinned regions for full gc Reviewed-by: sjohanss, ayang --- src/hotspot/share/gc/g1/g1Allocator.cpp | 12 +-- src/hotspot/share/gc/g1/g1Allocator.hpp | 40 ---------- .../share/gc/g1/g1Allocator.inline.hpp | 59 -------------- src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 80 +++++-------------- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 16 ++-- .../share/gc/g1/g1CollectionSetCandidates.cpp | 9 ++- src/hotspot/share/gc/g1/g1ConcurrentMark.cpp | 9 ++- src/hotspot/share/gc/g1/g1ConcurrentMark.hpp | 8 +- src/hotspot/share/gc/g1/g1FullCollector.cpp | 43 +++++++++- src/hotspot/share/gc/g1/g1FullCollector.hpp | 12 ++- .../share/gc/g1/g1FullCollector.inline.hpp | 45 +++++++++++ .../share/gc/g1/g1FullGCAdjustTask.cpp | 31 ++++--- .../share/gc/g1/g1FullGCCompactTask.cpp | 39 ++++----- .../share/gc/g1/g1FullGCHeapRegionAttr.hpp | 78 ++++++++++++++++++ src/hotspot/share/gc/g1/g1FullGCMarker.cpp | 7 +- src/hotspot/share/gc/g1/g1FullGCMarker.hpp | 5 +- .../share/gc/g1/g1FullGCMarker.inline.hpp | 10 ++- .../share/gc/g1/g1FullGCOopClosures.cpp | 6 +- .../share/gc/g1/g1FullGCOopClosures.hpp | 12 ++- .../gc/g1/g1FullGCOopClosures.inline.hpp | 10 ++- .../share/gc/g1/g1FullGCPrepareTask.cpp | 33 +++++--- .../share/gc/g1/g1FullGCPrepareTask.hpp | 6 +- .../g1/g1FullGCReferenceProcessorExecutor.cpp | 6 +- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 7 +- src/hotspot/share/gc/g1/heapRegion.hpp | 17 ++-- src/hotspot/share/gc/g1/heapRegion.inline.hpp | 44 +++++++--- src/hotspot/share/gc/shared/collectedHeap.cpp | 4 + src/hotspot/share/gc/shared/collectedHeap.hpp | 4 +- src/hotspot/share/memory/heapShared.cpp | 1 + .../share/memory/heapShared.inline.hpp | 8 +- src/hotspot/share/memory/metaspaceShared.cpp | 2 +- 31 files changed, 368 insertions(+), 295 deletions(-) create mode 100644 src/hotspot/share/gc/g1/g1FullCollector.inline.hpp create mode 100644 src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp diff --git a/src/hotspot/share/gc/g1/g1Allocator.cpp b/src/hotspot/share/gc/g1/g1Allocator.cpp index c20b4400cadae..1bf55c51579fc 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.cpp +++ b/src/hotspot/share/gc/g1/g1Allocator.cpp @@ -398,15 +398,8 @@ size_t G1PLABAllocator::undo_waste() const { return result; } -bool G1ArchiveAllocator::_archive_check_enabled = false; -G1ArchiveRegionMap G1ArchiveAllocator::_archive_region_map; - G1ArchiveAllocator* G1ArchiveAllocator::create_allocator(G1CollectedHeap* g1h, bool open) { - // Create the archive allocator, and also enable archive object checking - // in mark-sweep, since we will be creating archive regions. - G1ArchiveAllocator* result = new G1ArchiveAllocator(g1h, open); - enable_archive_object_check(); - return result; + return new G1ArchiveAllocator(g1h, open); } bool G1ArchiveAllocator::alloc_new_region() { @@ -434,9 +427,6 @@ bool G1ArchiveAllocator::alloc_new_region() { _bottom = hr->bottom(); _max = _bottom + HeapRegion::min_region_size_in_words(); - // Tell mark-sweep that objects in this region are not to be marked. - set_range_archive(MemRegion(_bottom, HeapRegion::GrainWords), _open); - // Since we've modified the old set, call update_sizes. _g1h->g1mm()->update_sizes(); return true; diff --git a/src/hotspot/share/gc/g1/g1Allocator.hpp b/src/hotspot/share/gc/g1/g1Allocator.hpp index 4346171466fe3..fdd3f90d4a282 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.hpp @@ -192,19 +192,6 @@ class G1PLABAllocator : public CHeapObj { void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz, uint node_index); }; -// G1ArchiveRegionMap is an array used to mark G1 regions as -// archive regions. This allows a quick check for whether an object -// should not be marked because it is in an archive region. -class G1ArchiveRegionMap : public G1BiasedMappedArray { -public: - static const uint8_t NoArchive = 0; - static const uint8_t OpenArchive = 1; - static const uint8_t ClosedArchive = 2; - -protected: - uint8_t default_value() const { return NoArchive; } -}; - // G1ArchiveAllocator is used to allocate memory in archive // regions. Such regions are not scavenged nor compacted by GC. // There are two types of archive regions, which are @@ -278,33 +265,6 @@ class G1ArchiveAllocator : public CHeapObj { void clear_used() { _summary_bytes_used = 0; } - - // Create the _archive_region_map which is used to identify archive objects. - static inline void enable_archive_object_check(); - - // Mark regions containing the specified address range as archive/non-archive. - static inline void set_range_archive(MemRegion range, bool open); - static inline void clear_range_archive(MemRegion range); - - // Check if the object is in closed archive - static inline bool is_closed_archive_object(oop object); - // Check if the object is in open archive - static inline bool is_open_archive_object(oop object); - // Check if the object is either in closed archive or open archive - static inline bool is_archived_object(oop object); - -private: - static bool _archive_check_enabled; - static G1ArchiveRegionMap _archive_region_map; - - // Check if an object is in a closed archive region using the _closed_archive_region_map. - static inline bool in_closed_archive_range(oop object); - // Check if an object is in open archive region using the _open_archive_region_map. - static inline bool in_open_archive_range(oop object); - - // Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range - // unnecessarily. - static inline bool archive_check_enabled(); }; #endif // SHARE_GC_G1_G1ALLOCATOR_HPP diff --git a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp index 6d4842920519d..73a07828378fd 100644 --- a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp +++ b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp @@ -119,63 +119,4 @@ inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest, return allocate_direct_or_new_plab(dest, word_sz, refill_failed, node_index); } -// Create the maps which is used to identify archive objects. -inline void G1ArchiveAllocator::enable_archive_object_check() { - if (_archive_check_enabled) { - return; - } - - _archive_check_enabled = true; - _archive_region_map.initialize(G1CollectedHeap::heap()->reserved(), - HeapRegion::GrainBytes); -} - -// Set the regions containing the specified address range as archive. -inline void G1ArchiveAllocator::set_range_archive(MemRegion range, bool open) { - assert(_archive_check_enabled, "archive range check not enabled"); - log_info(gc, cds)("Mark %s archive regions in map: [" PTR_FORMAT ", " PTR_FORMAT "]", - open ? "open" : "closed", - p2i(range.start()), - p2i(range.last())); - uint8_t const value = open ? G1ArchiveRegionMap::OpenArchive : G1ArchiveRegionMap::ClosedArchive; - _archive_region_map.set_by_address(range, value); -} - -// Clear the archive regions map containing the specified address range. -inline void G1ArchiveAllocator::clear_range_archive(MemRegion range) { - assert(_archive_check_enabled, "archive range check not enabled"); - log_info(gc, cds)("Clear archive regions in map: [" PTR_FORMAT ", " PTR_FORMAT "]", - p2i(range.start()), - p2i(range.last())); - _archive_region_map.set_by_address(range, G1ArchiveRegionMap::NoArchive); -} - -// Check if an object is in a closed archive region using the _archive_region_map. -inline bool G1ArchiveAllocator::in_closed_archive_range(oop object) { - return _archive_region_map.get_by_address(cast_from_oop(object)) == G1ArchiveRegionMap::ClosedArchive; -} - -inline bool G1ArchiveAllocator::in_open_archive_range(oop object) { - return _archive_region_map.get_by_address(cast_from_oop(object)) == G1ArchiveRegionMap::OpenArchive; -} - -// Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range -// unnecessarily. -inline bool G1ArchiveAllocator::archive_check_enabled() { - return _archive_check_enabled; -} - -inline bool G1ArchiveAllocator::is_closed_archive_object(oop object) { - return (archive_check_enabled() && in_closed_archive_range(object)); -} - -inline bool G1ArchiveAllocator::is_open_archive_object(oop object) { - return (archive_check_enabled() && in_open_archive_range(object)); -} - -inline bool G1ArchiveAllocator::is_archived_object(oop object) { - return archive_check_enabled() && - (_archive_region_map.get_by_address(cast_from_oop(object)) != G1ArchiveRegionMap::NoArchive); -} - #endif // SHARE_GC_G1_G1ALLOCATOR_INLINE_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 3b77b9b49f96e..70b534b6ff10b 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -570,10 +570,6 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, // when mmap'ing archived heap data in, so pre-touching is wasted. FlagSetting fs(AlwaysPreTouch, false); - // Enable archive object checking used by G1MarkSweep. We have to let it know - // about each archive range, so that objects in those ranges aren't marked. - G1ArchiveAllocator::enable_archive_object_check(); - // For each specified MemRegion range, allocate the corresponding G1 // regions and mark them as archive regions. We expect the ranges // in ascending starting address order, without overlap. @@ -649,9 +645,6 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, curr_region->set_top(top); curr_region = next_region; } - - // Notify mark-sweep of the archive - G1ArchiveAllocator::set_range_archive(curr_range, open); } return true; } @@ -802,9 +795,6 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { _hrm->shrink_at(curr_index, 1); uncommitted_regions++; } - - // Notify mark-sweep that this is no longer an archive range. - G1ArchiveAllocator::clear_range_archive(ranges[i]); } if (uncommitted_regions != 0) { @@ -815,8 +805,7 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { } oop G1CollectedHeap::materialize_archived_object(oop obj) { - assert(obj != NULL, "archived obj is NULL"); - assert(G1ArchiveAllocator::is_archived_object(obj), "must be archived object"); + assert(is_archived_object(obj), "not an archived obj"); // Loading an archived object makes it strongly reachable. If it is // loaded during concurrent marking, it must be enqueued to the SATB @@ -1016,8 +1005,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { // after this full GC. abandon_collection_set(collection_set()); - tear_down_region_sets(false /* free_list_only */); - + hrm()->remove_all_free_regions(); hrm()->prepare_for_full_collection_start(); } @@ -1073,17 +1061,7 @@ void G1CollectedHeap::verify_after_full_collection() { _hrm->verify_optional(); _verifier->verify_region_sets_optional(); _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull); - // Clear the previous marking bitmap, if needed for bitmap verification. - // Note we cannot do this when we clear the next marking bitmap in - // G1ConcurrentMark::abort() above since VerifyDuringGC verifies the - // objects marked during a full GC against the previous bitmap. - // But we need to clear it before calling check_bitmaps below since - // the full GC has compacted objects and updated TAMS but not updated - // the prev bitmap. - if (G1VerifyBitmaps) { - GCTraceTime(Debug, gc) tm("Clear Prev Bitmap for Verification"); - _cm->clear_prev_bitmap(workers()); - } + // This call implicitly verifies that the next bitmap is clear after Full GC. _verifier->check_bitmaps("Full GC End"); @@ -1347,7 +1325,7 @@ void G1CollectedHeap::shrink(size_t shrink_bytes) { // Instead of tearing down / rebuilding the free lists here, we // could instead use the remove_all_pending() method on free_list to // remove only the ones that we need to remove. - tear_down_region_sets(true /* free_list_only */); + hrm()->remove_all_free_regions(); shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); @@ -2395,6 +2373,10 @@ bool G1CollectedHeap::is_heterogeneous_heap() const { return G1Arguments::is_heterogeneous_heap(); } +bool G1CollectedHeap::is_archived_object(oop object) const { + return object != NULL && heap_region_containing(object)->is_archive(); +} + class PrintRegionClosure: public HeapRegionClosure { outputStream* _st; public: @@ -4565,45 +4547,23 @@ bool G1CollectedHeap::check_young_list_empty() { #endif // ASSERT -class TearDownRegionSetsClosure : public HeapRegionClosure { - HeapRegionSet *_old_set; - -public: - TearDownRegionSetsClosure(HeapRegionSet* old_set) : _old_set(old_set) { } - - bool do_heap_region(HeapRegion* r) { - if (r->is_old()) { - _old_set->remove(r); - } else if(r->is_young()) { - r->uninstall_surv_rate_group(); - } else { - // We ignore free regions, we'll empty the free list afterwards. - // We ignore humongous and archive regions, we're not tearing down these - // sets. - assert(r->is_archive() || r->is_free() || r->is_humongous(), - "it cannot be another type"); - } - return false; - } - - ~TearDownRegionSetsClosure() { - assert(_old_set->is_empty(), "post-condition"); - } -}; - -void G1CollectedHeap::tear_down_region_sets(bool free_list_only) { - assert_at_safepoint_on_vm_thread(); - - if (!free_list_only) { - TearDownRegionSetsClosure cl(&_old_set); - heap_region_iterate(&cl); - + // Remove the given HeapRegion from the appropriate region set. +void G1CollectedHeap::prepare_region_for_full_compaction(HeapRegion* hr) { + if (hr->is_old()) { + _old_set.remove(hr); + } else if (hr->is_young()) { // Note that emptying the _young_list is postponed and instead done as // the first step when rebuilding the regions sets again. The reason for // this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. + hr->uninstall_surv_rate_group(); + } else { + // We ignore free regions, we'll empty the free list afterwards. + // We ignore humongous and archive regions, we're not tearing down these + // sets. + assert(hr->is_archive() || hr->is_free() || hr->is_humongous(), + "it cannot be another type"); } - _hrm->remove_all_free_regions(); } void G1CollectedHeap::increase_used(size_t bytes) { diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 83f982f7c86c3..3048f9ceae104 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -177,20 +177,14 @@ class G1CollectedHeap : public CollectedHeap { // The block offset table for the G1 heap. G1BlockOffsetTable* _bot; - // Tears down the region sets / lists so that they are empty and the - // regions on the heap do not belong to a region set / list. The - // only exception is the humongous set which we leave unaltered. If - // free_list_only is true, it will only tear down the master free - // list. It is called before a Full GC (free_list_only == false) or - // before heap shrinking (free_list_only == true). - void tear_down_region_sets(bool free_list_only); +public: + void prepare_region_for_full_compaction(HeapRegion* hr); +private: // Rebuilds the region sets / lists so that they are repopulated to // reflect the contents of the heap. The only exception is the // humongous set which was not torn down in the first place. If - // free_list_only is true, it will only rebuild the master free - // list. It is called after a Full GC (free_list_only == false) or - // after heap shrinking (free_list_only == true). + // free_list_only is true, it will only rebuild the free list. void rebuild_region_sets(bool free_list_only); // Callback for region mapping changed events. @@ -1424,6 +1418,8 @@ class G1CollectedHeap : public CollectedHeap { virtual WorkGang* safepoint_workers() { return _workers; } + virtual bool is_archived_object(oop object) const; + // The methods below are here for convenience and dispatch the // appropriate method depending on value of the given VerifyOption // parameter. The values for that parameter, and their meanings, diff --git a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp index 310cb7bc0666d..d9904f2fc8f12 100644 --- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp +++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,12 @@ void G1CollectionSetCandidates::verify() const { for (; idx < _num_regions; idx++) { HeapRegion *cur = _regions[idx]; guarantee(cur != NULL, "Regions after _front_idx %u cannot be NULL but %u is", _front_idx, idx); - guarantee(G1CollectionSetChooser::should_add(cur), "Region %u should be eligible for addition.", cur->hrm_index()); + // The first disjunction filters out regions with objects that were explicitly + // pinned after being added to the collection set candidates. Archive regions + // should never have been added to the collection set though. + guarantee((cur->is_pinned() && !cur->is_archive()) || + G1CollectionSetChooser::should_add(cur), + "Region %u should be eligible for addition.", cur->hrm_index()); if (prev != NULL) { guarantee(prev->gc_efficiency() >= cur->gc_efficiency(), "GC efficiency for region %u: %1.4f smaller than for region %u: %1.4f", diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp index 35519e92fff6a..eca44ecdff92a 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp @@ -675,9 +675,9 @@ void G1ConcurrentMark::cleanup_for_next_mark() { guarantee(!_g1h->collector_state()->mark_or_rebuild_in_progress(), "invariant"); } -void G1ConcurrentMark::clear_prev_bitmap(WorkGang* workers) { +void G1ConcurrentMark::clear_next_bitmap(WorkGang* workers) { assert_at_safepoint_on_vm_thread(); - clear_bitmap(_prev_mark_bitmap, workers, false); + clear_bitmap(_next_mark_bitmap, workers, false); } class NoteStartOfMarkHRClosure : public HeapRegionClosure { @@ -1132,6 +1132,8 @@ void G1ConcurrentMark::remark() { // Install newly created mark bitmap as "prev". swap_mark_bitmaps(); + + _g1h->collector_state()->set_clearing_next_bitmap(true); { GCTraceTime(Debug, gc, phases) debug("Update Remembered Set Tracking Before Rebuild", _gc_timer_cm); @@ -1696,7 +1698,6 @@ void G1ConcurrentMark::swap_mark_bitmaps() { G1CMBitMap* temp = _prev_mark_bitmap; _prev_mark_bitmap = _next_mark_bitmap; _next_mark_bitmap = temp; - _g1h->collector_state()->set_clearing_next_bitmap(true); } // Closure for marking entries in SATB buffers. @@ -1975,7 +1976,7 @@ void G1ConcurrentMark::concurrent_cycle_abort() { // concurrent bitmap clearing. { GCTraceTime(Debug, gc) debug("Clear Next Bitmap"); - clear_bitmap(_next_mark_bitmap, _g1h->workers(), false); + clear_next_bitmap(_g1h->workers()); } // Note we cannot clear the previous marking bitmap here // since VerifyDuringGC verifies the objects marked during diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp index 6ac83ba49533f..2e0171a72eeda 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.hpp @@ -374,8 +374,6 @@ class G1ConcurrentMark : public CHeapObj { void report_object_count(bool mark_completed); - void swap_mark_bitmaps(); - void reclaim_empty_regions(); // After reclaiming empty regions, update heap sizes. @@ -539,8 +537,8 @@ class G1ConcurrentMark : public CHeapObj { // to be called concurrently to the mutator. It will yield to safepoint requests. void cleanup_for_next_mark(); - // Clear the previous marking bitmap during safepoint. - void clear_prev_bitmap(WorkGang* workers); + // Clear the next marking bitmap during safepoint. + void clear_next_bitmap(WorkGang* workers); // These two methods do the work that needs to be done at the start and end of the // concurrent start pause. @@ -563,6 +561,8 @@ class G1ConcurrentMark : public CHeapObj { void remark(); + void swap_mark_bitmaps(); + void cleanup(); // Mark in the previous bitmap. Caution: the prev bitmap is usually read-only, so use // this carefully. diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp index 9846ba625935a..33cd49fff3d5e 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.cpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "code/codeCache.hpp" #include "gc/g1/g1CollectedHeap.hpp" -#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCAdjustTask.hpp" #include "gc/g1/g1FullGCCompactTask.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" @@ -111,21 +111,23 @@ G1FullCollector::G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool c _array_queue_set(_num_workers), _preserved_marks_set(true), _serial_compaction_point(), - _is_alive(heap->concurrent_mark()->next_mark_bitmap()), + _is_alive(this, heap->concurrent_mark()->next_mark_bitmap()), _is_alive_mutator(heap->ref_processor_stw(), &_is_alive), _always_subject_to_discovery(), - _is_subject_mutator(heap->ref_processor_stw(), &_always_subject_to_discovery) { + _is_subject_mutator(heap->ref_processor_stw(), &_always_subject_to_discovery), + _region_attr_table() { assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint"); _preserved_marks_set.init(_num_workers); _markers = NEW_C_HEAP_ARRAY(G1FullGCMarker*, _num_workers, mtGC); _compaction_points = NEW_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _num_workers, mtGC); for (uint i = 0; i < _num_workers; i++) { - _markers[i] = new G1FullGCMarker(i, _preserved_marks_set.get(i), mark_bitmap()); + _markers[i] = new G1FullGCMarker(this, i, _preserved_marks_set.get(i)); _compaction_points[i] = new G1FullGCCompactionPoint(); _oop_queue_set.register_queue(i, marker(i)->oop_stack()); _array_queue_set.register_queue(i, marker(i)->objarray_stack()); } + _region_attr_table.initialize(heap->reserved(), HeapRegion::GrainBytes); } G1FullCollector::~G1FullCollector() { @@ -137,6 +139,19 @@ G1FullCollector::~G1FullCollector() { FREE_C_HEAP_ARRAY(G1FullGCCompactionPoint*, _compaction_points); } +class PrepareRegionsClosure : public HeapRegionClosure { + G1FullCollector* _collector; + +public: + PrepareRegionsClosure(G1FullCollector* collector) : _collector(collector) { } + + bool do_heap_region(HeapRegion* hr) { + G1CollectedHeap::heap()->prepare_region_for_full_compaction(hr); + _collector->update_attribute_table(hr); + return false; + } +}; + void G1FullCollector::prepare_collection() { _heap->policy()->record_full_collection_start(); @@ -149,6 +164,9 @@ void G1FullCollector::prepare_collection() { _heap->gc_prologue(true); _heap->prepare_heap_for_full_collection(); + PrepareRegionsClosure cl(this); + _heap->heap_region_iterate(&cl); + reference_processor()->enable_discovery(); reference_processor()->setup_policy(scope()->should_clear_soft_refs()); @@ -184,6 +202,10 @@ void G1FullCollector::complete_collection() { BiasedLocking::restore_marks(); + _heap->concurrent_mark()->swap_mark_bitmaps(); + // Prepare the bitmap for the next (potentially concurrent) marking. + _heap->concurrent_mark()->clear_next_bitmap(_heap->workers()); + _heap->prepare_heap_for_mutators(); _heap->policy()->record_full_collection_end(); @@ -194,6 +216,19 @@ void G1FullCollector::complete_collection() { _heap->print_heap_after_full_collection(scope()->heap_transition()); } +void G1FullCollector::update_attribute_table(HeapRegion* hr) { + if (hr->is_free()) { + return; + } + if (hr->is_closed_archive()) { + _region_attr_table.set_closed_archive(hr->hrm_index()); + } else if (hr->is_pinned()) { + _region_attr_table.set_pinned(hr->hrm_index()); + } else { + _region_attr_table.set_normal(hr->hrm_index()); + } +} + void G1FullCollector::phase1_mark_live_objects() { // Recursively traverse all live objects and mark them. GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer()); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp index 9c21f65fc2157..66b97ad40a776 100644 --- a/src/hotspot/share/gc/g1/g1FullCollector.hpp +++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1FULLCOLLECTOR_HPP #include "gc/g1/g1FullGCCompactionPoint.hpp" +#include "gc/g1/g1FullGCHeapRegionAttr.hpp" #include "gc/g1/g1FullGCMarker.hpp" #include "gc/g1/g1FullGCOopClosures.hpp" #include "gc/g1/g1FullGCScope.hpp" @@ -33,6 +34,7 @@ #include "gc/shared/referenceProcessor.hpp" #include "gc/shared/taskqueue.hpp" #include "memory/allocation.hpp" +#include "oops/oopsHierarchy.hpp" class AbstractGangTask; class G1CMBitMap; @@ -71,6 +73,8 @@ class G1FullCollector : StackObj { G1FullGCSubjectToDiscoveryClosure _always_subject_to_discovery; ReferenceProcessorSubjectToDiscoveryMutator _is_subject_mutator; + G1FullGCHeapRegionAttr _region_attr_table; + public: G1FullCollector(G1CollectedHeap* heap, bool explicit_gc, bool clear_soft_refs); ~G1FullCollector(); @@ -90,6 +94,12 @@ class G1FullCollector : StackObj { G1CMBitMap* mark_bitmap(); ReferenceProcessor* reference_processor(); + void update_attribute_table(HeapRegion* hr); + + inline bool is_in_pinned_or_closed(oop obj) const; + inline bool is_in_pinned(oop obj) const; + inline bool is_in_closed(oop obj) const; + private: void phase1_mark_live_objects(); void phase2_prepare_compaction(); diff --git a/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp new file mode 100644 index 0000000000000..1d45f67ed1aac --- /dev/null +++ b/src/hotspot/share/gc/g1/g1FullCollector.inline.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP +#define SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP + +#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullGCHeapRegionAttr.hpp" +#include "oops/oopsHierarchy.hpp" + +bool G1FullCollector::is_in_pinned_or_closed(oop obj) const { + return _region_attr_table.is_pinned_or_closed(cast_from_oop(obj)); +} + +bool G1FullCollector::is_in_pinned(oop obj) const { + return _region_attr_table.is_pinned(cast_from_oop(obj)); +} + +bool G1FullCollector::is_in_closed(oop obj) const { + return _region_attr_table.is_closed_archive(cast_from_oop(obj)); +} + +#endif // SHARE_GC_G1_G1FULLCOLLECTOR_INLINE_HPP + diff --git a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp index 3dfd30f67b2d0..ac5ba8834fb81 100644 --- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,7 +26,7 @@ #include "classfile/classLoaderDataGraph.hpp" #include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" -#include "gc/g1/g1FullCollector.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCAdjustTask.hpp" #include "gc/g1/g1FullGCCompactionPoint.hpp" #include "gc/g1/g1FullGCMarker.hpp" @@ -51,27 +51,26 @@ class G1AdjustLiveClosure : public StackObj { }; class G1AdjustRegionClosure : public HeapRegionClosure { + G1FullCollector* _collector; G1CMBitMap* _bitmap; uint _worker_id; public: - G1AdjustRegionClosure(G1CMBitMap* bitmap, uint worker_id) : - _bitmap(bitmap), + G1AdjustRegionClosure(G1FullCollector* collector, uint worker_id) : + _collector(collector), + _bitmap(collector->mark_bitmap()), _worker_id(worker_id) { } bool do_heap_region(HeapRegion* r) { - G1AdjustClosure cl; + G1AdjustClosure cl(_collector); if (r->is_humongous()) { + // Special handling for humongous regions to get somewhat better + // work distribution. oop obj = oop(r->humongous_start_region()->bottom()); obj->oop_iterate(&cl, MemRegion(r->bottom(), r->top())); - } else if (r->is_open_archive()) { - // Only adjust the open archive regions, the closed ones - // never change. - G1AdjustLiveClosure adjust(&cl); - r->apply_to_marked_objects(_bitmap, &adjust); - // Open archive regions will not be compacted and the marking information is - // no longer needed. Clear it here to avoid having to do it later. - _bitmap->clear_region(r); - } else { + } else if (!r->is_closed_archive() && !r->is_free()) { + // Closed archive regions never change references and only contain + // references into other closed regions and are always live. Free + // regions do not contain objects to iterate. So skip both. G1AdjustLiveClosure adjust(&cl); r->apply_to_marked_objects(_bitmap, &adjust); } @@ -85,7 +84,7 @@ G1FullGCAdjustTask::G1FullGCAdjustTask(G1FullCollector* collector) : _references_done(0), _weak_proc_task(collector->workers()), _hrclaimer(collector->workers()), - _adjust(), + _adjust(collector), _string_dedup_cleaning_task(NULL, &_adjust, false) { // Need cleared claim bits for the roots processing ClassLoaderDataGraph::clear_claimed_marks(); @@ -116,7 +115,7 @@ void G1FullGCAdjustTask::work(uint worker_id) { _string_dedup_cleaning_task.work(worker_id); // Now adjust pointers region by region - G1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id); + G1AdjustRegionClosure blk(collector(), worker_id); G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id); log_task("Adjust task", worker_id, start); } diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp index be35978ba17da..8a0a5c4c0eb98 100644 --- a/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCCompactTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,27 +34,19 @@ #include "oops/oop.inline.hpp" #include "utilities/ticks.hpp" -class G1ResetHumongousClosure : public HeapRegionClosure { +class G1ResetPinnedClosure : public HeapRegionClosure { G1CMBitMap* _bitmap; public: - G1ResetHumongousClosure(G1CMBitMap* bitmap) : - _bitmap(bitmap) { } + G1ResetPinnedClosure(G1CMBitMap* bitmap) : _bitmap(bitmap) { } - bool do_heap_region(HeapRegion* current) { - if (current->is_humongous()) { - if (current->is_starts_humongous()) { - oop obj = oop(current->bottom()); - if (_bitmap->is_marked(obj)) { - // Clear bitmap and fix mark word. - _bitmap->clear(obj); - obj->init_mark(); - } else { - assert(current->is_empty(), "Should have been cleared in phase 2."); - } - } - current->reset_humongous_during_compaction(); + bool do_heap_region(HeapRegion* r) { + if (!r->is_pinned()) { + return false; } + assert(!r->is_starts_humongous() || _bitmap->is_marked((oop)r->bottom()), + "must be, otherwise reclaimed earlier"); + r->reset_pinned_after_full_gc(); return false; } }; @@ -78,13 +70,16 @@ size_t G1FullGCCompactTask::G1CompactRegionClosure::apply(oop obj) { } void G1FullGCCompactTask::compact_region(HeapRegion* hr) { + assert(!hr->is_pinned(), "Should be no pinned region in compaction queue"); assert(!hr->is_humongous(), "Should be no humongous regions in compaction queue"); G1CompactRegionClosure compact(collector()->mark_bitmap()); hr->apply_to_marked_objects(collector()->mark_bitmap(), &compact); - // Once all objects have been moved the liveness information - // needs be cleared. - collector()->mark_bitmap()->clear_region(hr); - hr->complete_compaction(); + // Clear the liveness information for this region if necessary i.e. if we actually look at it + // for bitmap verification. Otherwise it is sufficient that we move the TAMS to bottom(). + if (G1VerifyBitmaps) { + collector()->mark_bitmap()->clear_region(hr); + } + hr->reset_compacted_after_full_gc(); } void G1FullGCCompactTask::work(uint worker_id) { @@ -96,7 +91,7 @@ void G1FullGCCompactTask::work(uint worker_id) { compact_region(*it); } - G1ResetHumongousClosure hc(collector()->mark_bitmap()); + G1ResetPinnedClosure hc(collector()->mark_bitmap()); G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&hc, &_claimer, worker_id); log_task("Compaction task", worker_id, start); } diff --git a/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp b/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp new file mode 100644 index 0000000000000..8297bd038110a --- /dev/null +++ b/src/hotspot/share/gc/g1/g1FullGCHeapRegionAttr.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP +#define SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP + +#include "gc/g1/g1BiasedArray.hpp" + +// This table is used to store attribute values of all HeapRegions that need +// fast access during the full collection. In particular some parts of the region +// type information is encoded in these per-region bytes. +// Value encoding has been specifically chosen to make required accesses fast. +class G1FullGCHeapRegionAttr : public G1BiasedMappedArray { + static const uint8_t Normal = 0; // Other kind of region + static const uint8_t Pinned = 1; // Region is a pinned (non-Closed Archive) region + static const uint8_t ClosedArchive = 2; // Region is a (pinned) Closed Archive region + + STATIC_ASSERT(ClosedArchive > Pinned); + + static const uint8_t Invalid = 255; + + bool is_invalid(HeapWord* obj) const { + return get_by_address(obj) == Invalid; + } + +protected: + uint8_t default_value() const { return Invalid; } + +public: + void set_closed_archive(uint idx) { set_by_index(idx, ClosedArchive); } + + bool is_closed_archive(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == ClosedArchive; + } + + void set_pinned(uint idx) { set_by_index(idx, Pinned); } + + bool is_pinned_or_closed(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) >= Pinned; + } + + bool is_pinned(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == Pinned; + } + + void set_normal(uint idx) { set_by_index(idx, Normal); } + + bool is_normal(HeapWord* obj) const { + assert(!is_invalid(obj), "not initialized yet"); + return get_by_address(obj) == Normal; + } +}; + +#endif // SHARE_GC_G1_G1FULLGCHEAPREGIONATTR_HPP diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp index 3bbf74adfc697..ed97bbfe94969 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.cpp @@ -30,9 +30,12 @@ #include "gc/shared/verifyOption.hpp" #include "memory/iterator.inline.hpp" -G1FullGCMarker::G1FullGCMarker(uint worker_id, PreservedMarks* preserved_stack, G1CMBitMap* bitmap) : +G1FullGCMarker::G1FullGCMarker(G1FullCollector* collector, + uint worker_id, + PreservedMarks* preserved_stack) : + _collector(collector), _worker_id(worker_id), - _bitmap(bitmap), + _bitmap(collector->mark_bitmap()), _oop_stack(), _objarray_stack(), _preserved_stack(preserved_stack), diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp index fc130fdc66f25..28b4946c35945 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.hpp @@ -43,8 +43,11 @@ typedef GenericTaskQueueSet OopQueueSet; typedef GenericTaskQueueSet ObjArrayTaskQueueSet; class G1CMBitMap; +class G1FullCollector; class G1FullGCMarker : public CHeapObj { + G1FullCollector* _collector; + uint _worker_id; // Backing mark bitmap G1CMBitMap* _bitmap; @@ -71,7 +74,7 @@ class G1FullGCMarker : public CHeapObj { inline void follow_array(objArrayOop array); inline void follow_array_chunk(objArrayOop array, int index); public: - G1FullGCMarker(uint worker_id, PreservedMarks* preserved_stack, G1CMBitMap* bitmap); + G1FullGCMarker(G1FullCollector* collector, uint worker_id, PreservedMarks* preserved_stack); ~G1FullGCMarker(); // Stack getters diff --git a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp index 11a66dea93480..75d8f6563051a 100644 --- a/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCMarker.inline.hpp @@ -28,6 +28,7 @@ #include "classfile/javaClasses.inline.hpp" #include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1FullGCMarker.hpp" #include "gc/g1/g1FullGCOopClosures.inline.hpp" #include "gc/g1/g1StringDedup.hpp" @@ -39,8 +40,7 @@ #include "utilities/debug.hpp" inline bool G1FullGCMarker::mark_object(oop obj) { - // Not marking closed archive objects. - if (G1ArchiveAllocator::is_closed_archive_object(obj)) { + if (_collector->is_in_closed(obj)) { return false; } @@ -53,7 +53,9 @@ inline bool G1FullGCMarker::mark_object(oop obj) { // Marked by us, preserve if needed. markWord mark = obj->mark(); if (obj->mark_must_be_preserved(mark) && - !G1ArchiveAllocator::is_open_archive_object(obj)) { + // It is not necessary to preserve marks for objects in pinned regions because + // we do not change their headers (i.e. forward them). + !_collector->is_in_pinned(obj)) { preserved_stack()->push(obj, mark); } @@ -73,7 +75,7 @@ template inline void G1FullGCMarker::mark_and_push(T* p) { _oop_stack.push(obj); assert(_bitmap->is_marked(obj), "Must be marked now - map self"); } else { - assert(_bitmap->is_marked(obj) || G1ArchiveAllocator::is_closed_archive_object(obj), + assert(_bitmap->is_marked(obj) || _collector->is_in_closed(obj), "Must be marked by other or closed archive object"); } } diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp index 876def6f55050..ba3b50e5a963e 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1FullCollector.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" #include "gc/g1/g1FullGCOopClosures.inline.hpp" #include "logging/logStream.hpp" @@ -32,6 +33,9 @@ #include "oops/compressedOops.inline.hpp" #include "oops/oop.inline.hpp" +G1IsAliveClosure::G1IsAliveClosure(G1FullCollector* collector) : + G1IsAliveClosure(collector, collector->mark_bitmap()) { } + void G1FollowStackClosure::do_void() { _marker->drain_stack(); } void G1FullKeepAliveClosure::do_oop(oop* p) { do_oop_work(p); } diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp index baca67dabd235..1690c99ea244b 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,13 @@ class G1FullGCMarker; // Below are closures used by the G1 Full GC. class G1IsAliveClosure : public BoolObjectClosure { + G1FullCollector* _collector; G1CMBitMap* _bitmap; public: - G1IsAliveClosure(G1CMBitMap* bitmap) : _bitmap(bitmap) { } + G1IsAliveClosure(G1FullCollector* collector); + G1IsAliveClosure(G1FullCollector* collector, G1CMBitMap* bitmap) : + _collector(collector), _bitmap(bitmap) { } virtual bool do_object_b(oop p); }; @@ -75,8 +78,11 @@ class G1MarkAndPushClosure : public OopIterateClosure { }; class G1AdjustClosure : public BasicOopIterateClosure { - template static inline void adjust_pointer(T* p); + G1FullCollector* _collector; + + template inline void adjust_pointer(T* p); public: + G1AdjustClosure(G1FullCollector* collector) : _collector(collector) { } template void do_oop_work(T* p) { adjust_pointer(p); } virtual void do_oop(oop* p); virtual void do_oop(narrowOop* p); diff --git a/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp b/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp index 16c34eace8df6..dfb56d50ca4ee 100644 --- a/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCOopClosures.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ #define SHARE_GC_G1_G1FULLGCOOPCLOSURES_INLINE_HPP #include "gc/g1/g1Allocator.inline.hpp" +#include "gc/g1/g1FullCollector.inline.hpp" #include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp" #include "gc/g1/g1FullGCMarker.inline.hpp" #include "gc/g1/g1FullGCOopClosures.hpp" @@ -69,8 +70,9 @@ template inline void G1AdjustClosure::adjust_pointer(T* p) { oop obj = CompressedOops::decode_not_null(heap_oop); assert(Universe::heap()->is_in(obj), "should be in heap"); - if (G1ArchiveAllocator::is_archived_object(obj)) { - // We never forward archive objects. + if (_collector->is_in_pinned_or_closed(obj)) { + // We never forward objects in pinned regions so there is no need to + // process them further. return; } @@ -94,7 +96,7 @@ inline void G1AdjustClosure::do_oop(oop* p) { do_oop_work(p); } inline void G1AdjustClosure::do_oop(narrowOop* p) { do_oop_work(p); } inline bool G1IsAliveClosure::do_object_b(oop p) { - return _bitmap->is_marked(p) || G1ArchiveAllocator::is_closed_archive_object(p); + return _bitmap->is_marked(p) || _collector->is_in_closed(p); } template diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp index 3658ae200d9ad..732a040ffb015 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,22 +40,28 @@ #include "utilities/ticks.hpp" bool G1FullGCPrepareTask::G1CalculatePointersClosure::do_heap_region(HeapRegion* hr) { - if (hr->is_humongous()) { - oop obj = oop(hr->humongous_start_region()->bottom()); - if (_bitmap->is_marked(obj)) { - if (hr->is_starts_humongous()) { - obj->forward_to(obj); + if (hr->is_pinned()) { + // There is no need to iterate and forward objects in pinned regions ie. + // prepare them for compaction. The adjust pointers phase will skip + // work for them. + if (hr->is_humongous()) { + oop obj = oop(hr->humongous_start_region()->bottom()); + if (!_bitmap->is_marked(obj)) { + free_humongous_region(hr); } } else { - free_humongous_region(hr); + assert(hr->is_archive(), "Only archive regions can also be pinned."); } - } else if (!hr->is_pinned()) { + } else { + assert(!hr->is_humongous(), "moving humongous objects not supported."); prepare_for_compaction(hr); } // Reset data structures not valid after Full GC. reset_region_metadata(hr); + _collector->update_attribute_table(hr); + return false; } @@ -78,7 +84,7 @@ bool G1FullGCPrepareTask::has_freed_regions() { void G1FullGCPrepareTask::work(uint worker_id) { Ticks start = Ticks::now(); G1FullGCCompactionPoint* compaction_point = collector()->compaction_point(worker_id); - G1CalculatePointersClosure closure(collector()->mark_bitmap(), compaction_point); + G1CalculatePointersClosure closure(collector(), compaction_point); G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer); // Update humongous region sets @@ -92,15 +98,18 @@ void G1FullGCPrepareTask::work(uint worker_id) { log_task("Prepare compaction task", worker_id, start); } -G1FullGCPrepareTask::G1CalculatePointersClosure::G1CalculatePointersClosure(G1CMBitMap* bitmap, +G1FullGCPrepareTask::G1CalculatePointersClosure::G1CalculatePointersClosure(G1FullCollector* collector, G1FullGCCompactionPoint* cp) : _g1h(G1CollectedHeap::heap()), - _bitmap(bitmap), + _collector(collector), + _bitmap(collector->mark_bitmap()), _cp(cp), _humongous_regions_removed(0) { } void G1FullGCPrepareTask::G1CalculatePointersClosure::free_humongous_region(HeapRegion* hr) { - FreeRegionList dummy_free_list("Dummy Free List for G1MarkSweep"); + assert(hr->is_humongous(), "must be but region %u is %s", hr->hrm_index(), hr->get_short_type_str()); + + FreeRegionList dummy_free_list("Humongous Dummy Free List for G1MarkSweep"); hr->set_containing_set(NULL); _humongous_regions_removed++; diff --git a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp index fcaf797a12f8f..124fb619c8e52 100644 --- a/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp +++ b/src/hotspot/share/gc/g1/g1FullGCPrepareTask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ #include "gc/shared/referenceProcessor.hpp" class G1CMBitMap; +class G1FullCollector; class G1FullGCPrepareTask : public G1FullGCTask { protected: @@ -52,6 +53,7 @@ class G1FullGCPrepareTask : public G1FullGCTask { class G1CalculatePointersClosure : public HeapRegionClosure { protected: G1CollectedHeap* _g1h; + G1FullCollector* _collector; G1CMBitMap* _bitmap; G1FullGCCompactionPoint* _cp; uint _humongous_regions_removed; @@ -62,7 +64,7 @@ class G1FullGCPrepareTask : public G1FullGCTask { void reset_region_metadata(HeapRegion* hr); public: - G1CalculatePointersClosure(G1CMBitMap* bitmap, + G1CalculatePointersClosure(G1FullCollector* collector, G1FullGCCompactionPoint* cp); void update_sets(); diff --git a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp index 1e180192612be..103fb4eca8843 100644 --- a/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp +++ b/src/hotspot/share/gc/g1/g1FullGCReferenceProcessorExecutor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,7 +57,7 @@ G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::G1RefProcTaskProxy(Proc void G1FullGCReferenceProcessingExecutor::G1RefProcTaskProxy::work(uint worker_id) { G1FullGCMarker* marker = _collector->marker(worker_id); - G1IsAliveClosure is_alive(_collector->mark_bitmap()); + G1IsAliveClosure is_alive(_collector); G1FullKeepAliveClosure keep_alive(marker); _proc_task.work(worker_id, is_alive, @@ -82,7 +82,7 @@ void G1FullGCReferenceProcessingExecutor::execute(STWGCTimer* timer, G1FullGCTra GCTraceTime(Debug, gc, phases) debug("Phase 1: Reference Processing", timer); // Process reference objects found during marking. G1FullGCMarker* marker = _collector->marker(0); - G1IsAliveClosure is_alive(_collector->mark_bitmap()); + G1IsAliveClosure is_alive(_collector); G1FullKeepAliveClosure keep_alive(marker); ReferenceProcessorPhaseTimes pt(timer, _reference_processor->max_num_queues()); AbstractRefProcTaskExecutor* executor = _reference_processor->processing_is_mt() ? this : NULL; diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index 3aa5b5f2071d4..d96bd5c211cea 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -240,8 +240,7 @@ class VerifyObjsInRegionClosure: public ObjectClosure { class VerifyArchiveOopClosure: public BasicOopIterateClosure { HeapRegion* _hr; public: - VerifyArchiveOopClosure(HeapRegion *hr) - : _hr(hr) { } + VerifyArchiveOopClosure(HeapRegion *hr) : _hr(hr) { } void do_oop(narrowOop *p) { do_oop_work(p); } void do_oop( oop *p) { do_oop_work(p); } @@ -249,12 +248,12 @@ class VerifyArchiveOopClosure: public BasicOopIterateClosure { oop obj = RawAccess<>::oop_load(p); if (_hr->is_open_archive()) { - guarantee(obj == NULL || G1ArchiveAllocator::is_archived_object(obj), + guarantee(obj == NULL || G1CollectedHeap::heap()->heap_region_containing(obj)->is_archive(), "Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT, p2i(p), p2i(obj)); } else { assert(_hr->is_closed_archive(), "should be closed archive region"); - guarantee(obj == NULL || G1ArchiveAllocator::is_closed_archive_object(obj), + guarantee(obj == NULL || G1CollectedHeap::heap()->heap_region_containing(obj)->is_closed_archive(), "Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT, p2i(p), p2i(obj)); } diff --git a/src/hotspot/share/gc/g1/heapRegion.hpp b/src/hotspot/share/gc/g1/heapRegion.hpp index 3756d5fe09482..a7566037b63a3 100644 --- a/src/hotspot/share/gc/g1/heapRegion.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.hpp @@ -123,7 +123,9 @@ class HeapRegion : public CHeapObj { bool is_empty() const { return used() == 0; } private: - void reset_after_compaction() { set_top(compaction_top()); } + void reset_compaction_top_after_compaction(); + + void reset_after_full_gc_common(); void clear(bool mangle_space); @@ -165,16 +167,11 @@ class HeapRegion : public CHeapObj { HeapWord* initialize_threshold(); HeapWord* cross_threshold(HeapWord* start, HeapWord* end); - // Update heap region to be consistent after Full GC compaction. - void reset_humongous_during_compaction() { - assert(is_humongous(), - "should only be called for humongous regions"); - zero_marked_bytes(); - init_top_at_mark_start(); - } - // Update heap region to be consistent after Full GC compaction. - void complete_compaction(); + // Update heap region that has been compacted to be consistent after Full GC. + void reset_compacted_after_full_gc(); + // Update pinned heap region (not compacted) to be consistent after Full GC. + void reset_pinned_after_full_gc(); // All allocated blocks are occupied by objects in a HeapRegion bool block_is_obj(const HeapWord* p) const; diff --git a/src/hotspot/share/gc/g1/heapRegion.inline.hpp b/src/hotspot/share/gc/g1/heapRegion.inline.hpp index 1736d4b512d93..31b787869c5f3 100644 --- a/src/hotspot/share/gc/g1/heapRegion.inline.hpp +++ b/src/hotspot/share/gc/g1/heapRegion.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -181,18 +181,44 @@ inline size_t HeapRegion::block_size(const HeapWord *addr) const { return block_size_using_bitmap(addr, G1CollectedHeap::heap()->concurrent_mark()->prev_mark_bitmap()); } -inline void HeapRegion::complete_compaction() { - // Reset space and bot after compaction is complete if needed. - reset_after_compaction(); - if (is_empty()) { - reset_bot(); - } +inline void HeapRegion::reset_compaction_top_after_compaction() { + set_top(compaction_top()); + _compaction_top = bottom(); +} + +inline void HeapRegion::reset_compacted_after_full_gc() { + assert(!is_pinned(), "must be"); - // After a compaction the mark bitmap is invalid, so we must - // treat all objects as being inside the unmarked area. + reset_compaction_top_after_compaction(); + // After a compaction the mark bitmap in a non-pinned regions is invalid. + // We treat all objects as being above PTAMS. zero_marked_bytes(); init_top_at_mark_start(); + reset_after_full_gc_common(); +} + +inline void HeapRegion::reset_pinned_after_full_gc() { + assert(!is_free(), "should not have compacted free region"); + assert(is_pinned(), "must be"); + + assert(compaction_top() == bottom(), + "region %u compaction_top " PTR_FORMAT " must not be different from bottom " PTR_FORMAT, + hrm_index(), p2i(compaction_top()), p2i(bottom())); + + _prev_top_at_mark_start = top(); // Keep existing top and usage. + _prev_marked_bytes = used(); + _next_top_at_mark_start = bottom(); + _next_marked_bytes = 0; + + reset_after_full_gc_common(); +} + +inline void HeapRegion::reset_after_full_gc_common() { + if (is_empty()) { + reset_bot(); + } + // Clear unused heap memory in debug builds. if (ZapUnusedHeapArea) { mangle_unused_area(); diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp index 4fb52049532ab..dd3c5e273168b 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.cpp +++ b/src/hotspot/share/gc/shared/collectedHeap.cpp @@ -599,6 +599,10 @@ void CollectedHeap::unpin_object(JavaThread* thread, oop obj) { ShouldNotReachHere(); } +bool CollectedHeap::is_archived_object(oop object) const { + return false; +} + void CollectedHeap::deduplicate_string(oop str) { // Do nothing, unless overridden in subclass. } diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp index 5df7ad7badbaf..0a6b1062f1c9a 100644 --- a/src/hotspot/share/gc/shared/collectedHeap.hpp +++ b/src/hotspot/share/gc/shared/collectedHeap.hpp @@ -510,11 +510,13 @@ class CollectedHeap : public CHeapObj { virtual oop pin_object(JavaThread* thread, oop obj); virtual void unpin_object(JavaThread* thread, oop obj); + // Is the given object inside a CDS archive area? + virtual bool is_archived_object(oop object) const; + // Deduplicate the string, iff the GC supports string deduplication. virtual void deduplicate_string(oop str); virtual bool is_oop(oop object) const; - // Non product verification and debugging. #ifndef PRODUCT // Support for PromotionFailureALot. Return true if it's time to cause a diff --git a/src/hotspot/share/memory/heapShared.cpp b/src/hotspot/share/memory/heapShared.cpp index d7b016f3fef67..c575541c2d5d6 100644 --- a/src/hotspot/share/memory/heapShared.cpp +++ b/src/hotspot/share/memory/heapShared.cpp @@ -53,6 +53,7 @@ #include "runtime/javaCalls.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/bitMap.inline.hpp" +#include "utilities/copy.hpp" #if INCLUDE_G1GC #include "gc/g1/g1CollectedHeap.hpp" #endif diff --git a/src/hotspot/share/memory/heapShared.inline.hpp b/src/hotspot/share/memory/heapShared.inline.hpp index b27996251c1fd..a022fcfb4bc77 100644 --- a/src/hotspot/share/memory/heapShared.inline.hpp +++ b/src/hotspot/share/memory/heapShared.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,17 +25,15 @@ #ifndef SHARE_MEMORY_HEAPSHARED_INLINE_HPP #define SHARE_MEMORY_HEAPSHARED_INLINE_HPP +#include "gc/shared/collectedHeap.inline.hpp" #include "oops/compressedOops.inline.hpp" #include "memory/heapShared.hpp" #include "utilities/align.hpp" -#if INCLUDE_G1GC -#include "gc/g1/g1Allocator.inline.hpp" -#endif #if INCLUDE_CDS_JAVA_HEAP bool HeapShared::is_archived_object(oop p) { - return (p == NULL) ? false : G1ArchiveAllocator::is_archived_object(p); + return Universe::heap()->is_archived_object(p); } inline oop HeapShared::decode_from_archive(narrowOop v) { diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index 59f488a6b6602..f5000089c2b5b 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -69,7 +69,7 @@ #include "utilities/defaultStream.hpp" #include "utilities/hashtable.inline.hpp" #if INCLUDE_G1GC -#include "gc/g1/g1CollectedHeap.hpp" +#include "gc/g1/g1CollectedHeap.inline.hpp" #endif ReservedSpace MetaspaceShared::_shared_rs; From 6ae5e5b6b7854855cd73ab9b6e132181e7ed9b1e Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 10 Nov 2020 16:48:21 +0000 Subject: [PATCH 049/124] 8221404: C2: Convert RegMask and IndexSet to use uintptr_t Reviewed-by: kvn, thartmann --- src/hotspot/share/opto/indexSet.cpp | 4 +- src/hotspot/share/opto/indexSet.hpp | 45 ++-- src/hotspot/share/opto/regmask.cpp | 201 +++++++------- src/hotspot/share/opto/regmask.hpp | 151 +++++------ .../overhead/SimpleRepeatCompilation.java | 249 ++++++++++++++++++ 5 files changed, 454 insertions(+), 196 deletions(-) create mode 100644 test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java diff --git a/src/hotspot/share/opto/indexSet.cpp b/src/hotspot/share/opto/indexSet.cpp index f4a596a4ff32a..eb90151eef084 100644 --- a/src/hotspot/share/opto/indexSet.cpp +++ b/src/hotspot/share/opto/indexSet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,7 +241,7 @@ IndexSet::IndexSet (IndexSet *set) { set_block(i, &_empty_block); } else { BitBlock *new_block = alloc_block(); - memcpy(new_block->words(), block->words(), sizeof(uint32_t) * words_per_block); + memcpy(new_block->words(), block->words(), sizeof(uintptr_t) * words_per_block); set_block(i, new_block); } } diff --git a/src/hotspot/share/opto/indexSet.hpp b/src/hotspot/share/opto/indexSet.hpp index 5f57faaeb32ee..7ed34116d1c86 100644 --- a/src/hotspot/share/opto/indexSet.hpp +++ b/src/hotspot/share/opto/indexSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,9 +60,12 @@ class IndexSet : public ResourceObj { // membership of the element in the set. // The lengths of the index bitfields - enum { bit_index_length = 5, - word_index_length = 3, - block_index_length = 8 // not used + enum { + // Each block consists of 256 bits + block_index_length = 8, + // Split over 4 or 8 words depending on bitness + word_index_length = block_index_length - LogBitsPerWord, + bit_index_length = block_index_length - word_index_length, }; // Derived constants used for manipulating the index bitfields @@ -88,7 +91,7 @@ class IndexSet : public ResourceObj { return mask_bits(element >> word_index_offset,word_index_mask); } static uint get_bit_index(uint element) { - return mask_bits(element,bit_index_mask); + return mask_bits(element, bit_index_mask); } //------------------------------ class BitBlock ---------------------------- @@ -102,17 +105,17 @@ class IndexSet : public ResourceObj { // All of BitBlocks fields and methods are declared private. We limit // access to IndexSet and IndexSetIterator. - // A BitBlock is composed of some number of 32 bit words. When a BitBlock + // A BitBlock is composed of some number of 32- or 64-bit words. When a BitBlock // is not in use by any IndexSet, it is stored on a free list. The next field - // is used by IndexSet to mainting this free list. + // is used by IndexSet to maintain this free list. union { - uint32_t _words[words_per_block]; + uintptr_t _words[words_per_block]; BitBlock *_next; } _data; // accessors - uint32_t* words() { return _data._words; } + uintptr_t* words() { return _data._words; } void set_next(BitBlock *next) { _data._next = next; } BitBlock *next() { return _data._next; } @@ -121,32 +124,32 @@ class IndexSet : public ResourceObj { // not assume that the block index has been masked out. void clear() { - memset(words(), 0, sizeof(uint32_t) * words_per_block); + memset(words(), 0, sizeof(uintptr_t) * words_per_block); } bool member(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - return ((words()[word_index] & (uint32_t)(0x1 << bit_index)) != 0); + return ((words()[word_index] & (uintptr_t(1) << bit_index)) != 0); } bool insert(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - uint32_t bit = (0x1 << bit_index); - uint32_t before = words()[word_index]; + uintptr_t bit = uintptr_t(1) << bit_index; + uintptr_t before = words()[word_index]; words()[word_index] = before | bit; return ((before & bit) != 0); } bool remove(uint element) { uint word_index = IndexSet::get_word_index(element); - uint bit_index = IndexSet::get_bit_index(element); + uintptr_t bit_index = IndexSet::get_bit_index(element); - uint32_t bit = (0x1 << bit_index); - uint32_t before = words()[word_index]; + uintptr_t bit = uintptr_t(1) << bit_index; + uintptr_t before = words()[word_index]; words()[word_index] = before & ~bit; return ((before & bit) != 0); } @@ -376,7 +379,7 @@ class IndexSetIterator { private: // The current word we are inspecting - uint32_t _current; + uintptr_t _current; // What element number are we currently on? uint _value; @@ -391,7 +394,7 @@ class IndexSetIterator { uint _max_blocks; // A pointer to the contents of the current block - uint32_t *_words; + uintptr_t* _words; // A pointer to the blocks in our set IndexSet::BitBlock **_blocks; @@ -447,7 +450,7 @@ class IndexSetIterator { // Return the next element of the set. uint next_value() { - uint current = _current; + uintptr_t current = _current; assert(current != 0, "sanity"); uint advance = count_trailing_zeros(current); assert(((current >> advance) & 0x1) == 1, "sanity"); diff --git a/src/hotspot/share/opto/regmask.cpp b/src/hotspot/share/opto/regmask.cpp index c09ed814bb001..f024387c98524 100644 --- a/src/hotspot/share/opto/regmask.cpp +++ b/src/hotspot/share/opto/regmask.cpp @@ -32,8 +32,6 @@ #include "utilities/population_count.hpp" #include "utilities/powerOfTwo.hpp" -#define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */ - //------------------------------dump------------------------------------------- #ifndef PRODUCT @@ -65,27 +63,29 @@ bool RegMask::is_vector(uint ireg) { } int RegMask::num_registers(uint ireg) { - switch(ireg) { - case Op_VecZ: - return SlotsPerVecZ; - case Op_VecY: - return SlotsPerVecY; - case Op_VecX: - return SlotsPerVecX; - case Op_VecD: - return SlotsPerVecD; - case Op_RegD: - case Op_RegL: + switch(ireg) { + case Op_VecZ: + return SlotsPerVecZ; + case Op_VecY: + return SlotsPerVecY; + case Op_VecX: + return SlotsPerVecX; + case Op_VecD: + return SlotsPerVecD; + case Op_RegD: + case Op_RegL: #ifdef _LP64 - case Op_RegP: + case Op_RegP: #endif - return 2; - case Op_VecA: - assert(Matcher::supports_scalable_vector(), "does not support scalable vector"); - return SlotsPerVecA; - } - // Op_VecS and the rest ideal registers. - return 1; + return 2; + case Op_VecA: + assert(Matcher::supports_scalable_vector(), "does not support scalable vector"); + return SlotsPerVecA; + default: + // Op_VecS and the rest ideal registers. + assert(ireg == Op_VecS || !is_vector(ireg), "unexpected, possibly multi-slot register"); + return 1; + } } int RegMask::num_registers(uint ireg, LRG &lrg) { @@ -101,14 +101,25 @@ int RegMask::num_registers(uint ireg, LRG &lrg) { return n_regs; } +static const uintptr_t zero = uintptr_t(0); // 0x00..00 +static const uintptr_t all = ~uintptr_t(0); // 0xFF..FF +static const uintptr_t fives = all/3; // 0x5555..55 + +// only indices of power 2 are accessed, so index 3 is only filled in for storage. +static const uintptr_t low_bits[5] = { fives, // 0x5555..55 + all/0xF, // 0x1111..11, + all/0xFF, // 0x0101..01, + zero, // 0x0000..00 + all/0xFFFF }; // 0x0001..01 + // Clear out partial bits; leave only bit pairs void RegMask::clear_to_pairs() { assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - bits &= ((bits & 0x55555555)<<1); // 1 hi-bit set for each pair - bits |= (bits>>1); // Smear 1 hi-bit into a pair - _A[i] = bits; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + bits &= ((bits & fives) << 1U); // 1 hi-bit set for each pair + bits |= (bits >> 1U); // Smear 1 hi-bit into a pair + _RM_UP[i] = bits; } assert(is_aligned_pairs(), "mask is not aligned, adjacent pairs"); } @@ -120,16 +131,16 @@ bool RegMask::is_misaligned_pair() const { bool RegMask::is_aligned_pairs() const { // Assert that the register mask contains only bit pairs. assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; while (bits) { // Check bits for pairing - int bit = bits & -bits; // Extract low bit + uintptr_t bit = uintptr_t(1) << find_lowest_bit(bits); // Extract low bit // Low bit is not odd means its mis-aligned. - if ((bit & 0x55555555) == 0) return false; + if ((bit & fives) == 0) return false; bits -= bit; // Remove bit from mask // Check for aligned adjacent bit - if ((bits & (bit<<1)) == 0) return false; - bits -= (bit<<1); // Remove other halve of pair + if ((bits & (bit << 1U)) == 0) return false; + bits -= (bit << 1U); // Remove other halve of pair } } return true; @@ -144,19 +155,19 @@ bool RegMask::is_bound1() const { // Return TRUE if the mask contains an adjacent pair of bits and no other bits. bool RegMask::is_bound_pair() const { if (is_AllStack()) return false; - int bit = -1; // Set to hold the one bit allowed + uintptr_t bit = all; // Set to hold the one bit allowed assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i]) { // Found some bits - if (bit != -1) return false; // Already had bits, so fail - bit = _A[i] & -(_A[i]); // Extract 1 bit from mask - if ((bit << 1) != 0) { // Bit pair stays in same word? - if ((bit | (bit<<1)) != _A[i]) + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i]) { // Found some bits + if (bit != all) return false; // Already had bits, so fail + bit = uintptr_t(1) << find_lowest_bit(_RM_UP[i]); // Extract lowest bit from mask + if ((bit << 1U) != 0) { // Bit pair stays in same word? + if ((bit | (bit << 1U)) != _RM_UP[i]) return false; // Require adjacent bit pair and no more bits } else { // Else its a split-pair case - if(bit != _A[i]) return false; // Found many bits, so fail + if (bit != _RM_UP[i]) return false; // Found many bits, so fail i++; // Skip iteration forward - if (i > _hwm || _A[i] != 1) + if (i > _hwm || _RM_UP[i] != 1) return false; // Require 1 lo bit in next word } } @@ -186,9 +197,6 @@ bool RegMask::is_valid_reg(OptoReg::Name reg, const int size) const { return true; } -// only indicies of power 2 are accessed, so index 3 is only filled in for storage. -static int low_bits[5] = { 0x55555555, 0x11111111, 0x01010101, 0x00000000, 0x00010001 }; - // Find the lowest-numbered register set in the mask. Return the // HIGHEST register number in the set, or BAD if no sets. // Works also for size 1. @@ -200,90 +208,90 @@ OptoReg::Name RegMask::find_first_set(LRG &lrg, const int size) const { assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i]) { // Found some bits + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i]) { // Found some bits // Convert to bit number, return hi bit in pair - return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(_A[i]) + (size - 1)); + return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(_RM_UP[i]) + (size - 1)); } } return OptoReg::Bad; } // Clear out partial bits; leave only aligned adjacent bit pairs -void RegMask::clear_to_sets(const int size) { +void RegMask::clear_to_sets(const unsigned int size) { if (size == 1) return; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); assert(valid_watermarks(), "sanity"); - int low_bits_mask = low_bits[size>>2]; - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - int sets = (bits & low_bits_mask); - for (int j = 1; j < size; j++) { - sets = (bits & (sets<<1)); // filter bits which produce whole sets + uintptr_t low_bits_mask = low_bits[size >> 2U]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + uintptr_t sets = (bits & low_bits_mask); + for (unsigned j = 1U; j < size; j++) { + sets = (bits & (sets << 1U)); // filter bits which produce whole sets } - sets |= (sets>>1); // Smear 1 hi-bit into a set + sets |= (sets >> 1U); // Smear 1 hi-bit into a set if (size > 2) { - sets |= (sets>>2); // Smear 2 hi-bits into a set + sets |= (sets >> 2U); // Smear 2 hi-bits into a set if (size > 4) { - sets |= (sets>>4); // Smear 4 hi-bits into a set + sets |= (sets >> 4U); // Smear 4 hi-bits into a set if (size > 8) { - sets |= (sets>>8); // Smear 8 hi-bits into a set + sets |= (sets >> 8U); // Smear 8 hi-bits into a set } } } - _A[i] = sets; + _RM_UP[i] = sets; } assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } // Smear out partial bits to aligned adjacent bit sets -void RegMask::smear_to_sets(const int size) { +void RegMask::smear_to_sets(const unsigned int size) { if (size == 1) return; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); assert(valid_watermarks(), "sanity"); - int low_bits_mask = low_bits[size>>2]; - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; - int sets = 0; - for (int j = 0; j < size; j++) { + uintptr_t low_bits_mask = low_bits[size >> 2U]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; + uintptr_t sets = 0; + for (unsigned j = 0; j < size; j++) { sets |= (bits & low_bits_mask); // collect partial bits - bits = bits>>1; + bits = bits >> 1U; } - sets |= (sets<<1); // Smear 1 lo-bit into a set + sets |= (sets << 1U); // Smear 1 lo-bit into a set if (size > 2) { - sets |= (sets<<2); // Smear 2 lo-bits into a set + sets |= (sets << 2U); // Smear 2 lo-bits into a set if (size > 4) { - sets |= (sets<<4); // Smear 4 lo-bits into a set + sets |= (sets << 4U); // Smear 4 lo-bits into a set if (size > 8) { - sets |= (sets<<8); // Smear 8 lo-bits into a set + sets |= (sets << 8U); // Smear 8 lo-bits into a set } } } - _A[i] = sets; + _RM_UP[i] = sets; } assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } // Assert that the register mask contains only bit sets. -bool RegMask::is_aligned_sets(const int size) const { +bool RegMask::is_aligned_sets(const unsigned int size) const { if (size == 1) return true; assert(2 <= size && size <= 16, "update low bits table"); assert(is_power_of_2(size), "sanity"); - int low_bits_mask = low_bits[size>>2]; + uintptr_t low_bits_mask = low_bits[size >> 2U]; assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; while (bits) { // Check bits for pairing - int bit = bits & -bits; // Extract low bit + uintptr_t bit = uintptr_t(1) << find_lowest_bit(bits); // Low bit is not odd means its mis-aligned. if ((bit & low_bits_mask) == 0) { return false; } // Do extra work since (bit << size) may overflow. - int hi_bit = bit << (size-1); // high bit - int set = hi_bit + ((hi_bit-1) & ~(bit-1)); + uintptr_t hi_bit = bit << (size-1); // high bit + uintptr_t set = hi_bit + ((hi_bit-1) & ~(bit-1)); // Check for aligned adjacent bits in this set if ((bits & set) != set) { return false; @@ -296,32 +304,29 @@ bool RegMask::is_aligned_sets(const int size) const { // Return TRUE if the mask contains one adjacent set of bits and no other bits. // Works also for size 1. -int RegMask::is_bound_set(const int size) const { +bool RegMask::is_bound_set(const unsigned int size) const { if (is_AllStack()) return false; assert(1 <= size && size <= 16, "update low bits table"); assert(valid_watermarks(), "sanity"); - int bit = -1; // Set to hold the one bit allowed - for (int i = _lwm; i <= _hwm; i++) { - if (_A[i] ) { // Found some bits - if (bit != -1) - return false; // Already had bits, so fail - bit = _A[i] & -_A[i]; // Extract low bit from mask - int hi_bit = bit << (size-1); // high bit + uintptr_t bit = all; // Set to hold the one bit allowed + for (unsigned i = _lwm; i <= _hwm; i++) { + if (_RM_UP[i] ) { // Found some bits + if (bit != all) + return false; // Already had bits, so fail + unsigned bit_index = find_lowest_bit(_RM_UP[i]); + bit = uintptr_t(1) << bit_index; + uintptr_t hi_bit = bit << (size - 1); // high bit if (hi_bit != 0) { // Bit set stays in same word? - int set = hi_bit + ((hi_bit-1) & ~(bit-1)); - if (set != _A[i]) + uintptr_t set = hi_bit + ((hi_bit-1) & ~(bit-1)); + if (set != _RM_UP[i]) return false; // Require adjacent bit set and no more bits } else { // Else its a split-set case - if (((-1) & ~(bit-1)) != _A[i]) + if ((all & ~(bit-1)) != _RM_UP[i]) return false; // Found many bits, so fail i++; // Skip iteration forward and check high part - // The lower (32-size) bits should be 0 since it is split case. - int clear_bit_size = 32-size; - int shift_back_size = 32-clear_bit_size; - int set = bit>>clear_bit_size; - set = set & -set; // Remove sign extension. - set = (((set << size) - 1) >> shift_back_size); - if (i > _hwm || _A[i] != set) + // The lower (BitsPerWord - size) bits should be 1 since it is split case. + uintptr_t set = (bit >> (BitsPerWord - bit_index)) - 1; + if (i > _hwm || _RM_UP[i] != set) return false; // Require expected low bits in next word } } @@ -346,8 +351,8 @@ bool RegMask::is_UP() const { uint RegMask::Size() const { uint sum = 0; assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - sum += population_count((unsigned)_A[i]); + for (unsigned i = _lwm; i <= _hwm; i++) { + sum += population_count(_RM_UP[i]); } return sum; } diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index d9165da6c9065..d59ef2d83371c 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -34,12 +34,12 @@ class LRG; //-------------Non-zero bit search methods used by RegMask--------------------- // Find lowest 1, undefined if empty/0 -static int find_lowest_bit(uint32_t mask) { +static unsigned int find_lowest_bit(uintptr_t mask) { return count_trailing_zeros(mask); } // Find highest 1, undefined if empty/0 -static int find_highest_bit(uint32_t mask) { - return count_leading_zeros(mask) ^ 31; +static unsigned int find_highest_bit(uintptr_t mask) { + return count_leading_zeros(mask) ^ (BitsPerWord - 1U); } //------------------------------RegMask---------------------------------------- @@ -48,37 +48,39 @@ static int find_highest_bit(uint32_t mask) { // just a collection of Register numbers. // The ADLC defines 2 macros, RM_SIZE and FORALL_BODY. -// RM_SIZE is the size of a register mask in words. +// RM_SIZE is the size of a register mask in 32-bit words. // FORALL_BODY replicates a BODY macro once per word in the register mask. // The usage is somewhat clumsy and limited to the regmask.[h,c]pp files. // However, it means the ADLC can redefine the unroll macro and all loops // over register masks will be unrolled by the correct amount. class RegMask { + + enum { + _WordBits = BitsPerWord, + _LogWordBits = LogBitsPerWord, + _RM_SIZE = LP64_ONLY(align_up(RM_SIZE, 2) >> 1) NOT_LP64(RM_SIZE) + }; + union { - double _dummy_force_double_alignment[RM_SIZE>>1]; // Array of Register Mask bits. This array is large enough to cover // all the machine registers and all parameters that need to be passed // on the stack (stack registers) up to some interesting limit. Methods // that need more parameters will NOT be compiled. On Intel, the limit // is something like 90+ parameters. - int _A[RM_SIZE]; + int _RM_I[RM_SIZE]; + uintptr_t _RM_UP[_RM_SIZE]; }; + // The low and high water marks represents the lowest and highest word // that might contain set register mask bits, respectively. We guarantee // that there are no bits in words outside this range, but any word at // and between the two marks can still be 0. - int _lwm; - int _hwm; - - enum { - _WordBits = BitsPerInt, - _LogWordBits = LogBitsPerInt, - _RM_SIZE = RM_SIZE // local constant, imported, then hidden by #undef - }; + unsigned int _lwm; + unsigned int _hwm; public: - enum { CHUNK_SIZE = RM_SIZE*_WordBits }; + enum { CHUNK_SIZE = RM_SIZE*BitsPerInt }; // SlotsPerLong is 2, since slots are 32 bits and longs are 64 bits. // Also, consider the maximum alignment size for a normally allocated @@ -108,13 +110,13 @@ class RegMask { FORALL_BODY # undef BODY int dummy = 0) { -# define BODY(I) _A[I] = a##I; +# define BODY(I) _RM_I[I] = a##I; FORALL_BODY # undef BODY _lwm = 0; - _hwm = RM_SIZE - 1; - while (_hwm > 0 && _A[_hwm] == 0) _hwm--; - while ((_lwm < _hwm) && _A[_lwm] == 0) _lwm++; + _hwm = _RM_SIZE - 1; + while (_hwm > 0 && _RM_UP[_hwm] == 0) _hwm--; + while ((_lwm < _hwm) && _RM_UP[_lwm] == 0) _lwm++; assert(valid_watermarks(), "post-condition"); } @@ -122,48 +124,41 @@ class RegMask { RegMask(RegMask *rm) { _hwm = rm->_hwm; _lwm = rm->_lwm; - for (int i = 0; i < RM_SIZE; i++) { - _A[i] = rm->_A[i]; + for (unsigned i = 0; i < _RM_SIZE; i++) { + _RM_UP[i] = rm->_RM_UP[i]; } assert(valid_watermarks(), "post-condition"); } // Construct an empty mask - RegMask() { - Clear(); - } + RegMask() : _RM_UP(), _lwm(_RM_SIZE - 1), _hwm(0) {} // Construct a mask with a single bit - RegMask(OptoReg::Name reg) { - Clear(); + RegMask(OptoReg::Name reg) : RegMask() { Insert(reg); } // Check for register being in mask - int Member(OptoReg::Name reg) const { + bool Member(OptoReg::Name reg) const { assert(reg < CHUNK_SIZE, ""); - return _A[reg>>_LogWordBits] & (1<<(reg&(_WordBits-1))); + + unsigned r = (unsigned)reg; + return _RM_UP[r >> _LogWordBits] & (uintptr_t(1) <<(r & (_WordBits - 1U))); } // The last bit in the register mask indicates that the mask should repeat // indefinitely with ONE bits. Returns TRUE if mask is infinite or // unbounded in size. Returns FALSE if mask is finite size. - int is_AllStack() const { return _A[RM_SIZE-1] >> (_WordBits-1); } - - // Work around an -xO3 optimization problme in WS6U1. The old way: - // void set_AllStack() { _A[RM_SIZE-1] |= (1<<(_WordBits-1)); } - // will cause _A[RM_SIZE-1] to be clobbered, not updated when set_AllStack() - // follows an Insert() loop, like the one found in init_spill_mask(). Using - // Insert() instead works because the index into _A in computed instead of - // constant. See bug 4665841. + bool is_AllStack() const { return _RM_UP[_RM_SIZE - 1U] >> (_WordBits - 1U); } + void set_AllStack() { Insert(OptoReg::Name(CHUNK_SIZE-1)); } // Test for being a not-empty mask. - int is_NotEmpty() const { + bool is_NotEmpty() const { assert(valid_watermarks(), "sanity"); - int tmp = 0; - for (int i = _lwm; i <= _hwm; i++) { - tmp |= _A[i]; + uintptr_t tmp = 0; + for (unsigned i = _lwm; i <= _hwm; i++) { + tmp |= _RM_UP[i]; } return tmp; } @@ -171,8 +166,8 @@ class RegMask { // Find lowest-numbered register from mask, or BAD if mask is empty. OptoReg::Name find_first_elem() const { assert(valid_watermarks(), "sanity"); - for (int i = _lwm; i <= _hwm; i++) { - int bits = _A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + uintptr_t bits = _RM_UP[i]; if (bits) { return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(bits)); } @@ -183,8 +178,10 @@ class RegMask { // Get highest-numbered register from mask, or BAD if mask is empty. OptoReg::Name find_last_elem() const { assert(valid_watermarks(), "sanity"); - for (int i = _hwm; i >= _lwm; i--) { - int bits = _A[i]; + // Careful not to overflow if _lwm == 0 + unsigned i = _hwm + 1; + while (i > _lwm) { + uintptr_t bits = _RM_UP[--i]; if (bits) { return OptoReg::Name((i<<_LogWordBits) + find_highest_bit(bits)); } @@ -199,13 +196,13 @@ class RegMask { // Verify watermarks are sane, i.e., within bounds and that no // register words below or above the watermarks have bits set. bool valid_watermarks() const { - assert(_hwm >= 0 && _hwm < RM_SIZE, "_hwm out of range: %d", _hwm); - assert(_lwm >= 0 && _lwm < RM_SIZE, "_lwm out of range: %d", _lwm); - for (int i = 0; i < _lwm; i++) { - assert(_A[i] == 0, "_lwm too high: %d regs at: %d", _lwm, i); + assert(_hwm < _RM_SIZE, "_hwm out of range: %d", _hwm); + assert(_lwm < _RM_SIZE, "_lwm out of range: %d", _lwm); + for (unsigned i = 0; i < _lwm; i++) { + assert(_RM_UP[i] == 0, "_lwm too high: %d regs at: %d", _lwm, i); } - for (int i = _hwm + 1; i < RM_SIZE; i++) { - assert(_A[i] == 0, "_hwm too low: %d regs at: %d", _hwm, i); + for (unsigned i = _hwm + 1; i < _RM_SIZE; i++) { + assert(_RM_UP[i] == 0, "_hwm too low: %d regs at: %d", _hwm, i); } return true; } @@ -233,27 +230,27 @@ class RegMask { OptoReg::Name find_first_set(LRG &lrg, const int size) const; // Clear out partial bits; leave only aligned adjacent bit sets of size. - void clear_to_sets(const int size); + void clear_to_sets(const unsigned int size); // Smear out partial bits to aligned adjacent bit sets. - void smear_to_sets(const int size); + void smear_to_sets(const unsigned int size); // Test that the mask contains only aligned adjacent bit sets - bool is_aligned_sets(const int size) const; + bool is_aligned_sets(const unsigned int size) const; // Test for a single adjacent set - int is_bound_set(const int size) const; + bool is_bound_set(const unsigned int size) const; static bool is_vector(uint ireg); static int num_registers(uint ireg); static int num_registers(uint ireg, LRG &lrg); // Fast overlap test. Non-zero if any registers in common. - int overlap(const RegMask &rm) const { + bool overlap(const RegMask &rm) const { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); - int hwm = MIN2(_hwm, rm._hwm); - int lwm = MAX2(_lwm, rm._lwm); - int result = 0; - for (int i = lwm; i <= hwm; i++) { - result |= _A[i] & rm._A[i]; + unsigned hwm = MIN2(_hwm, rm._hwm); + unsigned lwm = MAX2(_lwm, rm._lwm); + uintptr_t result = 0; + for (unsigned i = lwm; i <= hwm; i++) { + result |= _RM_UP[i] & rm._RM_UP[i]; } return result; } @@ -264,35 +261,39 @@ class RegMask { // Clear a register mask void Clear() { - _lwm = RM_SIZE - 1; + _lwm = _RM_SIZE - 1; _hwm = 0; - memset(_A, 0, sizeof(int)*RM_SIZE); + memset(_RM_UP, 0, sizeof(uintptr_t)*_RM_SIZE); assert(valid_watermarks(), "sanity"); } // Fill a register mask with 1's void Set_All() { _lwm = 0; - _hwm = RM_SIZE - 1; - memset(_A, 0xFF, sizeof(int)*RM_SIZE); + _hwm = _RM_SIZE - 1; + memset(_RM_UP, 0xFF, sizeof(uintptr_t)*_RM_SIZE); assert(valid_watermarks(), "sanity"); } // Insert register into mask void Insert(OptoReg::Name reg) { + assert(reg != OptoReg::Bad, "sanity"); + assert(reg != OptoReg::Special, "sanity"); assert(reg < CHUNK_SIZE, "sanity"); assert(valid_watermarks(), "pre-condition"); - int index = reg>>_LogWordBits; + unsigned r = (unsigned)reg; + unsigned index = r >> _LogWordBits; if (index > _hwm) _hwm = index; if (index < _lwm) _lwm = index; - _A[index] |= (1<<(reg&(_WordBits-1))); + _RM_UP[index] |= (uintptr_t(1) << (r & (_WordBits - 1U))); assert(valid_watermarks(), "post-condition"); } // Remove register from mask void Remove(OptoReg::Name reg) { assert(reg < CHUNK_SIZE, ""); - _A[reg>>_LogWordBits] &= ~(1<<(reg&(_WordBits-1))); + unsigned r = (unsigned)reg; + _RM_UP[r >> _LogWordBits] &= ~(uintptr_t(1) << (r & (_WordBits-1U))); } // OR 'rm' into 'this' @@ -301,8 +302,8 @@ class RegMask { // OR widens the live range if (_lwm > rm._lwm) _lwm = rm._lwm; if (_hwm < rm._hwm) _hwm = rm._hwm; - for (int i = _lwm; i <= _hwm; i++) { - _A[i] |= rm._A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + _RM_UP[i] |= rm._RM_UP[i]; } assert(valid_watermarks(), "sanity"); } @@ -312,8 +313,8 @@ class RegMask { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); // Do not evaluate words outside the current watermark range, as they are // already zero and an &= would not change that - for (int i = _lwm; i <= _hwm; i++) { - _A[i] &= rm._A[i]; + for (unsigned i = _lwm; i <= _hwm; i++) { + _RM_UP[i] &= rm._RM_UP[i]; } // Narrow the watermarks if &rm spans a narrower range. // Update after to ensure non-overlapping words are zeroed out. @@ -324,10 +325,10 @@ class RegMask { // Subtract 'rm' from 'this' void SUBTRACT(const RegMask &rm) { assert(valid_watermarks() && rm.valid_watermarks(), "sanity"); - int hwm = MIN2(_hwm, rm._hwm); - int lwm = MAX2(_lwm, rm._lwm); - for (int i = lwm; i <= hwm; i++) { - _A[i] &= ~rm._A[i]; + unsigned hwm = MIN2(_hwm, rm._hwm); + unsigned lwm = MAX2(_lwm, rm._lwm); + for (unsigned i = lwm; i <= hwm; i++) { + _RM_UP[i] &= ~rm._RM_UP[i]; } } diff --git a/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java b/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java new file mode 100644 index 0000000000000..0605050ed2a77 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.bench.vm.compiler.overhead; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; + +import java.util.concurrent.TimeUnit; +import java.util.Arrays; + +/** + * The purpose of these microbenchmarks is to use RepeatCompilation + * to produce a benchmark that focuses on the overhead of various JIT + * compilations themselves. + */ + +@State(Scope.Benchmark) +@BenchmarkMode(Mode.SingleShotTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 10, warmups = 1) +public class SimpleRepeatCompilation { + + public static final String MIXHASH_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.mixHashCode,intx,RepeatCompilation,500"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", MIXHASH_METHOD}) + public int mixHashCode_repeat() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", MIXHASH_METHOD}) + public int mixHashCode_repeat_c2() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", MIXHASH_METHOD}) + public int mixHashCode_repeat_c1() { + return loop_hashCode(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int mixHashCode_baseline() { + return loop_hashCode(); + } + + public int loop_hashCode() { + int value = 0; + for (int i = 0; i < 1_000_000; i++) { + value += mixHashCode("simple_string"); + } + return value; + } + + public int mixHashCode(String value) { + int h = value.hashCode(); + for (int i = 0; i < value.length(); i++) { + h = value.charAt(i) ^ h; + } + return h; + } + + public static final String TRIVIAL_MATH_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.trivialMath,intx,RepeatCompilation,2000"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch",TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat_c2() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", TRIVIAL_MATH_METHOD}) + public int trivialMath_repeat_c1() { + return loop_trivialMath(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int trivialMath_baseline() { + return loop_trivialMath(); + } + + public int loop_trivialMath() { + int value = 0; + for (int i = 0; i < 1_000_000; i++) { + value += trivialMath(i, i - 1); + } + return value; + } + + public int trivialMath(int a, int b) { + return a * b + a; + } + + + public static final String LARGE_METHOD + = "-XX:CompileCommand=option,org/openjdk/bench/vm/compiler/overhead/SimpleRepeatCompilation.largeMethod,intx,RepeatCompilation,100"; + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch",LARGE_METHOD}) + public int largeMethod_repeat() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:-TieredCompilation", LARGE_METHOD}) + public int largeMethod_repeat_c2() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch", "-XX:TieredStopAtLevel=1", LARGE_METHOD}) + public int largeMethod_repeat_c1() { + return loop_largeMethod(); + } + + @Benchmark + @Fork(jvmArgsAppend={"-Xbatch"}) + public int largeMethod_baseline() { + return loop_largeMethod(); + } + + public int loop_largeMethod() { + int value = 0; + for (int i = 0; i < 50_000; i++) { + value += largeMethod(i, i - 1); + } + return value; + } + + // Meaningless but largish method with plenty of locals + // to put more stress on register allocation + public int largeMethod(int a, int b) { + int c = b + 17; + int d = a + b + 6; + int e = c + d + 99; + int f = d + e + 919; + long val = 0; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + a -= b; + b += c; + c -= d; + d += e; + e -= a; + val = a - b + c - d + e - f; + } + } + } + int g = b; + int h = a; + int l = c; + int m = d; + int n = d; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + g += b; + h -= c; + l += d; + m -= e; + e += a; + val = g + h + l + m + n; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + a -= b; + b += c; + c -= d; + d += e; + e -= a; + val = a - b - c - d - e - f; + } + } + } + int o = b; + int p = a; + int q = c; + int r = d; + int s = d; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + o += b; + p += c; + q += d; + r += e; + s += a; + val = o + p + q + r + s; + } + } + } + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + g += b; + h -= c; + l += d; + m -= e; + e += a; + val = g + h + l + m + n; + } + } + } + return (int)(val ^ (val >> 32L)); + } +} From 643969a18411f8120d7021f124ebb38079e060da Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Tue, 10 Nov 2020 17:23:10 +0000 Subject: [PATCH 050/124] 8255822: Zero: improve build-time JVMTI handling Reviewed-by: dholmes, ihse --- make/hotspot/gensrc/GensrcJvmti.gmk | 11 -- src/hotspot/cpu/zero/zeroInterpreter_zero.cpp | 18 ++- .../interpreter/zero/bytecodeInterpreter.cpp | 141 ++++++------------ .../interpreter/zero/bytecodeInterpreter.hpp | 6 +- .../zero/bytecodeInterpreterWithChecks.xml | 28 ---- .../zero/bytecodeInterpreterWithChecks.xsl | 39 ----- 6 files changed, 60 insertions(+), 183 deletions(-) delete mode 100644 src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml delete mode 100644 src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl diff --git a/make/hotspot/gensrc/GensrcJvmti.gmk b/make/hotspot/gensrc/GensrcJvmti.gmk index 312c8bc737a08..b31a6f52292e7 100644 --- a/make/hotspot/gensrc/GensrcJvmti.gmk +++ b/make/hotspot/gensrc/GensrcJvmti.gmk @@ -106,17 +106,6 @@ $(eval $(call SetupJvmtiGeneration, jvmti.h, jvmtiH.xsl, \ $(eval $(call SetupJvmtiGeneration, jvmti.html, jvmti.xsl, \ -PARAM majorversion $(VERSION_FEATURE))) -JVMTI_BC_SRCDIR := $(TOPDIR)/src/hotspot/share/interpreter/zero - -ifeq ($(call check-jvm-feature, zero), true) - $(eval $(call SetupXslTransform, bytecodeInterpreterWithChecks.cpp, \ - XML_FILE := $(JVMTI_BC_SRCDIR)/bytecodeInterpreterWithChecks.xml, \ - XSL_FILE := $(JVMTI_BC_SRCDIR)/bytecodeInterpreterWithChecks.xsl, \ - OUTPUT_DIR := $(JVMTI_OUTPUTDIR), \ - DEPS := $(JVMTI_BC_SRCDIR)/bytecodeInterpreter.cpp, \ - )) -endif - ################################################################################ # Copy jvmti.h to include dir diff --git a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp index 909f6caa266b4..a865ef37a2fb2 100644 --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp @@ -70,10 +70,11 @@ void ZeroInterpreter::initialize_code() { // Allow c++ interpreter to do one initialization now that switches are set, etc. BytecodeInterpreter start_msg(BytecodeInterpreter::initialize); - if (JvmtiExport::can_post_interpreter_events()) - BytecodeInterpreter::runWithChecks(&start_msg); - else - BytecodeInterpreter::run(&start_msg); + if (JvmtiExport::can_post_interpreter_events()) { + BytecodeInterpreter::run(&start_msg); + } else { + BytecodeInterpreter::run(&start_msg); + } } void ZeroInterpreter::invoke_method(Method* method, address entry_point, TRAPS) { @@ -169,10 +170,11 @@ void ZeroInterpreter::main_loop(int recurse, TRAPS) { thread->set_last_Java_frame(); // Call the interpreter - if (JvmtiExport::can_post_interpreter_events()) - BytecodeInterpreter::runWithChecks(istate); - else - BytecodeInterpreter::run(istate); + if (JvmtiExport::can_post_interpreter_events()) { + BytecodeInterpreter::run(istate); + } else { + BytecodeInterpreter::run(istate); + } fixup_after_potential_safepoint(); // Clear the frame anchor diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index c39507e872da5..06f66a1346486 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -52,6 +52,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/threadCritical.hpp" #include "utilities/exceptions.hpp" +#include "utilities/macros.hpp" // no precompiled headers @@ -153,7 +154,7 @@ #endif #undef DEBUGGER_SINGLE_STEP_NOTIFY -#ifdef VM_JVMTI +#if INCLUDE_JVMTI /* NOTE: (kbr) This macro must be called AFTER the PC has been incremented. JvmtiExport::at_single_stepping_point() may cause a breakpoint opcode to get inserted at the current PC to allow the @@ -163,33 +164,31 @@ to get the current opcode. This will override any other prefetching that might have occurred. */ -#define DEBUGGER_SINGLE_STEP_NOTIFY() \ -{ \ - if (_jvmti_interp_events) { \ - if (JvmtiExport::should_post_single_step()) { \ - DECACHE_STATE(); \ - SET_LAST_JAVA_FRAME(); \ - ThreadInVMfromJava trans(THREAD); \ - JvmtiExport::at_single_stepping_point(THREAD, \ - istate->method(), \ - pc); \ - RESET_LAST_JAVA_FRAME(); \ - CACHE_STATE(); \ - if (THREAD->has_pending_popframe() && \ - !THREAD->pop_frame_in_process()) { \ - goto handle_Pop_Frame; \ - } \ - if (THREAD->jvmti_thread_state() && \ - THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ - goto handle_Early_Return; \ - } \ - opcode = *pc; \ - } \ - } \ +#define DEBUGGER_SINGLE_STEP_NOTIFY() \ +{ \ + if (JVMTI_ENABLED && JvmtiExport::should_post_single_step()) { \ + DECACHE_STATE(); \ + SET_LAST_JAVA_FRAME(); \ + ThreadInVMfromJava trans(THREAD); \ + JvmtiExport::at_single_stepping_point(THREAD, \ + istate->method(), \ + pc); \ + RESET_LAST_JAVA_FRAME(); \ + CACHE_STATE(); \ + if (THREAD->has_pending_popframe() && \ + !THREAD->pop_frame_in_process()) { \ + goto handle_Pop_Frame; \ + } \ + if (THREAD->jvmti_thread_state() && \ + THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ + goto handle_Early_Return; \ + } \ + opcode = *pc; \ + } \ } #else #define DEBUGGER_SINGLE_STEP_NOTIFY() -#endif +#endif // INCLUDE_JVMTI /* * CONTINUE - Macro for executing the next opcode. @@ -387,22 +386,18 @@ /* * BytecodeInterpreter::run(interpreterState istate) - * BytecodeInterpreter::runWithChecks(interpreterState istate) * * The real deal. This is where byte codes actually get interpreted. * Basically it's a big while loop that iterates until we return from * the method passed in. - * - * The runWithChecks is used if JVMTI is enabled. - * */ -#if defined(VM_JVMTI) -void -BytecodeInterpreter::runWithChecks(interpreterState istate) { -#else -void -BytecodeInterpreter::run(interpreterState istate) { -#endif + +// Instantiate two variants of the method for future linking. +template void BytecodeInterpreter::run(interpreterState istate); +template void BytecodeInterpreter::run(interpreterState istate); + +template +void BytecodeInterpreter::run(interpreterState istate) { // In order to simplify some tests based on switches set at runtime // we invoke the interpreter a single time after switches are enabled @@ -417,9 +412,6 @@ BytecodeInterpreter::run(interpreterState istate) { if (checkit && *c_addr != c_value) { os::breakpoint(); } -#ifdef VM_JVMTI - static bool _jvmti_interp_events = 0; -#endif #ifdef ASSERT if (istate->_msg != initialize) { @@ -555,9 +547,6 @@ BytecodeInterpreter::run(interpreterState istate) { switch (istate->msg()) { case initialize: { if (initialized++) ShouldNotReachHere(); // Only one initialize call. -#ifdef VM_JVMTI - _jvmti_interp_events = JvmtiExport::can_post_interpreter_events(); -#endif return; } break; @@ -667,17 +656,13 @@ BytecodeInterpreter::run(interpreterState istate) { } THREAD->clr_do_not_unlock(); - // Notify jvmti -#ifdef VM_JVMTI - if (_jvmti_interp_events) { - // Whenever JVMTI puts a thread in interp_only_mode, method - // entry/exit events are sent for that thread to track stack depth. - if (THREAD->is_interp_only_mode()) { - CALL_VM(InterpreterRuntime::post_method_entry(THREAD), - handle_exception); - } + // Notify jvmti. + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { + CALL_VM(InterpreterRuntime::post_method_entry(THREAD), + handle_exception); } -#endif /* VM_JVMTI */ goto run; } @@ -1800,8 +1785,7 @@ BytecodeInterpreter::run(interpreterState istate) { cache = cp->entry_at(index); } -#ifdef VM_JVMTI - if (_jvmti_interp_events) { + if (JVMTI_ENABLED) { int *count_addr; oop obj; // Check to see if a field modification watch has been set @@ -1820,7 +1804,6 @@ BytecodeInterpreter::run(interpreterState istate) { handle_exception); } } -#endif /* VM_JVMTI */ oop obj; if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { @@ -1898,8 +1881,7 @@ BytecodeInterpreter::run(interpreterState istate) { cache = cp->entry_at(index); } -#ifdef VM_JVMTI - if (_jvmti_interp_events) { + if (JVMTI_ENABLED) { int *count_addr; oop obj; // Check to see if a field modification watch has been set @@ -1925,7 +1907,6 @@ BytecodeInterpreter::run(interpreterState istate) { handle_exception); } } -#endif /* VM_JVMTI */ // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases // out so c++ compiler has a chance for constant prop to fold everything possible away. @@ -2404,11 +2385,9 @@ BytecodeInterpreter::run(interpreterState istate) { if (callee != NULL) { istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(5); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2466,11 +2445,9 @@ BytecodeInterpreter::run(interpreterState istate) { istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(5); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2536,11 +2513,9 @@ BytecodeInterpreter::run(interpreterState istate) { istate->set_callee(callee); istate->set_callee_entry_point(callee->from_interpreted_entry()); -#ifdef VM_JVMTI - if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { istate->set_callee_entry_point(callee->interpreter_entry()); } -#endif /* VM_JVMTI */ istate->set_bcp_advance(3); UPDATE_PC_AND_RETURN(0); // I'll be back... } @@ -2963,25 +2938,16 @@ BytecodeInterpreter::run(interpreterState istate) { // (with this note) in anticipation of changing the vm and the tests // simultaneously. - - // suppress_exit_event = suppress_exit_event || illegal_state_oop() != NULL; + // Whenever JVMTI puts a thread in interp_only_mode, method + // entry/exit events are sent for that thread to track stack depth. - -#ifdef VM_JVMTI - if (_jvmti_interp_events) { - // Whenever JVMTI puts a thread in interp_only_mode, method - // entry/exit events are sent for that thread to track stack depth. - if ( !suppress_exit_event && THREAD->is_interp_only_mode() ) { - { - // Prevent any HandleMarkCleaner from freeing our live handles - HandleMark __hm(THREAD); - CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); - } - } - } -#endif /* VM_JVMTI */ + if (JVMTI_ENABLED && !suppress_exit_event && THREAD->is_interp_only_mode()) { + // Prevent any HandleMarkCleaner from freeing our live handles + HandleMark __hm(THREAD); + CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); + } // // See if we are returning any exception @@ -3032,13 +2998,6 @@ BytecodeInterpreter::run(interpreterState istate) { return; } -/* - * All the code following this point is only produced once and is not present - * in the JVMTI version of the interpreter -*/ - -#ifndef VM_JVMTI - // This constructor should only be used to contruct the object to signal // interpreter initialization. All other instances should be created by // the frame manager. @@ -3169,5 +3128,3 @@ extern "C" { } } #endif // PRODUCT - -#endif // JVMTI diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp index 8ff3575c69de8..e4a09d492bd68 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.hpp @@ -503,12 +503,8 @@ static void dup2_x1(intptr_t *tos); /* insert top 2 slots three down */ static void dup2_x2(intptr_t *tos); /* insert top 2 slots four down */ static void swap(intptr_t *tos); /* swap top two elements */ -// umm don't like this method modifies its object - -// The Interpreter used when +template static void run(interpreterState istate); -// The interpreter used if JVMTI needs interpreter events -static void runWithChecks(interpreterState istate); static void astore(intptr_t* topOfStack, int stack_offset, intptr_t* locals, int locals_offset); diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml b/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml deleted file mode 100644 index 8cbf66593c717..0000000000000 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - -]> - - diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl b/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl deleted file mode 100644 index 14546dfc9a306..0000000000000 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreterWithChecks.xsl +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - -#define VM_JVMTI -#include "interpreter/zero/bytecodeInterpreter.cpp" - - - - - - - - From 6d8acd2696f41918d4233ddffe46e6c205f9dbc3 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Tue, 10 Nov 2020 17:51:52 +0000 Subject: [PATCH 051/124] 8256066: Tests use deprecated TestNG API that is no longer available in new versions Reviewed-by: jjg --- test/jdk/java/lang/invoke/ConstantIdentityMHTest.java | 6 ++---- test/jdk/java/lang/invoke/DropArgumentsTest.java | 9 +++------ test/jdk/java/lang/invoke/VarArgsTest.java | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/test/jdk/java/lang/invoke/ConstantIdentityMHTest.java b/test/jdk/java/lang/invoke/ConstantIdentityMHTest.java index 34c97a955ae36..c0db09bb42ae5 100644 --- a/test/jdk/java/lang/invoke/ConstantIdentityMHTest.java +++ b/test/jdk/java/lang/invoke/ConstantIdentityMHTest.java @@ -59,8 +59,7 @@ public void testZero(Class expectedtype, String expected) throws Throwable { assertEquals(MethodHandles.zero(expectedtype).type().toString(), expected); } - @Test - @ExpectedExceptions(NullPointerException.class) + @Test(expectedExceptions={ NullPointerException.class }) public void testZeroNPE() { MethodHandle mh = MethodHandles.zero(null); } @@ -73,8 +72,7 @@ void testEmpty() throws Throwable { assertEquals((String)mhEmpty.invoke("x","y"), null); } - @Test - @ExpectedExceptions(NullPointerException.class) + @Test(expectedExceptions = { NullPointerException.class }) void testEmptyNPE() { MethodHandle lenEmptyMH = MethodHandles.empty(null); } diff --git a/test/jdk/java/lang/invoke/DropArgumentsTest.java b/test/jdk/java/lang/invoke/DropArgumentsTest.java index be65b39b27658..fd36d07767cba 100644 --- a/test/jdk/java/lang/invoke/DropArgumentsTest.java +++ b/test/jdk/java/lang/invoke/DropArgumentsTest.java @@ -65,8 +65,7 @@ private Object[][] dropArgumentsToMatchNPEData() }; } - @Test(dataProvider = "dropArgumentsToMatchNPEData") - @ExpectedExceptions(NullPointerException.class) + @Test(dataProvider = "dropArgumentsToMatchNPEData", expectedExceptions = { NullPointerException.class }) public void dropArgumentsToMatchNPE(MethodHandle target, int pos, List> valueType, int skip) { MethodHandles.dropArgumentsToMatch(target, pos, valueType , skip); } @@ -85,14 +84,12 @@ private Object[][] dropArgumentsToMatchIAEData() }; } - @Test(dataProvider = "dropArgumentsToMatchIAEData") - @ExpectedExceptions(IllegalArgumentException.class) + @Test(dataProvider = "dropArgumentsToMatchIAEData", expectedExceptions = { IllegalArgumentException.class }) public void dropArgumentsToMatchIAE(MethodHandle target, int pos, List> valueType, int skip) { MethodHandles.dropArgumentsToMatch(target, pos, valueType , skip); } - @Test - @ExpectedExceptions(IllegalArgumentException.class) + @Test(expectedExceptions = { IllegalArgumentException.class }) public void dropArgumentsToMatchTestWithVoid() throws Throwable { MethodHandle cat = lookup().findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class)); diff --git a/test/jdk/java/lang/invoke/VarArgsTest.java b/test/jdk/java/lang/invoke/VarArgsTest.java index 2fef2cd90b0dc..23baee3be88fb 100644 --- a/test/jdk/java/lang/invoke/VarArgsTest.java +++ b/test/jdk/java/lang/invoke/VarArgsTest.java @@ -69,8 +69,7 @@ public void testWithVarargs2() throws Throwable { assertEquals("[two, too]", asListWithVarargs.invoke("two", "too").toString()); } - @Test - @ExpectedExceptions(IllegalArgumentException.class) + @Test(expectedExceptions = { IllegalArgumentException.class }) public void testWithVarargsIAE() throws Throwable { MethodHandle lenMH = publicLookup() .findVirtual(String.class, "length", methodType(int.class)); From bd3e65b576a039cd6ebaca215e21587311bf8b0d Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Tue, 10 Nov 2020 19:16:35 +0000 Subject: [PATCH 052/124] 8256052: Remove unused allocation type from fieldInfo Reviewed-by: redestad, lfoltan, hseigel --- .../share/classfile/classFileParser.cpp | 13 +- src/hotspot/share/oops/fieldInfo.hpp | 126 +++--------------- src/hotspot/share/oops/fieldStreams.hpp | 4 - src/hotspot/share/runtime/vmStructs.cpp | 1 - .../sun/jvm/hotspot/oops/InstanceKlass.java | 4 +- 5 files changed, 27 insertions(+), 121 deletions(-) diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 21cd57ad979c3..52767594516b6 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1557,14 +1557,13 @@ class ClassFileParser::FieldAllocationCount : public ResourceObj { } } - FieldAllocationType update(bool is_static, BasicType type) { + void update(bool is_static, BasicType type) { FieldAllocationType atype = basic_type_to_atype(is_static, type); if (atype != BAD_ALLOCATION_TYPE) { // Make sure there is no overflow with injected fields. assert(count[atype] < 0xFFFF, "More than 65535 fields"); count[atype]++; } - return atype; } }; @@ -1704,9 +1703,8 @@ void ClassFileParser::parse_fields(const ClassFileStream* const cfs, constantvalue_index); const BasicType type = cp->basic_type_for_signature_at(signature_index); - // Remember how many oops we encountered and compute allocation type - const FieldAllocationType atype = fac->update(is_static, type); - field->set_allocation_type(atype); + // Update FieldAllocationCount for this kind of field + fac->update(is_static, type); // After field is initialized with type, we can augment it with aux info if (parsed_annotations.has_any_annotations()) { @@ -1749,9 +1747,8 @@ void ClassFileParser::parse_fields(const ClassFileStream* const cfs, const BasicType type = Signature::basic_type(injected[n].signature()); - // Remember how many oops we encountered and compute allocation type - const FieldAllocationType atype = fac->update(false, type); - field->set_allocation_type(atype); + // Update FieldAllocationCount for this kind of field + fac->update(false, type); index++; } } diff --git a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp index 3f6ede3e769af..3e1836c6574e6 100644 --- a/src/hotspot/share/oops/fieldInfo.hpp +++ b/src/hotspot/share/oops/fieldInfo.hpp @@ -47,19 +47,21 @@ class FieldInfo { // as an array of 6 shorts. #define FIELDINFO_TAG_SIZE 2 -#define FIELDINFO_TAG_BLANK 0 -#define FIELDINFO_TAG_OFFSET 1 -#define FIELDINFO_TAG_TYPE_PLAIN 2 -#define FIELDINFO_TAG_TYPE_CONTENDED 3 -#define FIELDINFO_TAG_MASK 3 +#define FIELDINFO_TAG_OFFSET 1 << 0 +#define FIELDINFO_TAG_CONTENDED 1 << 1 // Packed field has the tag, and can be either of: // hi bits <--------------------------- lo bits // |---------high---------|---------low---------| - // ..........................................00 - blank + // ..........................................CO + // ..........................................00 - non-contended field + // [--contention_group--]....................10 - contended field with contention group // [------------------offset----------------]01 - real field offset - // ......................[-------type-------]10 - plain field with type - // [--contention_group--][-------type-------]11 - contended field with type and contention group + + // Bit O indicates if the packed field contains an offset (O=1) or not (O=0) + // Bit C indicates if the field is contended (C=1) or not (C=0) + // (if it is contended, the high packed field contains the contention group) + enum FieldOffset { access_flags_offset = 0, name_index_offset = 1, @@ -103,78 +105,22 @@ class FieldInfo { u2 access_flags() const { return _shorts[access_flags_offset]; } u4 offset() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_OFFSET: - return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_PLAIN: - fatal("Asking offset for the plain type field"); - case FIELDINFO_TAG_TYPE_CONTENDED: - fatal("Asking offset for the contended type field"); - case FIELDINFO_TAG_BLANK: - fatal("Asking offset for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) != 0, "Offset must have been set"); + return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; } bool is_contended() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - return false; - case FIELDINFO_TAG_TYPE_CONTENDED: - return true; -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking contended flag for the field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking contended flag for the blank field"); -#endif - } - ShouldNotReachHere(); - return false; + return (_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) != 0; } u2 contended_group() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - return 0; - case FIELDINFO_TAG_TYPE_CONTENDED: - return _shorts[high_packed_offset]; -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking the contended group for the field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking the contended group for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) == 0, "Offset must not have been set"); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) != 0, "Field must be contended"); + return _shorts[high_packed_offset]; } - u2 allocation_type() const { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - case FIELDINFO_TAG_TYPE_CONTENDED: - return (lo >> FIELDINFO_TAG_SIZE); -#ifndef PRODUCT - case FIELDINFO_TAG_OFFSET: - fatal("Asking the field type for field with offset"); - case FIELDINFO_TAG_BLANK: - fatal("Asking the field type for the blank field"); -#endif - } - ShouldNotReachHere(); - return 0; - } - bool is_offset_set() const { - return (_shorts[low_packed_offset] & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET; + return (_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET)!= 0; } Symbol* name(ConstantPool* cp) const { @@ -200,41 +146,11 @@ class FieldInfo { _shorts[high_packed_offset] = extract_high_short_from_int(val); } - void set_allocation_type(int type) { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_BLANK: - _shorts[low_packed_offset] = ((type << FIELDINFO_TAG_SIZE)) & 0xFFFF; - _shorts[low_packed_offset] &= ~FIELDINFO_TAG_MASK; - _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_PLAIN; - return; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_PLAIN: - case FIELDINFO_TAG_TYPE_CONTENDED: - case FIELDINFO_TAG_OFFSET: - fatal("Setting the field type with overwriting"); -#endif - } - ShouldNotReachHere(); - } - void set_contended_group(u2 val) { - u2 lo = _shorts[low_packed_offset]; - switch(lo & FIELDINFO_TAG_MASK) { - case FIELDINFO_TAG_TYPE_PLAIN: - _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_CONTENDED; - _shorts[high_packed_offset] = val; - return; -#ifndef PRODUCT - case FIELDINFO_TAG_TYPE_CONTENDED: - fatal("Overwriting contended group"); - case FIELDINFO_TAG_BLANK: - fatal("Setting contended group for the blank field"); - case FIELDINFO_TAG_OFFSET: - fatal("Setting contended group for field with offset"); -#endif - } - ShouldNotReachHere(); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_OFFSET) == 0, "Offset must not have been set"); + assert((_shorts[low_packed_offset] & FIELDINFO_TAG_CONTENDED) == 0, "Overwritting contended group"); + _shorts[low_packed_offset] |= FIELDINFO_TAG_CONTENDED; + _shorts[high_packed_offset] = val; } bool is_internal() const { diff --git a/src/hotspot/share/oops/fieldStreams.hpp b/src/hotspot/share/oops/fieldStreams.hpp index e36427acf52b2..d2387531f2906 100644 --- a/src/hotspot/share/oops/fieldStreams.hpp +++ b/src/hotspot/share/oops/fieldStreams.hpp @@ -134,10 +134,6 @@ class FieldStreamBase : public StackObj { return field()->offset(); } - int allocation_type() const { - return field()->allocation_type(); - } - void set_offset(int offset) { field()->set_offset(offset); } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 24a076e0d8421..ed9b0d3966600 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -2257,7 +2257,6 @@ typedef HashtableEntry KlassHashtableEntry; /*************************************/ \ \ declare_preprocessor_constant("FIELDINFO_TAG_SIZE", FIELDINFO_TAG_SIZE) \ - declare_preprocessor_constant("FIELDINFO_TAG_MASK", FIELDINFO_TAG_MASK) \ declare_preprocessor_constant("FIELDINFO_TAG_OFFSET", FIELDINFO_TAG_OFFSET) \ \ /************************************************/ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index ecd221577fec2..8357bbb370ebe 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -56,7 +56,6 @@ public void update(Observable o, Object data) { private static int HIGH_OFFSET; private static int FIELD_SLOTS; private static short FIELDINFO_TAG_SIZE; - private static short FIELDINFO_TAG_MASK; private static short FIELDINFO_TAG_OFFSET; // ClassState constants @@ -117,7 +116,6 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeExc HIGH_OFFSET = db.lookupIntConstant("FieldInfo::high_packed_offset").intValue(); FIELD_SLOTS = db.lookupIntConstant("FieldInfo::field_slots").intValue(); FIELDINFO_TAG_SIZE = db.lookupIntConstant("FIELDINFO_TAG_SIZE").shortValue(); - FIELDINFO_TAG_MASK = db.lookupIntConstant("FIELDINFO_TAG_MASK").shortValue(); FIELDINFO_TAG_OFFSET = db.lookupIntConstant("FIELDINFO_TAG_OFFSET").shortValue(); // read ClassState constants @@ -397,7 +395,7 @@ public int getFieldOffset(int index) { U2Array fields = getFields(); short lo = fields.at(index * FIELD_SLOTS + LOW_OFFSET); short hi = fields.at(index * FIELD_SLOTS + HIGH_OFFSET); - if ((lo & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET) { + if ((lo & FIELDINFO_TAG_OFFSET) == FIELDINFO_TAG_OFFSET) { return VM.getVM().buildIntFromShorts(lo, hi) >> FIELDINFO_TAG_SIZE; } throw new RuntimeException("should not reach here"); From a7f46919ff43ede12ed977512a3b0d93bc4cbc76 Mon Sep 17 00:00:00 2001 From: Jayashree S Kumar Date: Tue, 10 Nov 2020 19:36:59 +0000 Subject: [PATCH 053/124] 8244088: [Regression] Switch of Gnome theme ends up in deadlocked UI Reviewed-by: serb --- src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c | 2 +- src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h | 2 +- .../unix/native/libawt_xawt/awt/swing_GTKEngine.c | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 2d92f85ec5cdf..39f17a985af8e 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -708,7 +708,7 @@ static int gtk3_unload() */ static void flush_gtk_event_loop() { - while((*fp_g_main_context_iteration)(NULL)); + while((*fp_g_main_context_iteration)(NULL, FALSE)); } /* diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 51fd777848478..19a1e2ddaf537 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -401,7 +401,7 @@ static void (*fp_g_object_set)(gpointer object, const gchar *first_property_name, ...); -static gboolean (*fp_g_main_context_iteration)(GMainContext *context); +static gboolean (*fp_g_main_context_iteration)(GMainContext *context, gboolean may_block); static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix); static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter, gint max_tokens); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c index 7bfd0d8b860df..b507ae7a4c50a 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/swing_GTKEngine.c @@ -350,10 +350,8 @@ Java_com_sun_java_swing_plaf_gtk_GTKEngine_nativeFinishPainting( JNIEXPORT void JNICALL Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1switch_1theme( JNIEnv *env, jobject this) { - // Note that flush_gtk_event_loop takes care of locks (7053002) - gtk->gdk_threads_enter(); + // Note that gtk->flush_event_loop takes care of locks (7053002), gdk_threads_enter/gdk_threads_leave should not be used. gtk->flush_event_loop(); - gtk->gdk_threads_leave(); } /* From 7d4e86be3bb6a452dda1028d7272776ed5b1921c Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Tue, 10 Nov 2020 20:38:25 +0000 Subject: [PATCH 054/124] 8138588: VerifyMergedCPBytecodes option cleanup needed Reviewed-by: hseigel, dcubed, sspitsyn --- src/hotspot/share/prims/jvmtiRedefineClasses.cpp | 4 +++- src/hotspot/share/runtime/arguments.cpp | 1 + src/hotspot/share/runtime/globals.hpp | 4 ---- test/hotspot/jtreg/serviceability/sa/ClhsdbFlags.java | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp index ae238a8a6883c..277af0e98f17d 100644 --- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp +++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp @@ -1452,7 +1452,8 @@ jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { } } - if (VerifyMergedCPBytecodes) { +#ifdef ASSERT + { // verify what we have done during constant pool merging { RedefineVerifyMark rvm(the_class, scratch_class, state); @@ -1472,6 +1473,7 @@ jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) { } } } +#endif // ASSERT Rewriter::rewrite(scratch_class, THREAD); if (!HAS_PENDING_EXCEPTION) { diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 3ba33cc027ade..9a828e5aa1889 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -556,6 +556,7 @@ static SpecialFlag const special_jvm_flags[] = { { "InsertMemBarAfterArraycopy", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "Debugging", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "UseRDPCForConstantTableBase", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, + { "VerifyMergedCPBytecodes", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, #ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS // These entries will generate build errors. Their purpose is to test the macros. diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index aff2c78050cf1..171cc50566591 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -872,10 +872,6 @@ const intx ObjectAlignmentInBytes = 8; product(bool, StressLdcRewrite, false, \ "Force ldc -> ldc_w rewrite during RedefineClasses") \ \ - /* change to false by default sometime after Mustang */ \ - product(bool, VerifyMergedCPBytecodes, true, \ - "Verify bytecodes after RedefineClasses constant pool merging") \ - \ product(bool, AllowRedefinitionToAddDeleteMethods, false, \ "(Deprecated) Allow redefinition to add and delete private " \ "static or final methods for compatibility with old releases") \ diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbFlags.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbFlags.java index 0fe44a9980fbe..1d36795bfdf3f 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbFlags.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbFlags.java @@ -65,7 +65,6 @@ public static void runBasicTest() throws Exception { "UnlockDiagnosticVMOptions = true", "MaxFDLimit = false", "MaxJavaStackTraceDepth = 1024", - "VerifyMergedCPBytecodes", "ConcGCThreads", "UseThreadPriorities", "ShowHiddenFrames")); expStrMap.put("flags -nd", List.of( From f2a0bf3ea885acef666f408c9ea123916b79adcb Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Tue, 10 Nov 2020 20:39:30 +0000 Subject: [PATCH 055/124] 8256017: Remove unused elapsedTimer constructor Reviewed-by: tschatzl, hseigel --- src/hotspot/share/runtime/timer.cpp | 18 +----------------- src/hotspot/share/runtime/timer.hpp | 3 +-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/hotspot/share/runtime/timer.cpp b/src/hotspot/share/runtime/timer.cpp index d810f4524dbd8..94a410df6c8dd 100644 --- a/src/hotspot/share/runtime/timer.cpp +++ b/src/hotspot/share/runtime/timer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,22 +47,6 @@ jlong TimeHelper::micros_to_counter(jlong micros) { return micros * freq; } -elapsedTimer::elapsedTimer(jlong time, jlong timeUnitsPerSecond) { - _active = false; - jlong osTimeUnitsPerSecond = os::elapsed_frequency(); - assert(osTimeUnitsPerSecond % 1000 == 0, "must be"); - assert(timeUnitsPerSecond % 1000 == 0, "must be"); - while (osTimeUnitsPerSecond < timeUnitsPerSecond) { - timeUnitsPerSecond /= 1000; - time *= 1000; - } - while (osTimeUnitsPerSecond > timeUnitsPerSecond) { - timeUnitsPerSecond *= 1000; - time /= 1000; - } - _counter = time; -} - void elapsedTimer::add(elapsedTimer t) { _counter += t._counter; } diff --git a/src/hotspot/share/runtime/timer.hpp b/src/hotspot/share/runtime/timer.hpp index 9e29072a1241b..5a22d8e2e4431 100644 --- a/src/hotspot/share/runtime/timer.hpp +++ b/src/hotspot/share/runtime/timer.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ class elapsedTimer { bool _active; public: elapsedTimer() { _active = false; reset(); } - elapsedTimer(jlong time, jlong timeUnitsPerSecond); void add(elapsedTimer t); void start(); void stop(); From d6f1463cb33f62e718876b28eb23e864ef75586b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Tue, 10 Nov 2020 22:36:31 +0000 Subject: [PATCH 056/124] 8233332: Need to create exploded tests covering all forms of modules Reviewed-by: herrick, almatvee --- .../helpers/jdk/jpackage/test/HelloApp.java | 52 +++++++++++++++++-- .../jdk/jpackage/test/JavaAppDesc.java | 16 +++++- .../share/jdk/jpackage/tests/BasicTest.java | 2 + 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java index 0b9704b918257..c7f0bf1cfa33a 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/HelloApp.java @@ -34,10 +34,14 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import jdk.jpackage.test.Functional.ThrowingConsumer; import jdk.jpackage.test.Functional.ThrowingFunction; import jdk.jpackage.test.Functional.ThrowingSupplier; @@ -225,7 +229,7 @@ static void verifyOutputFile(Path outputFile, List args, public static Path createBundle(JavaAppDesc appDesc, Path outputDir) { String jmodFileName = appDesc.jmodFileName(); if (jmodFileName != null) { - final Path jmodFilePath = outputDir.resolve(jmodFileName); + final Path jmodPath = outputDir.resolve(jmodFileName); TKit.withTempDirectory("jmod-workdir", jmodWorkDir -> { var jarAppDesc = JavaAppDesc.parse(appDesc.toString()) .setBundleFileName("tmp.jar"); @@ -233,8 +237,7 @@ public static Path createBundle(JavaAppDesc appDesc, Path outputDir) { Executor exec = new Executor() .setToolProvider(JavaTool.JMOD) .addArguments("create", "--class-path") - .addArgument(jarPath) - .addArgument(jmodFilePath); + .addArgument(jarPath); if (appDesc.isWithMainClass()) { exec.addArguments("--main-class", appDesc.className()); @@ -244,11 +247,50 @@ public static Path createBundle(JavaAppDesc appDesc, Path outputDir) { exec.addArguments("--module-version", appDesc.moduleVersion()); } - Files.createDirectories(jmodFilePath.getParent()); + final Path jmodFilePath; + if (appDesc.isExplodedModule()) { + jmodFilePath = jmodWorkDir.resolve("tmp.jmod"); + exec.addArgument(jmodFilePath); + TKit.deleteDirectoryRecursive(jmodPath); + } else { + jmodFilePath = jmodPath; + exec.addArgument(jmodFilePath); + TKit.deleteIfExists(jmodPath); + } + + Files.createDirectories(jmodPath.getParent()); exec.execute(); + + if (appDesc.isExplodedModule()) { + TKit.trace(String.format("Explode [%s] module file...", + jmodFilePath.toAbsolutePath().normalize())); + // Explode contents of the root `classes` directory of + // temporary .jmod file + final Path jmodRootDir = Path.of("classes"); + try (var archive = new ZipFile(jmodFilePath.toFile())) { + archive.stream() + .filter(Predicate.not(ZipEntry::isDirectory)) + .sequential().forEachOrdered(ThrowingConsumer.toConsumer( + entry -> { + try (var in = archive.getInputStream(entry)) { + Path entryName = Path.of(entry.getName()); + if (entryName.startsWith(jmodRootDir)) { + entryName = jmodRootDir.relativize(entryName); + } + final Path fileName = jmodPath.resolve(entryName); + TKit.trace(String.format( + "Save [%s] zip entry in [%s] file...", + entry.getName(), + fileName.toAbsolutePath().normalize())); + Files.createDirectories(fileName.getParent()); + Files.copy(in, fileName); + } + })); + } + } }); - return jmodFilePath; + return jmodPath; } final JavaAppDesc jarAppDesc; diff --git a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java index b9a2451c49f54..a5db716faa996 100644 --- a/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java +++ b/test/jdk/tools/jpackage/helpers/jdk/jpackage/test/JavaAppDesc.java @@ -84,6 +84,10 @@ public String jarFileName() { } public String jmodFileName() { + if (isExplodedModule()) { + return bundleFileName; + } + if (bundleFileName != null && bundleFileName.endsWith(".jmod")) { return bundleFileName; } @@ -94,6 +98,10 @@ public boolean isWithBundleFileName() { return bundleFileName != null; } + public boolean isExplodedModule() { + return bundleFileName != null && bundleFileName.endsWith(".ejmod"); + } + public String moduleVersion() { return moduleVersion; } @@ -127,7 +135,7 @@ public String toString() { * Create Java application description form encoded string value. * * Syntax of encoded Java application description is - * [(jar_file|jmods_file):][module_name/]qualified_class_name[!][@module_version]. + * [(jar_file|jmods_file|exploded_jmods_file):][module_name/]qualified_class_name[!][@module_version]. * * E.g.: `duke.jar:com.other/com.other.foo.bar.Buz!@3.7` encodes modular * application. Module name is `com.other`. Main class is @@ -140,6 +148,12 @@ public String toString() { * `com.another.One`. Application will be * compiled and packed in `bar.jmod` jmod file. * + * E.g.: `bar.ejmod:com.another/com.another.One` encodes modular + * application. Module name is `com.another`. Main class is + * `com.another.One`. Application will be + * compiled and packed in temporary jmod file that will be exploded in + * `bar.ejmod` directory. + * * E.g.: `Ciao` encodes non-modular `Ciao` class in the default package. * jar command will not put main class attribute in the jar file. * Default name will be picked for jar file - `hello.jar`. diff --git a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java index c93c51ab24b88..e8cd0028257e4 100644 --- a/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java +++ b/test/jdk/tools/jpackage/share/jdk/jpackage/tests/BasicTest.java @@ -226,6 +226,8 @@ public void testNoName() { @Parameter("com.other/com.other.Hello") // Modular app in .jmod file @Parameter("hello.jmod:com.other/com.other.Hello") + // Modular app in exploded .jmod file + @Parameter("hello.ejmod:com.other/com.other.Hello") public void testApp(String javaAppDesc) { JavaAppDesc appDesc = JavaAppDesc.parse(javaAppDesc); JPackageCommand cmd = JPackageCommand.helloAppImage(appDesc); From 0a41ca6b7503c7d901b450ac8d006e5546a58d6d Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Tue, 10 Nov 2020 23:26:02 +0000 Subject: [PATCH 057/124] 8254354: Add a withInvokeExactBehavior() VarHandle combinator Reviewed-by: psandoz, chegar --- .../java/lang/invoke/IndirectVarHandle.java | 25 +- .../classes/java/lang/invoke/Invokers.java | 6 + .../invoke/MemoryAccessVarHandleBase.java | 4 +- .../MemoryAccessVarHandleGenerator.java | 72 +- .../classes/java/lang/invoke/VarHandle.java | 115 ++- .../java/lang/invoke/VarHandleGuards.java | 942 ++++++++++++------ .../classes/java/lang/invoke/VarHandles.java | 81 +- .../lang/invoke/X-VarHandle.java.template | 114 ++- .../X-VarHandleByteArrayView.java.template | 52 +- .../invoke/VarHandles/VarHandleTestExact.java | 430 ++++++++ .../java/lang/invoke/VarHandleExact.java | 84 ++ .../jdk/incubator/foreign/VarHandleExact.java | 86 ++ 12 files changed, 1606 insertions(+), 405 deletions(-) create mode 100644 test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java create mode 100644 test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java create mode 100644 test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java diff --git a/src/java.base/share/classes/java/lang/invoke/IndirectVarHandle.java b/src/java.base/share/classes/java/lang/invoke/IndirectVarHandle.java index 85e17a640c8a3..2ef81c145196b 100644 --- a/src/java.base/share/classes/java/lang/invoke/IndirectVarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/IndirectVarHandle.java @@ -53,7 +53,12 @@ private final Class[] coordinates; IndirectVarHandle(VarHandle target, Class value, Class[] coordinates, BiFunction handleFactory) { - super(new VarForm(value, coordinates)); + this(target, value, coordinates, handleFactory, new VarForm(value, coordinates), false); + } + + private IndirectVarHandle(VarHandle target, Class value, Class[] coordinates, + BiFunction handleFactory, VarForm form, boolean exact) { + super(form, exact); this.handleFactory = handleFactory; this.target = target; this.directTarget = target.asDirect(); @@ -72,8 +77,8 @@ public List> coordinateTypes() { } @Override - MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType(directTarget.getClass(), value, coordinates); + MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType(null, value, coordinates); } @Override @@ -90,6 +95,20 @@ VarHandle target() { return target; } + @Override + public VarHandle withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new IndirectVarHandle(target, value, coordinates, handleFactory, vform, true); + } + + @Override + public VarHandle withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new IndirectVarHandle(target, value, coordinates, handleFactory, vform, false); + } + @Override @ForceInline MethodHandle getMethodHandle(int mode) { diff --git a/src/java.base/share/classes/java/lang/invoke/Invokers.java b/src/java.base/share/classes/java/lang/invoke/Invokers.java index 9e44de433374d..ee1f9baa3da94 100644 --- a/src/java.base/share/classes/java/lang/invoke/Invokers.java +++ b/src/java.base/share/classes/java/lang/invoke/Invokers.java @@ -27,6 +27,7 @@ import jdk.internal.vm.annotation.DontInline; import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Hidden; import jdk.internal.vm.annotation.Stable; import java.lang.reflect.Array; @@ -463,7 +464,12 @@ private static LambdaForm varHandleMethodInvokerHandleForm(VarHandle.AccessMode @ForceInline /*non-public*/ + @Hidden static MethodHandle checkVarHandleGenericType(VarHandle handle, VarHandle.AccessDescriptor ad) { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } // Test for exact match on invoker types // TODO match with erased types and add cast of return value to lambda form MethodHandle mh = handle.getMethodHandle(ad.mode); diff --git a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java index dee289fa1573a..0176cbf6d474d 100644 --- a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java +++ b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java @@ -42,8 +42,8 @@ abstract class MemoryAccessVarHandleBase extends VarHandle { /** alignment constraint (in bytes, expressed as a bit mask) **/ final long alignmentMask; - MemoryAccessVarHandleBase(VarForm form, boolean be, long length, long offset, long alignmentMask) { - super(form); + MemoryAccessVarHandleBase(VarForm form, boolean be, long length, long offset, long alignmentMask, boolean exact) { + super(form, exact); this.be = be; this.length = length; this.offset = offset; diff --git a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java index 6e66fc1ab4298..addd09f82b722 100644 --- a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java @@ -71,6 +71,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD; import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE; import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD; +import static jdk.internal.org.objectweb.asm.Opcodes.NEW; import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY; import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; @@ -95,6 +96,12 @@ class MemoryAccessVarHandleGenerator { private final static MethodHandle ADD_OFFSETS_HANDLE; private final static MethodHandle MUL_OFFSETS_HANDLE; + private final static MethodType CONSTR_TYPE = MethodType.methodType(void.class, VarForm.class, + boolean.class, long.class, long.class, long.class, boolean.class, long[].class); + // MemoryAccessVarHandleBase + private final static MethodType SUPER_CONTR_TYPE = MethodType.methodType(void.class, VarForm.class, + boolean.class, long.class, long.class, long.class, boolean.class); + static { helperClassCache = new HashMap<>(); helperClassCache.put(byte.class, MemoryAccessVarHandleByteHelper.class); @@ -140,7 +147,7 @@ class MemoryAccessVarHandleGenerator { Arrays.fill(components, long.class); this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components); this.helperClass = helperClassCache.get(carrier); - this.implClassName = helperClass.getName().replace('.', '/') + dimensions; + this.implClassName = internalName(helperClass) + dimensions; // live constants Class[] intermediate = new Class[dimensions]; Arrays.fill(intermediate, long.class); @@ -164,8 +171,7 @@ MethodHandle generateHandleFactory() { VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components); - MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class); - MethodHandle constr = lookup.findConstructor(implCls, constrType); + MethodHandle constr = lookup.findConstructor(implCls, CONSTR_TYPE); constr = MethodHandles.insertArguments(constr, 0, form); return constr; } catch (Throwable ex) { @@ -202,6 +208,9 @@ byte[] generateClassBytes() { addCarrierAccessor(cw); + addAsExact(cw); + addAsGeneric(cw); + for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) { addAccessModeMethodIfNeeded(mode, cw); } @@ -253,23 +262,23 @@ void addStaticInitializer(ClassWriter cw) { } void addConstructor(ClassWriter cw) { - MethodType constrType = MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class, long[].class); - MethodVisitor mv = cw.visitMethod(0, "", constrType.toMethodDescriptorString(), null, null); + MethodVisitor mv = cw.visitMethod(0, "", CONSTR_TYPE.toMethodDescriptorString(), null, null); mv.visitCode(); //super call mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 1); // vform mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class)); - mv.visitVarInsn(ILOAD, 2); - mv.visitVarInsn(LLOAD, 3); - mv.visitVarInsn(LLOAD, 5); - mv.visitVarInsn(LLOAD, 7); + mv.visitVarInsn(ILOAD, 2); // be + mv.visitVarInsn(LLOAD, 3); // length + mv.visitVarInsn(LLOAD, 5); // offset + mv.visitVarInsn(LLOAD, 7); // alignmentMask + mv.visitVarInsn(ILOAD, 9); // exact mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "", - MethodType.methodType(void.class, VarForm.class, boolean.class, long.class, long.class, long.class).toMethodDescriptorString(), false); + SUPER_CONTR_TYPE.toMethodDescriptorString(), false); //init dimensions for (int i = 0 ; i < dimensions ; i++) { mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 9); + mv.visitVarInsn(ALOAD, 10); mv.visitLdcInsn(i); mv.visitInsn(LALOAD); mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J"); @@ -280,11 +289,10 @@ void addConstructor(ClassWriter cw) { } void addAccessModeTypeMethod(ClassWriter cw) { - MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessMode.class); + MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessType.class); MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(GETFIELD, Type.getInternalName(VarHandle.AccessMode.class), "at", VarHandle.AccessType.class.descriptorString()); mv.visitLdcInsn(Type.getType(MemoryAddressProxy.class)); mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class)); mv.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString()); @@ -409,6 +417,38 @@ void addCarrierAccessor(ClassWriter cw) { mv.visitEnd(); } + private void addAsExact(ClassWriter cw) { + addAsExactOrAsGeneric(cw, "asExact", true); + } + + private void addAsGeneric(ClassWriter cw) { + addAsExactOrAsGeneric(cw, "asGeneric", false); + } + + private void addAsExactOrAsGeneric(ClassWriter cw, String name, boolean exact) { + MethodVisitor mv = cw.visitMethod(ACC_FINAL, name, "()Ljava/lang/invoke/VarHandle;", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, implClassName); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, internalName(VarHandle.class), "vform", VarForm.class.descriptorString()); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "be", boolean.class.descriptorString()); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "length", long.class.descriptorString()); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "offset", long.class.descriptorString()); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "alignmentMask", long.class.descriptorString()); + mv.visitIntInsn(BIPUSH, exact ? 1 : 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, implClassName, "strides", "()[J", false); + mv.visitMethodInsn(INVOKESPECIAL, implClassName, "", CONSTR_TYPE.descriptorString(), false); + mv.visitInsn(ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + // shared code generation helpers private static int getSlotsForType(Class c) { @@ -418,6 +458,10 @@ private static int getSlotsForType(Class c) { return 1; } + private static String internalName(Class cls) { + return cls.getName().replace('.', '/'); + } + /** * Emits an actual return instruction conforming to the given return type. */ diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandle.java b/src/java.base/share/classes/java/lang/invoke/VarHandle.java index 4f7d5a54db0e2..698eb8374a132 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandle.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandle.java @@ -282,8 +282,8 @@ * match fails, it means that the access mode method which the caller is * invoking is not present on the individual VarHandle being invoked. * - *

    - * Invocation of an access mode method behaves as if an invocation of + *

    + * Invocation of an access mode method behaves, by default, as if an invocation of * {@link MethodHandle#invoke}, where the receiving method handle accepts the * VarHandle instance as the leading argument. More specifically, the * following, where {@code {access-mode}} corresponds to the access mode method @@ -318,7 +318,7 @@ * widen primitive values, as if by {@link MethodHandle#asType asType} (see also * {@link MethodHandles#varHandleInvoker}). * - * More concisely, such behaviour is equivalent to: + * More concisely, such behavior is equivalent to: *

     {@code
      * VarHandle vh = ..
      * VarHandle.AccessMode am = VarHandle.AccessMode.valueFromMethodName("{access-mode}");
    @@ -328,6 +328,37 @@
      * }
    * Where, in this case, the method handle is bound to the VarHandle instance. * + *

    + * A VarHandle's invocation behavior can be adjusted (see {@link #withInvokeExactBehavior}) such that invocation of + * an access mode method behaves as if invocation of {@link MethodHandle#invokeExact}, + * where the receiving method handle accepts the VarHandle instance as the leading argument. + * More specifically, the following, where {@code {access-mode}} corresponds to the access mode method + * name: + *

     {@code
    + * VarHandle vh = ..
    + * R r = (R) vh.{access-mode}(p1, p2, ..., pN);
    + * }
    + * behaves as if: + *
     {@code
    + * VarHandle vh = ..
    + * VarHandle.AccessMode am = VarHandle.AccessMode.valueFromMethodName("{access-mode}");
    + * MethodHandle mh = MethodHandles.varHandleExactInvoker(
    + *                       am,
    + *                       vh.accessModeType(am));
    + *
    + * R r = (R) mh.invokeExact(vh, p1, p2, ..., pN)
    + * }
    + * (modulo access mode methods do not declare throwing of {@code Throwable}). + * + * More concisely, such behavior is equivalent to: + *
     {@code
    + * VarHandle vh = ..
    + * VarHandle.AccessMode am = VarHandle.AccessMode.valueFromMethodName("{access-mode}");
    + * MethodHandle mh = vh.toMethodHandle(am);
    + *
    + * R r = (R) mh.invokeExact(p1, p2, ..., pN)
    + * }
    + * Where, in this case, the method handle is bound to the VarHandle instance. * *

    Invocation checking

    * In typical programs, VarHandle access mode type matching will usually @@ -425,7 +456,7 @@ * {@link java.lang.invoke.MethodHandles#varHandleInvoker}. The * {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual} * API is also able to return a method handle to call an access mode method for - * any specified access mode type and is equivalent in behaviour to + * any specified access mode type and is equivalent in behavior to * {@link java.lang.invoke.MethodHandles#varHandleInvoker}. * *

    Interoperation between VarHandles and Java generics

    @@ -446,9 +477,15 @@ */ public abstract class VarHandle implements Constable { final VarForm vform; + final boolean exact; VarHandle(VarForm vform) { + this(vform, false); + } + + VarHandle(VarForm vform, boolean exact) { this.vform = vform; + this.exact = exact; } RuntimeException unsupported() { @@ -465,6 +502,18 @@ VarHandle asDirect() { VarHandle target() { return null; } + /** + * Returns {@code true} if this VarHandle has invoke-exact behavior. + * + * @see #withInvokeExactBehavior() + * @see #withInvokeBehavior() + * @return {@code true} if this VarHandle has invoke-exact behavior. + * @since 16 + */ + public boolean hasInvokeExactBehavior() { + return exact; + } + // Plain accessors /** @@ -1541,6 +1590,44 @@ VarHandle asDirect() { @IntrinsicCandidate Object getAndBitwiseXorRelease(Object... args); + /** + * Returns a VarHandle, with access to the same variable(s) as this VarHandle, but whose + * invocation behavior of access mode methods is adjusted to + * invoke-exact behavior. + *

    + * If this VarHandle already has invoke-exact behavior this VarHandle is returned. + *

    + * Invoking {@link #hasInvokeExactBehavior()} on the returned var handle + * is guaranteed to return {@code true}. + * + * @apiNote + * Invoke-exact behavior guarantees that upon invocation of an access mode method + * the types and arity of the arguments must match the {@link #accessModeType(AccessMode) access mode type}, + * otherwise a {@link WrongMethodTypeException} is thrown. + * + * @see #withInvokeBehavior() + * @see #hasInvokeExactBehavior() + * @return a VarHandle with invoke-exact behavior + * @since 16 + */ + public abstract VarHandle withInvokeExactBehavior(); + + /** + * Returns a VarHandle, with access to the same variable(s) as this VarHandle, but whose + * invocation behavior of access mode methods is adjusted to + * invoke behavior. + *

    + * If this VarHandle already has invoke behavior this VarHandle is returned. + *

    + * Invoking {@link #hasInvokeExactBehavior()} on the returned var handle + * is guaranteed to return {@code false}. + * + * @see #withInvokeExactBehavior() + * @see #hasInvokeExactBehavior() + * @return a VarHandle with invoke behavior + * @since 16 + */ + public abstract VarHandle withInvokeBehavior(); enum AccessType { GET(Object.class), @@ -1859,6 +1946,7 @@ static MemberName getMemberName(int ordinal, VarForm vform) { } static final class AccessDescriptor { + final MethodType symbolicMethodTypeExact; final MethodType symbolicMethodTypeErased; final MethodType symbolicMethodTypeInvoker; final Class returnType; @@ -1866,6 +1954,7 @@ static final class AccessDescriptor { final int mode; public AccessDescriptor(MethodType symbolicMethodType, int type, int mode) { + this.symbolicMethodTypeExact = symbolicMethodType; this.symbolicMethodTypeErased = symbolicMethodType.erase(); this.symbolicMethodTypeInvoker = symbolicMethodType.insertParameterTypes(0, VarHandle.class); this.returnType = symbolicMethodType.returnType(); @@ -1922,15 +2011,25 @@ public List> coordinateTypes() { * @return the access mode type for the given access mode */ public final MethodType accessModeType(AccessMode accessMode) { + return accessModeType(accessMode.at.ordinal()); + } + + @ForceInline + final MethodType accessModeType(int accessTypeOrdinal) { TypesAndInvokers tis = getTypesAndInvokers(); - MethodType mt = tis.methodType_table[accessMode.at.ordinal()]; + MethodType mt = tis.methodType_table[accessTypeOrdinal]; if (mt == null) { - mt = tis.methodType_table[accessMode.at.ordinal()] = - accessModeTypeUncached(accessMode); + mt = tis.methodType_table[accessTypeOrdinal] = + accessModeTypeUncached(accessTypeOrdinal); } return mt; } - abstract MethodType accessModeTypeUncached(AccessMode accessMode); + + final MethodType accessModeTypeUncached(int accessTypeOrdinal) { + return accessModeTypeUncached(AccessType.values()[accessTypeOrdinal]); + } + + abstract MethodType accessModeTypeUncached(AccessType accessMode); /** * Returns {@code true} if the given access mode is supported, otherwise diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java b/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java index 9df0fe1a54074..36bf67f0559fb 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandleGuards.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,18 +25,23 @@ package java.lang.invoke; import jdk.internal.vm.annotation.ForceInline; +import jdk.internal.vm.annotation.Hidden; // This class is auto-generated by java.lang.invoke.VarHandles$GuardMethodGenerator. Do not edit. final class VarHandleGuards { @ForceInline @LambdaForm.Compiled + @Hidden final static Object guard_L_L(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { Object r = MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); return ad.returnType.cast(r); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -44,14 +49,17 @@ final static Object guard_L_L(VarHandle handle, Object arg0, VarHandle.AccessDes @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LL_V(VarHandle handle, Object arg0, Object arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -59,49 +67,65 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static Object guard_LL_L(VarHandle handle, Object arg0, Object arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - Object r = MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - return ad.returnType.cast(r); + @Hidden + final static boolean guard_LLL_Z(VarHandle handle, Object arg0, Object arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LLL_Z(VarHandle handle, Object arg0, Object arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static Object guard_LLL_L(VarHandle handle, Object arg0, Object arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + return ad.returnType.cast(r); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static Object guard_LLL_L(VarHandle handle, Object arg0, Object arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { + @Hidden + final static Object guard_LL_L(VarHandle handle, Object arg0, Object arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + Object r = MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); return ad.returnType.cast(r); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled + @Hidden final static int guard_L_I(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (int) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -109,14 +133,17 @@ final static int guard_L_I(VarHandle handle, Object arg0, VarHandle.AccessDescri @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LI_V(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -124,47 +151,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static int guard_LI_I(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LII_Z(VarHandle handle, Object arg0, int arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LII_Z(VarHandle handle, Object arg0, int arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_LII_I(VarHandle handle, Object arg0, int arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static int guard_LII_I(VarHandle handle, Object arg0, int arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_LI_I(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled + @Hidden final static long guard_L_J(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (long) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -172,14 +215,17 @@ final static long guard_L_J(VarHandle handle, Object arg0, VarHandle.AccessDescr @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LJ_V(VarHandle handle, Object arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -187,47 +233,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static long guard_LJ_J(VarHandle handle, Object arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LJJ_Z(VarHandle handle, Object arg0, long arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LJJ_Z(VarHandle handle, Object arg0, long arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_LJJ_J(VarHandle handle, Object arg0, long arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static long guard_LJJ_J(VarHandle handle, Object arg0, long arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_LJ_J(VarHandle handle, Object arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled + @Hidden final static float guard_L_F(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (float) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -235,14 +297,17 @@ final static float guard_L_F(VarHandle handle, Object arg0, VarHandle.AccessDesc @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LF_V(VarHandle handle, Object arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -250,47 +315,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static float guard_LF_F(VarHandle handle, Object arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LFF_Z(VarHandle handle, Object arg0, float arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LFF_Z(VarHandle handle, Object arg0, float arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_LFF_F(VarHandle handle, Object arg0, float arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static float guard_LFF_F(VarHandle handle, Object arg0, float arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_LF_F(VarHandle handle, Object arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled + @Hidden final static double guard_L_D(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (double) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -298,14 +379,17 @@ final static double guard_L_D(VarHandle handle, Object arg0, VarHandle.AccessDes @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LD_V(VarHandle handle, Object arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -313,48 +397,64 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static double guard_LD_D(VarHandle handle, Object arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LDD_Z(VarHandle handle, Object arg0, double arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LDD_Z(VarHandle handle, Object arg0, double arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_LDD_D(VarHandle handle, Object arg0, double arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled - final static double guard_LDD_D(VarHandle handle, Object arg0, double arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_LD_D(VarHandle handle, Object arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled + @Hidden final static Object guard__L(VarHandle handle, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { Object r = MethodHandle.linkToStatic(handle, handle.vform.getMemberName(ad.mode)); return ad.returnType.cast(r); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect()); } @@ -362,14 +462,17 @@ final static Object guard__L(VarHandle handle, VarHandle.AccessDescriptor ad) th @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_L_V(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -377,11 +480,15 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled + @Hidden final static boolean guard_LL_Z(VarHandle handle, Object arg0, Object arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -389,11 +496,15 @@ final static boolean guard_LL_Z(VarHandle handle, Object arg0, Object arg1, VarH @ForceInline @LambdaForm.Compiled + @Hidden final static int guard__I(VarHandle handle, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (int) MethodHandle.linkToStatic(handle, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect()); } @@ -401,14 +512,17 @@ final static int guard__I(VarHandle handle, VarHandle.AccessDescriptor ad) throw @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_I_V(VarHandle handle, int arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -416,47 +530,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static int guard_I_I(VarHandle handle, int arg0, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_II_Z(VarHandle handle, int arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_II_Z(VarHandle handle, int arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_II_I(VarHandle handle, int arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static int guard_II_I(VarHandle handle, int arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_I_I(VarHandle handle, int arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } } @ForceInline @LambdaForm.Compiled + @Hidden final static long guard__J(VarHandle handle, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (long) MethodHandle.linkToStatic(handle, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect()); } @@ -464,14 +594,17 @@ final static long guard__J(VarHandle handle, VarHandle.AccessDescriptor ad) thro @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_J_V(VarHandle handle, long arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -479,47 +612,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static long guard_J_J(VarHandle handle, long arg0, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_JJ_Z(VarHandle handle, long arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_JJ_Z(VarHandle handle, long arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_JJ_J(VarHandle handle, long arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static long guard_JJ_J(VarHandle handle, long arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_J_J(VarHandle handle, long arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } } @ForceInline @LambdaForm.Compiled + @Hidden final static float guard__F(VarHandle handle, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (float) MethodHandle.linkToStatic(handle, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect()); } @@ -527,14 +676,17 @@ final static float guard__F(VarHandle handle, VarHandle.AccessDescriptor ad) thr @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_F_V(VarHandle handle, float arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -542,47 +694,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static float guard_F_F(VarHandle handle, float arg0, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_FF_Z(VarHandle handle, float arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_FF_Z(VarHandle handle, float arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_FF_F(VarHandle handle, float arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static float guard_FF_F(VarHandle handle, float arg0, float arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_F_F(VarHandle handle, float arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } } @ForceInline @LambdaForm.Compiled + @Hidden final static double guard__D(VarHandle handle, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (double) MethodHandle.linkToStatic(handle, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect()); } @@ -590,14 +758,17 @@ final static double guard__D(VarHandle handle, VarHandle.AccessDescriptor ad) th @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_D_V(VarHandle handle, double arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } @@ -605,48 +776,64 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static double guard_D_D(VarHandle handle, double arg0, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_DD_Z(VarHandle handle, double arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_DD_Z(VarHandle handle, double arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_DD_D(VarHandle handle, double arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } } @ForceInline @LambdaForm.Compiled - final static double guard_DD_D(VarHandle handle, double arg0, double arg1, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_D_D(VarHandle handle, double arg0, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0); } } @ForceInline @LambdaForm.Compiled + @Hidden final static Object guard_LI_L(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { Object r = MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); return ad.returnType.cast(r); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -654,14 +841,17 @@ final static Object guard_LI_L(VarHandle handle, Object arg0, int arg1, VarHandl @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LIL_V(VarHandle handle, Object arg0, int arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -669,52 +859,67 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static Object guard_LIL_L(VarHandle handle, Object arg0, int arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - return ad.returnType.cast(r); + @Hidden + final static boolean guard_LILL_Z(VarHandle handle, Object arg0, int arg1, Object arg2, Object arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LILL_Z(VarHandle handle, Object arg0, int arg1, Object arg2, Object arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static Object guard_LILL_L(VarHandle handle, Object arg0, int arg1, Object arg2, Object arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + return ad.returnType.cast(r); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static Object guard_LILL_L(VarHandle handle, Object arg0, int arg1, Object arg2, Object arg3, VarHandle.AccessDescriptor ad) throws Throwable { + @Hidden + final static Object guard_LIL_L(VarHandle handle, Object arg0, int arg1, Object arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + Object r = MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); return ad.returnType.cast(r); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LII_V(VarHandle handle, Object arg0, int arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -722,11 +927,15 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled + @Hidden final static boolean guard_LIII_Z(VarHandle handle, Object arg0, int arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } @@ -734,11 +943,15 @@ final static boolean guard_LIII_Z(VarHandle handle, Object arg0, int arg1, int a @ForceInline @LambdaForm.Compiled + @Hidden final static int guard_LIII_I(VarHandle handle, Object arg0, int arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } @@ -746,11 +959,15 @@ final static int guard_LIII_I(VarHandle handle, Object arg0, int arg1, int arg2, @ForceInline @LambdaForm.Compiled + @Hidden final static long guard_LI_J(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (long) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -758,14 +975,17 @@ final static long guard_LI_J(VarHandle handle, Object arg0, int arg1, VarHandle. @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LIJ_V(VarHandle handle, Object arg0, int arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -773,47 +993,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static long guard_LIJ_J(VarHandle handle, Object arg0, int arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LIJJ_Z(VarHandle handle, Object arg0, int arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LIJJ_Z(VarHandle handle, Object arg0, int arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_LIJJ_J(VarHandle handle, Object arg0, int arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static long guard_LIJJ_J(VarHandle handle, Object arg0, int arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static long guard_LIJ_J(VarHandle handle, Object arg0, int arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled + @Hidden final static float guard_LI_F(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (float) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -821,14 +1057,17 @@ final static float guard_LI_F(VarHandle handle, Object arg0, int arg1, VarHandle @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LIF_V(VarHandle handle, Object arg0, int arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -836,47 +1075,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static float guard_LIF_F(VarHandle handle, Object arg0, int arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LIFF_Z(VarHandle handle, Object arg0, int arg1, float arg2, float arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LIFF_Z(VarHandle handle, Object arg0, int arg1, float arg2, float arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_LIFF_F(VarHandle handle, Object arg0, int arg1, float arg2, float arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static float guard_LIFF_F(VarHandle handle, Object arg0, int arg1, float arg2, float arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static float guard_LIF_F(VarHandle handle, Object arg0, int arg1, float arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (float) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (float) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled + @Hidden final static double guard_LI_D(VarHandle handle, Object arg0, int arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (double) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -884,14 +1139,17 @@ final static double guard_LI_D(VarHandle handle, Object arg0, int arg1, VarHandl @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LID_V(VarHandle handle, Object arg0, int arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -899,47 +1157,63 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static double guard_LID_D(VarHandle handle, Object arg0, int arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LIDD_Z(VarHandle handle, Object arg0, int arg1, double arg2, double arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LIDD_Z(VarHandle handle, Object arg0, int arg1, double arg2, double arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_LIDD_D(VarHandle handle, Object arg0, int arg1, double arg2, double arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static double guard_LIDD_D(VarHandle handle, Object arg0, int arg1, double arg2, double arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static double guard_LID_D(VarHandle handle, Object arg0, int arg1, double arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (double) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (double) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled + @Hidden final static int guard_LJ_I(VarHandle handle, Object arg0, long arg1, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (int) MethodHandle.linkToStatic(handle, arg0, arg1, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1); } @@ -947,14 +1221,17 @@ final static int guard_LJ_I(VarHandle handle, Object arg0, long arg1, VarHandle. @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LJI_V(VarHandle handle, Object arg0, long arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -962,50 +1239,65 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled - final static int guard_LJI_I(VarHandle handle, Object arg0, long arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + @Hidden + final static boolean guard_LJII_Z(VarHandle handle, Object arg0, long arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); + return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static boolean guard_LJII_Z(VarHandle handle, Object arg0, long arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_LJII_I(VarHandle handle, Object arg0, long arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } } @ForceInline @LambdaForm.Compiled - final static int guard_LJII_I(VarHandle handle, Object arg0, long arg1, int arg2, int arg3, VarHandle.AccessDescriptor ad) throws Throwable { - if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { - return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); + @Hidden + final static int guard_LJI_I(VarHandle handle, Object arg0, long arg1, int arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); } - else { + if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { + return (int) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); - return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); + return (int) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } } @ForceInline @LambdaForm.Compiled + @Hidden final static void guard_LJJ_V(VarHandle handle, Object arg0, long arg1, long arg2, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { + } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { MethodHandle.linkToStatic(handle, arg0, arg1, arg2, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2); } @@ -1013,11 +1305,15 @@ else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbol @ForceInline @LambdaForm.Compiled + @Hidden final static boolean guard_LJJJ_Z(VarHandle handle, Object arg0, long arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (boolean) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (boolean) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } @@ -1025,11 +1321,15 @@ final static boolean guard_LJJJ_Z(VarHandle handle, Object arg0, long arg1, long @ForceInline @LambdaForm.Compiled + @Hidden final static long guard_LJJJ_J(VarHandle handle, Object arg0, long arg1, long arg2, long arg3, VarHandle.AccessDescriptor ad) throws Throwable { + if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { + throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " + + ad.symbolicMethodTypeExact); + } if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { return (long) MethodHandle.linkToStatic(handle, arg0, arg1, arg2, arg3, handle.vform.getMemberName(ad.mode)); - } - else { + } else { MethodHandle mh = handle.getMethodHandle(ad.mode); return (long) mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(handle.asDirect(), arg0, arg1, arg2, arg3); } diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index 60d90104a982a..b06fedac185e4 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -31,8 +31,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -43,6 +45,8 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE; import static java.lang.invoke.MethodHandleStatics.VAR_HANDLE_IDENTITY_ADAPT; import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Collectors.toList; final class VarHandles { @@ -331,7 +335,8 @@ static VarHandle makeMemoryAddressViewHandle(Class carrier, long alignmentMas .generateHandleFactory()); try { - return maybeAdapt((VarHandle)fac.invoke(be, size, offset, alignmentMask, strides)); + boolean exact = false; + return maybeAdapt((VarHandle)fac.invoke(be, size, offset, alignmentMask, exact, strides)); } catch (Throwable ex) { throw new IllegalStateException(ex); } @@ -341,7 +346,7 @@ private static VarHandle maybeAdapt(VarHandle target) { if (!VAR_HANDLE_IDENTITY_ADAPT) return target; target = filterValue(target, MethodHandles.identity(target.varType()), MethodHandles.identity(target.varType())); - MethodType mtype = target.accessModeType(VarHandle.AccessMode.GET).dropParameterTypes(0, 1); + MethodType mtype = target.accessModeType(VarHandle.AccessMode.GET); for (int i = 0 ; i < mtype.parameterCount() ; i++) { target = filterCoordinates(target, i, MethodHandles.identity(mtype.parameterType(i))); } @@ -671,33 +676,42 @@ private static boolean isCheckedException(Class clazz) { // static final String GUARD_METHOD_SIG_TEMPLATE = " _()"; // // static final String GUARD_METHOD_TEMPLATE = -// "@ForceInline\n" + -// "@LambdaForm.Compiled\n" + -// "final static throws Throwable {\n" + -// " if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" + -// " MethodHandle.linkToStatic();\n" + -// " }\n" + -// " else {\n" + -// " MethodHandle mh = handle.getMethodHandle(ad.mode);\n" + -// " mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic();\n" + -// " }\n" + -// "}"; +// """ +// @ForceInline +// @LambdaForm.Compiled +// @Hidden +// final static throws Throwable { +// if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { +// throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " +// + ad.symbolicMethodTypeExact); +// } +// if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { +// MethodHandle.linkToStatic(); +// } else { +// MethodHandle mh = handle.getMethodHandle(ad.mode); +// mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(); +// } +// }"""; // // static final String GUARD_METHOD_TEMPLATE_V = -// "@ForceInline\n" + -// "@LambdaForm.Compiled\n" + -// "final static throws Throwable {\n" + -// " if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodType) {\n" + -// " MethodHandle.linkToStatic();\n" + -// " }\n" + -// " else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodType) {\n" + -// " MethodHandle.linkToStatic();\n" + -// " }\n" + -// " else {\n" + -// " MethodHandle mh = handle.getMethodHandle(ad.mode);\n" + -// " mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic();\n" + -// " }\n" + -// "}"; +// """ +// @ForceInline +// @LambdaForm.Compiled +// @Hidden +// final static throws Throwable { +// if (handle.hasInvokeExactBehavior() && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) { +// throw new WrongMethodTypeException("expected " + handle.accessModeType(ad.type) + " but found " +// + ad.symbolicMethodTypeExact); +// } +// if (handle.isDirect() && handle.vform.methodType_table[ad.type] == ad.symbolicMethodTypeErased) { +// MethodHandle.linkToStatic(); +// } else if (handle.isDirect() && handle.vform.getMethodType_V(ad.type) == ad.symbolicMethodTypeErased) { +// MethodHandle.linkToStatic(); +// } else { +// MethodHandle mh = handle.getMethodHandle(ad.mode); +// mh.asType(ad.symbolicMethodTypeInvoker).invokeBasic(); +// } +// }"""; // // // A template for deriving the operations // // could be supported by annotating VarHandle directly with the @@ -733,6 +747,7 @@ private static boolean isCheckedException(Class clazz) { // System.out.println("package java.lang.invoke;"); // System.out.println(); // System.out.println("import jdk.internal.vm.annotation.ForceInline;"); +// System.out.println("import jdk.internal.vm.annotation.Hidden;"); // System.out.println(); // System.out.println("// This class is auto-generated by " + // GuardMethodGenerator.class.getName() + @@ -785,11 +800,8 @@ private static boolean isCheckedException(Class clazz) { // hts.flatMap(ht -> Stream.of(VarHandleTemplate.class.getMethods()). // map(m -> generateMethodType(m, ht.receiver, ht.value, ht.intermediates))). // distinct(). -// map(mt -> generateMethod(mt)). -// forEach(s -> { -// System.out.println(s); -// System.out.println(); -// }); +// map(GuardMethodGenerator::generateMethod). +// forEach(System.out::println); // // System.out.println("}"); // } @@ -845,6 +857,7 @@ private static boolean isCheckedException(Class clazz) { // // List LINK_TO_INVOKER_ARGS = params.keySet().stream(). // collect(toList()); +// LINK_TO_INVOKER_ARGS.set(0, LINK_TO_INVOKER_ARGS.get(0) + ".asDirect()"); // // RETURN = returnType == void.class // ? "" @@ -860,7 +873,7 @@ private static boolean isCheckedException(Class clazz) { // // String RETURN_ERASED = returnType != Object.class // ? "" -// : " return ad.returnType.cast(r);"; +// : "\n return ad.returnType.cast(r);"; // // String template = returnType == void.class // ? GUARD_METHOD_TEMPLATE_V @@ -877,7 +890,7 @@ private static boolean isCheckedException(Class clazz) { // collect(joining(", "))). // replace("", LINK_TO_INVOKER_ARGS.stream(). // collect(joining(", "))) -// ; +// .indent(4); // } // // static String className(Class c) { diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template index bad2c675b5c57..e24f5af15ebc8 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandle.java.template @@ -45,12 +45,12 @@ final class VarHandle$Type$s { #end[Object] FieldInstanceReadOnly(Class receiverType, long fieldOffset{#if[Object]?, Class fieldType}) { - this(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadOnly.FORM); + this(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadOnly.FORM, false); } protected FieldInstanceReadOnly(Class receiverType, long fieldOffset{#if[Object]?, Class fieldType}, - VarForm form) { - super(form); + VarForm form, boolean exact) { + super(form, exact); this.fieldOffset = fieldOffset; this.receiverType = receiverType; #if[Object] @@ -59,8 +59,22 @@ final class VarHandle$Type$s { } @Override - final MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType(receiverType, {#if[Object]?fieldType:$type$.class}); + public FieldInstanceReadOnly withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType}, vform, true); + } + + @Override + public FieldInstanceReadOnly withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new FieldInstanceReadOnly(receiverType, fieldOffset{#if[Object]?, fieldType}, vform, false); + } + + @Override + final MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType(receiverType, {#if[Object]?fieldType:$type$.class}); } @Override @@ -110,7 +124,26 @@ final class VarHandle$Type$s { static final class FieldInstanceReadWrite extends FieldInstanceReadOnly { FieldInstanceReadWrite(Class receiverType, long fieldOffset{#if[Object]?, Class fieldType}) { - super(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadWrite.FORM); + this(receiverType, fieldOffset{#if[Object]?, fieldType}, false); + } + + private FieldInstanceReadWrite(Class receiverType, long fieldOffset{#if[Object]?, Class fieldType}, + boolean exact) { + super(receiverType, fieldOffset{#if[Object]?, fieldType}, FieldInstanceReadWrite.FORM, exact); + } + + @Override + public FieldInstanceReadWrite withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType}, true); + } + + @Override + public FieldInstanceReadWrite withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new FieldInstanceReadWrite(receiverType, fieldOffset{#if[Object]?, fieldType}, false); } @ForceInline @@ -356,12 +389,12 @@ final class VarHandle$Type$s { #end[Object] FieldStaticReadOnly(Object base, long fieldOffset{#if[Object]?, Class fieldType}) { - this(base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadOnly.FORM); + this(base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadOnly.FORM, false); } protected FieldStaticReadOnly(Object base, long fieldOffset{#if[Object]?, Class fieldType}, - VarForm form) { - super(form); + VarForm form, boolean exact) { + super(form, exact); this.base = base; this.fieldOffset = fieldOffset; #if[Object] @@ -369,6 +402,20 @@ final class VarHandle$Type$s { #end[Object] } + @Override + public FieldStaticReadOnly withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new FieldStaticReadOnly(base, fieldOffset{#if[Object]?, fieldType}, vform, true); + } + + @Override + public FieldStaticReadOnly withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new FieldStaticReadOnly(base, fieldOffset{#if[Object]?, fieldType}, vform, false); + } + @Override public Optional describeConstable() { var fieldTypeRef = {#if[Object]?fieldType:$type$.class}.describeConstable(); @@ -385,8 +432,8 @@ final class VarHandle$Type$s { } @Override - final MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType(null, {#if[Object]?fieldType:$type$.class}); + final MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType(null, {#if[Object]?fieldType:$type$.class}); } @ForceInline @@ -423,7 +470,26 @@ final class VarHandle$Type$s { static final class FieldStaticReadWrite extends FieldStaticReadOnly { FieldStaticReadWrite(Object base, long fieldOffset{#if[Object]?, Class fieldType}) { - super(base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadWrite.FORM); + this(base, fieldOffset{#if[Object]?, fieldType}, false); + } + + private FieldStaticReadWrite(Object base, long fieldOffset{#if[Object]?, Class fieldType}, + boolean exact) { + super(base, fieldOffset{#if[Object]?, fieldType}, FieldStaticReadWrite.FORM, exact); + } + + @Override + public FieldStaticReadWrite withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new FieldStaticReadWrite(base, fieldOffset{#if[Object]?, fieldType}, true); + } + + @Override + public FieldStaticReadWrite withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new FieldStaticReadWrite(base, fieldOffset{#if[Object]?, fieldType}, false); } @ForceInline @@ -670,7 +736,11 @@ final class VarHandle$Type$s { #end[Object] Array(int abase, int ashift{#if[Object]?, Class arrayType}) { - super(Array.FORM); + this(abase, ashift{#if[Object]?, arrayType}, false); + } + + private Array(int abase, int ashift{#if[Object]?, Class arrayType}, boolean exact) { + super(Array.FORM, exact); this.abase = abase; this.ashift = ashift; #if[Object] @@ -679,6 +749,20 @@ final class VarHandle$Type$s { #end[Object] } + @Override + public Array withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new Array(abase, ashift{#if[Object]?, arrayType}, true); + } + + @Override + public Array withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new Array(abase, ashift{#if[Object]?, arrayType}, false); + } + @Override public Optional describeConstable() { var arrayTypeRef = {#if[Object]?arrayType:$type$[].class}.describeConstable(); @@ -689,8 +773,8 @@ final class VarHandle$Type$s { } @Override - final MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?arrayType.getComponentType():$type$.class}, int.class); + final MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType({#if[Object]?arrayType:$type$[].class}, {#if[Object]?arrayType.getComponentType():$type$.class}, int.class); } #if[Object] diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template index 9ac3d25866eb5..cdbe6df68c4cd 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template @@ -68,8 +68,8 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { private static abstract class ByteArrayViewVarHandle extends VarHandle { final boolean be; - ByteArrayViewVarHandle(VarForm form, boolean be) { - super(form); + ByteArrayViewVarHandle(VarForm form, boolean be, boolean exact) { + super(form, exact); this.be = be; } } @@ -77,12 +77,30 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static final class ArrayHandle extends ByteArrayViewVarHandle { ArrayHandle(boolean be) { - super(ArrayHandle.FORM, be); + this(be, false); + } + + private ArrayHandle(boolean be, boolean exact) { + super(ArrayHandle.FORM, be, exact); + } + + @Override + public ArrayHandle withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new ArrayHandle(be, true); + } + + @Override + public ArrayHandle withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new ArrayHandle(be, false); } @Override - final MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType(byte[].class, $type$.class, int.class); + final MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType(byte[].class, $type$.class, int.class); } @ForceInline @@ -555,12 +573,30 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static final class ByteBufferHandle extends ByteArrayViewVarHandle { ByteBufferHandle(boolean be) { - super(ByteBufferHandle.FORM, be); + this(be, false); + } + + private ByteBufferHandle(boolean be, boolean exact) { + super(ByteBufferHandle.FORM, be, exact); + } + + @Override + public ByteBufferHandle withInvokeExactBehavior() { + return hasInvokeExactBehavior() + ? this + : new ByteBufferHandle(be, true); + } + + @Override + public ByteBufferHandle withInvokeBehavior() { + return !hasInvokeExactBehavior() + ? this + : new ByteBufferHandle(be, false); } @Override - final MethodType accessModeTypeUncached(AccessMode accessMode) { - return accessMode.at.accessModeType(ByteBuffer.class, $type$.class, int.class); + final MethodType accessModeTypeUncached(AccessType at) { + return at.accessModeType(ByteBuffer.class, $type$.class, int.class); } @ForceInline diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java new file mode 100644 index 0000000000000..f40885f8cbe34 --- /dev/null +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules jdk.incubator.foreign + * + * @run testng/othervm -Xverify:all VarHandleTestExact + * @run testng/othervm -Xverify:all -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=true -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestExact + * @run testng/othervm -Xverify:all -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=false VarHandleTestExact + * @run testng/othervm -Xverify:all -Djava.lang.invoke.VarHandle.VAR_HANDLE_GUARDS=false -Djava.lang.invoke.VarHandle.VAR_HANDLE_IDENTITY_ADAPT=true VarHandleTestExact + */ + +import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemorySegment; +import org.testng.SkipException; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.Array; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.*; + +public class VarHandleTestExact { + + private static class Widget { + static Object objectField_SRW; + static long longField_SRW; + static double doubleField_SRW; + static Long aLongField_SRW; + + Object objectField_RW; + long longField_RW; + double doubleField_RW; + Long aLongField_RW; + + final static Object objectField_SRO = new Object(); + final static long longField_SRO = 1234L; + final static double doubleField_SRO = 1234D; + final static Long aLongField_SRO = 1234L; + + final Object objectField_RO = new Object(); + final long longField_RO = 1234L; + final double doubleField_RO = 1234D; + final Long aLongField_RO = 1234L; + } + + @Test(dataProvider = "dataObjectAccess") + public void testExactSet(String fieldBaseName, Class fieldType, boolean ro, Object testValue, + SetX setter, GetX getter, + SetStaticX staticSetter, GetStaticX staticGetter) + throws NoSuchFieldException, IllegalAccessException { + if (ro) throw new SkipException("Can not test setter with read only field"); + VarHandle vh = MethodHandles.lookup().findVarHandle(Widget.class, fieldBaseName + "_RW", fieldType); + assertFalse(vh.hasInvokeExactBehavior()); + Widget w = new Widget(); + + try { + vh.set(w, testValue); + vh.withInvokeBehavior().set(w, testValue); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + setter.set(vh, w, testValue); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(),".*\\Qexpected (Widget," + fieldType.getSimpleName() + ")void \\E.*"); + } + } + + @Test(dataProvider = "dataObjectAccess") + public void testExactGet(String fieldBaseName, Class fieldType, boolean ro, Object testValue, + SetX setter, GetX getter, + SetStaticX staticSetter, GetStaticX staticGetter) + throws NoSuchFieldException, IllegalAccessException { + VarHandle vh = MethodHandles.lookup().findVarHandle(Widget.class, fieldBaseName + (ro ? "_RO" : "_RW"), fieldType); + assertFalse(vh.hasInvokeExactBehavior()); + Widget w = new Widget(); + + try { + Object o = vh.get(w); + Object o2 = vh.withInvokeBehavior().get(w); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + getter.get(vh, w); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(),".*\\Qexpected (Widget)" + fieldType.getSimpleName() + " \\E.*"); + } + } + + @Test(dataProvider = "dataObjectAccess") + public void testExactSetStatic(String fieldBaseName, Class fieldType, boolean ro, Object testValue, + SetX setter, GetX getter, + SetStaticX staticSetter, GetStaticX staticGetter) + throws NoSuchFieldException, IllegalAccessException { + if (ro) throw new SkipException("Can not test setter with read only field"); + VarHandle vh = MethodHandles.lookup().findStaticVarHandle(Widget.class, fieldBaseName + "_SRW", fieldType); + assertFalse(vh.hasInvokeExactBehavior()); + + try { + vh.set(testValue); + vh.withInvokeBehavior().set(testValue); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + staticSetter.set(vh, testValue); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(),".*\\Qexpected (" + fieldType.getSimpleName() + ")void \\E.*"); + } + } + + @Test(dataProvider = "dataObjectAccess") + public void testExactGetStatic(String fieldBaseName, Class fieldType, boolean ro, Object testValue, + SetX setter, GetX getter, + SetStaticX staticSetter, GetStaticX staticGetter) + throws NoSuchFieldException, IllegalAccessException { + VarHandle vh = MethodHandles.lookup().findStaticVarHandle(Widget.class, fieldBaseName + (ro ? "_SRO" : "_SRW"), fieldType); + assertFalse(vh.hasInvokeExactBehavior()); + + try { + Object o = vh.get(); + Object o2 = vh.withInvokeBehavior().get(); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + staticGetter.get(vh); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(),".*\\Qexpected ()" + fieldType.getSimpleName() + " \\E.*"); + } + } + + @Test(dataProvider = "dataSetArray") + public void testExactArraySet(Class arrayClass, Object testValue, SetArrayX setter) { + VarHandle vh = MethodHandles.arrayElementVarHandle(arrayClass); + Object arr = Array.newInstance(arrayClass.componentType(), 1); + assertFalse(vh.hasInvokeExactBehavior()); + + try { + vh.set(arr, 0, testValue); + vh.withInvokeBehavior().set(arr, 0, testValue); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + setter.set(vh, arr, testValue); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(), + ".*\\Qexpected (" + arrayClass.getSimpleName() + ",int," + arrayClass.componentType().getSimpleName() + ")void \\E.*"); + } + } + + @Test(dataProvider = "dataSetBuffer") + public void testExactBufferSet(Class arrayClass, Object testValue, SetBufferX setter) { + VarHandle vh = MethodHandles.byteBufferViewVarHandle(arrayClass, ByteOrder.nativeOrder()); + assertFalse(vh.hasInvokeExactBehavior()); + ByteBuffer buff = ByteBuffer.allocateDirect(8); + + try { + vh.set(buff, 0, testValue); + vh.withInvokeBehavior().set(buff, 0, testValue); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + setter.set(vh, buff, testValue); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(), + ".*\\Qexpected (ByteBuffer,int," + arrayClass.componentType().getSimpleName() + ")void \\E.*"); + } + } + + @Test(dataProvider = "dataSetMemorySegment") + public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX setter) { + VarHandle vh = MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder()); + assertFalse(vh.hasInvokeExactBehavior()); + try (MemorySegment seg = MemorySegment.allocateNative(8)) { + MemoryAddress base = seg.baseAddress(); + try { + vh.set(base, testValue); + vh.withInvokeBehavior().set(base, testValue); + } catch (WrongMethodTypeException wmte) { + fail("Unexpected exception", wmte); + } + + vh = vh.withInvokeExactBehavior(); + assertTrue(vh.hasInvokeExactBehavior()); + try { + setter.set(vh, base, testValue); // should throw + fail("Exception expected"); + } catch (WrongMethodTypeException wmte) { + assertMatches(wmte.getMessage(), + ".*\\Qexpected (MemoryAddress," + carrier.getSimpleName() + ")void \\E.*"); + } + } + } + + private static void assertMatches(String str, String pattern) { + if (!str.matches(pattern)) { + throw new AssertionError("'" + str + "' did not match the pattern '" + pattern + "'."); + } + } + + private interface SetX { + void set(VarHandle vh, Widget w, Object testValue); + } + + private interface SetStaticX { + void set(VarHandle vh, Object testValue); + } + + private interface GetX { + void get(VarHandle vh, Widget w); + } + + private interface GetStaticX { + void get(VarHandle vh); + } + + private interface SetArrayX { + void set(VarHandle vh, Object array, Object testValue); + } + + private interface SetBufferX { + void set(VarHandle vh, ByteBuffer buff, Object testValue); + } + + private interface SetSegmentX { + void set(VarHandle vh, MemoryAddress addr, Object testValue); + } + + private static void consume(Object o) {} + + private static void testCaseObjectAccess(List cases, String fieldBaseName, Class fieldType, Object testValue, + SetX setter, GetX getter, + SetStaticX staticSetter, GetStaticX staticGetter) { + cases.add(new Object[] { fieldBaseName, fieldType, false, testValue, setter, getter, staticSetter, staticGetter }); + cases.add(new Object[] { fieldBaseName, fieldType, true, testValue, setter, getter, staticSetter, staticGetter }); + } + + private static void testCaseArraySet(List cases, Class arrayType, Object testValue, SetArrayX setter) { + cases.add(new Object[] { arrayType, testValue, setter }); + } + + private static void testCaseBufferSet(List cases, Class arrayType, Object testValue, SetBufferX setter) { + cases.add(new Object[] { arrayType, testValue, setter }); + } + + private static void testCaseSegmentSet(List cases, Class carrier, Object testValue, SetSegmentX setter) { + cases.add(new Object[] { carrier, testValue, setter }); + } + + @DataProvider + public static Object[][] dataObjectAccess() { + List cases = new ArrayList<>(); + + // create a bunch of different sig-poly call sites + testCaseObjectAccess(cases, "objectField", Object.class, "abcd", + (vh, w, tv) -> vh.set(w, (String) tv), + (vh, w) -> consume((String) vh.get(w)), + (vh, tv) -> vh.set((String) tv), + (vh) -> consume((String) vh.get())); + testCaseObjectAccess(cases, "objectField", Object.class, Integer.valueOf(1234), + (vh, w, tv) -> vh.set(w, (Integer) tv), + (vh, w) -> consume((Integer) vh.get(w)), + (vh, tv) -> vh.set((Integer) tv), + (vh) -> consume((Integer) vh.get())); + testCaseObjectAccess(cases, "longField", long.class, 1234, + (vh, w, tv) -> vh.set(w, (int) tv), + (vh, w) -> consume((int) vh.get(w)), + (vh, tv) -> vh.set((int) tv), + (vh) -> consume((int) vh.get())); + testCaseObjectAccess(cases, "longField", long.class, (short) 1234, + (vh, w, tv) -> vh.set(w, (short) tv), + (vh, w) -> consume((short) vh.get(w)), + (vh, tv) -> vh.set((short) tv), + (vh) -> consume((short) vh.get())); + testCaseObjectAccess(cases, "longField", long.class, (char) 1234, + (vh, w, tv) -> vh.set(w, (char) tv), + (vh, w) -> consume((char) vh.get(w)), + (vh, tv) -> vh.set((char) tv), + (vh) -> consume((char) vh.get())); + testCaseObjectAccess(cases, "longField", long.class, (byte) 1234, + (vh, w, tv) -> vh.set(w, (byte) tv), + (vh, w) -> consume((byte) vh.get(w)), + (vh, tv) -> vh.set((byte) tv), + (vh) -> consume((byte) vh.get())); + testCaseObjectAccess(cases, "longField", long.class, Long.valueOf(1234L), + (vh, w, tv) -> vh.set(w, (Long) tv), + (vh, w) -> consume((Long) vh.get(w)), + (vh, tv) -> vh.set((Long) tv), + (vh) -> consume((Long) vh.get())); + testCaseObjectAccess(cases, "doubleField", double.class, 1234F, + (vh, w, tv) -> vh.set(w, (float) tv), + (vh, w) -> consume((float) vh.get(w)), + (vh, tv) -> vh.set((float) tv), + (vh) -> consume((float) vh.get())); + testCaseObjectAccess(cases, "doubleField", double.class, 1234, + (vh, w, tv) -> vh.set(w, (int) tv), + (vh, w) -> consume((int) vh.get(w)), + (vh, tv) -> vh.set((int) tv), + (vh) -> consume((int) vh.get())); + testCaseObjectAccess(cases, "doubleField", double.class, 1234L, + (vh, w, tv) -> vh.set(w, (long) tv), + (vh, w) -> consume((long) vh.get(w)), + (vh, tv) -> vh.set((long) tv), + (vh) -> consume((long) vh.get())); + testCaseObjectAccess(cases, "doubleField", double.class, Double.valueOf(1234D), + (vh, w, tv) -> vh.set(w, (Double) tv), + (vh, w) -> consume((Double) vh.get(w)), + (vh, tv) -> vh.set((Double) tv), + (vh) -> consume((Double) vh.get())); + testCaseObjectAccess(cases, "aLongField", Long.class, 1234L, + (vh, w, tv) -> vh.set(w, (long) tv), + (vh, w) -> consume((long) vh.get(w)), + (vh, tv) -> vh.set((long) tv), + (vh) -> consume((long) vh.get())); + + return cases.toArray(Object[][]::new); + } + + @DataProvider + public static Object[][] dataSetArray() { + List cases = new ArrayList<>(); + + // create a bunch of different sig-poly call sites + testCaseArraySet(cases, Object[].class, "abcd", (vh, arr, tv) -> vh.set((Object[]) arr, 0, (String) tv)); + testCaseArraySet(cases, Object[].class, Integer.valueOf(1234), (vh, arr, tv) -> vh.set((Object[]) arr, (Integer) tv)); + testCaseArraySet(cases, long[].class, 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (int) tv)); + testCaseArraySet(cases, long[].class, (short) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (short) tv)); + testCaseArraySet(cases, long[].class, (char) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (char) tv)); + testCaseArraySet(cases, long[].class, (byte) 1234, (vh, arr, tv) -> vh.set((long[]) arr, 0, (byte) tv)); + testCaseArraySet(cases, long[].class, Long.valueOf(1234L), (vh, arr, tv) -> vh.set((long[]) arr, 0, (Long) tv)); + testCaseArraySet(cases, double[].class, 1234F, (vh, arr, tv) -> vh.set((double[]) arr, 0, (float) tv)); + testCaseArraySet(cases, double[].class, 1234, (vh, arr, tv) -> vh.set((double[]) arr, 0, (int) tv)); + testCaseArraySet(cases, double[].class, 1234L, (vh, arr, tv) -> vh.set((double[]) arr, 0, (long) tv)); + testCaseArraySet(cases, double[].class, Double.valueOf(1234D), (vh, arr, tv) -> vh.set((double[]) arr, 0, (Double) tv)); + testCaseArraySet(cases, Long[].class, 1234L, (vh, arr, tv) -> vh.set((Long[]) arr, 0, (long) tv)); + + return cases.toArray(Object[][]::new); + } + + @DataProvider + public static Object[][] dataSetBuffer() { + List cases = new ArrayList<>(); + + // create a bunch of different sig-poly call sites + testCaseBufferSet(cases, long[].class, 1234, (vh, buff, tv) -> vh.set(buff, 0, (int) tv)); + testCaseBufferSet(cases, long[].class, (short) 1234, (vh, buff, tv) -> vh.set(buff, 0, (short) tv)); + testCaseBufferSet(cases, long[].class, (char) 1234, (vh, buff, tv) -> vh.set(buff, 0, (char) tv)); + testCaseBufferSet(cases, long[].class, (byte) 1234, (vh, buff, tv) -> vh.set(buff, 0, (byte) tv)); + testCaseBufferSet(cases, long[].class, Long.valueOf(1234L), (vh, buff, tv) -> vh.set(buff, 0, (Long) tv)); + testCaseBufferSet(cases, double[].class, 1234F, (vh, buff, tv) -> vh.set(buff, 0, (float) tv)); + testCaseBufferSet(cases, double[].class, 1234, (vh, buff, tv) -> vh.set(buff, 0, (int) tv)); + testCaseBufferSet(cases, double[].class, 1234L, (vh, buff, tv) -> vh.set(buff, 0, (long) tv)); + testCaseBufferSet(cases, double[].class, Double.valueOf(1234D), (vh, buff, tv) -> vh.set(buff, 0, (Double) tv)); + + return cases.toArray(Object[][]::new); + } + + @DataProvider + public static Object[][] dataSetMemorySegment() { + List cases = new ArrayList<>(); + + // create a bunch of different sig-poly call sites + testCaseSegmentSet(cases, long.class, 1234, (vh, addr, tv) -> vh.set(addr, (int) tv)); + testCaseSegmentSet(cases, long.class, (char) 1234, (vh, addr, tv) -> vh.set(addr, (char) tv)); + testCaseSegmentSet(cases, long.class, (short) 1234, (vh, addr, tv) -> vh.set(addr, (short) tv)); + testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, addr, tv) -> vh.set(addr, (byte) tv)); + testCaseSegmentSet(cases, double.class, 1234F, (vh, addr, tv) -> vh.set(addr, (float) tv)); + + return cases.toArray(Object[][]::new); + } + +} diff --git a/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java new file mode 100644 index 0000000000000..1b5e4f776011d --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/invoke/VarHandleExact.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.java.lang.invoke; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(3) +public class VarHandleExact { + + static final VarHandle exact; + static final VarHandle generic; + + static { + try { + generic = MethodHandles.lookup().findVarHandle(Data.class, "longField", long.class); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + exact = generic.withInvokeExactBehavior(); + } + + Data data; + + static class Data { + long longField; + } + + @Setup + public void setup() { + data = new Data(); + } + + @Benchmark + public void exact_exactInvocation() { + exact.set(data, (long) 42); + } + + @Benchmark + public void generic_genericInvocation() { + generic.set(data, 42); + } + + @Benchmark + public void generic_exactInvocation() { + generic.set(data, (long) 42); + } +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java new file mode 100644 index 0000000000000..ec920b90ae82c --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemorySegment; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.concurrent.TimeUnit; + +import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3, jvmArgsAppend = { "--add-modules", "jdk.incubator.foreign" }) +public class VarHandleExact { + + static final VarHandle exact; + static final VarHandle generic; + + static { + generic = MemoryHandles.withStride(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 4); + exact = generic.withInvokeExactBehavior(); + } + + MemorySegment data; + + @Setup + public void setup() { + data = MemorySegment.allocateNative(JAVA_INT); + } + + @TearDown + public void tearDown() { + data.close(); + } + + @Benchmark + public void exact_exactInvocation() { + exact.set(data.baseAddress(), (long) 0, 42); + } + + @Benchmark + public void generic_genericInvocation() { + generic.set(data.baseAddress(), 0, 42); + } + + @Benchmark + public void generic_exactInvocation() { + generic.set(data.baseAddress(), (long) 0, 42); + } +} From be635258fa0b7c25441ab23ff9ec0f86655dc5ca Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 11 Nov 2020 01:29:33 +0000 Subject: [PATCH 058/124] 8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI) Reviewed-by: kizune, aivanov --- .../share/classes/java/awt/Robot.java | 32 +- .../classes/java/awt/peer/RobotPeer.java | 12 +- .../sun/java2d/SunGraphicsEnvironment.java | 124 +++++-- .../classes/sun/awt/Win32GraphicsDevice.java | 2 +- .../sun/awt/windows/WComponentPeer.java | 12 + .../classes/sun/awt/windows/WDialogPeer.java | 6 +- .../classes/sun/awt/windows/WFramePeer.java | 17 +- .../classes/sun/awt/windows/WRobotPeer.java | 9 +- .../classes/sun/awt/windows/WWindowPeer.java | 106 +----- .../native/libawt/windows/MouseInfo.cpp | 6 +- .../native/libawt/windows/awt_Component.cpp | 66 +++- .../native/libawt/windows/awt_Component.h | 5 + .../native/libawt/windows/awt_Cursor.cpp | 6 +- .../native/libawt/windows/awt_DnDDS.cpp | 18 +- .../native/libawt/windows/awt_FileDialog.cpp | 13 +- .../native/libawt/windows/awt_Frame.cpp | 8 +- .../native/libawt/windows/awt_List.cpp | 9 +- .../windows/awt_Win32GraphicsConfig.cpp | 8 +- .../windows/awt_Win32GraphicsDevice.cpp | 59 +++- .../libawt/windows/awt_Win32GraphicsDevice.h | 9 +- .../native/libawt/windows/awt_Window.cpp | 302 +++++++----------- .../native/libawt/windows/awt_Window.h | 15 +- .../windows/native/libawt/windows/awtmsg.h | 6 +- .../SetComponentsBounds.java | 120 +++++++ .../EmbeddedFrameGrabTest.java | 6 +- .../MaximizedToOppositeScreenSmall.java | 15 +- .../FullscreenWindowProps.java | 102 ++++++ .../ListMultipleSelectTest.java | 152 +++++++++ .../MouseEventTest/MouseEventTest.java | 2 +- .../CheckCommonColors/CheckCommonColors.java | 35 +- .../LocationAtScreenCorner.java | 7 +- .../awt/Window/SlowMotion/SlowMotion.java | 95 ++++++ .../WindowSizeDifferentScreens.java | 113 +++++++ .../dnd/Button2DragTest/Button2DragTest.java | 15 +- .../8149849/DNDTextToScaledArea.java | 36 ++- 35 files changed, 1117 insertions(+), 431 deletions(-) create mode 100644 test/jdk/java/awt/Component/SetComponentsBounds/SetComponentsBounds.java create mode 100644 test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java create mode 100644 test/jdk/java/awt/List/ListMultipleSelectTest/ListMultipleSelectTest.java create mode 100644 test/jdk/java/awt/Window/SlowMotion/SlowMotion.java create mode 100644 test/jdk/java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java diff --git a/src/java.desktop/share/classes/java/awt/Robot.java b/src/java.desktop/share/classes/java/awt/Robot.java index 535fe58a676a6..a7af9903e4f01 100644 --- a/src/java.desktop/share/classes/java/awt/Robot.java +++ b/src/java.desktop/share/classes/java/awt/Robot.java @@ -43,6 +43,9 @@ import sun.awt.image.SunWritableRaster; import sun.java2d.SunGraphicsEnvironment; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpace; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs; + /** * This class is used to generate native system input events * for the purposes of test automation, self-running demos, and @@ -385,13 +388,9 @@ private static void checkKeycodeArgument(int keycode) { */ public synchronized Color getPixelColor(int x, int y) { checkScreenCaptureAllowed(); - AffineTransform tx = GraphicsEnvironment. - getLocalGraphicsEnvironment().getDefaultScreenDevice(). - getDefaultConfiguration().getDefaultTransform(); - x = (int) (x * tx.getScaleX()); - y = (int) (y * tx.getScaleY()); - Color color = new Color(peer.getRGBPixel(x, y)); - return color; + Point point = peer.useAbsoluteCoordinates() ? toDeviceSpaceAbs(x, y) + : toDeviceSpace(x, y); + return new Color(peer.getRGBPixel(point.x, point.y)); } /** @@ -523,17 +522,16 @@ public synchronized BufferedImage createScreenCapture(Rectangle screenRect) { imageArray[0] = highResolutionImage; } else { - - int sX = (int) Math.floor(screenRect.x * uiScaleX); - int sY = (int) Math.floor(screenRect.y * uiScaleY); - int sWidth = (int) Math.ceil(screenRect.width * uiScaleX); - int sHeight = (int) Math.ceil(screenRect.height * uiScaleY); - int[] temppixels; - Rectangle scaledRect = new Rectangle(sX, sY, sWidth, sHeight); - temppixels = peer.getRGBPixels(scaledRect); - + Rectangle scaledRect; + if (peer.useAbsoluteCoordinates()) { + scaledRect = toDeviceSpaceAbs(gc, screenRect.x, + screenRect.y, screenRect.width, screenRect.height); + } else { + scaledRect = toDeviceSpace(gc, screenRect.x, + screenRect.y, screenRect.width, screenRect.height); + } // HighResolutionImage - pixels = temppixels; + pixels = peer.getRGBPixels(scaledRect); buffer = new DataBufferInt(pixels, pixels.length); raster = Raster.createPackedRaster(buffer, scaledRect.width, scaledRect.height, scaledRect.width, bandmasks, null); diff --git a/src/java.desktop/share/classes/java/awt/peer/RobotPeer.java b/src/java.desktop/share/classes/java/awt/peer/RobotPeer.java index 4c77e19307351..10a901817a204 100644 --- a/src/java.desktop/share/classes/java/awt/peer/RobotPeer.java +++ b/src/java.desktop/share/classes/java/awt/peer/RobotPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,4 +117,14 @@ public interface RobotPeer * @see Robot#createScreenCapture(Rectangle) */ int[] getRGBPixels(Rectangle bounds); + + /** + * Determines if absolute coordinates should be used by this peer. + * + * @return {@code true} if absolute coordinates should be used, + * {@code false} otherwise + */ + default boolean useAbsoluteCoordinates() { + return false; + } } diff --git a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java index 0f3f195fb197d..c5ce7ebd3155c 100644 --- a/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java +++ b/src/java.desktop/share/classes/sun/java2d/SunGraphicsEnvironment.java @@ -27,6 +27,7 @@ import java.awt.AWTError; import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; @@ -349,53 +350,124 @@ public static GraphicsConfiguration getGraphicsConfigurationAtPoint( } /** - * Converts coordinates from the user's space to the device space using - * appropriate device transformation. + * Returns the bounds of the graphics configuration in device space. + * + * @param config the graphics configuration which bounds are requested + * @return the bounds of the area covered by this + * {@code GraphicsConfiguration} in device space (pixels) + */ + public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) { + AffineTransform tx = config.getDefaultTransform(); + Rectangle bounds = config.getBounds(); + bounds.width *= tx.getScaleX(); + bounds.height *= tx.getScaleY(); + return bounds; + } + + /** + * Converts the size (w, h) from the device space to the user's space using + * passed graphics configuration. + * + * @param gc the graphics configuration to be used for transformation + * @param w the width in the device space + * @param h the height in the device space + * @return the size in the user's space + */ + public static Dimension toUserSpace(GraphicsConfiguration gc, + int w, int h) { + AffineTransform tx = gc.getDefaultTransform(); + return new Dimension( + Region.clipRound(w / tx.getScaleX()), + Region.clipRound(h / tx.getScaleY()) + ); + } + + /** + * Converts absolute coordinates from the user's space to the device space + * using appropriate device transformation. * - * @param x coordinate in the user space - * @param y coordinate in the user space - * @return the point which uses device space(pixels) + * @param x absolute coordinate in the user's space + * @param y absolute coordinate in the user's space + * @return the point which uses device space (pixels) */ - public static Point convertToDeviceSpace(double x, double y) { + public static Point toDeviceSpaceAbs(int x, int y) { GraphicsConfiguration gc = getLocalGraphicsEnvironment() - .getDefaultScreenDevice().getDefaultConfiguration(); + .getDefaultScreenDevice().getDefaultConfiguration(); gc = getGraphicsConfigurationAtPoint(gc, x, y); + return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation(); + } + /** + * Converts the rectangle from the user's space to the device space using + * appropriate device transformation. + * + * @param rect the rectangle in the user's space + * @return the rectangle which uses device space (pixels) + */ + public static Rectangle toDeviceSpaceAbs(Rectangle rect) { + GraphicsConfiguration gc = getLocalGraphicsEnvironment() + .getDefaultScreenDevice().getDefaultConfiguration(); + gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y); + return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height); + } + + /** + * Converts absolute coordinates (x, y) and the size (w, h) from the user's + * space to the device space using passed graphics configuration. + * + * @param gc the graphics configuration to be used for transformation + * @param x absolute coordinate in the user's space + * @param y absolute coordinate in the user's space + * @param w the width in the user's space + * @param h the height in the user's space + * @return the rectangle which uses device space (pixels) + */ + public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc, + int x, int y, int w, int h) { AffineTransform tx = gc.getDefaultTransform(); - x = Region.clipRound(x * tx.getScaleX()); - y = Region.clipRound(y * tx.getScaleY()); - return new Point((int) x, (int) y); + Rectangle screen = gc.getBounds(); + return new Rectangle( + screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()), + screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()), + Region.clipRound(w * tx.getScaleX()), + Region.clipRound(h * tx.getScaleY()) + ); } /** - * Converts bounds from the user's space to the device space using + * Converts coordinates from the user's space to the device space using * appropriate device transformation. * - * @param bounds the rectangle in the user space - * @return the rectangle which uses device space(pixels) + * @param x coordinate in the user's space + * @param y coordinate in the user's space + * @return the point which uses device space (pixels) */ - public static Rectangle convertToDeviceSpace(Rectangle bounds) { + public static Point toDeviceSpace(int x, int y) { GraphicsConfiguration gc = getLocalGraphicsEnvironment() .getDefaultScreenDevice().getDefaultConfiguration(); - gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y); - return convertToDeviceSpace(gc, bounds); + gc = getGraphicsConfigurationAtPoint(gc, x, y); + return toDeviceSpace(gc, x, y, 0, 0).getLocation(); } /** - * Converts bounds from the user's space to the device space using - * appropriate device transformation of the passed graphics configuration. + * Converts coordinates (x, y) and the size (w, h) from the user's + * space to the device space using passed graphics configuration. * - * @param bounds the rectangle in the user space - * @return the rectangle which uses device space(pixels) + * @param gc the graphics configuration to be used for transformation + * @param x coordinate in the user's space + * @param y coordinate in the user's space + * @param w the width in the user's space + * @param h the height in the user's space + * @return the rectangle which uses device space (pixels) */ - public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc, - Rectangle bounds) { + public static Rectangle toDeviceSpace(GraphicsConfiguration gc, + int x, int y, int w, int h) { AffineTransform tx = gc.getDefaultTransform(); return new Rectangle( - Region.clipRound(bounds.x * tx.getScaleX()), - Region.clipRound(bounds.y * tx.getScaleY()), - Region.clipRound(bounds.width * tx.getScaleX()), - Region.clipRound(bounds.height * tx.getScaleY()) + Region.clipRound(x * tx.getScaleX()), + Region.clipRound(y * tx.getScaleY()), + Region.clipRound(w * tx.getScaleX()), + Region.clipRound(h * tx.getScaleY()) ); } } diff --git a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java index b4962f2a5d735..52a3678606433 100644 --- a/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java +++ b/src/java.desktop/windows/classes/sun/awt/Win32GraphicsDevice.java @@ -467,7 +467,7 @@ public synchronized void setDisplayMode(DisplayMode dm) { // display mode Rectangle screenBounds = getDefaultConfiguration().getBounds(); w.setBounds(screenBounds.x, screenBounds.y, - dm.getWidth(), dm.getHeight()); + screenBounds.width, screenBounds.height); // Note: no call to replaceSurfaceData is required here since // replacement will be caused by an upcoming display change event } else { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java index d8114aed4535c..c07d116abfbcb 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WComponentPeer.java @@ -533,7 +533,11 @@ public void run() { @Override public boolean updateGraphicsData(GraphicsConfiguration gc) { + var old = getGraphicsConfiguration().getDefaultTransform(); winGraphicsConfig = (Win32GraphicsConfig)gc; + if (gc != null && !old.equals(gc.getDefaultTransform())) { + syncBounds(); // the bounds of the peer depend on the DPI + } try { replaceSurfaceData(); } catch (InvalidPipeException e) { @@ -542,6 +546,14 @@ public boolean updateGraphicsData(GraphicsConfiguration gc) { return false; } + /** + * Make sure that the native peer's coordinates are in sync with the target. + */ + void syncBounds() { + Rectangle r = ((Component) target).getBounds(); + setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS); + } + //This will return null for Components not yet added to a Container @Override public ColorModel getColorModel() { diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java index ea4fc4aaa41ac..930064d598120 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WDialogPeer.java @@ -36,6 +36,8 @@ import sun.awt.AWTAccessor; import sun.awt.im.InputMethodManager; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; + final class WDialogPeer extends WWindowPeer implements DialogPeer { // Toolkit & peer internals @@ -117,8 +119,8 @@ public Dimension getMinimumSize() { if (((Dialog)target).isUndecorated()) { return super.getMinimumSize(); } - return new Dimension(scaleDownX(getSysMinWidth()), - scaleDownY(getSysMinHeight())); + return toUserSpace(getGraphicsConfiguration(), + getSysMinWidth(), getSysMinHeight()); } @Override diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java index eaa830fc8af64..8671f25f308a4 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WFramePeer.java @@ -38,7 +38,9 @@ import sun.awt.im.InputMethodManager; import sun.security.action.GetPropertyAction; -import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace; +import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; class WFramePeer extends WWindowPeer implements FramePeer { @@ -97,10 +99,9 @@ public final void setMaximizedBounds(Rectangle b) { */ private Rectangle adjustMaximizedBounds(Rectangle bounds) { // All calculations should be done in the device space - bounds = convertToDeviceSpace(bounds); - + bounds = toDeviceSpaceAbs(bounds); GraphicsConfiguration gc = getGraphicsConfiguration(); - Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds()); + Rectangle currentDevBounds = getGCDeviceBounds(gc); // Prepare data for WM_GETMINMAXINFO message. // ptMaxPosition should be in coordinate system of the current monitor, // not the main monitor, or monitor on which we maximize the window. @@ -148,13 +149,13 @@ public void reshape(int x, int y, int width, int height) { @Override public final Dimension getMinimumSize() { + GraphicsConfiguration gc = getGraphicsConfiguration(); Dimension d = new Dimension(); if (!((Frame)target).isUndecorated()) { - d.setSize(scaleDownX(getSysMinWidth()), - scaleDownY(getSysMinHeight())); + d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight())); } - if (((Frame)target).getMenuBar() != null) { - d.height += scaleDownY(getSysMenuHeight()); + if (((Frame) target).getMenuBar() != null) { + d.height += toUserSpace(gc, 0, getSysMenuHeight()).height; } return d; } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java index 4d17500e079a1..ad924dd6e1f5e 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WRobotPeer.java @@ -29,14 +29,14 @@ import java.awt.Rectangle; import java.awt.peer.RobotPeer; -import sun.java2d.SunGraphicsEnvironment; +import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs; final class WRobotPeer implements RobotPeer { public native void mouseMoveImpl(int x, int y); @Override public void mouseMove(int x, int y) { - Point point = SunGraphicsEnvironment.convertToDeviceSpace(x, y); + Point point = toDeviceSpaceAbs(x, y); mouseMoveImpl(point.x, point.y); } @Override @@ -64,5 +64,10 @@ public int getRGBPixel(int x, int y) { return pixelArray; } + @Override + public boolean useAbsoluteCoordinates() { + return true; + } + private native void getRGBPixels(int x, int y, int width, int height, int[] pixelArray); } diff --git a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java index 5db663c6f7f53..6c3d059457de3 100644 --- a/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/java.desktop/windows/classes/sun/awt/windows/WWindowPeer.java @@ -63,10 +63,11 @@ import sun.awt.Win32GraphicsConfig; import sun.awt.Win32GraphicsDevice; import sun.awt.Win32GraphicsEnvironment; -import sun.java2d.SunGraphicsEnvironment; import sun.java2d.pipe.Region; import sun.util.logging.PlatformLogger; +import static sun.java2d.SunGraphicsEnvironment.toUserSpace; + public class WWindowPeer extends WPanelPeer implements WindowPeer, DisplayChangedListener { @@ -108,8 +109,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, * WindowStateEvent is posted to the EventQueue. */ private WindowListener windowListener; - private float scaleX; - private float scaleY; /** * Initialize JNI field IDs @@ -222,8 +221,6 @@ void initialize() { GraphicsConfiguration gc = getGraphicsConfiguration(); Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice(); gd.addDisplayChangedListener(this); - scaleX = gd.getDefaultScaleX(); - scaleY = gd.getDefaultScaleY(); initActiveWindowsTracking((Window)target); @@ -310,6 +307,12 @@ public void show() { } } + @Override + final void syncBounds() { + // Windows will take care of the top-level window/frame/dialog, and + // update the location/size when DPI changes. + } + // Synchronize the insets members (here & in helper) with actual window // state. native void updateInsets(Insets i); @@ -438,9 +441,10 @@ public void updateMinimumSize() { minimumSize = ((Component)target).getMinimumSize(); } if (minimumSize != null) { - int w = Math.max(minimumSize.width, scaleDownX(getSysMinWidth())); - int h = Math.max(minimumSize.height, scaleDownY(getSysMinHeight())); - setMinSize(w, h); + Dimension sysMin = toUserSpace(getGraphicsConfiguration(), + getSysMinWidth(), getSysMinHeight()); + setMinSize(Math.max(minimumSize.width, sysMin.width), + Math.max(minimumSize.height, sysMin.height)); } else { setMinSize(0, 0); } @@ -598,21 +602,6 @@ public void updateGC() { AWTAccessor.getComponentAccessor(). setGraphicsConfiguration((Component)target, winGraphicsConfig); - - checkDPIChange(oldDev, newDev); - } - - private void checkDPIChange(Win32GraphicsDevice oldDev, - Win32GraphicsDevice newDev) { - float newScaleX = newDev.getDefaultScaleX(); - float newScaleY = newDev.getDefaultScaleY(); - - if (scaleX != newScaleX || scaleY != newScaleY) { - windowDPIChange(oldDev.getScreen(), scaleX, scaleY, - newDev.getScreen(), newScaleX, newScaleY); - scaleX = newScaleX; - scaleY = newScaleY; - } } /** @@ -666,77 +655,9 @@ boolean isTargetUndecorated() { return true; } - // These are the peer bounds. They get updated at: - // 1. the WWindowPeer.setBounds() method. - // 2. the native code (on WM_SIZE/WM_MOVE) - private volatile int sysX = 0; - private volatile int sysY = 0; - private volatile int sysW = 0; - private volatile int sysH = 0; - @Override public native void repositionSecurityWarning(); - @Override - public void setBounds(int x, int y, int width, int height, int op) { - sysX = x; - sysY = y; - sysW = width; - sysH = height; - - int cx = x + width / 2; - int cy = y + height / 2; - GraphicsConfiguration current = getGraphicsConfiguration(); - GraphicsConfiguration other = SunGraphicsEnvironment - .getGraphicsConfigurationAtPoint(current, cx, cy); - if (!current.equals(other)) { - AffineTransform tx = other.getDefaultTransform(); - double otherScaleX = tx.getScaleX(); - double otherScaleY = tx.getScaleY(); - initScales(); - if (scaleX != otherScaleX || scaleY != otherScaleY) { - x = (int) Math.floor(x * otherScaleX / scaleX); - y = (int) Math.floor(y * otherScaleY / scaleY); - } - } - - super.setBounds(x, y, width, height, op); - } - - private void initScales() { - - if (scaleX >= 1 && scaleY >= 1) { - return; - } - - GraphicsConfiguration gc = getGraphicsConfiguration(); - if (gc instanceof Win32GraphicsConfig) { - Win32GraphicsDevice gd = ((Win32GraphicsConfig) gc).getDevice(); - scaleX = gd.getDefaultScaleX(); - scaleY = gd.getDefaultScaleY(); - } else { - AffineTransform tx = gc.getDefaultTransform(); - scaleX = (float) tx.getScaleX(); - scaleY = (float) tx.getScaleY(); - } - } - - final int scaleUpX(int x) { - return Region.clipRound(x * scaleX); - } - - final int scaleUpY(int y) { - return Region.clipRound(y * scaleY); - } - - final int scaleDownX(int x) { - return Region.clipRound(x / scaleX); - } - - final int scaleDownY(int y) { - return Region.clipRound(y / scaleY); - } - @Override public void print(Graphics g) { // We assume we print the whole frame, @@ -905,9 +826,6 @@ private void updateWindow(boolean repaint) { } } - native void windowDPIChange(int prevScreen, float prevScaleX, float prevScaleY, - int newScreen, float newScaleX, float newScaleY); - /* * The method maps the list of the active windows to the window's AppContext, * then the method registers ActiveWindowListener, GuiDisposedListener listeners; diff --git a/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp b/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp index 37fbc914519ef..560764e8b28f0 100644 --- a/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp +++ b/src/java.desktop/windows/native/libawt/windows/MouseInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -106,8 +106,8 @@ Java_sun_awt_windows_WMouseInfoPeer_fillPointWithCoords(JNIEnv *env, jclass cls, yID = env->GetFieldID(pointClass, "y", "I"); CHECK_NULL_RETURN(yID, (jint)0); - int x = (device == NULL) ? pt.x : device->ScaleDownX(pt.x); - int y = (device == NULL) ? pt.y : device->ScaleDownY(pt.y); + int x = (device == NULL) ? pt.x : device->ScaleDownAbsX(pt.x); + int y = (device == NULL) ? pt.y : device->ScaleDownAbsY(pt.y); env->SetIntField(point, xID, x); env->SetIntField(point, yID, y); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp index 9e15a77514a15..1ed97ba94d241 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp @@ -603,7 +603,7 @@ AwtComponent::CreateHWnd(JNIEnv *env, LPCWSTR title, /* * Fix for 4046446. */ - SetWindowPos(GetHWnd(), 0, x, y, w, h, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE); + Reshape(x, y, w, h); /* Set default colors. */ m_colorForeground = colorForeground; @@ -1087,6 +1087,7 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) { WIN_MSG(WM_DESTROY) WIN_MSG(WM_MOVE) WIN_MSG(WM_SIZE) + WIN_MSG(WM_DPICHANGED) WIN_MSG(WM_ACTIVATE) WIN_MSG(WM_SETFOCUS) WIN_MSG(WM_KILLFOCUS) @@ -1505,9 +1506,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) case WM_SIZE: { RECT r; - // fix 4128317 : use GetClientRect for full 32-bit int precision and + // fix 4128317 : use GetWindowRect for full 32-bit int precision and // to avoid negative client area dimensions overflowing 16-bit params - robi - ::GetClientRect( GetHWnd(), &r ); + ::GetWindowRect(GetHWnd(), &r); mr = WmSize(static_cast(wParam), r.right - r.left, r.bottom - r.top); //mr = WmSize(wParam, LOWORD(lParam), HIWORD(lParam)); SetCompositionWindow(r); @@ -3888,8 +3889,8 @@ void AwtComponent::OpenCandidateWindow(int x, int y) } HWND hTop = GetTopLevelParentForWindow(hWnd); ::ClientToScreen(hTop, &p); - int sx = ScaleUpX(x) - p.x; - int sy = ScaleUpY(y) - p.y; + int sx = ScaleUpAbsX(x) - p.x; + int sy = ScaleUpAbsY(y) - p.y; if (!m_bitsCandType) { SetCandidateWindow(m_bitsCandType, sx, sy); return; @@ -4767,34 +4768,71 @@ void AwtComponent::FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha) } } +int AwtComponent::GetScreenImOn() { + HWND hWindow = GetAncestor(GetHWnd(), GA_ROOT); + AwtComponent *comp = AwtComponent::GetComponent(hWindow); + if (comp && comp->IsTopLevel()) { + return comp->GetScreenImOn(); + } + return AwtWin32GraphicsDevice::DeviceIndexForWindow(hWindow); +} + int AwtComponent::ScaleUpX(int x) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? x : device->ScaleUpX(x); } +int AwtComponent::ScaleUpAbsX(int x) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleUpAbsX(x); +} + int AwtComponent::ScaleUpY(int y) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? y : device->ScaleUpY(y); } +int AwtComponent::ScaleUpAbsY(int y) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleUpAbsY(y); +} + int AwtComponent::ScaleDownX(int x) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? x : device->ScaleDownX(x); } +int AwtComponent::ScaleDownAbsX(int x) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? x : device->ScaleDownAbsX(x); +} + int AwtComponent::ScaleDownY(int y) { - int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()); + int screen = GetScreenImOn(); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); return device == NULL ? y : device->ScaleDownY(y); } +int AwtComponent::ScaleDownAbsY(int y) { + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + return device == NULL ? y : device->ScaleDownAbsY(y); +} + jintArray AwtComponent::CreatePrintedPixels(SIZE &loc, SIZE &size, int alpha) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); @@ -5090,7 +5128,7 @@ void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y, id, when, modifiers, ScaleDownX(x + insets.left), ScaleDownY(y + insets.top), - ScaleDownX(xAbs), ScaleDownY(yAbs), + ScaleDownAbsX(xAbs), ScaleDownAbsY(yAbs), clickCount, popupTrigger, button); if (safe_ExceptionOccurred(env)) { @@ -5163,8 +5201,8 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y, id, when, modifiers, ScaleDownX(x + insets.left), ScaleDownY(y + insets.top), - ScaleDownX(xAbs), - ScaleDownY(yAbs), + ScaleDownAbsX(xAbs), + ScaleDownAbsY(yAbs), clickCount, popupTrigger, scrollType, scrollAmount, roundedWheelRotation, preciseWheelRotation); @@ -5674,8 +5712,8 @@ jobject AwtComponent::_GetLocationOnScreen(void *param) RECT rect; VERIFY(::GetWindowRect(p->GetHWnd(),&rect)); result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V", - p->ScaleDownX(rect.left), - p->ScaleDownY(rect.top)); + p->ScaleDownAbsX(rect.left), + p->ScaleDownAbsY(rect.top)); } ret: env->DeleteGlobalRef(self); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Component.h b/src/java.desktop/windows/native/libawt/windows/awt_Component.h index 1e3e076927304..d950e78abda34 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Component.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Component.h @@ -275,6 +275,7 @@ class AwtComponent : public AwtObject { /* * methods on this component */ + virtual int GetScreenImOn(); virtual void Show(); virtual void Hide(); virtual void Reshape(int x, int y, int w, int h); @@ -755,9 +756,13 @@ class AwtComponent : public AwtObject { virtual void FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha); int ScaleUpX(int x); + int ScaleUpAbsX(int x); int ScaleUpY(int y); + int ScaleUpAbsY(int y); int ScaleDownX(int x); + int ScaleDownAbsX(int x); int ScaleDownY(int y); + int ScaleDownAbsY(int y); private: /* A bitmask keeps the button's numbers as MK_LBUTTON, MK_MBUTTON, MK_RBUTTON diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp index 642c9e9b14c6b..4a1a4c08131aa 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Cursor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -475,8 +475,8 @@ Java_sun_awt_windows_WGlobalCursorManager_getCursorPos(JNIEnv *env, int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(screen); - int x = (device == NULL) ? p.x : device->ScaleDownX(p.x); - int y = (device == NULL) ? p.y : device->ScaleDownY(p.y); + int x = (device == NULL) ? p.x : device->ScaleDownAbsX(p.x); + int y = (device == NULL) ? p.y : device->ScaleDownAbsY(p.y); env->SetIntField(point, AwtCursor::pointXID, x); env->SetIntField(point, AwtCursor::pointYID, y); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp b/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp index 390b9250abafa..0f9fb662bbf5c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_DnDDS.cpp @@ -1174,14 +1174,14 @@ HRESULT __stdcall AwtDragSource::GetProcessId(FORMATETC __RPC_FAR *pFormatEtc, S return S_OK; } -static void ScaleDown(POINT &pt) { +static void ScaleDownAbs(POINT &pt) { HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY); int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); Devices::InstanceAccess devices; AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (device) { - pt.x = device->ScaleDownX(pt.x); - pt.y = device->ScaleDownY(pt.y); + pt.x = device->ScaleDownAbsX(pt.x); + pt.y = device->ScaleDownAbsY(pt.y); } } @@ -1190,7 +1190,7 @@ DECLARE_JAVA_CLASS(dSCClazz, "sun/awt/windows/WDragSourceContextPeer") void AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCenter, dSCClazz, "dragEnter", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCenter, targetActions, modifiers, pt.x, pt.y); @@ -1203,7 +1203,7 @@ AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCmotion, dSCClazz, "dragMotion", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCmotion, targetActions, modifiers, pt.x, pt.y); @@ -1216,7 +1216,7 @@ AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCchanged, dSCClazz, "operationChanged", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); @@ -1229,7 +1229,7 @@ AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions, void AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCexit, dSCClazz, "dragExit", "(II)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCexit, pt.x, pt.y); @@ -1242,7 +1242,7 @@ AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) { void AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success, jint operations, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCddfinished, dSCClazz, "dragDropFinished", "(ZIII)V"); DASSERT(!JNU_IsNull(env, self)); env->CallVoidMethod(self, dSCddfinished, success, operations, pt.x, pt.y); @@ -1255,7 +1255,7 @@ AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success, void AwtDragSource::call_dSCmouseMoved(JNIEnv* env, jobject self, jint targetActions, jint modifiers, POINT pt) { - ScaleDown(pt); + ScaleDownAbs(pt); DECLARE_VOID_JAVA_METHOD(dSCmouseMoved, dSCClazz, "dragMouseMoved", "(IIII)V"); DASSERT(!JNU_IsNull(env, self)); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp b/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp index 119b2c3680c2b..e8128372ceb0a 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_FileDialog.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -631,18 +631,18 @@ Java_sun_awt_windows_WFileDialogPeer_toBack(JNIEnv *env, jobject peer) CATCH_BAD_ALLOC; } -int ScaleDownX(int x, HWND hwnd) { +int ScaleDownAbsX(int x, HWND hwnd) { int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); - return device == NULL ? x : device->ScaleDownX(x); + return device == NULL ? x : device->ScaleDownAbsX(x); } -int ScaleDownY(int y, HWND hwnd) { +int ScaleDownAbsY(int y, HWND hwnd) { int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd); Devices::InstanceAccess devices; AwtWin32GraphicsDevice* device = devices->GetDevice(screen); - return device == NULL ? y : device->ScaleDownY(y); + return device == NULL ? y : device->ScaleDownAbsY(y); } jobject AwtFileDialog::_GetLocationOnScreen(void *param) @@ -657,7 +657,8 @@ jobject AwtFileDialog::_GetLocationOnScreen(void *param) RECT rect; VERIFY(::GetWindowRect(hwnd, &rect)); result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V", - ScaleDownX(rect.left, hwnd), ScaleDownY(rect.top, hwnd)); + ScaleDownAbsX(rect.left, hwnd), + ScaleDownAbsY(rect.top, hwnd)); } if (result != NULL) diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp index 8f2fcd84f6f5b..d8873b6bec291 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Frame.cpp @@ -328,17 +328,13 @@ AwtFrame* AwtFrame::Create(jobject self, jobject parent) frame->CreateHWnd(env, L"", style, exStyle, - 0, 0, 0, 0, + x, y, width, height, hwndParent, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOWFRAME), self); - /* - * Reshape here instead of during create, so that a - * WM_NCCALCSIZE is sent. - */ - frame->Reshape(x, y, width, height); + frame->RecalcNonClient(); } } } catch (...) { diff --git a/src/java.desktop/windows/native/libawt/windows/awt_List.cpp b/src/java.desktop/windows/native/libawt/windows/awt_List.cpp index 69484f024d799..a04f6182a5b5f 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_List.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_List.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -288,14 +288,17 @@ void AwtList::SetMultiSelect(BOOL ms) { UnsubclassHWND(); AwtToolkit::DestroyComponentHWND(m_hwnd); - CreateHWnd(env, L"", style, exStyle, - rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + CreateHWnd(env, L"", style, exStyle, 0, 0, 0, 0, parentHWnd, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW), peer); + SetWindowPos(GetHWnd(), 0, + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE); + SendListMessage(WM_SETFONT, (WPARAM)font, (LPARAM)FALSE); SendListMessage(LB_SETITEMHEIGHT, 0, MAKELPARAM(itemHeight, 0)); SendListMessage(LB_RESETCONTENT); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp index 7d13fbe2d87e7..463c1fdee10db 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsConfig.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -99,16 +99,12 @@ JNIEXPORT jobject JNICALL AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) { - - int x = (device == NULL) ? rRW.left : device->ScaleDownX(rRW.left); - int y = (device == NULL) ? rRW.top : device->ScaleDownY(rRW.top); int w = (device == NULL) ? rRW.right - rRW.left : device->ScaleDownX(rRW.right - rRW.left); int h = (device == NULL) ? rRW.bottom - rRW.top : device->ScaleDownY(rRW.bottom - rRW.top); - bounds = env->NewObject(clazz, mid, x, y, w, h); - + bounds = env->NewObject(clazz, mid, rRW.left, rRW.top, w, h); } else { // 4910760 - don't return a null bounds, return the bounds of the diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp index 2b6af9d8f2cac..79e7b9d105016 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.cpp @@ -77,6 +77,7 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen, this->devicesArray = arr; this->scaleX = 1; this->scaleY = 1; + disableScaleAutoRefresh = FALSE; javaDevice = NULL; colorData = new ImgColorData; colorData->grayscale = GS_NOTGRAY; @@ -633,21 +634,45 @@ int AwtWin32GraphicsDevice::ScaleUpX(int x) return ClipRound(x * scaleX); } +int AwtWin32GraphicsDevice::ScaleUpAbsX(int x) +{ + LONG screen = pMonitorInfo->rcMonitor.left; + return screen + ClipRound((x - screen) * scaleX); +} + int AwtWin32GraphicsDevice::ScaleUpY(int y) { return ClipRound(y * scaleY); } +int AwtWin32GraphicsDevice::ScaleUpAbsY(int y) +{ + LONG screen = pMonitorInfo->rcMonitor.top; + return screen + ClipRound((y - screen) * scaleY); +} + int AwtWin32GraphicsDevice::ScaleDownX(int x) { return ClipRound(x / scaleX); } +int AwtWin32GraphicsDevice::ScaleDownAbsX(int x) +{ + LONG screen = pMonitorInfo->rcMonitor.left; + return screen + ClipRound((x - screen) / scaleX); +} + int AwtWin32GraphicsDevice::ScaleDownY(int y) { return ClipRound(y / scaleY); } +int AwtWin32GraphicsDevice::ScaleDownAbsY(int y) +{ + LONG screen = pMonitorInfo->rcMonitor.top; + return screen + ClipRound((y - screen) / scaleY); +} + int AwtWin32GraphicsDevice::ClipRound(double value) { value -= 0.5; @@ -666,11 +691,13 @@ int AwtWin32GraphicsDevice::ClipRound(double value) void AwtWin32GraphicsDevice::InitDesktopScales() { - float dpiX = -1.0f; - float dpiY = -1.0f; - GetScreenDpi(GetMonitor(), &dpiX, &dpiY); - if (dpiX > 0 && dpiY > 0) { - SetScale(dpiX / 96, dpiY / 96); + if (!disableScaleAutoRefresh) { + float dpiX = -1.0f; + float dpiY = -1.0f; + GetScreenDpi(GetMonitor(), &dpiX, &dpiY); + if (dpiX > 0 && dpiY > 0) { + SetScale(dpiX / 96, dpiY / 96); + } } } @@ -694,6 +721,11 @@ void AwtWin32GraphicsDevice::DisableOffscreenAcceleration() // REMIND: noop for now } +void AwtWin32GraphicsDevice::DisableScaleAutoRefresh() +{ + disableScaleAutoRefresh = TRUE; +} + /** * Invalidates the GraphicsDevice object associated with this * device by disabling offscreen acceleration and calling @@ -754,6 +786,21 @@ void AwtWin32GraphicsDevice::ResetAllMonitorInfo() } } +/** + * This function updates the scale factor for all monitors on the system. + */ +void AwtWin32GraphicsDevice::ResetAllDesktopScales() +{ + if (!Devices::GetInstance()){ + return; + } + Devices::InstanceAccess devices; + int devicesNum = devices->GetNumDevices(); + for (int deviceIndex = 0; deviceIndex < devicesNum; deviceIndex++) { + devices->GetDevice(deviceIndex)->InitDesktopScales(); + } +} + void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice( HMONITOR hMonitor) { @@ -1393,6 +1440,7 @@ JNIEXPORT void JNICALL AwtWin32GraphicsDevice *device = devices->GetDevice(screen); if (device != NULL ) { + device->DisableScaleAutoRefresh(); device->SetScale(scaleX, scaleY); } } @@ -1441,4 +1489,3 @@ Java_sun_awt_Win32GraphicsDevice_initNativeScale device->InitDesktopScales(); } } - diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h index d5fc0d57150d6..f298aad68da8c 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Win32GraphicsDevice.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,15 +65,20 @@ class AwtWin32GraphicsDevice { int GetDeviceIndex() { return screen; } void Release(); void DisableOffscreenAcceleration(); + void DisableScaleAutoRefresh(); void Invalidate(JNIEnv *env); void InitDesktopScales(); void SetScale(float scaleX, float scaleY); float GetScaleX(); float GetScaleY(); int ScaleUpX(int x); + int ScaleUpAbsX(int x); int ScaleUpY(int y); + int ScaleUpAbsY(int x); int ScaleDownX(int x); + int ScaleDownAbsX(int x); int ScaleDownY(int y); + int ScaleDownAbsY(int y); static int DeviceIndexForWindow(HWND hWnd); static jobject GetColorModel(JNIEnv *env, jboolean dynamic, @@ -88,6 +93,7 @@ class AwtWin32GraphicsDevice { static HMONITOR GetMonitor(int deviceIndex); static LPMONITORINFO GetMonitorInfo(int deviceIndex); static void ResetAllMonitorInfo(); + static void ResetAllDesktopScales(); static BOOL IsPrimaryPalettized() { return primaryPalettized; } static int GetDefaultDeviceIndex() { return primaryIndex; } static void DisableOffscreenAccelerationForDevice(HMONITOR hMonitor); @@ -117,6 +123,7 @@ class AwtWin32GraphicsDevice { Devices *devicesArray; float scaleX; float scaleY; + BOOL disableScaleAutoRefresh; static HDC MakeDCFromMonitor(HMONITOR); static int ClipRound(double value); diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp b/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp index e73c2615a079e..dbca3d778c8b7 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp +++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.cpp @@ -153,17 +153,6 @@ struct SetFullScreenExclusiveModeStateStruct { jboolean isFSEMState; }; -// struct for _WindowDPIChange() method -struct ScaleStruct { - jobject window; - jint prevScreen; - jfloat prevScaleX; - jfloat prevScaleY; - jint screen; - jfloat scaleX; - jfloat scaleY; -}; - struct OverrideHandle { jobject frame; HWND handle; @@ -179,10 +168,6 @@ jfieldID AwtWindow::autoRequestFocusID; jfieldID AwtWindow::securityWarningWidthID; jfieldID AwtWindow::securityWarningHeightID; -jfieldID AwtWindow::sysXID; -jfieldID AwtWindow::sysYID; -jfieldID AwtWindow::sysWID; -jfieldID AwtWindow::sysHID; jfieldID AwtWindow::windowTypeID; jmethodID AwtWindow::notifyWindowStateChangedMID; @@ -1128,20 +1113,19 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent) // specify WS_EX_TOOLWINDOW to remove parentless windows from taskbar exStyle |= WS_EX_TOOLWINDOW; } + jint x = env->GetIntField(target, AwtComponent::xID); + jint y = env->GetIntField(target, AwtComponent::yID); + jint width = env->GetIntField(target, AwtComponent::widthID); + jint height = env->GetIntField(target, AwtComponent::heightID); + window->CreateHWnd(env, L"", style, exStyle, - 0, 0, 0, 0, + x, y, width, height, (awtParent != NULL) ? awtParent->GetHWnd() : NULL, NULL, ::GetSysColor(COLOR_WINDOWTEXT), ::GetSysColor(COLOR_WINDOW), self); - - jint x = env->GetIntField(target, AwtComponent::xID); - jint y = env->GetIntField(target, AwtComponent::yID); - jint width = env->GetIntField(target, AwtComponent::widthID); - jint height = env->GetIntField(target, AwtComponent::heightID); - /* * Initialize icon as inherited from parent if it exists */ @@ -1151,13 +1135,7 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent) window->m_iconInherited = TRUE; } window->DoUpdateIcon(); - - - /* - * Reshape here instead of during create, so that a WM_NCCALCSIZE - * is sent. - */ - window->Reshape(x, y, width, height); + window->RecalcNonClient(); } } catch (...) { env->DeleteLocalRef(target); @@ -1215,6 +1193,48 @@ void AwtWindow::moveToDefaultLocation() { VERIFY(::SetWindowPos(GetHWnd(), NULL, defLoc.left, defLoc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER)); } +/** + * Override AwtComponent::Reshape() to handle absolute screen coordinates used + * by the top-level windows. + */ +void AwtWindow::Reshape(int x, int y, int w, int h) { + if (IsEmbeddedFrame()) { + // Not the "real" top level window + return AwtComponent::Reshape(x, y, w, h); + } + // Yes, use x,y in user's space to find the nearest monitor in device space. + POINT pt = {x + w / 2, y + h / 2}; + Devices::InstanceAccess devices; + HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor); + AwtWin32GraphicsDevice *device = devices->GetDevice(screen); + // Try to set the correct size and jump to the correct location, even if it is + // on the different monitor. Note that for the "size" we use the current + // monitor, so the WM_DPICHANGED will adjust it for the "target" monitor. + int scaleUpAbsX = device == NULL ? x : device->ScaleUpAbsX(x); + int scaleUpAbsY = device == NULL ? y : device->ScaleUpAbsY(y); + ReshapeNoScale(scaleUpAbsX, scaleUpAbsY, ScaleUpX(w), ScaleUpY(h)); + // The window manager may tweak the size for different reasons, so try + // to make sure our window has the correct size in the user's space. + // NOOP if the size was changed already or changing is in progress. + RECT rc; + ::GetWindowRect(GetHWnd(), &rc); + ReshapeNoScale(rc.left, rc.top, ScaleUpX(w), ScaleUpY(h)); + // the window manager may ignore our "SetWindowPos" request, in this, + // case the WmMove/WmSize will not come and we need to manually resync + // the "java.awt.Window" locations, because "java.awt.Window" already + // uses location ignored by the window manager. + ::GetWindowRect(GetHWnd(), &rc); + if (x != ScaleDownAbsX(rc.left) || y != ScaleDownAbsY(rc.top)) { + WmMove(rc.left, rc.top); + } + int userW = ScaleDownX(rc.right - rc.left); + int userH = ScaleDownY(rc.bottom - rc.top); + if (w != userW || h != userH) { + WmSize(SIZENORMAL, rc.right - rc.left, rc.bottom - rc.top); + } +} + void AwtWindow::Show() { m_visible = true; @@ -1774,6 +1794,15 @@ MsgRouting AwtWindow::WmShowWindow(BOOL show, UINT status) return AwtCanvas::WmShowWindow(show, status); } +void AwtWindow::WmDPIChanged(const LPARAM &lParam) { + // need to update the scales now, otherwise the ReshapeNoScale() will + // calculate the bounds wrongly + AwtWin32GraphicsDevice::ResetAllDesktopScales(); + RECT *r = (RECT *) lParam; + ReshapeNoScale(r->left, r->top, r->right - r->left, r->bottom - r->top); + CheckIfOnNewScreen(true); +} + /* * Override AwtComponent's move handling to first update the * java AWT target's position fields directly, since Windows @@ -1789,30 +1818,21 @@ MsgRouting AwtWindow::WmMove(int x, int y) // NOTE: See also AwtWindow::Reshape return mrDoDefault; } - - if (m_screenNum == -1) { - // Set initial value - m_screenNum = GetScreenImOn(); - } - else { - CheckIfOnNewScreen(); - } + // Check for the new screen and update the java peer + CheckIfOnNewScreen(false); // postpone if different DPI /* Update the java AWT target component's fields directly */ JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(1) < 0) { return mrConsume; } - jobject peer = GetPeer(env); - jobject target = env->GetObjectField(peer, AwtObject::targetID); + jobject target = GetTarget(env); RECT rect; ::GetWindowRect(GetHWnd(), &rect); - (env)->SetIntField(target, AwtComponent::xID, ScaleDownX(rect.left)); - (env)->SetIntField(target, AwtComponent::yID, ScaleDownY(rect.top)); - (env)->SetIntField(peer, AwtWindow::sysXID, ScaleDownX(rect.left)); - (env)->SetIntField(peer, AwtWindow::sysYID, ScaleDownY(rect.top)); + (env)->SetIntField(target, AwtComponent::xID, ScaleDownAbsX(rect.left)); + (env)->SetIntField(target, AwtComponent::yID, ScaleDownAbsY(rect.top)); SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED); env->DeleteLocalRef(target); @@ -1857,13 +1877,22 @@ MsgRouting AwtWindow::WmSizing() MsgRouting AwtWindow::WmEnterSizeMove() { m_winSizeMove = TRUE; + // Below is a workaround, see CheckWindowDPIChange + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum); + if (device) { + prevScaleRec.screen = m_screenNum; + prevScaleRec.scaleX = device->GetScaleX(); + prevScaleRec.scaleY = device->GetScaleY(); + } + // Above is a workaround return mrDoDefault; } MsgRouting AwtWindow::WmExitSizeMove() { m_winSizeMove = FALSE; - CheckWindowDPIChange(); + CheckWindowDPIChange(); // workaround return mrDoDefault; } @@ -1880,6 +1909,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h) UpdateSecurityWarningVisibility(); return mrDoDefault; } + // Check for the new screen and update the java peer + CheckIfOnNewScreen(false); // postpone if different DPI JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); if (env->EnsureLocalCapacity(1) < 0) @@ -1887,15 +1918,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h) jobject target = GetTarget(env); // fix 4167248 : ensure the insets are up-to-date before using BOOL insetsChanged = UpdateInsets(NULL); - int newWidth = w + m_insets.left + m_insets.right; - int newHeight = h + m_insets.top + m_insets.bottom; - - (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(newWidth)); - (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(newHeight)); - - jobject peer = GetPeer(env); - (env)->SetIntField(peer, AwtWindow::sysWID, ScaleDownX(newWidth)); - (env)->SetIntField(peer, AwtWindow::sysHID, ScaleDownY(newHeight)); + (env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(w)); + (env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(h)); if (!AwtWindow::IsResizing()) { WindowResized(); @@ -1977,6 +2001,11 @@ LRESULT AwtWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) LRESULT retValue = 0L; switch(message) { + case WM_DPICHANGED: { + WmDPIChanged(lParam); + mr = mrConsume; + break; + } case WM_GETICON: mr = WmGetIcon(wParam, retValue); break; @@ -2120,14 +2149,28 @@ int AwtWindow::GetScreenImOn() { return scrnNum; } -/* Check to see if we've been moved onto another screen. +/* + * Check to see if we've been moved onto another screen. * If so, update internal data, surfaces, etc. */ - -void AwtWindow::CheckIfOnNewScreen() { +void AwtWindow::CheckIfOnNewScreen(BOOL force) { int curScrn = GetScreenImOn(); if (curScrn != m_screenNum) { // we've been moved + // if moved from one monitor to another with different DPI, we should + // update the m_screenNum only if the size was updated as well in the + // WM_DPICHANGED. + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* oldDevice = devices->GetDevice(m_screenNum); + AwtWin32GraphicsDevice* newDevice = devices->GetDevice(curScrn); + if (!force && m_winSizeMove && oldDevice && newDevice) { + if (oldDevice->GetScaleX() != newDevice->GetScaleX() + || oldDevice->GetScaleY() != newDevice->GetScaleY()) { + // scales are different, wait for WM_DPICHANGED + return; + } + } + JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); jclass peerCls = env->GetObjectClass(m_peerObject); @@ -2149,65 +2192,37 @@ void AwtWindow::CheckIfOnNewScreen() { } } +// The shared code is not ready to the top-level window which crosses a few +// monitors with different DPI. Popup windows will start to use wrong screen, +// will be placed in the wrong place and will use the wrong size, see 8249164 +// So we will "JUMP TO" the new screen. void AwtWindow::CheckWindowDPIChange() { - - if (prevScaleRec.screen != -1 ) { - float prevScaleX = prevScaleRec.scaleX; - float prevScaleY = prevScaleRec.scaleY; - - if (prevScaleX >= 1 && prevScaleY >= 1) { - Devices::InstanceAccess devices; - AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum); - if (device) { - float scaleX = device->GetScaleX(); - float scaleY = device->GetScaleY(); - if (prevScaleX != scaleX || prevScaleY != scaleY) { - WindowDPIChange(prevScaleRec.screen, prevScaleX, prevScaleY, - m_screenNum, scaleX, scaleY); - } - } - } - prevScaleRec.screen = -1; - } -} - -void AwtWindow::WindowDPIChange(int prevScreen, - float prevScaleX, float prevScaleY, - int screen, float scaleX, - float scaleY) -{ - int x; - int y; - int w; - int h; - RECT rect; - - if (prevScaleX == scaleX && prevScaleY == scaleY) { - return; - } - - ::GetWindowRect(GetHWnd(), &rect); - x = rect.left; - y = rect.top; - w = (rect.right - rect.left) * scaleX / prevScaleX; - h = (rect.bottom - rect.top) * scaleY / prevScaleY; - - if (prevScreen != screen) { + if (prevScaleRec.screen != -1 && prevScaleRec.screen != m_screenNum) { Devices::InstanceAccess devices; - AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + AwtWin32GraphicsDevice *device = devices->GetDevice(m_screenNum); if (device) { - RECT bounds; - if (MonitorBounds(device->GetMonitor(), &bounds)) { - x = x < bounds.left ? bounds.left : x; - y = y < bounds.top ? bounds.top : y; - - x = (x + w > bounds.right) ? bounds.right - w : x; - y = (y + h > bounds.bottom) ? bounds.bottom - h : y; + if (prevScaleRec.scaleX != device->GetScaleX() + || prevScaleRec.scaleY != device->GetScaleY()) { + RECT rect; + ::GetWindowRect(GetHWnd(), &rect); + int x = rect.left; + int y = rect.top; + int w = rect.right - rect.left; + int h = rect.bottom - rect.top; + RECT bounds; + if (MonitorBounds(device->GetMonitor(), &bounds)) { + x = x < bounds.left ? bounds.left : x; + y = y < bounds.top ? bounds.top : y; + x = (x + w > bounds.right) ? bounds.right - w : x; + y = (y + h > bounds.bottom) ? bounds.bottom - h : y; + } + ReshapeNoScale(x, y, w, h); } } + prevScaleRec.screen = -1; + prevScaleRec.scaleX = -1.0f; + prevScaleRec.scaleY = -1.0f; } - - ReshapeNoScale(x, y, w, h); } BOOL AwtWindow::IsFocusableWindow() { @@ -2582,15 +2597,11 @@ void AwtWindow::_ReshapeFrame(void *param) { env->SetIntField(target, AwtComponent::widthID, w = minWidth); - env->SetIntField(peer, AwtWindow::sysWID, - w); } if (h < minHeight) { env->SetIntField(target, AwtComponent::heightID, h = minHeight); - env->SetIntField(peer, AwtWindow::sysHID, - h); } } env->DeleteLocalRef(target); @@ -3257,39 +3268,6 @@ void AwtWindow::_GetNativeWindowSize(void* param) { env->DeleteGlobalRef(self); } -void AwtWindow::_WindowDPIChange(void* param) -{ - JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); - - ScaleStruct *ss = (ScaleStruct *)param; - jobject self = ss->window; - jint prevScreen = ss->prevScreen; - jfloat prevScaleX = ss->prevScaleX; - jfloat prevScaleY = ss->prevScaleY; - jint screen = ss->screen; - jfloat scaleX = ss->scaleX; - jfloat scaleY = ss->scaleY; - - PDATA pData; - JNI_CHECK_PEER_GOTO(self, ret); - AwtWindow *window = (AwtWindow *)pData; - - if (window->m_winSizeMove) { - if (window->prevScaleRec.screen == -1) { - window->prevScaleRec.screen = prevScreen; - window->prevScaleRec.scaleX = prevScaleX; - window->prevScaleRec.scaleY = prevScaleY; - } - } - else { - window->WindowDPIChange(prevScreen, prevScaleX, prevScaleY, - screen, scaleX, scaleY); - } - -ret: - env->DeleteGlobalRef(self); - delete ss; -} extern "C" int getSystemMetricValue(int msgType); extern "C" { @@ -3347,11 +3325,6 @@ Java_sun_awt_windows_WWindowPeer_initIDs(JNIEnv *env, jclass cls) { TRY; - CHECK_NULL(AwtWindow::sysXID = env->GetFieldID(cls, "sysX", "I")); - CHECK_NULL(AwtWindow::sysYID = env->GetFieldID(cls, "sysY", "I")); - CHECK_NULL(AwtWindow::sysWID = env->GetFieldID(cls, "sysW", "I")); - CHECK_NULL(AwtWindow::sysHID = env->GetFieldID(cls, "sysH", "I")); - AwtWindow::windowTypeID = env->GetFieldID(cls, "windowType", "Ljava/awt/Window$Type;"); @@ -3991,33 +3964,6 @@ Java_sun_awt_windows_WWindowPeer_repositionSecurityWarning(JNIEnv *env, CATCH_BAD_ALLOC; } -/* -* Class: sun_awt_windows_WWindowPeer -* Method: windowDPIChange -* Signature: (IFFIFF)V -*/ -JNIEXPORT void JNICALL -Java_sun_awt_windows_WWindowPeer_windowDPIChange(JNIEnv *env, jobject self, - jint prevScreen, jfloat prevScaleX, jfloat prevScaleY, - jint screen, jfloat scaleX, jfloat scaleY) -{ - TRY; - - ScaleStruct *ss = new ScaleStruct; - ss->window = env->NewGlobalRef(self); - ss->prevScreen = prevScreen; - ss->prevScaleX = prevScaleX; - ss->prevScaleY = prevScaleY; - ss->screen = screen; - ss->scaleX = scaleX; - ss->scaleY = scaleY; - - AwtToolkit::GetInstance().InvokeFunction(AwtWindow::_WindowDPIChange, ss); - // global refs and ss are deleted in _WindowDPIChange - - CATCH_BAD_ALLOC; -} - /* * Class: sun_awt_windows_WLightweightFramePeer * Method: overrideNativeHandle diff --git a/src/java.desktop/windows/native/libawt/windows/awt_Window.h b/src/java.desktop/windows/native/libawt/windows/awt_Window.h index 38f012864dfa5..6e035cc8f38dc 100644 --- a/src/java.desktop/windows/native/libawt/windows/awt_Window.h +++ b/src/java.desktop/windows/native/libawt/windows/awt_Window.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,12 +58,6 @@ class AwtWindow : public AwtCanvas { static jfieldID securityWarningHeightID; /* sun.awt.windows.WWindowPeer field and method IDs */ - // The coordinates at the peer. - static jfieldID sysXID; - static jfieldID sysYID; - static jfieldID sysWID; - static jfieldID sysHID; - static jfieldID windowTypeID; static jmethodID notifyWindowStateChangedMID; @@ -129,6 +123,7 @@ class AwtWindow : public AwtCanvas { return FALSE; } + virtual void Reshape(int x, int y, int w, int h); virtual void Invalidate(RECT* r); virtual void Show(); virtual void SetResizable(BOOL isResizable); @@ -136,7 +131,7 @@ class AwtWindow : public AwtCanvas { virtual void RecalcNonClient(); virtual void RedrawNonClient(); virtual int GetScreenImOn(); - virtual void CheckIfOnNewScreen(); + virtual void CheckIfOnNewScreen(BOOL force); virtual void Grab(); virtual void Ungrab(); virtual void Ungrab(BOOL doPost); @@ -248,7 +243,6 @@ class AwtWindow : public AwtCanvas { static void _RepositionSecurityWarning(void* param); static void _SetFullScreenExclusiveModeState(void* param); static void _GetNativeWindowSize(void* param); - static void _WindowDPIChange(void* param); static void _OverrideHandle(void *param); inline static BOOL IsResizing() { @@ -409,8 +403,7 @@ class AwtWindow : public AwtCanvas { void InitOwner(AwtWindow *owner); void CheckWindowDPIChange(); - void WindowDPIChange(int prevScreen, float prevScaleX, float prevScaleY, - int newScreen, float scaleX, float scaleY); + void WmDPIChanged(const LPARAM &lParam); Type m_windowType; void InitType(JNIEnv *env, jobject peer); diff --git a/src/java.desktop/windows/native/libawt/windows/awtmsg.h b/src/java.desktop/windows/native/libawt/windows/awtmsg.h index ba8a44925919a..c4f85fad41c86 100644 --- a/src/java.desktop/windows/native/libawt/windows/awtmsg.h +++ b/src/java.desktop/windows/native/libawt/windows/awtmsg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,10 @@ extern const UINT SYSCOMMAND_IMM; * See winuser.h for details. */ +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif //WM_DPICHANGED + #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif //WM_MOUSEWHEEL diff --git a/test/jdk/java/awt/Component/SetComponentsBounds/SetComponentsBounds.java b/test/jdk/java/awt/Component/SetComponentsBounds/SetComponentsBounds.java new file mode 100644 index 0000000000000..2967bc17f27bf --- /dev/null +++ b/test/jdk/java/awt/Component/SetComponentsBounds/SetComponentsBounds.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Button; +import java.awt.Canvas; +import java.awt.Checkbox; +import java.awt.Choice; +import java.awt.Component; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Label; +import java.awt.List; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.ScrollPane; +import java.awt.Scrollbar; +import java.awt.TextArea; +import java.awt.TextField; +import java.awt.Window; + +/** + * @test + * @key headful + * @bug 8211999 + * @run main/othervm SetComponentsBounds + * @run main/othervm -Dsun.java2d.uiScale=1 SetComponentsBounds + * @run main/othervm -Dsun.java2d.uiScale=2.25 SetComponentsBounds + */ +public final class SetComponentsBounds { + + private static final int X = 111; + private static final int Y = 222; + private static final int WIDTH = 321; + private static final int HEIGHT = 123; + + public static void main(String[] args) throws Exception { + var ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + for (GraphicsDevice gd : ge.getScreenDevices()) { + test(gd.getDefaultConfiguration(), true); + test(gd.getDefaultConfiguration(), false); + } + } + + private static void test(GraphicsConfiguration gc, boolean visible) throws Exception { + Rectangle screen = gc.getBounds(); + Window frame = new Frame(); + try { + frame.setLayout(null); // trigger use the minimum size of + // the peer + frame.setBounds(screen.x + 100, screen.y + 100, 500, 500); + frame.add(new Button()); + frame.add(new Canvas()); + frame.add(new Checkbox()); + frame.add(new Choice()); + frame.add(new Label()); + frame.add(new List()); + frame.add(new Scrollbar()); + frame.add(new ScrollPane()); + frame.add(new TextArea()); + frame.add(new TextField()); + for (Component comp : frame.getComponents()) { + comp.setBounds(X, Y, WIDTH, HEIGHT); + } + if (visible) { + frame.setVisible(true); + } else { + frame.pack(); + } + Robot robot = new Robot(); + robot.waitForIdle(); + checkGC(gc, frame); + for (Component comp : frame.getComponents()) { + Rectangle bounds = comp.getBounds(); + if (bounds.x != X || bounds.y != Y || bounds.width != WIDTH) { + System.err.println("Screen bounds:" + screen); + System.err.println("Component:" + comp); + throw new RuntimeException("Wrong bounds:" + bounds); + } + if (bounds.height > HEIGHT) { + // different check for HEIGHT, it depends on the font + throw new RuntimeException("Wrong height:" + bounds.height); + } + checkGC(gc, comp); + } + } finally { + frame.dispose(); + } + } + + private static void checkGC(GraphicsConfiguration gc, Component comp) { + GraphicsConfiguration compGC = comp.getGraphicsConfiguration(); + if (compGC != gc) { + System.err.println("Expected GC:" + gc); + System.err.println("Actual GC:" + compGC); + throw new RuntimeException(); + } + } +} diff --git a/test/jdk/java/awt/EmbeddedFrame/EmbeddedFrameGrabTest/EmbeddedFrameGrabTest.java b/test/jdk/java/awt/EmbeddedFrame/EmbeddedFrameGrabTest/EmbeddedFrameGrabTest.java index e7fb0b8c6a067..e186e7ac1320a 100644 --- a/test/jdk/java/awt/EmbeddedFrame/EmbeddedFrameGrabTest/EmbeddedFrameGrabTest.java +++ b/test/jdk/java/awt/EmbeddedFrame/EmbeddedFrameGrabTest/EmbeddedFrameGrabTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @key headful - * @bug 6345003 8171363 + * @bug 6345003 8171363 8211999 * @summary grab problems with EmbeddedFrame * @requires (os.family == "windows") * @modules java.desktop/java.awt.peer @@ -67,6 +67,7 @@ private void init() throws Exception { final Frame frame = new Frame("AWT Frame"); frame.pack(); frame.setSize(200, 200); + frame.setLocationRelativeTo(null); FramePeer frame_peer = AWTAccessor.getComponentAccessor() .getPeer(frame); Class comp_peer_class @@ -88,6 +89,7 @@ private void init() throws Exception { final Panel p = new Panel(); p.setLayout(new BorderLayout()); embedded_frame.add(p, BorderLayout.CENTER); + embedded_frame.setBounds(0, 0, 150, 150); embedded_frame.validate(); p.add(combo); p.validate(); diff --git a/test/jdk/java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenSmall.java b/test/jdk/java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenSmall.java index eac72fd0fbbaa..bd749cc94a99a 100644 --- a/test/jdk/java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenSmall.java +++ b/test/jdk/java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenSmall.java @@ -21,6 +21,7 @@ * questions. */ +import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; @@ -30,7 +31,7 @@ /** * @test - * @bug 8176359 8231564 + * @bug 8176359 8231564 8211999 * @key headful * @requires (os.family == "windows" | os.family == "mac") * @summary setMaximizedBounds() should work if set to the screen other than @@ -58,15 +59,23 @@ public static void main(String[] args) throws Exception { Rectangle framAt = gd1.getDefaultConfiguration().getBounds(); framAt.grow(-framAt.width / 2 + 100, -framAt.height / 2 + 100); for (GraphicsDevice gd2 : gds) { - Rectangle maxTo = gd2.getDefaultConfiguration().getBounds(); - maxTo.grow(-maxTo.width / 2 + 120, -maxTo.height / 2 + 120); Frame frame = new Frame(gd1.getDefaultConfiguration()); try { + frame.setLayout(null); // trigger use the minimum size of + // the peer frame.setBounds(framAt); + frame.setVisible(true); robot.waitForIdle(); robot.delay(1000); + Dimension minimumSize = frame.getMinimumSize(); + minimumSize.width = Math.max(minimumSize.width, 120); + minimumSize.height = Math.max(minimumSize.height, 120); + Rectangle maxTo = gd2.getDefaultConfiguration().getBounds(); + maxTo.grow(-maxTo.width / 2 + minimumSize.width, + -maxTo.height / 2 + minimumSize.height); + frame.setMaximizedBounds(maxTo); frame.setExtendedState(Frame.MAXIMIZED_BOTH); robot.waitForIdle(); diff --git a/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java new file mode 100644 index 0000000000000..d444ee5f0f1be --- /dev/null +++ b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.DisplayMode; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; + +/** + * @test + * @bug 8211999 + * @key headful + * @summary verifies the full-screen window bounds and graphics configuration + */ +public final class FullscreenWindowProps { + + public static void main(String[] args) throws Exception { + var ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + + if (!gd.isFullScreenSupported()) { + return; + } + + Frame frame = new Frame() { + @Override + public void paint(Graphics g) { + super.paint(g); + g.setColor(Color.GREEN); + g.fillRect(0, 0, getWidth(), getHeight()); + } + }; + try { + frame.setBackground(Color.MAGENTA); + frame.setVisible(true); + gd.setFullScreenWindow(frame); + Thread.sleep(4000); + + for (DisplayMode dm : gd.getDisplayModes()) { + if (dm.getWidth() == 1024 && dm.getHeight() == 768) { + gd.setDisplayMode(dm); + Thread.sleep(4000); + break; + } + } + + GraphicsConfiguration frameGC = frame.getGraphicsConfiguration(); + Rectangle frameBounds = frame.getBounds(); + + GraphicsConfiguration screenGC = gd.getDefaultConfiguration(); + Rectangle screenBounds = screenGC.getBounds(); + + if (frameGC != screenGC) { + System.err.println("Expected: " + screenGC); + System.err.println("Actual: " + frameGC); + throw new RuntimeException(); + } + + checkSize(frameBounds.x, screenBounds.x, "x"); + checkSize(frameBounds.y, screenBounds.y, "Y"); + checkSize(frameBounds.width, screenBounds.width, "width"); + checkSize(frameBounds.height, screenBounds.height, "height"); + } finally { + gd.setFullScreenWindow(null); + frame.dispose(); + Thread.sleep(10000); + } + } + + private static void checkSize(int actual, int expected, String prop) { + if (Math.abs(actual - expected) > 30) { // let's allow size variation, + // the bug is reproduced anyway + System.err.println("Expected: " + expected); + System.err.println("Actual: " + actual); + throw new RuntimeException(prop + " is wrong"); + } + } +} \ No newline at end of file diff --git a/test/jdk/java/awt/List/ListMultipleSelectTest/ListMultipleSelectTest.java b/test/jdk/java/awt/List/ListMultipleSelectTest/ListMultipleSelectTest.java new file mode 100644 index 0000000000000..ef8ee7def0d1d --- /dev/null +++ b/test/jdk/java/awt/List/ListMultipleSelectTest/ListMultipleSelectTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.List; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; + +/** + * @test + * @bug 4909485 8211999 + * @key headful + */ +public final class ListMultipleSelectTest { + + private static List aList; + private static Panel panel; + private static Point p; + private static Robot robot; + private static int[] selected; + + public static void main(String[] args) throws Exception { + Frame frame = new Frame(); + try { + panel = new Panel(); + aList = new List(); + aList.add("Test item1"); + aList.add("Test item2"); + aList.add("Test item3"); + aList.add("Test item4"); + + frame.setLayout(new FlowLayout()); //list should fill whole frame's space + panel.add(aList); + frame.setSize(200, 200); + frame.setLocationRelativeTo(null); + panel.setVisible(true); + frame.add(panel); + frame.setVisible(true); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(" InterruptedException. Test failed. "); + } + + Dimension listSize = aList.getSize(); + p = aList.getLocationOnScreen(); + int stepY = listSize.height / aList.getItemCount(); + +// System.out.println("itemCount = "+aList.getItemCount()); +// System.out.println("Size Of aList="+ listSize); +// System.out.println("stepY = "+stepY); +// System.out.println("Point:" +p); + System.out.println("Multiple mode is ON"); + aList.setMultipleMode(true); +//================================================= + robot = new Robot(); + robot.setAutoDelay(0); + robot.setAutoWaitForIdle(false); + robot.delay(10); + robot.waitForIdle(); + +//================================================= + + for (int i = 0; i < aList.getItemCount(); i++) { + //select all items in the List + mousePress(p.x + listSize.width / 2, p.y + stepY / 2 + stepY * i); + } + + selected = aList.getSelectedIndexes(); + System.out.println("Multiple mode is ON"); + aList.setMultipleMode(true); + int[] newSelected = aList.getSelectedIndexes(); + if (!java.util.Arrays.equals(newSelected, selected)) { + throw new RuntimeException(" Incorrect item remains selected " + + "after setMultipleMode(true). "); + } + + aList.setMultipleMode(false); + System.out.println("Multiple mode is OFF"); + selected = aList.getSelectedIndexes(); + if (selected[0] != 3 || selected.length != 1) { + throw new RuntimeException(" Incorrect item remains selected " + + "after setMultipleMode(false) or it is more then one " + + "item remaining.Forward. "); + } + + System.out.println("Multiple mode is ON"); + aList.setMultipleMode(true); + + if (selected[0] != 3 || selected.length != 1) { + throw new RuntimeException(" Incorrect item remains selected " + + "after setMultipleMode(true) or it is more then one " + + "item remaining. "); + } + + deselectAll(); + for (int i = aList.getItemCount() - 1; i >= 0; i--) { + mousePress(p.x + listSize.width / 2, p.y + stepY / 2 + stepY * i); + } + + System.out.println("Multiple mode is OFF"); + aList.setMultipleMode(false); + + selected = aList.getSelectedIndexes(); + if (selected[0] != 0 || selected.length != 1) { + throw new RuntimeException(" Incorrect item remains selected " + + "after setMultipleMode(false) or it is more then one " + + "item remaining.Backward. "); + } + System.out.println("Test succeeded."); + } finally { + frame.dispose(); + } + } + + private static void mousePress(int x, int y) { + robot.mouseMove(x, y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.delay(1000); + } + + private static void deselectAll() { + for (int i = 0; i < selected.length; i++) { + aList.deselect(selected[i]); + } + } +} diff --git a/test/jdk/java/awt/Multiscreen/MouseEventTest/MouseEventTest.java b/test/jdk/java/awt/Multiscreen/MouseEventTest/MouseEventTest.java index 45d9aa5c80b61..c6494dc314ba5 100644 --- a/test/jdk/java/awt/Multiscreen/MouseEventTest/MouseEventTest.java +++ b/test/jdk/java/awt/Multiscreen/MouseEventTest/MouseEventTest.java @@ -24,7 +24,7 @@ /* @test @key headful - @bug 8017472 + @bug 8017472 8211999 @summary MouseEvent has wrong coordinates when using multiple monitors @run main MouseEventTest */ diff --git a/test/jdk/java/awt/Robot/CheckCommonColors/CheckCommonColors.java b/test/jdk/java/awt/Robot/CheckCommonColors/CheckCommonColors.java index 061b8953200df..57419b0ac5aa9 100644 --- a/test/jdk/java/awt/Robot/CheckCommonColors/CheckCommonColors.java +++ b/test/jdk/java/awt/Robot/CheckCommonColors/CheckCommonColors.java @@ -24,6 +24,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Rectangle; import java.awt.Robot; @@ -38,7 +40,7 @@ /** * @test * @key headful - * @bug 8215105 + * @bug 8215105 8211999 * @summary tests that Robot can capture the common colors without artifacts */ public final class CheckCommonColors { @@ -48,16 +50,20 @@ public final class CheckCommonColors { public static void main(final String[] args) throws Exception { robot = new Robot(); - try { - test(); - } finally { - frame.dispose(); + var ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + for (GraphicsDevice gd : ge.getScreenDevices()) { + try { + test(gd.getDefaultConfiguration().getBounds()); + } finally { + frame.dispose(); + } } } - private static void test() { + private static void test(Rectangle screen) { frame.setSize(400, 400); - frame.setLocationRelativeTo(null); + frame.setLocation((int)screen.getCenterX() - 200, + (int)screen.getCenterY() - 200); frame.setUndecorated(true); for (final Color color : List.of(Color.WHITE, Color.LIGHT_GRAY, Color.GRAY, Color.DARK_GRAY, @@ -69,16 +75,25 @@ private static void test() { robot.waitForIdle(); frame.setBackground(color); frame.setVisible(true); - checkPixels(color); + checkPixels(color, true); + checkPixels(color, false); } } - private static void checkPixels(final Color color) { + private static void checkPixels(final Color color, boolean useRect) { + System.out.println("color = " + color + ", useRect = " + useRect); int attempt = 0; while (true) { Point p = frame.getLocationOnScreen(); p.translate(frame.getWidth() / 2, frame.getHeight() / 2); - Color pixel = robot.getPixelColor(p.x, p.y); + Color pixel; + Rectangle rect = new Rectangle(p.x, p.y, 1, 1); + if (useRect) { + BufferedImage bi = robot.createScreenCapture(rect); + pixel = new Color(bi.getRGB(0, 0)); + } else { + pixel = robot.getPixelColor(rect.x, rect.y); + } if (color.equals(pixel)) { return; } diff --git a/test/jdk/java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java b/test/jdk/java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java index 5fe2f9b2e5c4e..b6a186290225a 100644 --- a/test/jdk/java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java +++ b/test/jdk/java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ /** * @test * @key headful - * @bug 8201364 8232433 + * @bug 8201364 8232433 8211999 * @summary Component.getLocation() should returns correct location if * Component.setBounds() was ignored by the OS */ @@ -64,8 +64,11 @@ private static void test(final boolean undecorated) throws AWTException { Rectangle bounds = device.getDefaultConfiguration().getBounds(); test(robot, frame, bounds.x, bounds.y); test(robot, frame, bounds.width, bounds.y); + test(robot, frame, bounds.x + bounds.width, bounds.y); test(robot, frame, bounds.x, bounds.height); + test(robot, frame, bounds.x, bounds.y + bounds.height); test(robot, frame, bounds.width, bounds.height); + test(robot, frame, bounds.x + bounds.width, bounds.y + bounds.height); } frame.dispose(); } diff --git a/test/jdk/java/awt/Window/SlowMotion/SlowMotion.java b/test/jdk/java/awt/Window/SlowMotion/SlowMotion.java new file mode 100644 index 0000000000000..ac6c51ad62152 --- /dev/null +++ b/test/jdk/java/awt/Window/SlowMotion/SlowMotion.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; + +/** + * @test + * @key headful + * @bug 8211999 + * @run main/timeout=300 SlowMotion + * @summary places the window on the screen outside of any insets, and waits to + * catch any strange window moving + */ +public final class SlowMotion { + + // some additional space, if getScreenInsets() does not work, say on Linux + private static final int SAFE = 100; + private static final int HEIGHT = 350; + private static final int WIDTH = 279; + private static Robot robot; + + public static void main(final String[] args) throws Exception { + robot = new Robot(); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] sds = ge.getScreenDevices(); + + for (GraphicsDevice sd : sds) { + GraphicsConfiguration gc = sd.getDefaultConfiguration(); + Rectangle bounds = gc.getBounds(); + bounds.translate(SAFE, SAFE); + Point point = new Point(bounds.x, bounds.y); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc); + while (point.y < bounds.y + bounds.height - insets.bottom - HEIGHT - SAFE * 2) { + while (point.x < bounds.x + bounds.width - insets.right - WIDTH - SAFE * 2) { + test(point, new Frame()); + test(point, new Window(null)); + test(point, new Dialog((Dialog) null)); + point.translate(bounds.width / 6, 0); + } + point.setLocation(bounds.x, point.y + bounds.height / 5); + } + } + } + + private static void test(final Point loc, Window window) { + try { + window.setBounds(loc.x, loc.y, WIDTH, HEIGHT); + window.setVisible(true); + robot.delay(1000); // intentionally very slow, we try to catch + // very very last suspicion event + Rectangle bounds = window.getBounds(); + if (loc.x != bounds.x || loc.y != bounds.y + || bounds.width != WIDTH || bounds.height != HEIGHT) { + System.err.println("Component = " + window); + System.err.println("Actual bounds = " + bounds); + System.err.println("Expected location = " + loc); + System.err.println("Expected width = " + WIDTH); + System.err.println("Expected height = " + HEIGHT); + throw new RuntimeException(); + } + } finally { + window.dispose(); + } + } +} diff --git a/test/jdk/java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java b/test/jdk/java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java new file mode 100644 index 0000000000000..4ac5aeeac1a70 --- /dev/null +++ b/test/jdk/java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Color; +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Window; + +/** + * @test + * @key headful + * @bug 8211999 + * @summary The test creates the packed/unpacked top level components on the + * different screens and compares their bounds + * @run main/othervm WindowSizeDifferentScreens + * @run main/othervm -Dsun.java2d.uiScale=1 WindowSizeDifferentScreens + * @run main/othervm -Dsun.java2d.uiScale=1.25 WindowSizeDifferentScreens + */ +public final class WindowSizeDifferentScreens { + + public static void main(String[] args) throws Exception { + test("window"); + test("dialog"); + test("frame"); + } + + private static void test(String top) throws Exception { + var ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + Robot robot = new Robot(); + Window main = getTopLevel(top); + try { + main.setVisible(true); + robot.waitForIdle(); + main.setSize(500, 500); + robot.waitForIdle(); + for (GraphicsDevice gd : ge.getScreenDevices()) { + Rectangle bounds = gd.getDefaultConfiguration().getBounds(); + Point point = bounds.getLocation(); + point.translate(100, 100); + main.setLocation(point); + main.setBackground(Color.RED); + robot.waitForIdle(); + + Window packed = getTopLevel(top); + Window unpacked = getTopLevel(top); + try { + packed.pack(); + robot.waitForIdle(); + packed.setBackground(Color.GREEN); + unpacked.setBackground(Color.BLUE); + packed.setSize(500, 500); + unpacked.setSize(500, 500); + packed.setLocation(point); + unpacked.setLocation(point); + robot.waitForIdle(); + packed.setVisible(true); + unpacked.setVisible(true); + robot.waitForIdle(); + Rectangle mBounds = main.getBounds(); + Rectangle pBounds = packed.getBounds(); + Rectangle uBounds = unpacked.getBounds(); + + if (!mBounds.equals(uBounds) || + !mBounds.equals(pBounds)) { + System.err.println("Expected bounds: " + mBounds); + System.err.println("Actual unpacked: " + uBounds); + System.err.println("Actual packed: " + pBounds); + throw new RuntimeException(); + } + } finally { + packed.dispose(); + unpacked.dispose(); + } + } + } finally { + main.dispose(); + } + } + + private static Window getTopLevel(String top) { + return switch (top) { + case "window" -> new Window(null); + case "dialog" -> new Dialog((Dialog) null); + case "frame" -> new Frame(); + default -> throw new IllegalArgumentException("Unexpected: " + top); + }; + } +} diff --git a/test/jdk/java/awt/dnd/Button2DragTest/Button2DragTest.java b/test/jdk/java/awt/dnd/Button2DragTest/Button2DragTest.java index 9400f854da124..75e32db4ed96e 100644 --- a/test/jdk/java/awt/dnd/Button2DragTest/Button2DragTest.java +++ b/test/jdk/java/awt/dnd/Button2DragTest/Button2DragTest.java @@ -23,10 +23,10 @@ import java.awt.Color; import java.awt.Frame; -import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Robot; import java.awt.datatransfer.StringSelection; import java.awt.dnd.DnDConstants; @@ -47,7 +47,7 @@ /** * @test * @key headful - * @bug 4955110 8238575 + * @bug 4955110 8238575 8211999 * @summary tests that DragSourceDragEvent.getDropAction() accords to its new * spec (does not depend on the user drop action) * @library ../../regtesthelpers @@ -57,6 +57,7 @@ */ public final class Button2DragTest { + private static final int SIZE = 200; private volatile boolean dropSuccess; private volatile boolean locationValid = true; @@ -79,8 +80,8 @@ public void run() { final DragSourceListener dragSourceListener = new DragSourceListener() { private void checkLocation(DragSourceEvent dsde) { if (!frame.getBounds().contains(dsde.getLocation())) { - System.err.println("Expected in" + frame.getBounds()); - System.err.println("Actual" + dsde.getLocation()); + System.err.println("Expected in: " + frame.getBounds()); + System.err.println("Actual: " + dsde.getLocation()); locationValid = false; } } @@ -130,8 +131,10 @@ public void drop(DropTargetDropEvent dtde) { frame.setBackground(Color.GREEN); frame.setUndecorated(true); - frame.setSize(200, 200); - frame.setLocationRelativeTo(null); + Rectangle screen = frame.getGraphicsConfiguration().getBounds(); + int x = (int) (screen.getCenterX() - SIZE / 2); + int y = (int) (screen.getCenterY() - SIZE / 2); + frame.setBounds(x, y, SIZE, SIZE); frame.setVisible(true); Robot robot = Util.createRobot(); diff --git a/test/jdk/javax/swing/JTextArea/8149849/DNDTextToScaledArea.java b/test/jdk/javax/swing/JTextArea/8149849/DNDTextToScaledArea.java index a68a11c5303e5..f0c4234b10e32 100644 --- a/test/jdk/javax/swing/JTextArea/8149849/DNDTextToScaledArea.java +++ b/test/jdk/javax/swing/JTextArea/8149849/DNDTextToScaledArea.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,10 @@ import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; import java.awt.Point; +import java.awt.Rectangle; import java.awt.Robot; import java.awt.event.InputEvent; import javax.swing.JFrame; @@ -35,13 +38,15 @@ /** * @test * @key headful - * @bug 8149849 + * @bug 8149849 8211999 * @summary [hidpi] DnD issues (cannot DnD from JFileChooser to JEditorPane or * other text component) when scale > 1 + * @run main/othervm DNDTextToScaledArea * @run main/othervm -Dsun.java2d.uiScale=2 DNDTextToScaledArea */ public class DNDTextToScaledArea { + private static final int SIZE = 300; private static final String TEXT = "ABCDEFGH"; private static JFrame frame; private static JTextArea srcTextArea; @@ -51,10 +56,17 @@ public class DNDTextToScaledArea { private static volatile boolean passed = false; public static void main(String[] args) throws Exception { + var lge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + for (GraphicsDevice device : lge.getScreenDevices()) { + test(device); + } + } + + private static void test(GraphicsDevice device) throws Exception { Robot robot = new Robot(); - robot.setAutoDelay(50); + robot.setAutoDelay(150); - SwingUtilities.invokeAndWait(DNDTextToScaledArea::createAndShowGUI); + SwingUtilities.invokeAndWait(() -> createAndShowGUI(device)); robot.waitForIdle(); SwingUtilities.invokeAndWait(() -> { @@ -62,6 +74,11 @@ public static void main(String[] args) throws Exception { dstPoint = getPoint(dstTextArea, 0.75); }); robot.waitForIdle(); + // check the destination + robot.mouseMove(dstPoint.x, dstPoint.y); + robot.mousePress(InputEvent.BUTTON1_MASK); + robot.mouseRelease(InputEvent.BUTTON1_MASK); + robot.waitForIdle(); dragAndDrop(robot, srcPoint, dstPoint); robot.waitForIdle(); @@ -77,11 +94,12 @@ public static void main(String[] args) throws Exception { } } - private static void createAndShowGUI() { - - frame = new JFrame(); - frame.setSize(300, 300); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + private static void createAndShowGUI(GraphicsDevice device) { + frame = new JFrame(device.getDefaultConfiguration()); + Rectangle screen = device.getDefaultConfiguration().getBounds(); + int x = (int) (screen.getCenterX() - SIZE / 2); + int y = (int) (screen.getCenterY() - SIZE / 2); + frame.setBounds(x, y, SIZE, SIZE); JPanel panel = new JPanel(new BorderLayout()); From 5de99da75c0b295328d382446b8751ae2a9720f5 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Wed, 11 Nov 2020 01:31:03 +0000 Subject: [PATCH 059/124] 8237495: Java MIDI fails with a dereferenced memory error when asked to send a raw 0xF7 Reviewed-by: kizune --- .../share/native/libjsound/MidiOutDevice.c | 4 +- .../SysexMessage/SendRawSysexMessage.java | 121 ++++++++++++++++++ 2 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java diff --git a/src/java.desktop/share/native/libjsound/MidiOutDevice.c b/src/java.desktop/share/native/libjsound/MidiOutDevice.c index 7411564cc5f80..3738eba9381fa 100644 --- a/src/java.desktop/share/native/libjsound/MidiOutDevice.c +++ b/src/java.desktop/share/native/libjsound/MidiOutDevice.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -133,7 +133,7 @@ Java_com_sun_media_sound_MidiOutDevice_nSendLongMessage(JNIEnv* e, jobject thisO } /* "continuation" sysex messages start with F7 (instead of F0), but are sent without the F7. */ - if (data[0] == 0xF7) { + if (data[0] == 0xF7 && size > 1) { data++; size--; } diff --git a/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java b/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java new file mode 100644 index 0000000000000..591def651fb39 --- /dev/null +++ b/test/jdk/javax/sound/midi/SysexMessage/SendRawSysexMessage.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiMessage; +import javax.sound.midi.MidiSystem; +import javax.sound.midi.MidiUnavailableException; +import javax.sound.midi.Receiver; +import javax.sound.midi.ShortMessage; +import javax.sound.midi.SysexMessage; + +import static javax.sound.midi.SysexMessage.SPECIAL_SYSTEM_EXCLUSIVE; +import static javax.sound.midi.SysexMessage.SYSTEM_EXCLUSIVE; + +/** + * @test + * @bug 8237495 + * @summary fail with a dereferenced memory error when asked to send a raw 0xF7 + */ +public final class SendRawSysexMessage { + + private static final class RawMidiMessage extends MidiMessage { + @Override + public RawMidiMessage clone() { + return new RawMidiMessage(getMessage()); + } + @Override + public int getStatus() { + return SYSTEM_EXCLUSIVE; // not that this really matters + } + + RawMidiMessage(byte[] data) { + super(data.clone()); + } + } + + public static void main(String[] args) throws Exception { + MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); + System.err.println("List of devices to test:"); + for (int i = 0; i < infos.length; i++) { + System.err.printf("\t%d.\t%s%n", i, infos[i]); + } + for (int i = 0; i < infos.length; i++) { + System.err.printf("%d.\t%s%n", i, infos[i]); + try { + test(infos[i]); + } catch (MidiUnavailableException ignored){ + // try next + } + } + } + + private static void test(MidiDevice.Info info) throws Exception { + try (MidiDevice device = MidiSystem.getMidiDevice(info)) { + System.err.println("Sending to " + device + " (" + info + ")"); + if (!device.isOpen()) + device.open(); + try (Receiver r = device.getReceiver()) { + System.err.println("note on"); + r.send(new ShortMessage(ShortMessage.NOTE_ON, 5, 5), -1); + System.err.println("sysex"); + r.send(new SysexMessage(new byte[]{ + (byte) SYSTEM_EXCLUSIVE, 0x0, 0x03, 0x04, + (byte) SPECIAL_SYSTEM_EXCLUSIVE}, 5), -1); + System.err.println("raw 1"); + r.send(new RawMidiMessage(new byte[]{ + (byte) 0x02, 0x02, 0x03, 0x04}), -1); + System.err.println("raw 2"); + r.send(new RawMidiMessage(new byte[]{ + (byte) 0x09, 0x02, 0x03, 0x04}), -1); + System.err.println("raw 3"); + r.send(new RawMidiMessage(new byte[]{ + (byte) SYSTEM_EXCLUSIVE, 0x02, 0x03, 0x04}), -1); + System.err.println("raw 4"); + r.send(new RawMidiMessage(new byte[]{ + (byte) 0x02, 0x02, 0x03, 0x04}), -1); + System.err.println("raw 5"); + r.send(new RawMidiMessage(new byte[]{ + (byte) 0x02, 0x02, 0x03, + (byte) SPECIAL_SYSTEM_EXCLUSIVE}), -1); + System.err.println("raw 6"); + r.send(new RawMidiMessage(new byte[]{ + (byte) SYSTEM_EXCLUSIVE, 0x02, 0x03, 0x04}), -1); + System.err.println("sleep"); + Thread.sleep(1000); + System.err.println("raw 7"); + r.send(new RawMidiMessage(new byte[]{ + (byte) 0x02, 0x02, 0x03, 0x04}), -1); + System.err.println("sleep"); + Thread.sleep(1000); + System.err.println("raw 8"); + r.send(new RawMidiMessage(new byte[]{ + (byte) SPECIAL_SYSTEM_EXCLUSIVE}), -1); + System.err.println("note off"); + r.send(new ShortMessage(ShortMessage.NOTE_OFF, 5, 5), -1); + System.err.println("done, should quit"); + System.err.println(); + } + } + } +} From 8638cd9acf5c33612eaf82ae3219f68456ea9621 Mon Sep 17 00:00:00 2001 From: Dong Bo Date: Wed, 11 Nov 2020 01:51:27 +0000 Subject: [PATCH 060/124] 8255625: AArch64: Implement Base64.encodeBlock accelerator/intrinsic Reviewed-by: aph --- .../cpu/aarch64/stubGenerator_aarch64.cpp | 148 ++++++++++++++++++ .../cpu/aarch64/vm_version_aarch64.cpp | 4 + .../openjdk/bench/java/util/Base64Encode.java | 74 +++++++++ 3 files changed, 226 insertions(+) create mode 100644 test/micro/org/openjdk/bench/java/util/Base64Encode.java diff --git a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp index 93ccbb7e85963..09ea53871658a 100644 --- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp @@ -5403,6 +5403,150 @@ class StubGenerator: public StubCodeGenerator { return start; } + void generate_base64_encode_simdround(Register src, Register dst, + FloatRegister codec, u8 size) { + + FloatRegister in0 = v4, in1 = v5, in2 = v6; + FloatRegister out0 = v16, out1 = v17, out2 = v18, out3 = v19; + FloatRegister ind0 = v20, ind1 = v21, ind2 = v22, ind3 = v23; + + Assembler::SIMD_Arrangement arrangement = size == 16 ? __ T16B : __ T8B; + + __ ld3(in0, in1, in2, arrangement, __ post(src, 3 * size)); + + __ ushr(ind0, arrangement, in0, 2); + + __ ushr(ind1, arrangement, in1, 2); + __ shl(in0, arrangement, in0, 6); + __ orr(ind1, arrangement, ind1, in0); + __ ushr(ind1, arrangement, ind1, 2); + + __ ushr(ind2, arrangement, in2, 4); + __ shl(in1, arrangement, in1, 4); + __ orr(ind2, arrangement, in1, ind2); + __ ushr(ind2, arrangement, ind2, 2); + + __ shl(ind3, arrangement, in2, 2); + __ ushr(ind3, arrangement, ind3, 2); + + __ tbl(out0, arrangement, codec, 4, ind0); + __ tbl(out1, arrangement, codec, 4, ind1); + __ tbl(out2, arrangement, codec, 4, ind2); + __ tbl(out3, arrangement, codec, 4, ind3); + + __ st4(out0, out1, out2, out3, arrangement, __ post(dst, 4 * size)); + } + + /** + * Arguments: + * + * Input: + * c_rarg0 - src_start + * c_rarg1 - src_offset + * c_rarg2 - src_length + * c_rarg3 - dest_start + * c_rarg4 - dest_offset + * c_rarg5 - isURL + * + */ + address generate_base64_encodeBlock() { + + static const char toBase64[64] = { + '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', + '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', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + }; + + static const char toBase64URL[64] = { + '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', + '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', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' + }; + + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "encodeBlock"); + address start = __ pc(); + + Register src = c_rarg0; // source array + Register soff = c_rarg1; // source start offset + Register send = c_rarg2; // source end offset + Register dst = c_rarg3; // dest array + Register doff = c_rarg4; // position for writing to dest array + Register isURL = c_rarg5; // Base64 or URL chracter set + + // c_rarg6 and c_rarg7 are free to use as temps + Register codec = c_rarg6; + Register length = c_rarg7; + + Label ProcessData, Process48B, Process24B, Process3B, SIMDExit, Exit; + + __ add(src, src, soff); + __ add(dst, dst, doff); + __ sub(length, send, soff); + + // load the codec base address + __ lea(codec, ExternalAddress((address) toBase64)); + __ cbz(isURL, ProcessData); + __ lea(codec, ExternalAddress((address) toBase64URL)); + + __ BIND(ProcessData); + + // too short to formup a SIMD loop, roll back + __ cmp(length, (u1)24); + __ br(Assembler::LT, Process3B); + + __ ld1(v0, v1, v2, v3, __ T16B, Address(codec)); + + __ BIND(Process48B); + __ cmp(length, (u1)48); + __ br(Assembler::LT, Process24B); + generate_base64_encode_simdround(src, dst, v0, 16); + __ sub(length, length, 48); + __ b(Process48B); + + __ BIND(Process24B); + __ cmp(length, (u1)24); + __ br(Assembler::LT, SIMDExit); + generate_base64_encode_simdround(src, dst, v0, 8); + __ sub(length, length, 24); + + __ BIND(SIMDExit); + __ cbz(length, Exit); + + __ BIND(Process3B); + // 3 src bytes, 24 bits + __ ldrb(r10, __ post(src, 1)); + __ ldrb(r11, __ post(src, 1)); + __ ldrb(r12, __ post(src, 1)); + __ orrw(r11, r11, r10, Assembler::LSL, 8); + __ orrw(r12, r12, r11, Assembler::LSL, 8); + // codec index + __ ubfmw(r15, r12, 18, 23); + __ ubfmw(r14, r12, 12, 17); + __ ubfmw(r13, r12, 6, 11); + __ andw(r12, r12, 63); + // get the code based on the codec + __ ldrb(r15, Address(codec, r15, Address::uxtw(0))); + __ ldrb(r14, Address(codec, r14, Address::uxtw(0))); + __ ldrb(r13, Address(codec, r13, Address::uxtw(0))); + __ ldrb(r12, Address(codec, r12, Address::uxtw(0))); + __ strb(r15, __ post(dst, 1)); + __ strb(r14, __ post(dst, 1)); + __ strb(r13, __ post(dst, 1)); + __ strb(r12, __ post(dst, 1)); + __ sub(length, length, 3); + __ cbnz(length, Process3B); + + __ BIND(Exit); + __ ret(lr); + + return start; + } + // Continuation point for throwing of implicit exceptions that are // not handled in the current activation. Fabricates an exception // oop and initiates normal exception dispatching in this @@ -6481,6 +6625,10 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); } + if (UseBASE64Intrinsics) { + StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock(); + } + // data cache line writeback StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp index f9d836ffb411f..2a6553d9c2143 100644 --- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp @@ -332,6 +332,10 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } + if (FLAG_IS_DEFAULT(UseBASE64Intrinsics)) { + UseBASE64Intrinsics = true; + } + if (is_zva_enabled()) { if (FLAG_IS_DEFAULT(UseBlockZeroing)) { FLAG_SET_DEFAULT(UseBlockZeroing, true); diff --git a/test/micro/org/openjdk/bench/java/util/Base64Encode.java b/test/micro/org/openjdk/bench/java/util/Base64Encode.java new file mode 100644 index 0000000000000..297f216eb49ee --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/Base64Encode.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, Huawei Technologies Co. Ltd. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openjdk.micro.bench.java.util; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Base64; +import java.util.Random; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +public class Base64Encode { + + private Base64.Encoder encoder; + private ArrayList unencoded; + private byte[] encoded; + + private static final int TESTSIZE = 1000; + + @Param({"1", "2", "3", "6", "7", "9", "10", "48", "512", "1000", "20000"}) + private int maxNumBytes; + + @Setup + public void setup() { + Random r = new Random(1123); + + int dstLen = ((maxNumBytes + 16) / 3) * 4; + + encoder = Base64.getEncoder(); + unencoded = new ArrayList (); + encoded = new byte[dstLen]; + + for (int i = 0; i < TESTSIZE; i++) { + int srcLen = 1 + r.nextInt(maxNumBytes); + byte[] src = new byte[srcLen]; + r.nextBytes(src); + unencoded.add(src); + } + } + + @Benchmark + @OperationsPerInvocation(TESTSIZE) + public void testBase64Encode(Blackhole bh) { + for (byte[] s : unencoded) { + encoder.encode(s, encoded); + bh.consume(encoded); + } + } +} From 35284e4667fe904bd2df42d625e769dfd98920d3 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 11 Nov 2020 08:05:20 +0000 Subject: [PATCH 061/124] 8255916: [macos] javax/swing/JInternalFrame/6647340/bug6647340.java timed out Reviewed-by: serb --- .../JInternalFrame/6647340/bug6647340.java | 97 ++++++++----------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/test/jdk/javax/swing/JInternalFrame/6647340/bug6647340.java b/test/jdk/javax/swing/JInternalFrame/6647340/bug6647340.java index e277cc83e1089..15972c504fafe 100644 --- a/test/jdk/javax/swing/JInternalFrame/6647340/bug6647340.java +++ b/test/jdk/javax/swing/JInternalFrame/6647340/bug6647340.java @@ -26,9 +26,6 @@ * @bug 6647340 * @summary Checks that iconified internal frame follows * the main frame borders properly. - * @author Mikhail Lapshin - * @library /lib/client/ - * @build ExtendedRobot * @run main bug6647340 */ @@ -38,22 +35,22 @@ public class bug6647340 { private JFrame frame; - private Point location; + private volatile Point location; + private volatile Point iconloc; private JInternalFrame jif; - private static ExtendedRobot robot = createRobot(); + private static Robot robot; public static void main(String[] args) throws Exception { + robot = new Robot(); final bug6647340 test = new bug6647340(); try { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - test.setupUI(); - } - }); + SwingUtilities.invokeAndWait(() -> test.setupUI()); + robot.waitForIdle(); + robot.delay(1000); test.test(); } finally { if (test.frame != null) { - test.frame.dispose(); + SwingUtilities.invokeAndWait(() -> test.frame.dispose()); } } } @@ -77,77 +74,69 @@ private void setupUI() { } private void test() throws Exception { - sync(); test1(); - sync(); + + robot.waitForIdle(); + robot.delay(500); check1(); - sync(); + robot.waitForIdle(); + test2(); - sync(); + robot.waitForIdle(); + robot.delay(500); check2(); } private void test1() throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - setIcon(true); - location = jif.getDesktopIcon().getLocation(); - Dimension size = frame.getSize(); - frame.setSize(size.width + 100, size.height + 100); - } + SwingUtilities.invokeAndWait(() -> { + setIcon(true); + location = jif.getDesktopIcon().getLocation(); + Dimension size = frame.getSize(); + frame.setSize(size.width + 100, size.height + 100); }); } private void test2() throws Exception { - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - setIcon(false); - } + SwingUtilities.invokeAndWait(() -> { + setIcon(false); }); - sync(); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - Dimension size = frame.getSize(); - frame.setSize(size.width - 100, size.height - 100); - } + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + Dimension size = frame.getSize(); + frame.setSize(size.width - 100, size.height - 100); }); - sync(); - SwingUtilities.invokeAndWait(new Runnable() { - public void run() { - setIcon(true); - } + robot.waitForIdle(); + robot.delay(500); + + SwingUtilities.invokeAndWait(() -> { + setIcon(true); }); } - private void check1() { - if (!jif.getDesktopIcon().getLocation().equals(location)) { + private void check1() throws Exception { + SwingUtilities.invokeAndWait(() -> { + iconloc = jif.getDesktopIcon().getLocation(); + }); + if (!iconloc.equals(location)) { System.out.println("First test passed"); } else { throw new RuntimeException("Icon isn't shifted with the frame bounds"); } } - private void check2() { - if (jif.getDesktopIcon().getLocation().equals(location)) { + private void check2() throws Exception { + SwingUtilities.invokeAndWait(() -> { + iconloc = jif.getDesktopIcon().getLocation(); + }); + if (iconloc.equals(location)) { System.out.println("Second test passed"); } else { throw new RuntimeException("Icon isn't located near the frame bottom"); } } - private static void sync() { - robot.waitForIdle(); - } - private static ExtendedRobot createRobot() { - try { - ExtendedRobot robot = new ExtendedRobot(); - return robot; - }catch(Exception ex) { - ex.printStackTrace(); - throw new Error("Unexpected Failure"); - } - } - private void setIcon(boolean b) { try { jif.setIcon(b); From 5181f9cea27e16a64c1223c17efebf862a2fca80 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Wed, 11 Nov 2020 08:08:39 +0000 Subject: [PATCH 062/124] 7190978: javax/swing/JComponent/7154030/bug7154030.java fails on mac Reviewed-by: serb --- test/jdk/ProblemList.txt | 1 - .../swing/JComponent/7154030/bug7154030.java | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index d1c155a7b9caa..3cc33d44c6147 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -730,7 +730,6 @@ javax/sound/midi/Sequencer/MetaCallback.java 8178698 linux-all javax/swing/plaf/basic/BasicTextUI/8001470/bug8001470.java 8233177 linux-all,windows-all javax/swing/border/TestTitledBorderLeak.java 8213531 linux-all -javax/swing/JComponent/7154030/bug7154030.java 7190978 generic-all javax/swing/JComponent/6683775/bug6683775.java 8172337 generic-all javax/swing/JWindow/ShapedAndTranslucentWindows/ShapedTranslucentPerPixelTranslucentGradient.java 8233582 linux-all javax/swing/JWindow/ShapedAndTranslucentWindows/ShapedPerPixelTranslucentGradient.java 8233582 linux-all diff --git a/test/jdk/javax/swing/JComponent/7154030/bug7154030.java b/test/jdk/javax/swing/JComponent/7154030/bug7154030.java index 81a3548486d8b..4835c07bf94f1 100644 --- a/test/jdk/javax/swing/JComponent/7154030/bug7154030.java +++ b/test/jdk/javax/swing/JComponent/7154030/bug7154030.java @@ -39,6 +39,8 @@ import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; +import javax.imageio.ImageIO; +import java.io.File; /* * @test @@ -57,6 +59,7 @@ public class bug7154030 { private static JButton button = null; private static JFrame frame; + private static int locx, locy, frw, frh; public static void main(String[] args) throws Exception { try { @@ -86,14 +89,21 @@ public void run() { frame.setContentPane(desktop); frame.setSize(300, 300); - frame.setLocation(0, 0); + frame.setLocationRelativeTo(null); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } }); - robot.waitForIdle(500); - imageInit = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + robot.waitForIdle(1000); + + Rectangle bounds = frame.getBounds(); + locx = bounds.x; + locy = bounds.y; + frw = bounds.width; + frh = bounds.height; + + imageInit = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); SwingUtilities.invokeAndWait(new Runnable() { @@ -104,8 +114,10 @@ public void run() { }); robot.waitForIdle(500); - imageShow = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + imageShow = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); if (Util.compareBufferedImages(imageInit, imageShow)) { + ImageIO.write(imageInit, "png", new File("imageInit.png")); + ImageIO.write(imageShow, "png", new File("imageShow.png")); throw new Exception("Failed to show opaque button"); } @@ -119,9 +131,11 @@ public void run() { }); robot.waitForIdle(500); - imageHide = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + imageHide = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); if (!Util.compareBufferedImages(imageInit, imageHide)) { + ImageIO.write(imageInit, "png", new File("imageInit.png")); + ImageIO.write(imageHide, "png", new File("imageHide.png")); throw new Exception("Failed to hide opaque button"); } @@ -136,7 +150,7 @@ public void run() { }); robot.waitForIdle(500); - imageInit = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + imageInit = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); SwingUtilities.invokeAndWait(new Runnable() { @@ -147,7 +161,7 @@ public void run() { }); robot.waitForIdle(500); - imageShow = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + imageShow = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); SwingUtilities.invokeAndWait(new Runnable() { @@ -158,13 +172,17 @@ public void run() { }); if (Util.compareBufferedImages(imageInit, imageShow)) { + ImageIO.write(imageInit, "png", new File("imageInit.png")); + ImageIO.write(imageShow, "png", new File("imageShow.png")); throw new Exception("Failed to show non-opaque button"); } robot.waitForIdle(500); - imageHide = robot.createScreenCapture(new Rectangle(0, 0, 300, 300)); + imageHide = robot.createScreenCapture(new Rectangle(locx, locy, frw, frh)); if (!Util.compareBufferedImages(imageInit, imageHide)) { + ImageIO.write(imageInit, "png", new File("imageInit.png")); + ImageIO.write(imageHide, "png", new File("imageHide.png")); throw new Exception("Failed to hide non-opaque button"); } } finally { From 129ff97fe6de30926f426f0d1cce61176da44d9a Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 11 Nov 2020 09:11:02 +0000 Subject: [PATCH 063/124] 8231599: NPE when loading a preview classfile from a future Java version Reviewed-by: vromero --- .../com/sun/tools/javac/jvm/ClassReader.java | 6 +- .../TooNewMajorVersionTest.java | 133 ++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 4528d0de44da8..6f22fc9787008 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -2577,10 +2577,12 @@ private void readClassBuffer(ClassSymbol c) throws IOException { majorVersion = nextChar(); int maxMajor = Version.MAX().major; int maxMinor = Version.MAX().minor; + boolean previewClassFile = + minorVersion == ClassFile.PREVIEW_MINOR_VERSION; if (majorVersion > maxMajor || majorVersion * 1000 + minorVersion < Version.MIN().major * 1000 + Version.MIN().minor) { - if (majorVersion == (maxMajor + 1)) + if (majorVersion == (maxMajor + 1) && !previewClassFile) log.warning(Warnings.BigMajorVersion(currentClassFile, majorVersion, maxMajor)); @@ -2592,7 +2594,7 @@ private void readClassBuffer(ClassSymbol c) throws IOException { Integer.toString(maxMinor)); } - if (minorVersion == ClassFile.PREVIEW_MINOR_VERSION) { + if (previewClassFile) { if (!preview.isEnabled()) { log.error(preview.disabledError(currentClassFile, majorVersion)); } else { diff --git a/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java b/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java new file mode 100644 index 0000000000000..c3fd0e1d37629 --- /dev/null +++ b/test/langtools/tools/javac/preview/classReaderTest/TooNewMajorVersionTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8231599 + * @summary Verify javac does not crash on preview classfiles from the future + Java versions. + * @library /tools/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.jvm + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask + * @run main TooNewMajorVersionTest + */ + +import toolbox.JavacTask; +import toolbox.Task; +import toolbox.TestRunner; +import toolbox.ToolBox; + +import com.sun.tools.javac.jvm.ClassFile.Version; + +import java.io.RandomAccessFile; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +public class TooNewMajorVersionTest extends TestRunner { + + protected ToolBox tb; + + TooNewMajorVersionTest() { + super(System.err); + tb = new ToolBox(); + } + + public static void main(String... args) throws Exception { + TooNewMajorVersionTest t = new TooNewMajorVersionTest(); + t.runTests(); + } + + protected void runTests() throws Exception { + runTests(m -> new Object[] { Paths.get(m.getName()) }); + } + + @Test + public void brokenMajorVersionWithPreview(Path base) throws Exception { + Path src = base.resolve("src"); + tb.writeJavaFiles(src, """ + class C { + private Object o = null; + private boolean b = o instanceof String s; + } + """); + Path classes = base.resolve("classes"); + + Files.createDirectories(classes); + + for (int upgrade = 1; upgrade < 3; upgrade++) { + new JavacTask(tb) + .outdir(classes) + .options("-XDforcePreview", + "--enable-preview", + "--release", String.valueOf(Runtime.version().feature())) + .files(tb.findJavaFiles(src)) + .run() + .writeAll(); + + Path classfile = classes.resolve("C.class"); + int wrongMajor; + + try (RandomAccessFile f = new RandomAccessFile(classfile.toFile(), "rw")) { + f.readInt(); + short minor = f.readShort(); + if (minor != (-1)) { + throw new AssertionError("Unexpected minor version: " + minor); + } + long point = f.getFilePointer(); + short major = f.readShort(); + f.seek(point); + f.writeShort(wrongMajor = major + upgrade); + } + + Path test = base.resolve("test"); + tb.writeJavaFiles(test, "class Test extends C {}"); + Path testClasses = base.resolve("classes"); + + Files.createDirectories(testClasses); + + for (String extraOption : new String[] {"-XDignored", "--enable-preview"}) { + List log = new JavacTask(tb) + .outdir(testClasses) + .options("-XDrawDiagnostics", + "-classpath", classes.toString(), + "--release", String.valueOf(Runtime.version().feature()), + extraOption) + .files(tb.findJavaFiles(test)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + List expected = List.of( + "Test.java:1:20: compiler.err.cant.access: C, (compiler.misc.bad.class.file.header: C.class, (compiler.misc.wrong.version: " + wrongMajor + ", 65535, " + Version.MAX().major + ", 0))", + "1 error" + ); + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + } + } +} From 6e8b86281117efabe1b323737af9c24c79e3e8af Mon Sep 17 00:00:00 2001 From: prajwal_kumaraswamy Date: Wed, 11 Nov 2020 09:24:42 +0000 Subject: [PATCH 064/124] 8255559: Leak File Descriptors Because of ResolverLocalFilesystem#engineResolveURI() Reviewed-by: weijun --- .../xml/dsig/internal/dom/DOMReference.java | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java index b2ec7815af052..c44ffd93b454d 100644 --- a/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java +++ b/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java @@ -500,38 +500,44 @@ private byte[] transform(Data dereferencedData, } boolean secVal = Utils.secureValidation(context); - xi.setSecureValidation(secVal); - if (context instanceof XMLSignContext && c14n11 - && !xi.isOctetStream() && !xi.isOutputStreamSet()) { - TransformService spi = null; - if (provider == null) { - spi = TransformService.getInstance(c14nalg, "DOM"); - } else { - try { - spi = TransformService.getInstance(c14nalg, "DOM", provider); - } catch (NoSuchAlgorithmException nsae) { + try { + xi.setSecureValidation(secVal); + if (context instanceof XMLSignContext && c14n11 + && !xi.isOctetStream() && !xi.isOutputStreamSet()) { + TransformService spi = null; + if (provider == null) { spi = TransformService.getInstance(c14nalg, "DOM"); + } else { + try { + spi = TransformService.getInstance(c14nalg, "DOM", provider); + } catch (NoSuchAlgorithmException nsae) { + spi = TransformService.getInstance(c14nalg, "DOM"); + } } - } - DOMTransform t = new DOMTransform(spi); - Element transformsElem = null; - String dsPrefix = DOMUtils.getSignaturePrefix(context); - if (allTransforms.isEmpty()) { - transformsElem = DOMUtils.createElement( - refElem.getOwnerDocument(), - "Transforms", XMLSignature.XMLNS, dsPrefix); - refElem.insertBefore(transformsElem, - DOMUtils.getFirstChildElement(refElem)); + DOMTransform t = new DOMTransform(spi); + Element transformsElem = null; + String dsPrefix = DOMUtils.getSignaturePrefix(context); + if (allTransforms.isEmpty()) { + transformsElem = DOMUtils.createElement( + refElem.getOwnerDocument(), + "Transforms", XMLSignature.XMLNS, dsPrefix); + refElem.insertBefore(transformsElem, + DOMUtils.getFirstChildElement(refElem)); + } else { + transformsElem = DOMUtils.getFirstChildElement(refElem); + } + t.marshal(transformsElem, dsPrefix, + (DOMCryptoContext) context); + allTransforms.add(t); + xi.updateOutputStream(os, true); } else { - transformsElem = DOMUtils.getFirstChildElement(refElem); + xi.updateOutputStream(os); + } + } finally { + if(xi.getOctetStreamReal() != null) { + xi.getOctetStreamReal().close(); } - t.marshal(transformsElem, dsPrefix, - (DOMCryptoContext)context); - allTransforms.add(t); - xi.updateOutputStream(os, true); - } else { - xi.updateOutputStream(os); } } os.flush(); From 79ac041844fc2eeb1c6e6f0906f9597ead8461cd Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Wed, 11 Nov 2020 09:59:56 +0000 Subject: [PATCH 065/124] 8256025: AArch64: MachCallRuntimeNode::ret_addr_offset() is incorrect for stub calls Reviewed-by: aph --- src/hotspot/cpu/aarch64/aarch64.ad | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index 4c209d8698120..ff82cd08cc168 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -1753,7 +1753,9 @@ int MachCallDynamicJavaNode::ret_addr_offset() int MachCallRuntimeNode::ret_addr_offset() { // for generated stubs the call will be - // far_call(addr) + // bl(addr) + // or with far branches + // bl(trampoline_stub) // for real runtime callouts it will be six instructions // see aarch64_enc_java_to_runtime // adr(rscratch2, retaddr) @@ -1762,7 +1764,7 @@ int MachCallRuntimeNode::ret_addr_offset() { // blr(rscratch1) CodeBlob *cb = CodeCache::find_blob(_entry_point); if (cb) { - return MacroAssembler::far_branch_size(); + return 1 * NativeInstruction::instruction_size; } else { return 6 * NativeInstruction::instruction_size; } From 432c387e21c4291d353a6b30a4bbc2e41c944c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Casta=C3=B1eda=20Lozano?= Date: Wed, 11 Nov 2020 10:18:36 +0000 Subject: [PATCH 066/124] 8254317: C2: Resource consumption of ConvI2LNode::Ideal() grows exponentially Prevent exponential number of calls to ConvI2LNode::Ideal() when AddIs are used multiple times by other AddIs in the optimization ConvI2L(AddI(x, y)) -> AddL(ConvI2L(x), ConvI2L(y)). This is achieved by (1) reusing existing ConvI2Ls if possible rather than eagerly creating new ones and (2) postponing the optimization of newly created ConvI2Ls. Remove hook node solution introduced in 8217359, since this is subsumed by (2). Use phase->is_IterGVN() rather than can_reshape to check if ConvI2LNode::Ideal() is called within iterative GVN, for clarity. Add regression tests that cover different shapes and sizes of AddI subgraphs, implicitly checking (by not timing out) that there is no combinatorial explosion. Co-authored-by: Vladimir Ivanov Reviewed-by: vlivanov, kvn --- src/hotspot/share/opto/convertnode.cpp | 32 ++-- .../TestMoveConvI2LThroughAddIs.java | 164 ++++++++++++++++++ 2 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/conversions/TestMoveConvI2LThroughAddIs.java diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index 304b0380753d3..c08e8591fb1ba 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -258,14 +258,30 @@ static inline bool long_ranges_overlap(jlong lo1, jlong hi1, // Two ranges overlap iff one range's low point falls in the other range. return (lo2 <= lo1 && lo1 <= hi2) || (lo1 <= lo2 && lo2 <= hi1); } + +// If there is an existing ConvI2L node with the given parent and type, return +// it. Otherwise, create and return a new one. Both reusing existing ConvI2L +// nodes and postponing the idealization of new ones are needed to avoid an +// explosion of recursive Ideal() calls when compiling long AddI chains. +static Node* find_or_make_convI2L(PhaseIterGVN* igvn, Node* parent, + const TypeLong* type) { + Node* n = new ConvI2LNode(parent, type); + Node* existing = igvn->hash_find_insert(n); + if (existing != NULL) { + n->destruct(igvn); + return existing; + } + return igvn->register_new_node_with_optimizer(n); +} #endif //------------------------------Ideal------------------------------------------ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { + PhaseIterGVN *igvn = phase->is_IterGVN(); const TypeLong* this_type = this->type()->is_long(); Node* this_changed = NULL; - if (can_reshape) { + if (igvn != NULL) { // Do NOT remove this node's type assertion until no more loop ops can happen. if (phase->C->post_loop_opts_phase()) { const TypeInt* in_type = phase->type(in(1))->isa_int(); @@ -334,10 +350,9 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { Node* z = in(1); int op = z->Opcode(); if (op == Op_AddI || op == Op_SubI) { - if (!can_reshape) { - // Postpone this optimization to after parsing because with deep AddNode - // chains a large amount of dead ConvI2L nodes might be created that are - // not removed during parsing. As a result, we might hit the node limit. + if (igvn == NULL) { + // Postpone this optimization to iterative GVN, where we can handle deep + // AddI chains without an exponential number of recursive Ideal() calls. phase->record_for_igvn(this); return this_changed; } @@ -399,11 +414,8 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) { } assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow"); assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow"); - Node* cx = phase->C->constrained_convI2L(phase, x, TypeInt::make(rxlo, rxhi, widen), NULL); - Node *hook = new Node(1); - hook->init_req(0, cx); // Add a use to cx to prevent him from dying - Node* cy = phase->C->constrained_convI2L(phase, y, TypeInt::make(rylo, ryhi, widen), NULL); - hook->destruct(phase); + Node* cx = find_or_make_convI2L(igvn, x, TypeLong::make(rxlo, rxhi, widen)); + Node* cy = find_or_make_convI2L(igvn, y, TypeLong::make(rylo, ryhi, widen)); switch (op) { case Op_AddI: return new AddLNode(cx, cy); case Op_SubI: return new SubLNode(cx, cy); diff --git a/test/hotspot/jtreg/compiler/conversions/TestMoveConvI2LThroughAddIs.java b/test/hotspot/jtreg/compiler/conversions/TestMoveConvI2LThroughAddIs.java new file mode 100644 index 0000000000000..f93677b3754b4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/conversions/TestMoveConvI2LThroughAddIs.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.conversions; + +import java.util.Random; +import jdk.test.lib.Asserts; + +/* + * @test + * @bug 8254317 + * @requires vm.compiler2.enabled + * @summary Exercises the optimization that moves integer-to-long conversions + * upwards through different shapes of integer addition + * subgraphs. Contains three small functional tests and two stress + * tests that resulted in a compilation time and memory explosion + * before fixing bug 8254317. The stress tests run with -Xbatch to wait + * for C2, so that a timeout or an out-of-memory error is triggered if + * there was an explosion. These tests use a timeout of 30s to catch + * the explosion earlier. + * @library /test/lib / + * @run main/othervm + * compiler.conversions.TestMoveConvI2LThroughAddIs functional + * @run main/othervm/timeout=30 -Xbatch + * compiler.conversions.TestMoveConvI2LThroughAddIs stress1 + * @run main/othervm/timeout=30 -Xbatch + * compiler.conversions.TestMoveConvI2LThroughAddIs stress2 + */ + +public class TestMoveConvI2LThroughAddIs { + + // Number of repetitions of each test. Should be sufficiently large for the + // method under test to be compiled with C2. + static final int N = 100_000; + + // Chain-shaped functional test. + static long testChain(boolean cnd) { + int a = cnd ? 1 : 2; + int b = a + a; + int c = b + b; + int d = c + c; + return d; + } + + // Tree-shaped functional test. + static long testTree(boolean cnd) { + int a0 = cnd ? 1 : 2; + int a1 = cnd ? 1 : 2; + int a2 = cnd ? 1 : 2; + int a3 = cnd ? 1 : 2; + int a4 = cnd ? 1 : 2; + int a5 = cnd ? 1 : 2; + int a6 = cnd ? 1 : 2; + int a7 = cnd ? 1 : 2; + int b0 = a0 + a1; + int b1 = a2 + a3; + int b2 = a4 + a5; + int b3 = a6 + a7; + int c0 = b0 + b1; + int c1 = b2 + b3; + int d = c0 + c1; + return d; + } + + // DAG-shaped functional test. + static long testDAG(boolean cnd) { + int a0 = cnd ? 1 : 2; + int a1 = cnd ? 1 : 2; + int a2 = cnd ? 1 : 2; + int a3 = cnd ? 1 : 2; + int b0 = a0 + a1; + int b1 = a1 + a2; + int b2 = a2 + a3; + int c0 = b0 + b1; + int c1 = b1 + b2; + int d = c0 + c1; + return d; + } + + // Chain-shaped stress test. Before fixing bug 8254317, this test would + // result in an out-of-memory error after minutes running. + static long testStress1(boolean cnd) { + // C2 infers a finite, small value range for a. Note that there are + // different ways to achieve this, for example a might take the value of + // the induction variable in an outer counted loop. + int a = cnd ? 1 : 2; + // C2 fully unrolls this loop, creating a long chain of AddIs. + for (int i = 0; i < 28; i++) { + a = a + a; + } + // C2 places a ConvI2L at the end of the AddI chain. + return a; + } + + // DAG-shaped stress test. Before fixing bug 8254317, this test would result + // in an out-of-memory error after minutes running. + static long testStress2(boolean cnd) { + int a = cnd ? 1 : 2; + int b = a; + int c = a + a; + for (int i = 0; i < 20; i++) { + b = b + c; + c = b + c; + } + int d = b + c; + return d; + } + + public static void main(String[] args) { + // We use a random number generator to avoid constant propagation in C2 + // and produce a variable ("a" in the different tests) with a finite, + // small value range. + Random rnd = new Random(); + switch(args[0]) { + case "functional": + // Small, functional tests. + for (int i = 0; i < N; i++) { + boolean cnd = rnd.nextBoolean(); + Asserts.assertEQ(testChain(cnd), cnd ? 8L : 16L); + Asserts.assertEQ(testTree(cnd), cnd ? 8L : 16L); + Asserts.assertEQ(testDAG(cnd), cnd ? 8L : 16L); + } + break; + case "stress1": + // Chain-shaped stress test. + for (int i = 0; i < N; i++) { + boolean cnd = rnd.nextBoolean(); + Asserts.assertEQ(testStress1(cnd), + cnd ? 268435456L : 536870912L); + } + break; + case "stress2": + // DAG-shaped stress test. + for (int i = 0; i < N; i++) { + boolean cnd = rnd.nextBoolean(); + Asserts.assertEQ(testStress2(cnd), + cnd ? 701408733L : 1402817466L); + } + break; + default: + System.out.println("invalid mode"); + } + } +} From 362feaae24cb14e0ff851205644504703a236ae5 Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Wed, 11 Nov 2020 11:09:26 +0000 Subject: [PATCH 067/124] 8254661: arm32: additional cleanup after fixing SIGSEGV Reviewed-by: ngasson, shade --- src/hotspot/cpu/arm/interp_masm_arm.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/cpu/arm/interp_masm_arm.cpp b/src/hotspot/cpu/arm/interp_masm_arm.cpp index a819c0fa8486d..01ff3a5d39c98 100644 --- a/src/hotspot/cpu/arm/interp_masm_arm.cpp +++ b/src/hotspot/cpu/arm/interp_masm_arm.cpp @@ -983,7 +983,7 @@ void InterpreterMacroAssembler::lock_object(Register Rlock) { // Unlocks an object. Used in monitorexit bytecode and remove_activation. // -// Argument: R1: Points to BasicObjectLock structure for lock +// Argument: R0: Points to BasicObjectLock structure for lock // Throw an IllegalMonitorException if object is not locked by current thread // Blows volatile registers R0-R3, Rtemp, LR. Calls VM. void InterpreterMacroAssembler::unlock_object(Register Rlock) { @@ -996,8 +996,7 @@ void InterpreterMacroAssembler::unlock_object(Register Rlock) { const Register Robj = R2; const Register Rmark = R3; - const Register Rresult = R0; - assert_different_registers(Robj, Rmark, Rlock, R0, Rtemp); + assert_different_registers(Robj, Rmark, Rlock, Rtemp); const int obj_offset = BasicObjectLock::obj_offset_in_bytes(); const int lock_offset = BasicObjectLock::lock_offset_in_bytes (); From ed615e3ca0d681e8e67cdbf1d5d964979ccd7888 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Wed, 11 Nov 2020 11:43:47 +0000 Subject: [PATCH 068/124] 4907798: MEMORY LEAK: javax.swing.plaf.basic.BasicPopupMenuUI$MenuKeyboardHelper Reviewed-by: psadhukhan, serb --- .../swing/plaf/basic/BasicPopupMenuUI.java | 3 +- .../plaf/windows/WindowsPopupMenuUI.java | 1 + .../swing/JMenu/PopupReferenceMemoryLeak.java | 213 ++++++++++++++++++ 3 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 test/jdk/javax/swing/JMenu/PopupReferenceMemoryLeak.java diff --git a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java index 243d465ac3e18..a26f0511432f1 100644 --- a/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java +++ b/src/java.desktop/share/classes/javax/swing/plaf/basic/BasicPopupMenuUI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1226,6 +1226,7 @@ public void stateChanged(ChangeEvent ev) { // menu hidden -- return focus to where it had been before // and uninstall menu keybindings removeItems(); + menuInputMap = null; } else { if (popup != lastPopup) { receivedKeyPressed = false; diff --git a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java index d85880d47e75d..87e89fca3dce6 100644 --- a/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java +++ b/src/java.desktop/windows/classes/com/sun/java/swing/plaf/windows/WindowsPopupMenuUI.java @@ -108,6 +108,7 @@ public void stateChanged(ChangeEvent ev) { WindowsGraphicsUtils.repaintMnemonicsInWindow(win); } } + repaintRoot = null; } else { Component c = (Component)path[0]; if (c instanceof JPopupMenu) c = ((JPopupMenu)c).getInvoker(); diff --git a/test/jdk/javax/swing/JMenu/PopupReferenceMemoryLeak.java b/test/jdk/javax/swing/JMenu/PopupReferenceMemoryLeak.java new file mode 100644 index 0000000000000..ffc8c28f41837 --- /dev/null +++ b/test/jdk/javax/swing/JMenu/PopupReferenceMemoryLeak.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4907798 + * @key headful + * @summary Check for memory leak in menu subsystem + * @run main/othervm -Xmx8m PopupReferenceMemoryLeak + */ + +import javax.swing.AbstractAction; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +import javax.swing.WindowConstants; +import java.awt.BorderLayout; +import java.awt.Robot; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +import static javax.swing.UIManager.getInstalledLookAndFeels; + +public class PopupReferenceMemoryLeak { + static volatile WeakReference referenceToFrame1; + static JFrame frame1, frame2; + + public static void main(String[] args) throws Exception { + Robot robot = new Robot(); + robot.setAutoDelay(200); + for (UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) { + String lafName = laf.getName(); + System.out.println("Testing LaF: " + lafName); + if (lafName == null || lafName.startsWith("Mac OS X")) { + // Aqua Look and Feel uses system menus we can't really test it + continue; + } + setLookAndFeel(laf); + PopupReferenceMemoryLeak newTest = new PopupReferenceMemoryLeak(); + SwingUtilities.invokeAndWait(newTest::createUI); + try { + boolean passed = false; + robot.waitForIdle(); + Thread.sleep(2000); + robot.mouseMove(200, 200); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + robot.keyPress(KeyEvent.VK_F10); + robot.keyRelease(KeyEvent.VK_F10); + robot.keyPress(KeyEvent.VK_F); + robot.keyRelease(KeyEvent.VK_F); + robot.keyPress(KeyEvent.VK_C); + robot.keyRelease(KeyEvent.VK_C); + robot.waitForIdle(); + Thread.sleep(2000); + robot.mouseMove(600, 200); + robot.waitForIdle(); + Thread.sleep(2000); + robot.mousePress(InputEvent.BUTTON1_DOWN_MASK); + robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); + + // Workaround for Linux issues when sometimes there + // is a ref to last opened frame from native code + JFrame frame3 = new JFrame("Workaround"); + frame3.setSize(100, 100); + frame3.setLocation(0,0); + frame3.setVisible(true); + Thread.sleep(1000); + frame3.setVisible(false); + frame3.dispose(); + + // Force GC three times to see if it clears the old frame + for (int i = 0; i < 3; i++) { + try { + ArrayList gc = new ArrayList(); + while (true) { + gc.add(new int[100000]); + } + } catch (Throwable ignore) { + } + robot.waitForIdle(); + Thread.sleep(1000); + if (referenceToFrame1.get() == null) { + // Frame was released + passed = true; + break; + } + } + if (!passed) { + robot.waitForIdle(); + robot.keyPress(KeyEvent.VK_F10); + robot.keyRelease(KeyEvent.VK_F10); + robot.keyPress(KeyEvent.VK_T); + robot.keyRelease(KeyEvent.VK_T); + robot.keyPress(KeyEvent.VK_M); + robot.keyRelease(KeyEvent.VK_M); + robot.waitForIdle(); + Thread.sleep(2000); + for (int i = 0; i < 5; i++) { + try { + ArrayList gc = new ArrayList(); + while (true) { + gc.add(new int[100000]); + } + } catch (Throwable ignore) { + } + robot.waitForIdle(); + Thread.sleep(1000); + if (referenceToFrame1.get() == null) { + // Frame was released + throw new RuntimeException("Frame cleared only after menu activated on frame2"); + } + } + throw new RuntimeException("Test finished but menu has not cleared the reference!"); + } + } catch (Exception re) { + throw new RuntimeException(re.getLocalizedMessage()); + } finally { + if (frame2 != null) { + frame2.setVisible(false); + frame2.dispose(); + } + } + } + } + + private static void setLookAndFeel(UIManager.LookAndFeelInfo laf) { + try { + UIManager.setLookAndFeel(laf.getClassName()); + } catch (UnsupportedLookAndFeelException ignored) { + System.out.println("Unsupported LookAndFeel: " + laf.getClassName()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + + public void createUI() { + frame1 = new JFrame("Main test window"); + JMenuBar menuBar1 = new JMenuBar(); + JMenu file1 = new JMenu("File"); + file1.setMnemonic('f'); + JMenuItem close1 = new JMenuItem("Close"); + close1.setMnemonic('c'); + close1.addActionListener(new FrameCloser(frame1)); + file1.add(close1); + menuBar1.add(file1); + frame1.setJMenuBar(menuBar1); + frame1.setSize(200, 200); + frame1.setLocation(100, 100); + frame1.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame1.setVisible(true); + referenceToFrame1 = new WeakReference(frame1); + frame1 = null; + + frame2 = new JFrame("Secondary"); + JMenuBar menuBar2 = new JMenuBar(); + JMenu test = new JMenu("Test"); + test.setMnemonic('T'); + JMenuItem memoryTest = new JMenuItem("Memory"); + memoryTest.setMnemonic('M'); + test.add(memoryTest); + menuBar2.add(test); + frame2.setJMenuBar(menuBar2); + frame2.setLayout(new BorderLayout()); + frame2.setSize(200, 200); + frame2.setLocation(500, 100); + frame2.setVisible(true); + } + + class FrameCloser extends AbstractAction { + JFrame frame; + public FrameCloser(JFrame f) { + this.frame = f; + } + + @Override + public void actionPerformed(ActionEvent e) { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + this.frame = null; + } + } + } +} From 436019b8bb172515e74118d75bafa71bdcd1c9c2 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Wed, 11 Nov 2020 15:28:09 +0000 Subject: [PATCH 069/124] 8256166: [C2] Registers get confused on Big Endian after 8221404 Reviewed-by: redestad, thartmann --- src/hotspot/share/opto/regmask.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index d59ef2d83371c..bed1c416e1aaa 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -110,7 +110,12 @@ class RegMask { FORALL_BODY # undef BODY int dummy = 0) { +#if defined(VM_LITTLE_ENDIAN) || !defined(_LP64) # define BODY(I) _RM_I[I] = a##I; +#else + // We need to swap ints. +# define BODY(I) _RM_I[I ^ 1] = a##I; +#endif FORALL_BODY # undef BODY _lwm = 0; From 6247736fc9dedf60881639b768be68a2de8bd981 Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Wed, 11 Nov 2020 15:34:03 +0000 Subject: [PATCH 070/124] 8256018: Adler32/CRC32/CRC32C missing reachabilityFence Reviewed-by: naoto, alanb --- .../share/classes/java/util/zip/Adler32.java | 9 +++++++-- src/java.base/share/classes/java/util/zip/CRC32.java | 9 +++++++-- src/java.base/share/classes/java/util/zip/CRC32C.java | 11 ++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/java.base/share/classes/java/util/zip/Adler32.java b/src/java.base/share/classes/java/util/zip/Adler32.java index ee7c06e619d42..33aac1ad79d00 100644 --- a/src/java.base/share/classes/java/util/zip/Adler32.java +++ b/src/java.base/share/classes/java/util/zip/Adler32.java @@ -25,6 +25,7 @@ package java.util.zip; +import java.lang.ref.Reference; import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer; @@ -96,8 +97,12 @@ public void update(ByteBuffer buffer) { int rem = limit - pos; if (rem <= 0) return; - if (buffer instanceof DirectBuffer) { - adler = updateByteBuffer(adler, ((DirectBuffer)buffer).address(), pos, rem); + if (buffer.isDirect()) { + try { + adler = updateByteBuffer(adler, ((DirectBuffer)buffer).address(), pos, rem); + } finally { + Reference.reachabilityFence(buffer); + } } else if (buffer.hasArray()) { adler = updateBytes(adler, buffer.array(), pos + buffer.arrayOffset(), rem); } else { diff --git a/src/java.base/share/classes/java/util/zip/CRC32.java b/src/java.base/share/classes/java/util/zip/CRC32.java index a70afb90e5139..17264eb179840 100644 --- a/src/java.base/share/classes/java/util/zip/CRC32.java +++ b/src/java.base/share/classes/java/util/zip/CRC32.java @@ -25,6 +25,7 @@ package java.util.zip; +import java.lang.ref.Reference; import java.nio.ByteBuffer; import java.util.Objects; @@ -95,8 +96,12 @@ public void update(ByteBuffer buffer) { int rem = limit - pos; if (rem <= 0) return; - if (buffer instanceof DirectBuffer) { - crc = updateByteBuffer(crc, ((DirectBuffer)buffer).address(), pos, rem); + if (buffer.isDirect()) { + try { + crc = updateByteBuffer(crc, ((DirectBuffer)buffer).address(), pos, rem); + } finally { + Reference.reachabilityFence(buffer); + } } else if (buffer.hasArray()) { crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(), rem); } else { diff --git a/src/java.base/share/classes/java/util/zip/CRC32C.java b/src/java.base/share/classes/java/util/zip/CRC32C.java index 0359a79881685..fa138d4d3f657 100644 --- a/src/java.base/share/classes/java/util/zip/CRC32C.java +++ b/src/java.base/share/classes/java/util/zip/CRC32C.java @@ -24,6 +24,7 @@ */ package java.util.zip; +import java.lang.ref.Reference; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -170,9 +171,13 @@ public void update(ByteBuffer buffer) { return; } - if (buffer instanceof DirectBuffer) { - crc = updateDirectByteBuffer(crc, ((DirectBuffer) buffer).address(), - pos, limit); + if (buffer.isDirect()) { + try { + crc = updateDirectByteBuffer(crc, ((DirectBuffer) buffer).address(), + pos, limit); + } finally { + Reference.reachabilityFence(buffer); + } } else if (buffer.hasArray()) { crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(), limit + buffer.arrayOffset()); From 421a7c3b41343e60d52b6d9ee263dfd0a4d53ce1 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 11 Nov 2020 16:06:08 +0000 Subject: [PATCH 071/124] 8256182: Update qemu-debootstrap cross-compilation recipe Reviewed-by: ihse --- doc/building.html | 37 +++++++++++++++++++++++-------------- doc/building.md | 42 +++++++++++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/doc/building.html b/doc/building.html index 8774e5562d633..318a24aa84071 100644 --- a/doc/building.html +++ b/doc/building.html @@ -629,21 +629,30 @@

    Creating And Using Sys

    Fortunately, you can create sysroots for foreign architectures with tools provided by your OS. On Debian/Ubuntu systems, one could use qemu-deboostrap to create the target system chroot, which would have the native libraries and headers specific to that target system. After that, we can use the cross-compiler on the build system, pointing into chroot to get the build dependencies right. This allows building for foreign architectures with native compilation speed.

    For example, cross-compiling to AArch64 from x86_64 could be done like this:

      -
    • Install cross-compiler on the build system:
    • -
    -
    apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu
    -
      -
    • Create chroot on the build system, configuring it for target system:
    • -
    -
    sudo qemu-debootstrap --arch=arm64 --verbose \
    -       --include=fakeroot,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng12-dev \
    -       --resolve-deps jessie /chroots/arm64 http://httpredir.debian.org/debian/
    -
      -
    • Configure and build with newly created chroot as sysroot/toolchain-path:
    • -
    -
    CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure --openjdk-target=aarch64-linux-gnu --with-sysroot=/chroots/arm64/ --with-toolchain-path=/chroots/arm64/
    +
  • Install cross-compiler on the build system:

    +
    apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu
  • +
  • Create chroot on the build system, configuring it for target system:

    +
    sudo qemu-debootstrap \
    +  --arch=arm64 \
    +  --verbose \
    +  --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev \
    +  --resolve-deps \
    +  buster \
    +  ~/sysroot-arm64 \
    +  http://httpredir.debian.org/debian/
  • +
  • Make sure the symlinks inside the newly created chroot point to proper locations:

    +
    sudo chroot ~/sysroot-arm64 symlinks -cr .
  • +
  • Configure and build with newly created chroot as sysroot/toolchain-path:

    +
    CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure \
    + --openjdk-target=aarch64-linux-gnu \
    + --with-sysroot=~/sysroot-arm64 \
    + --with-toolchain-path=~/sysroot-arm64 \
    + --with-freetype-lib=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/ \
    + --with-freetype-include=~/sysroot-arm64/usr/include/freetype2/ \
    + --x-libraries=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/
     make images
    -ls build/linux-aarch64-normal-server-release/
    +ls build/linux-aarch64-server-release/
  • +

    The build does not create new files in that chroot, so it can be reused for multiple builds without additional cleanup.

    Architectures that are known to successfully cross-compile like this are:

    diff --git a/doc/building.md b/doc/building.md index fa6b0ae31b6b8..e0ac5c7b6c7df 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1086,23 +1086,39 @@ for foreign architectures with native compilation speed. For example, cross-compiling to AArch64 from x86_64 could be done like this: * Install cross-compiler on the *build* system: -``` -apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu -``` + ``` + apt install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu + ``` * Create chroot on the *build* system, configuring it for *target* system: -``` -sudo qemu-debootstrap --arch=arm64 --verbose \ - --include=fakeroot,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng12-dev \ - --resolve-deps jessie /chroots/arm64 http://httpredir.debian.org/debian/ -``` + ``` + sudo qemu-debootstrap \ + --arch=arm64 \ + --verbose \ + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev \ + --resolve-deps \ + buster \ + ~/sysroot-arm64 \ + http://httpredir.debian.org/debian/ + ``` + + * Make sure the symlinks inside the newly created chroot point to proper locations: + ``` + sudo chroot ~/sysroot-arm64 symlinks -cr . + ``` * Configure and build with newly created chroot as sysroot/toolchain-path: -``` -CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure --openjdk-target=aarch64-linux-gnu --with-sysroot=/chroots/arm64/ --with-toolchain-path=/chroots/arm64/ -make images -ls build/linux-aarch64-normal-server-release/ -``` + ``` + CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ sh ./configure \ + --openjdk-target=aarch64-linux-gnu \ + --with-sysroot=~/sysroot-arm64 \ + --with-toolchain-path=~/sysroot-arm64 \ + --with-freetype-lib=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/ \ + --with-freetype-include=~/sysroot-arm64/usr/include/freetype2/ \ + --x-libraries=~/sysroot-arm64/usr/lib/aarch64-linux-gnu/ + make images + ls build/linux-aarch64-server-release/ + ``` The build does not create new files in that chroot, so it can be reused for multiple builds without additional cleanup. From 2e19026d45dda217073d3f279753f1e7b5fb7fde Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Wed, 11 Nov 2020 16:20:11 +0000 Subject: [PATCH 072/124] 8253064: monitor list simplifications and getting rid of TSM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Erik Österlund Reviewed-by: eosterlund, rehn, coleenp --- src/hotspot/share/oops/markWord.cpp | 35 +- src/hotspot/share/oops/markWord.hpp | 12 +- src/hotspot/share/runtime/globals.hpp | 14 + .../share/runtime/monitorDeflationThread.cpp | 98 + .../share/runtime/monitorDeflationThread.hpp | 48 + src/hotspot/share/runtime/mutexLocker.cpp | 2 + src/hotspot/share/runtime/mutexLocker.hpp | 1 + src/hotspot/share/runtime/objectMonitor.cpp | 158 +- src/hotspot/share/runtime/objectMonitor.hpp | 69 +- .../share/runtime/objectMonitor.inline.hpp | 81 - src/hotspot/share/runtime/safepoint.cpp | 11 +- src/hotspot/share/runtime/safepoint.hpp | 1 - src/hotspot/share/runtime/serviceThread.cpp | 12 +- src/hotspot/share/runtime/synchronizer.cpp | 1991 ++++------------- src/hotspot/share/runtime/synchronizer.hpp | 76 +- src/hotspot/share/runtime/thread.cpp | 28 +- src/hotspot/share/runtime/thread.hpp | 9 +- src/hotspot/share/runtime/vmStructs.cpp | 11 +- .../runtime/MonitorDeflationThread.java | 41 + .../jvm/hotspot/runtime/ObjectMonitor.java | 4 - .../sun/jvm/hotspot/runtime/Thread.java | 1 + .../sun/jvm/hotspot/runtime/Threads.java | 5 +- .../gtest/runtime/test_objectMonitor.cpp | 3 - .../runtime/logging/SafepointCleanupTest.java | 1 - .../serviceability/sa/ClhsdbPrintStatics.java | 1 - 25 files changed, 809 insertions(+), 1904 deletions(-) create mode 100644 src/hotspot/share/runtime/monitorDeflationThread.cpp create mode 100644 src/hotspot/share/runtime/monitorDeflationThread.hpp create mode 100644 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/MonitorDeflationThread.java diff --git a/src/hotspot/share/oops/markWord.cpp b/src/hotspot/share/oops/markWord.cpp index 5104ff975ad6d..acc007d2d48fd 100644 --- a/src/hotspot/share/oops/markWord.cpp +++ b/src/hotspot/share/oops/markWord.cpp @@ -25,9 +25,42 @@ #include "precompiled.hpp" #include "oops/markWord.hpp" #include "runtime/thread.inline.hpp" -#include "runtime/objectMonitor.hpp" +#include "runtime/objectMonitor.inline.hpp" #include "utilities/ostream.hpp" +markWord markWord::displaced_mark_helper() const { + assert(has_displaced_mark_helper(), "check"); + if (has_monitor()) { + // Has an inflated monitor. Must be checked before has_locker(). + ObjectMonitor* monitor = this->monitor(); + return monitor->header(); + } + if (has_locker()) { // has a stack lock + BasicLock* locker = this->locker(); + return locker->displaced_header(); + } + // This should never happen: + fatal("bad header=" INTPTR_FORMAT, value()); + return markWord(value()); +} + +void markWord::set_displaced_mark_helper(markWord m) const { + assert(has_displaced_mark_helper(), "check"); + if (has_monitor()) { + // Has an inflated monitor. Must be checked before has_locker(). + ObjectMonitor* monitor = this->monitor(); + monitor->set_header(m); + return; + } + if (has_locker()) { // has a stack lock + BasicLock* locker = this->locker(); + locker->set_displaced_header(m); + return; + } + // This should never happen: + fatal("bad header=" INTPTR_FORMAT, value()); +} + void markWord::print_on(outputStream* st, bool print_monitor_info) const { if (is_marked()) { // last bits = 11 st->print(" marked(" INTPTR_FORMAT ")", value()); diff --git a/src/hotspot/share/oops/markWord.hpp b/src/hotspot/share/oops/markWord.hpp index 5e5cae12a2c6a..3af579aed0314 100644 --- a/src/hotspot/share/oops/markWord.hpp +++ b/src/hotspot/share/oops/markWord.hpp @@ -286,16 +286,8 @@ class markWord { bool has_displaced_mark_helper() const { return ((value() & unlocked_value) == 0); } - markWord displaced_mark_helper() const { - assert(has_displaced_mark_helper(), "check"); - uintptr_t ptr = (value() & ~monitor_value); - return *(markWord*)ptr; - } - void set_displaced_mark_helper(markWord m) const { - assert(has_displaced_mark_helper(), "check"); - uintptr_t ptr = (value() & ~monitor_value); - ((markWord*)ptr)->_value = m._value; - } + markWord displaced_mark_helper() const; + void set_displaced_mark_helper(markWord m) const; markWord copy_set_hash(intptr_t hash) const { uintptr_t tmp = value() & (~hash_mask_in_place); tmp |= ((hash & hash_mask) << hash_shift); diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 171cc50566591..83d80da53cca4 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -728,6 +728,20 @@ const intx ObjectAlignmentInBytes = 8; "MonitorUsedDeflationThreshold is exceeded (0 is off).") \ range(0, max_jint) \ \ + /* notice: the max range value here is max_jint, not max_intx */ \ + /* because of overflow issue */ \ + product(intx, AvgMonitorsPerThreadEstimate, 1024, DIAGNOSTIC, \ + "Used to estimate a variable ceiling based on number of threads " \ + "for use with MonitorUsedDeflationThreshold (0 is off).") \ + range(0, max_jint) \ + \ + /* notice: the max range value here is max_jint, not max_intx */ \ + /* because of overflow issue */ \ + product(intx, MonitorDeflationMax, 1000000, DIAGNOSTIC, \ + "The maximum number of monitors to deflate, unlink and delete " \ + "at one time (minimum is 1024).") \ + range(1024, max_jint) \ + \ product(intx, MonitorUsedDeflationThreshold, 90, EXPERIMENTAL, \ "Percentage of used monitors before triggering deflation (0 is " \ "off). The check is performed on GuaranteedSafepointInterval " \ diff --git a/src/hotspot/share/runtime/monitorDeflationThread.cpp b/src/hotspot/share/runtime/monitorDeflationThread.cpp new file mode 100644 index 0000000000000..0b1dd2587fdc4 --- /dev/null +++ b/src/hotspot/share/runtime/monitorDeflationThread.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/javaClasses.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/java.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/monitorDeflationThread.hpp" +#include "runtime/mutexLocker.hpp" + +MonitorDeflationThread* MonitorDeflationThread::_instance = NULL; + +void MonitorDeflationThread::initialize() { + EXCEPTION_MARK; + + const char* name = "Monitor Deflation Thread"; + Handle string = java_lang_String::create_from_str(name, CHECK); + + // Initialize thread_oop to put it into the system threadGroup + Handle thread_group (THREAD, Universe::system_thread_group()); + Handle thread_oop = JavaCalls::construct_new_instance( + SystemDictionary::Thread_klass(), + vmSymbols::threadgroup_string_void_signature(), + thread_group, + string, + CHECK); + + { + MutexLocker mu(THREAD, Threads_lock); + MonitorDeflationThread* thread = new MonitorDeflationThread(&monitor_deflation_thread_entry); + + // At this point it may be possible that no osthread was created for the + // JavaThread due to lack of memory. We would have to throw an exception + // in that case. However, since this must work and we do not allow + // exceptions anyway, check and abort if this fails. + if (thread == NULL || thread->osthread() == NULL) { + vm_exit_during_initialization("java.lang.OutOfMemoryError", + os::native_thread_creation_failed_msg()); + } + + java_lang_Thread::set_thread(thread_oop(), thread); + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); + java_lang_Thread::set_daemon(thread_oop()); + thread->set_threadObj(thread_oop()); + _instance = thread; + + Threads::add(thread); + Thread::start(thread); + } +} + +void MonitorDeflationThread::monitor_deflation_thread_entry(JavaThread* jt, TRAPS) { + while (true) { + { + // Need state transition ThreadBlockInVM so that this thread + // will be handled by safepoint correctly when this thread is + // notified at a safepoint. + + // This ThreadBlockInVM object is not also considered to be + // suspend-equivalent because MonitorDeflationThread is not + // visible to external suspension. + + ThreadBlockInVM tbivm(jt); + + MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); + while (!ObjectSynchronizer::is_async_deflation_needed()) { + // Wait until notified that there is some work to do. + // We wait for GuaranteedSafepointInterval so that + // is_async_deflation_needed() is checked at the same interval. + ml.wait(GuaranteedSafepointInterval); + } + } + + (void)ObjectSynchronizer::deflate_idle_monitors(); + } +} diff --git a/src/hotspot/share/runtime/monitorDeflationThread.hpp b/src/hotspot/share/runtime/monitorDeflationThread.hpp new file mode 100644 index 0000000000000..f76a32c26bd7a --- /dev/null +++ b/src/hotspot/share/runtime/monitorDeflationThread.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_RUNTIME_MONITORDEFLATIONTHREAD_HPP +#define SHARE_RUNTIME_MONITORDEFLATIONTHREAD_HPP + +#include "runtime/thread.hpp" + +// A hidden from external view JavaThread for deflating idle monitors. + +class MonitorDeflationThread : public JavaThread { + friend class VMStructs; + private: + static MonitorDeflationThread* _instance; + + static void monitor_deflation_thread_entry(JavaThread* thread, TRAPS); + MonitorDeflationThread(ThreadFunction entry_point) : JavaThread(entry_point) {}; + + public: + static void initialize(); + + // Hide this thread from external view. + bool is_hidden_from_external_view() const { return true; } + bool is_monitor_deflation_thread() const { return true; } +}; + +#endif // SHARE_RUNTIME_MONITORDEFLATIONTHREAD_HPP diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index 36d19e03c97a2..9a784bb501aa6 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -116,6 +116,7 @@ Mutex* OldSets_lock = NULL; Monitor* RootRegionScan_lock = NULL; Mutex* Management_lock = NULL; +Monitor* MonitorDeflation_lock = NULL; Monitor* Service_lock = NULL; Monitor* Notification_lock = NULL; Monitor* PeriodicTask_lock = NULL; @@ -244,6 +245,7 @@ void mutex_init() { def(Patching_lock , PaddedMutex , special, true, _safepoint_check_never); // used for safepointing and code patching. def(CompiledMethod_lock , PaddedMutex , special-1, true, _safepoint_check_never); + def(MonitorDeflation_lock , PaddedMonitor, tty-2, true, _safepoint_check_never); // used for monitor deflation thread operations def(Service_lock , PaddedMonitor, tty-2, true, _safepoint_check_never); // used for service thread operations if (UseNotificationThread) { diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 41d3c0910e9e5..95a3989bdb60e 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -111,6 +111,7 @@ extern Mutex* OldSets_lock; // protects the old region sets extern Monitor* RootRegionScan_lock; // used to notify that the CM threads have finished scanning the IM snapshot regions extern Mutex* Management_lock; // a lock used to serialize JVM management +extern Monitor* MonitorDeflation_lock; // a lock used for monitor deflation thread operation extern Monitor* Service_lock; // a lock used for service thread operation extern Monitor* Notification_lock; // a lock used for notification thread operation extern Monitor* PeriodicTask_lock; // protects the periodic task structure diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index c8967bfbc1f94..8cbe4dd93325e 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -265,6 +265,29 @@ static void check_object_context() { #endif // ASSERT } +ObjectMonitor::ObjectMonitor(oop object) : + _header(markWord::zero()), + _object(_oop_storage, object), + _owner(NULL), + _previous_owner_tid(0), + _next_om(NULL), + _recursions(0), + _EntryList(NULL), + _cxq(NULL), + _succ(NULL), + _Responsible(NULL), + _Spinner(0), + _SpinDuration(ObjectMonitor::Knob_SpinLimit), + _contentions(0), + _WaitSet(NULL), + _waiters(0), + _WaitSetLock(0) +{ } + +ObjectMonitor::~ObjectMonitor() { + _object.release(_oop_storage); +} + oop ObjectMonitor::object() const { check_object_context(); if (_object.is_null()) { @@ -280,15 +303,6 @@ oop ObjectMonitor::object_peek() const { return _object.peek(); } -void ObjectMonitor::set_object(oop obj) { - check_object_context(); - if (_object.is_null()) { - _object = WeakHandle(_oop_storage, obj); - } else { - _object.replace(obj); - } -} - // ----------------------------------------------------------------------------- // Enter support @@ -363,7 +377,10 @@ bool ObjectMonitor::enter(TRAPS) { EventJavaMonitorEnter event; if (event.should_commit()) { event.set_monitorClass(object()->klass()); - event.set_address((uintptr_t)object_addr()); + // Set an address that is 'unique enough', such that events close in + // time and with the same address are likely (but not guaranteed) to + // belong to the same object. + event.set_address((uintptr_t)this); } { // Change java thread status to indicate blocked on monitor enter. @@ -475,6 +492,110 @@ int ObjectMonitor::TryLock(Thread * Self) { return -1; } +// Deflate the specified ObjectMonitor if not in-use. Returns true if it +// was deflated and false otherwise. +// +// The async deflation protocol sets owner to DEFLATER_MARKER and +// makes contentions negative as signals to contending threads that +// an async deflation is in progress. There are a number of checks +// as part of the protocol to make sure that the calling thread has +// not lost the race to a contending thread. +// +// The ObjectMonitor has been successfully async deflated when: +// (contentions < 0) +// Contending threads that see that condition know to retry their operation. +// +bool ObjectMonitor::deflate_monitor() { + if (is_busy() != 0) { + // Easy checks are first - the ObjectMonitor is busy so no deflation. + return false; + } + + if (ObjectSynchronizer::is_final_audit() && owner_is_DEFLATER_MARKER()) { + // The final audit can see an already deflated ObjectMonitor on the + // in-use list because MonitorList::unlink_deflated() might have + // blocked for the final safepoint before unlinking all the deflated + // monitors. + assert(contentions() < 0, "must be negative: contentions=%d", contentions()); + // Already returned 'true' when it was originally deflated. + return false; + } + + const oop obj = object_peek(); + + if (obj == NULL) { + // If the object died, we can recycle the monitor without racing with + // Java threads. The GC already broke the association with the object. + set_owner_from(NULL, DEFLATER_MARKER); + assert(contentions() >= 0, "must be non-negative: contentions=%d", contentions()); + _contentions = -max_jint; + } else { + // Attempt async deflation protocol. + + // Set a NULL owner to DEFLATER_MARKER to force any contending thread + // through the slow path. This is just the first part of the async + // deflation dance. + if (try_set_owner_from(NULL, DEFLATER_MARKER) != NULL) { + // The owner field is no longer NULL so we lost the race since the + // ObjectMonitor is now busy. + return false; + } + + if (contentions() > 0 || _waiters != 0) { + // Another thread has raced to enter the ObjectMonitor after + // is_busy() above or has already entered and waited on + // it which makes it busy so no deflation. Restore owner to + // NULL if it is still DEFLATER_MARKER. + if (try_set_owner_from(DEFLATER_MARKER, NULL) != DEFLATER_MARKER) { + // Deferred decrement for the JT EnterI() that cancelled the async deflation. + add_to_contentions(-1); + } + return false; + } + + // Make a zero contentions field negative to force any contending threads + // to retry. This is the second part of the async deflation dance. + if (Atomic::cmpxchg(&_contentions, (jint)0, -max_jint) != 0) { + // Contentions was no longer 0 so we lost the race since the + // ObjectMonitor is now busy. Restore owner to NULL if it is + // still DEFLATER_MARKER: + if (try_set_owner_from(DEFLATER_MARKER, NULL) != DEFLATER_MARKER) { + // Deferred decrement for the JT EnterI() that cancelled the async deflation. + add_to_contentions(-1); + } + return false; + } + } + + // Sanity checks for the races: + guarantee(owner_is_DEFLATER_MARKER(), "must be deflater marker"); + guarantee(contentions() < 0, "must be negative: contentions=%d", + contentions()); + guarantee(_waiters == 0, "must be 0: waiters=%d", _waiters); + guarantee(_cxq == NULL, "must be no contending threads: cxq=" + INTPTR_FORMAT, p2i(_cxq)); + guarantee(_EntryList == NULL, + "must be no entering threads: EntryList=" INTPTR_FORMAT, + p2i(_EntryList)); + + if (obj != NULL) { + if (log_is_enabled(Trace, monitorinflation)) { + ResourceMark rm; + log_trace(monitorinflation)("deflate_monitor: object=" INTPTR_FORMAT + ", mark=" INTPTR_FORMAT ", type='%s'", + p2i(obj), obj->mark().value(), + obj->klass()->external_name()); + } + + // Install the old mark word if nobody else has already done it. + install_displaced_markword_in_object(obj); + } + + // We leave owner == DEFLATER_MARKER and contentions < 0 + // to force any racing threads to retry. + return true; // Success, ObjectMonitor has been deflated. +} + // Install the displaced mark word (dmw) of a deflating ObjectMonitor // into the header of the object associated with the monitor. This // idempotent method is called by a thread that is deflating a @@ -1351,7 +1472,10 @@ static void post_monitor_wait_event(EventJavaMonitorWait* event, assert(monitor != NULL, "invariant"); event->set_monitorClass(monitor->object()->klass()); event->set_timeout(timeout); - event->set_address((uintptr_t)monitor->object_addr()); + // Set an address that is 'unique enough', such that events close in + // time and with the same address are likely (but not guaranteed) to + // belong to the same object. + event->set_address((uintptr_t)monitor); event->set_notifier(notifier_tid); event->set_timedOut(timedout); event->commit(); @@ -2124,7 +2248,6 @@ void ObjectMonitor::print() const { print_on(tty); } // (ObjectMonitor) 0x00007fdfb6012e40 = { // _header = 0x0000000000000001 // _object = 0x000000070ff45fd0 -// _allocation_state = Old // _pad_buf0 = { // [0] = '\0' // ... @@ -2155,17 +2278,6 @@ void ObjectMonitor::print_debug_style_on(outputStream* st) const { st->print_cr("(ObjectMonitor*) " INTPTR_FORMAT " = {", p2i(this)); st->print_cr(" _header = " INTPTR_FORMAT, header().value()); st->print_cr(" _object = " INTPTR_FORMAT, p2i(object_peek())); - st->print(" _allocation_state = "); - if (is_free()) { - st->print("Free"); - } else if (is_old()) { - st->print("Old"); - } else if (is_new()) { - st->print("New"); - } else { - st->print("unknown=%d", _allocation_state); - } - st->cr(); st->print_cr(" _pad_buf0 = {"); st->print_cr(" [0] = '\\0'"); st->print_cr(" ..."); diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp index f36943f4d6c23..d624d2d724ef1 100644 --- a/src/hotspot/share/runtime/objectMonitor.hpp +++ b/src/hotspot/share/runtime/objectMonitor.hpp @@ -127,7 +127,7 @@ class ObjectWaiter : public StackObj { #define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE #endif -class ObjectMonitor { +class ObjectMonitor : public CHeapObj { friend class ObjectSynchronizer; friend class ObjectWaiter; friend class VMStructs; @@ -139,20 +139,12 @@ class ObjectMonitor { // Enforced by the assert() in header_addr(). volatile markWord _header; // displaced object header word - mark WeakHandle _object; // backward object pointer - typedef enum { - Free = 0, // Free must be 0 for monitor to be free after memset(..,0,..). - New, - Old, - ChainMarker - } AllocationState; - AllocationState _allocation_state; // Separate _header and _owner on different cache lines since both can - // have busy multi-threaded access. _header, _object and _allocation_state - // are set at initial inflation. _object and _allocation_state don't - // change until deflation so _object and _allocation_state are good - // choices to share the cache line with _header. + // have busy multi-threaded access. _header and _object are set at initial + // inflation. The _object does not change, so it is a good choice to share + // its cache line with _header. DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(volatile markWord) + - sizeof(WeakHandle) + sizeof(AllocationState)); + sizeof(WeakHandle)); // Used by async deflation as a marker in the _owner field: #define DEFLATER_MARKER reinterpret_cast(-1) void* volatile _owner; // pointer to owning thread OR BasicLock @@ -179,7 +171,7 @@ class ObjectMonitor { jint _contentions; // Number of active contentions in enter(). It is used by is_busy() // along with other fields to determine if an ObjectMonitor can be // deflated. It is also used by the async deflation protocol. See - // ObjectSynchronizer::deflate_monitor(). + // ObjectMonitor::deflate_monitor(). protected: ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the monitor volatile jint _waiters; // number of waiting threads @@ -268,8 +260,6 @@ class ObjectMonitor { void release_clear_owner(void* old_value); // Simply set _owner field to new_value; current value must match old_value. void set_owner_from(void* old_value, void* new_value); - // Simply set _owner field to new_value; current value must match old_value1 or old_value2. - void set_owner_from(void* old_value1, void* old_value2, void* new_value); // Simply set _owner field to self; current value must match basic_lock_p. void set_owner_from_BasicLock(void* basic_lock_p, Thread* self); // Try to set _owner field to new_value if the current value matches @@ -301,55 +291,15 @@ class ObjectMonitor { ObjectWaiter* next_waiter(ObjectWaiter* o) { return o->_next; } Thread* thread_of_waiter(ObjectWaiter* o) { return o->_thread; } - protected: - // We don't typically expect or want the ctors or dtors to run. - // normal ObjectMonitors are type-stable and immortal. - ObjectMonitor() { ::memset((void*)this, 0, sizeof(*this)); } - - ~ObjectMonitor() { - // TODO: Add asserts ... - // _cxq == 0 _succ == NULL _owner == NULL _waiters == 0 - // _contentions == 0 _EntryList == NULL etc - } - - private: - void Recycle() { - // TODO: add stronger asserts ... - // _cxq == 0 _succ == NULL _owner == NULL _waiters == 0 - // _contentions == 0 EntryList == NULL - // _recursions == 0 _WaitSet == NULL -#ifdef ASSERT - stringStream ss; - assert((is_busy() | _recursions) == 0, "freeing in-use monitor: %s, " - "recursions=" INTX_FORMAT, is_busy_to_string(&ss), _recursions); -#endif - _succ = NULL; - _EntryList = NULL; - _cxq = NULL; - _WaitSet = NULL; - _recursions = 0; - } - - public: + ObjectMonitor(oop object); + ~ObjectMonitor(); oop object() const; oop object_peek() const; - oop* object_addr(); - void set_object(oop obj); - void release_set_allocation_state(AllocationState s); - void set_allocation_state(AllocationState s); - AllocationState allocation_state() const; - AllocationState allocation_state_acquire() const; - bool is_free() const; - bool is_old() const; - bool is_new() const; - bool is_chainmarker() const; // Returns true if the specified thread owns the ObjectMonitor. Otherwise // returns false and throws IllegalMonitorStateException (IMSE). bool check_owner(Thread* THREAD); - void clear(); - void clear_common(); bool enter(TRAPS); void exit(bool not_suspended, TRAPS); @@ -380,6 +330,9 @@ class ObjectMonitor { int TrySpin(Thread* self); void ExitEpilog(Thread* self, ObjectWaiter* Wakee); bool ExitSuspendEquivalent(JavaThread* self); + + // Deflation support + bool deflate_monitor(); void install_displaced_markword_in_object(const oop obj); }; diff --git a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp index 0cfc6b8ce4e31..6e25a7ba5557f 100644 --- a/src/hotspot/share/runtime/objectMonitor.inline.hpp +++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp @@ -42,7 +42,6 @@ inline markWord ObjectMonitor::header() const { } inline volatile markWord* ObjectMonitor::header_addr() { - assert((intptr_t)this == (intptr_t)&_header, "sync code expects this"); return &_header; } @@ -72,37 +71,6 @@ inline bool ObjectMonitor::is_being_async_deflated() { return contentions() < 0; } -inline void ObjectMonitor::clear() { - assert(Atomic::load(&_header).value() != 0, "must be non-zero"); - assert(_owner == NULL, "must be NULL: owner=" INTPTR_FORMAT, p2i(_owner)); - - Atomic::store(&_header, markWord::zero()); - - clear_common(); -} - -inline void ObjectMonitor::clear_common() { - // Async deflation protocol uses the header, owner and contentions - // fields. While the ObjectMonitor being deflated is on the global - // free list, we leave those three fields alone; contentions < 0 - // will force any racing threads to retry. The header field is used - // by install_displaced_markword_in_object() to restore the object's - // header so we cannot check its value here. - guarantee(_owner == NULL || _owner == DEFLATER_MARKER, - "must be NULL or DEFLATER_MARKER: owner=" INTPTR_FORMAT, - p2i(_owner)); - assert(contentions() <= 0, "must not be positive: contentions=%d", contentions()); - assert(_waiters == 0, "must be 0: waiters=%d", _waiters); - assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions); - - set_allocation_state(Free); - set_object(NULL); -} - -inline oop* ObjectMonitor::object_addr() { - return (oop*)&_object; -} - // Return number of threads contending for this monitor. inline jint ObjectMonitor::contentions() const { return Atomic::load(&_contentions); @@ -141,23 +109,6 @@ inline void ObjectMonitor::set_owner_from(void* old_value, void* new_value) { p2i(old_value), p2i(new_value)); } -// Simply set _owner field to new_value; current value must match old_value1 or old_value2. -// (Simple means no memory sync needed.) -inline void ObjectMonitor::set_owner_from(void* old_value1, void* old_value2, void* new_value) { - void* prev = Atomic::load(&_owner); - assert(prev == old_value1 || prev == old_value2, - "unexpected prev owner=" INTPTR_FORMAT ", expected1=" - INTPTR_FORMAT " or expected2=" INTPTR_FORMAT, p2i(prev), - p2i(old_value1), p2i(old_value2)); - _owner = new_value; - log_trace(monitorinflation, owner)("set_owner_from(old1=" INTPTR_FORMAT - ", old2=" INTPTR_FORMAT "): mid=" - INTPTR_FORMAT ", prev=" INTPTR_FORMAT - ", new=" INTPTR_FORMAT, p2i(old_value1), - p2i(old_value2), p2i(this), p2i(prev), - p2i(new_value)); -} - // Simply set _owner field to self; current value must match basic_lock_p. inline void ObjectMonitor::set_owner_from_BasicLock(void* basic_lock_p, Thread* self) { #ifdef ASSERT @@ -188,38 +139,6 @@ inline void* ObjectMonitor::try_set_owner_from(void* old_value, void* new_value) return prev; } -inline void ObjectMonitor::release_set_allocation_state(ObjectMonitor::AllocationState s) { - Atomic::release_store(&_allocation_state, s); -} - -inline void ObjectMonitor::set_allocation_state(ObjectMonitor::AllocationState s) { - _allocation_state = s; -} - -inline ObjectMonitor::AllocationState ObjectMonitor::allocation_state() const { - return _allocation_state; -} - -inline ObjectMonitor::AllocationState ObjectMonitor::allocation_state_acquire() const { - return Atomic::load_acquire(&_allocation_state); -} - -inline bool ObjectMonitor::is_free() const { - return _allocation_state == Free; -} - -inline bool ObjectMonitor::is_old() const { - return allocation_state_acquire() == Old; -} - -inline bool ObjectMonitor::is_new() const { - return _allocation_state == New; -} - -inline bool ObjectMonitor::is_chainmarker() const { - return _allocation_state == ChainMarker; -} - // The _next_om field can be concurrently read and modified so we // use Atomic operations to disable compiler optimizations that // might try to elide loading and/or storing this field. diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index a32763fd03d5e..ea3a174dae091 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -544,11 +544,6 @@ class ParallelSPCleanupTask : public AbstractGangTask { Threads::threads_do(&cl); } - if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_DEFLATE_MONITORS)) { - Tracer t("deflating idle monitors"); - ObjectSynchronizer::do_safepoint_work(); - } - if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES)) { Tracer t("updating inline caches"); InlineCacheBuffer::update_inline_caches(); @@ -610,6 +605,12 @@ void SafepointSynchronize::do_cleanup_tasks() { } assert(InlineCacheBuffer::is_empty(), "should have cleaned up ICBuffer"); + + if (log_is_enabled(Debug, monitorinflation)) { + // The VMThread calls do_final_audit_and_print_stats() which calls + // audit_and_print_stats() at the Info level at VM exit time. + ObjectSynchronizer::audit_and_print_stats(false /* on_exit */); + } } // Methods for determining if a JavaThread is safepoint safe. diff --git a/src/hotspot/share/runtime/safepoint.hpp b/src/hotspot/share/runtime/safepoint.hpp index c9e52b522cd00..df9086be145a0 100644 --- a/src/hotspot/share/runtime/safepoint.hpp +++ b/src/hotspot/share/runtime/safepoint.hpp @@ -71,7 +71,6 @@ class SafepointSynchronize : AllStatic { // The enums are listed in the order of the tasks when done serially. enum SafepointCleanupTasks { SAFEPOINT_CLEANUP_LAZY_ROOT_PROCESSING, - SAFEPOINT_CLEANUP_DEFLATE_MONITORS, SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES, SAFEPOINT_CLEANUP_COMPILATION_POLICY, SAFEPOINT_CLEANUP_SYMBOL_TABLE_REHASH, diff --git a/src/hotspot/share/runtime/serviceThread.cpp b/src/hotspot/share/runtime/serviceThread.cpp index 56f4c0ebba687..42a840fbeecc0 100644 --- a/src/hotspot/share/runtime/serviceThread.cpp +++ b/src/hotspot/share/runtime/serviceThread.cpp @@ -143,7 +143,6 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { bool thread_id_table_work = false; bool protection_domain_table_work = false; bool oopstorage_work = false; - bool deflate_idle_monitors = false; JvmtiDeferredEvent jvmti_event; bool oop_handles_to_release = false; bool cldg_cleanup_work = false; @@ -174,13 +173,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { (protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) | (oopstorage_work = OopStorage::has_cleanup_work_and_reset()) | (oop_handles_to_release = (_oop_handle_list != NULL)) | - (cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) | - (deflate_idle_monitors = ObjectSynchronizer::is_async_deflation_needed()) + (cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) ) == 0) { // Wait until notified that there is some work to do. - // We wait for GuaranteedSafepointInterval so that - // is_async_deflation_needed() is checked at the same interval. - ml.wait(GuaranteedSafepointInterval); + ml.wait(); } if (has_jvmti_events) { @@ -233,10 +229,6 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) { cleanup_oopstorages(); } - if (deflate_idle_monitors) { - ObjectSynchronizer::deflate_idle_monitors(); - } - if (oop_handles_to_release) { release_oop_handles(); } diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index a0c90ce66a7b0..f5055af4c7c63 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -24,9 +24,9 @@ #include "precompiled.hpp" #include "classfile/vmSymbols.hpp" +#include "jfr/jfrEvents.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" -#include "jfr/jfrEvents.hpp" #include "memory/allocation.inline.hpp" #include "memory/metaspaceShared.hpp" #include "memory/padded.hpp" @@ -57,6 +57,118 @@ #include "utilities/events.hpp" #include "utilities/preserveException.hpp" +class MonitorList { + ObjectMonitor* volatile _head; + volatile size_t _count; + volatile size_t _max; + +public: + void add(ObjectMonitor* monitor); + size_t unlink_deflated(Thread* self, LogStream* ls, elapsedTimer* timer_p, + GrowableArray* unlinked_list); + size_t count() const; + size_t max() const; + + class Iterator; + Iterator iterator() const; +}; + +class MonitorList::Iterator { + ObjectMonitor* _current; + +public: + Iterator(ObjectMonitor* head) : _current(head) {} + bool has_next() const { return _current != NULL; } + ObjectMonitor* next(); +}; + +void MonitorList::add(ObjectMonitor* m) { + ObjectMonitor* head; + do { + head = Atomic::load(&_head); + m->set_next_om(head); + } while (Atomic::cmpxchg(&_head, head, m) != head); + + size_t count = Atomic::add(&_count, 1u); + if (count > max()) { + Atomic::inc(&_max); + } +} + +size_t MonitorList::count() const { + return Atomic::load(&_count); +} + +size_t MonitorList::max() const { + return Atomic::load(&_max); +} + +// Walk the in-use list and unlink (at most MonitorDeflationMax) deflated +// ObjectMonitors. Returns the number of unlinked ObjectMonitors. +size_t MonitorList::unlink_deflated(Thread* self, LogStream* ls, + elapsedTimer* timer_p, + GrowableArray* unlinked_list) { + size_t unlinked_count = 0; + ObjectMonitor* prev = NULL; + ObjectMonitor* head = Atomic::load_acquire(&_head); + ObjectMonitor* m = head; + do { + if (m->is_being_async_deflated()) { + // Find next live ObjectMonitor. + ObjectMonitor* next = m; + do { + ObjectMonitor* next_next = next->next_om(); + unlinked_count++; + unlinked_list->append(next); + next = next_next; + if (unlinked_count >= (size_t)MonitorDeflationMax) { + // Reached the max so bail out on the gathering loop. + break; + } + } while (next != NULL && next->is_being_async_deflated()); + if (prev == NULL) { + ObjectMonitor* prev_head = Atomic::cmpxchg(&_head, head, next); + if (prev_head != head) { + // Find new prev ObjectMonitor that just got inserted. + for (ObjectMonitor* n = prev_head; n != m; n = n->next_om()) { + prev = n; + } + prev->set_next_om(next); + } + } else { + prev->set_next_om(next); + } + if (unlinked_count >= (size_t)MonitorDeflationMax) { + // Reached the max so bail out on the searching loop. + break; + } + m = next; + } else { + prev = m; + m = m->next_om(); + } + + if (self->is_Java_thread()) { + // A JavaThread must check for a safepoint/handshake and honor it. + ObjectSynchronizer::chk_for_block_req(self->as_Java_thread(), "unlinking", + "unlinked_count", unlinked_count, + ls, timer_p); + } + } while (m != NULL); + Atomic::sub(&_count, unlinked_count); + return unlinked_count; +} + +MonitorList::Iterator MonitorList::iterator() const { + return Iterator(Atomic::load_acquire(&_head)); +} + +ObjectMonitor* MonitorList::Iterator::next() { + ObjectMonitor* current = _current; + _current = current->next_om(); + return current; +} + // The "core" versions of monitor enter and exit reside in this file. // The interpreter and compilers contain specialized transliterated // variants of the enter-exit fast-path operations. See c2_MacroAssembler_x86.cpp @@ -74,7 +186,7 @@ char* bytes = NULL; \ int len = 0; \ jlong jtid = SharedRuntime::get_java_tid(thread); \ - Symbol* klassname = ((oop)(obj))->klass()->name(); \ + Symbol* klassname = obj->klass()->name(); \ if (klassname != NULL) { \ bytes = (char*)klassname->bytes(); \ len = klassname->utf8_length(); \ @@ -118,312 +230,26 @@ int dtrace_waited_probe(ObjectMonitor* monitor, Handle obj, Thread* thr) { #define NINFLATIONLOCKS 256 static volatile intptr_t gInflationLocks[NINFLATIONLOCKS]; -// global list of blocks of monitors -PaddedObjectMonitor* ObjectSynchronizer::g_block_list = NULL; +static MonitorList _in_use_list; +// The ratio of the current _in_use_list count to the ceiling is used +// to determine if we are above MonitorUsedDeflationThreshold and need +// to do an async monitor deflation cycle. The ceiling is increased by +// AvgMonitorsPerThreadEstimate when a thread is added to the system +// and is decreased by AvgMonitorsPerThreadEstimate when a thread is +// removed from the system. +// Note: If the _in_use_list max exceeds the ceiling, then +// monitors_used_above_threshold() will use the in_use_list max instead +// of the thread count derived ceiling because we have used more +// ObjectMonitors than the estimated average. +// +// Start the ceiling with the estimate for one thread. +// This is a 'jint' because the range of AvgMonitorsPerThreadEstimate +// is 0..max_jint: +static jint _in_use_list_ceiling = AvgMonitorsPerThreadEstimate; bool volatile ObjectSynchronizer::_is_async_deflation_requested = false; bool volatile ObjectSynchronizer::_is_final_audit = false; jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0; -struct ObjectMonitorListGlobals { - char _pad_prefix[OM_CACHE_LINE_SIZE]; - // These are highly shared list related variables. - // To avoid false-sharing they need to be the sole occupants of a cache line. - - // Global ObjectMonitor free list. Newly allocated and deflated - // ObjectMonitors are prepended here. - ObjectMonitor* _free_list; - DEFINE_PAD_MINUS_SIZE(1, OM_CACHE_LINE_SIZE, sizeof(ObjectMonitor*)); - - // Global ObjectMonitor in-use list. When a JavaThread is exiting, - // ObjectMonitors on its per-thread in-use list are prepended here. - ObjectMonitor* _in_use_list; - DEFINE_PAD_MINUS_SIZE(2, OM_CACHE_LINE_SIZE, sizeof(ObjectMonitor*)); - - // Global ObjectMonitor wait list. Deflated ObjectMonitors wait on - // this list until after a handshake or a safepoint for platforms - // that don't support handshakes. After the handshake or safepoint, - // the deflated ObjectMonitors are prepended to free_list. - ObjectMonitor* _wait_list; - DEFINE_PAD_MINUS_SIZE(3, OM_CACHE_LINE_SIZE, sizeof(ObjectMonitor*)); - - int _free_count; // # on free_list - DEFINE_PAD_MINUS_SIZE(4, OM_CACHE_LINE_SIZE, sizeof(int)); - - int _in_use_count; // # on in_use_list - DEFINE_PAD_MINUS_SIZE(5, OM_CACHE_LINE_SIZE, sizeof(int)); - - int _population; // # Extant -- in circulation - DEFINE_PAD_MINUS_SIZE(6, OM_CACHE_LINE_SIZE, sizeof(int)); - - int _wait_count; // # on wait_list - DEFINE_PAD_MINUS_SIZE(7, OM_CACHE_LINE_SIZE, sizeof(int)); -}; -static ObjectMonitorListGlobals om_list_globals; - - -// =====================> Spin-lock functions - -// ObjectMonitors are not lockable outside of this file. We use spin-locks -// implemented using a bit in the _next_om field instead of the heavier -// weight locking mechanisms for faster list management. - -#define OM_LOCK_BIT 0x1 - -// Return true if the ObjectMonitor is locked. -// Otherwise returns false. -static bool is_locked(ObjectMonitor* om) { - return ((intptr_t)om->next_om_acquire() & OM_LOCK_BIT) == OM_LOCK_BIT; -} - -// Mark an ObjectMonitor* with OM_LOCK_BIT and return it. -static ObjectMonitor* mark_om_ptr(ObjectMonitor* om) { - return (ObjectMonitor*)((intptr_t)om | OM_LOCK_BIT); -} - -// Return the unmarked next field in an ObjectMonitor. Note: the next -// field may or may not have been marked with OM_LOCK_BIT originally. -static ObjectMonitor* unmarked_next(ObjectMonitor* om) { - return (ObjectMonitor*)((intptr_t)om->next_om() & ~OM_LOCK_BIT); -} - -// Try to lock an ObjectMonitor. Returns true if locking was successful. -// Otherwise returns false. -static bool try_om_lock(ObjectMonitor* om) { - // Get current next field without any OM_LOCK_BIT value. - ObjectMonitor* next = unmarked_next(om); - if (om->try_set_next_om(next, mark_om_ptr(next)) != next) { - return false; // Cannot lock the ObjectMonitor. - } - return true; -} - -// Lock an ObjectMonitor. -static void om_lock(ObjectMonitor* om) { - while (true) { - if (try_om_lock(om)) { - return; - } - } -} - -// Unlock an ObjectMonitor. -static void om_unlock(ObjectMonitor* om) { - ObjectMonitor* next = om->next_om(); - guarantee(((intptr_t)next & OM_LOCK_BIT) == OM_LOCK_BIT, "next=" INTPTR_FORMAT - " must have OM_LOCK_BIT=%x set.", p2i(next), OM_LOCK_BIT); - - next = (ObjectMonitor*)((intptr_t)next & ~OM_LOCK_BIT); // Clear OM_LOCK_BIT. - om->release_set_next_om(next); -} - -// Get the list head after locking it. Returns the list head or NULL -// if the list is empty. -static ObjectMonitor* get_list_head_locked(ObjectMonitor** list_p) { - while (true) { - // Acquire semantics not needed on this list load since we're - // checking for NULL here or following up with a cmpxchg() via - // try_om_lock() below and we retry on cmpxchg() failure. - ObjectMonitor* mid = Atomic::load(list_p); - if (mid == NULL) { - return NULL; // The list is empty. - } - if (try_om_lock(mid)) { - // Acquire semantics not needed on this list load since memory is - // already consistent due to the cmpxchg() via try_om_lock() above. - if (Atomic::load(list_p) != mid) { - // The list head changed before we could lock it so we have to retry. - om_unlock(mid); - continue; - } - return mid; - } - } -} - -#undef OM_LOCK_BIT - - -// =====================> List Management functions - -// Prepend a list of ObjectMonitors to the specified *list_p. 'tail' is -// the last ObjectMonitor in the list and there are 'count' on the list. -// Also updates the specified *count_p. -static void prepend_list_to_common(ObjectMonitor* list, ObjectMonitor* tail, - int count, ObjectMonitor** list_p, - int* count_p) { - while (true) { - // Acquire semantics not needed on this list load since we're - // following up with a cmpxchg() via try_om_lock() below and we - // retry on cmpxchg() failure. - ObjectMonitor* cur = Atomic::load(list_p); - // Prepend list to *list_p. - if (!try_om_lock(tail)) { - // Failed to lock tail due to a list walker so try it all again. - continue; - } - // Release semantics not needed on this "unlock" since memory is - // already consistent due to the cmpxchg() via try_om_lock() above. - tail->set_next_om(cur); // tail now points to cur (and unlocks tail) - if (cur == NULL) { - // No potential race with takers or other prependers since - // *list_p is empty. - if (Atomic::cmpxchg(list_p, cur, list) == cur) { - // Successfully switched *list_p to the list value. - Atomic::add(count_p, count); - break; - } - // Implied else: try it all again - } else { - if (!try_om_lock(cur)) { - continue; // failed to lock cur so try it all again - } - // We locked cur so try to switch *list_p to the list value. - if (Atomic::cmpxchg(list_p, cur, list) != cur) { - // The list head has changed so unlock cur and try again: - om_unlock(cur); - continue; - } - Atomic::add(count_p, count); - om_unlock(cur); - break; - } - } -} - -// Prepend a newly allocated block of ObjectMonitors to g_block_list and -// om_list_globals._free_list. Also updates om_list_globals._population -// and om_list_globals._free_count. -void ObjectSynchronizer::prepend_block_to_lists(PaddedObjectMonitor* new_blk) { - // First we handle g_block_list: - while (true) { - PaddedObjectMonitor* cur = Atomic::load(&g_block_list); - // Prepend new_blk to g_block_list. The first ObjectMonitor in - // a block is reserved for use as linkage to the next block. - new_blk[0].set_next_om(cur); - if (Atomic::cmpxchg(&g_block_list, cur, new_blk) == cur) { - // Successfully switched g_block_list to the new_blk value. - Atomic::add(&om_list_globals._population, _BLOCKSIZE - 1); - break; - } - // Implied else: try it all again - } - - // Second we handle om_list_globals._free_list: - prepend_list_to_common(new_blk + 1, &new_blk[_BLOCKSIZE - 1], _BLOCKSIZE - 1, - &om_list_globals._free_list, &om_list_globals._free_count); -} - -// Prepend a list of ObjectMonitors to om_list_globals._free_list. -// 'tail' is the last ObjectMonitor in the list and there are 'count' -// on the list. Also updates om_list_globals._free_count. -static void prepend_list_to_global_free_list(ObjectMonitor* list, - ObjectMonitor* tail, int count) { - prepend_list_to_common(list, tail, count, &om_list_globals._free_list, - &om_list_globals._free_count); -} - -// Prepend a list of ObjectMonitors to om_list_globals._wait_list. -// 'tail' is the last ObjectMonitor in the list and there are 'count' -// on the list. Also updates om_list_globals._wait_count. -static void prepend_list_to_global_wait_list(ObjectMonitor* list, - ObjectMonitor* tail, int count) { - prepend_list_to_common(list, tail, count, &om_list_globals._wait_list, - &om_list_globals._wait_count); -} - -// Prepend a list of ObjectMonitors to om_list_globals._in_use_list. -// 'tail' is the last ObjectMonitor in the list and there are 'count' -// on the list. Also updates om_list_globals._in_use_list. -static void prepend_list_to_global_in_use_list(ObjectMonitor* list, - ObjectMonitor* tail, int count) { - prepend_list_to_common(list, tail, count, &om_list_globals._in_use_list, - &om_list_globals._in_use_count); -} - -// Prepend an ObjectMonitor to the specified list. Also updates -// the specified counter. -static void prepend_to_common(ObjectMonitor* m, ObjectMonitor** list_p, - int* count_p) { - while (true) { - om_lock(m); // Lock m so we can safely update its next field. - ObjectMonitor* cur = NULL; - // Lock the list head to guard against races with a list walker - // or async deflater thread (which only races in om_in_use_list): - if ((cur = get_list_head_locked(list_p)) != NULL) { - // List head is now locked so we can safely switch it. Release - // semantics not needed on this "unlock" since memory is already - // consistent due to the cmpxchg() via get_list_head_locked() above. - m->set_next_om(cur); // m now points to cur (and unlocks m) - OrderAccess::storestore(); // Make sure set_next_om() is seen first. - Atomic::store(list_p, m); // Switch list head to unlocked m. - om_unlock(cur); - break; - } - // The list is empty so try to set the list head. - assert(cur == NULL, "cur must be NULL: cur=" INTPTR_FORMAT, p2i(cur)); - // Release semantics not needed on this "unlock" since memory - // is already consistent. - m->set_next_om(cur); // m now points to NULL (and unlocks m) - if (Atomic::cmpxchg(list_p, cur, m) == cur) { - // List head is now unlocked m. - break; - } - // Implied else: try it all again - } - Atomic::inc(count_p); -} - -// Prepend an ObjectMonitor to a per-thread om_free_list. -// Also updates the per-thread om_free_count. -static void prepend_to_om_free_list(Thread* self, ObjectMonitor* m) { - prepend_to_common(m, &self->om_free_list, &self->om_free_count); -} - -// Prepend an ObjectMonitor to a per-thread om_in_use_list. -// Also updates the per-thread om_in_use_count. -static void prepend_to_om_in_use_list(Thread* self, ObjectMonitor* m) { - prepend_to_common(m, &self->om_in_use_list, &self->om_in_use_count); -} - -// Take an ObjectMonitor from the start of the specified list. Also -// decrements the specified counter. Returns NULL if none are available. -static ObjectMonitor* take_from_start_of_common(ObjectMonitor** list_p, - int* count_p) { - ObjectMonitor* take = NULL; - // Lock the list head to guard against races with a list walker - // or async deflater thread (which only races in om_list_globals._free_list): - if ((take = get_list_head_locked(list_p)) == NULL) { - return NULL; // None are available. - } - ObjectMonitor* next = unmarked_next(take); - // Switch locked list head to next (which unlocks the list head, but - // leaves take locked). Release semantics not needed on this "unlock" - // since memory is already consistent due to the cmpxchg() via - // get_list_head_locked() above. - Atomic::store(list_p, next); - Atomic::dec(count_p); - // Unlock take, but leave the next value for any lagging list - // walkers. It will get cleaned up when take is prepended to - // the in-use list: - om_unlock(take); - return take; -} - -// Take an ObjectMonitor from the start of the om_list_globals._free_list. -// Also updates om_list_globals._free_count. Returns NULL if none are -// available. -static ObjectMonitor* take_from_start_of_global_free_list() { - return take_from_start_of_common(&om_list_globals._free_list, - &om_list_globals._free_count); -} - -// Take an ObjectMonitor from the start of a per-thread free-list. -// Also updates om_free_count. Returns NULL if none are available. -static ObjectMonitor* take_from_start_of_om_free_list(Thread* self) { - return take_from_start_of_common(&self->om_free_list, &self->om_free_count); -} - - // =====================> Quick functions // The quick_* forms are special fast-path variants used to improve @@ -1308,43 +1134,60 @@ JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_ob // Visitors ... void ObjectSynchronizer::monitors_iterate(MonitorClosure* closure) { - PaddedObjectMonitor* block = Atomic::load(&g_block_list); - while (block != NULL) { - assert(block->is_chainmarker(), "must be a block header"); - for (int i = _BLOCKSIZE - 1; i > 0; i--) { - ObjectMonitor* mid = (ObjectMonitor *)(block + i); - if (mid->object_peek() != NULL) { - // Only process with closure if the object is set. - - // monitors_iterate() is only called at a safepoint or when the - // target thread is suspended or when the target thread is - // operating on itself. The current closures in use today are - // only interested in an owned ObjectMonitor and ownership - // cannot be dropped under the calling contexts so the - // ObjectMonitor cannot be async deflated. - closure->do_monitor(mid); - } + MonitorList::Iterator iter = _in_use_list.iterator(); + while (iter.has_next()) { + ObjectMonitor* mid = iter.next(); + if (!mid->is_being_async_deflated() && mid->object_peek() != NULL) { + // Only process with closure if the object is set. + + // monitors_iterate() is only called at a safepoint or when the + // target thread is suspended or when the target thread is + // operating on itself. The current closures in use today are + // only interested in an owned ObjectMonitor and ownership + // cannot be dropped under the calling contexts so the + // ObjectMonitor cannot be async deflated. + closure->do_monitor(mid); } - // unmarked_next() is not needed with g_block_list (no locking - // used with block linkage _next_om fields). - block = (PaddedObjectMonitor*)block->next_om(); } } -static bool monitors_used_above_threshold() { - int population = Atomic::load(&om_list_globals._population); - if (population == 0) { +static bool monitors_used_above_threshold(MonitorList* list) { + // Start with ceiling based on a per-thread estimate: + size_t ceiling = ObjectSynchronizer::in_use_list_ceiling(); + if (ceiling < list->max()) { + // The max used by the system has exceeded the ceiling so use that: + ceiling = list->max(); + } + if (ceiling == 0) { return false; } if (MonitorUsedDeflationThreshold > 0) { - int monitors_used = population - Atomic::load(&om_list_globals._free_count) - - Atomic::load(&om_list_globals._wait_count); - int monitor_usage = (monitors_used * 100LL) / population; - return monitor_usage > MonitorUsedDeflationThreshold; + size_t monitors_used = list->count(); + size_t monitor_usage = (monitors_used * 100LL) / ceiling; + return int(monitor_usage) > MonitorUsedDeflationThreshold; } return false; } +size_t ObjectSynchronizer::in_use_list_ceiling() { + // _in_use_list_ceiling is a jint so this cast could lose precision, + // but in reality the ceiling should never get that high. + return (size_t)_in_use_list_ceiling; +} + +void ObjectSynchronizer::dec_in_use_list_ceiling() { + Atomic::add(&_in_use_list_ceiling, (jint)-AvgMonitorsPerThreadEstimate); +#ifdef ASSERT + size_t l_in_use_list_ceiling = in_use_list_ceiling(); +#endif + assert(l_in_use_list_ceiling > 0, "in_use_list_ceiling=" SIZE_FORMAT + ": must be > 0", l_in_use_list_ceiling); +} + +void ObjectSynchronizer::inc_in_use_list_ceiling() { + Atomic::add(&_in_use_list_ceiling, (jint)AvgMonitorsPerThreadEstimate); +} + bool ObjectSynchronizer::is_async_deflation_needed() { if (is_async_deflation_requested()) { // Async deflation request. @@ -1352,11 +1195,11 @@ bool ObjectSynchronizer::is_async_deflation_needed() { } if (AsyncDeflationInterval > 0 && time_since_last_async_deflation_ms() > AsyncDeflationInterval && - monitors_used_above_threshold()) { + monitors_used_above_threshold(&_in_use_list)) { // It's been longer than our specified deflate interval and there // are too many monitors in use. We don't deflate more frequently // than AsyncDeflationInterval (unless is_async_deflation_requested) - // in order to not swamp the ServiceThread. + // in order to not swamp the MonitorDeflationThread. return true; } return false; @@ -1369,7 +1212,7 @@ bool ObjectSynchronizer::request_deflate_idle_monitors() { jlong last_time = last_async_deflation_time_ns(); set_is_async_deflation_requested(true); { - MonitorLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + MonitorLocker ml(MonitorDeflation_lock, Mutex::_no_safepoint_check_flag); ml.notify_all(); } const int N_CHECKS = 5; @@ -1398,410 +1241,6 @@ jlong ObjectSynchronizer::time_since_last_async_deflation_ms() { return (os::javaTimeNanos() - last_async_deflation_time_ns()) / (NANOUNITS / MILLIUNITS); } - -// ----------------------------------------------------------------------------- -// ObjectMonitor Lifecycle -// ----------------------- -// Inflation unlinks monitors from om_list_globals._free_list or a per-thread -// free list and associates them with objects. Async deflation disassociates -// idle monitors from objects. Such scavenged monitors are returned to the -// om_list_globals._free_list. -// -// ObjectMonitors reside in type-stable memory (TSM) and are immortal. -// -// Lifecycle: -// -- unassigned and on the om_list_globals._free_list -// -- unassigned and on a per-thread free list -// -- assigned to an object. The object is inflated and the mark refers -// to the ObjectMonitor. - -ObjectMonitor* ObjectSynchronizer::om_alloc(Thread* self) { - // A large MAXPRIVATE value reduces both list lock contention - // and list coherency traffic, but also tends to increase the - // number of ObjectMonitors in circulation as well as the - // scavenge costs. As usual, we lean toward time in space-time - // tradeoffs. - const int MAXPRIVATE = 1024; - NoSafepointVerifier nsv; - - for (;;) { - ObjectMonitor* m; - - // 1: try to allocate from the thread's local om_free_list. - // Threads will attempt to allocate first from their local list, then - // from the global list, and only after those attempts fail will the - // thread attempt to instantiate new monitors. Thread-local free lists - // improve allocation latency, as well as reducing coherency traffic - // on the shared global list. - m = take_from_start_of_om_free_list(self); - if (m != NULL) { - guarantee(m->object_peek() == NULL, "invariant"); - m->set_allocation_state(ObjectMonitor::New); - prepend_to_om_in_use_list(self, m); - return m; - } - - // 2: try to allocate from the global om_list_globals._free_list - // If we're using thread-local free lists then try - // to reprovision the caller's free list. - // Acquire semantics not needed on this list load since memory - // is already consistent due to the cmpxchg() via - // take_from_start_of_om_free_list() above. - if (Atomic::load(&om_list_globals._free_list) != NULL) { - // Reprovision the thread's om_free_list. - // Use bulk transfers to reduce the allocation rate and heat - // on various locks. - for (int i = self->om_free_provision; --i >= 0;) { - ObjectMonitor* take = take_from_start_of_global_free_list(); - if (take == NULL) { - break; // No more are available. - } - guarantee(take->object_peek() == NULL, "invariant"); - // We allowed 3 field values to linger during async deflation. - // Clear or restore them as appropriate. - take->set_header(markWord::zero()); - // DEFLATER_MARKER is the only non-NULL value we should see here. - take->try_set_owner_from(DEFLATER_MARKER, NULL); - if (take->contentions() < 0) { - // Add back max_jint to restore the contentions field to its - // proper value. - take->add_to_contentions(max_jint); - -#ifdef ASSERT - jint l_contentions = take->contentions(); - assert(l_contentions >= 0, "must not be negative: l_contentions=%d, contentions=%d", - l_contentions, take->contentions()); -#endif - } - take->Recycle(); - // Since we're taking from the global free-list, take must be Free. - // om_release() also sets the allocation state to Free because it - // is called from other code paths. - assert(take->is_free(), "invariant"); - om_release(self, take, false); - } - self->om_free_provision += 1 + (self->om_free_provision / 2); - if (self->om_free_provision > MAXPRIVATE) self->om_free_provision = MAXPRIVATE; - continue; - } - - // 3: allocate a block of new ObjectMonitors - // Both the local and global free lists are empty -- resort to malloc(). - // In the current implementation ObjectMonitors are TSM - immortal. - // Ideally, we'd write "new ObjectMonitor[_BLOCKSIZE], but we want - // each ObjectMonitor to start at the beginning of a cache line, - // so we use align_up(). - // A better solution would be to use C++ placement-new. - // BEWARE: As it stands currently, we don't run the ctors! - assert(_BLOCKSIZE > 1, "invariant"); - size_t neededsize = sizeof(PaddedObjectMonitor) * _BLOCKSIZE; - PaddedObjectMonitor* temp; - size_t aligned_size = neededsize + (OM_CACHE_LINE_SIZE - 1); - void* real_malloc_addr = NEW_C_HEAP_ARRAY(char, aligned_size, mtInternal); - temp = (PaddedObjectMonitor*)align_up(real_malloc_addr, OM_CACHE_LINE_SIZE); - (void)memset((void *) temp, 0, neededsize); - - // Format the block. - // initialize the linked list, each monitor points to its next - // forming the single linked free list, the very first monitor - // will points to next block, which forms the block list. - // The trick of using the 1st element in the block as g_block_list - // linkage should be reconsidered. A better implementation would - // look like: class Block { Block * next; int N; ObjectMonitor Body [N] ; } - - for (int i = 1; i < _BLOCKSIZE; i++) { - temp[i].set_next_om((ObjectMonitor*)&temp[i + 1]); - assert(temp[i].is_free(), "invariant"); - } - - // terminate the last monitor as the end of list - temp[_BLOCKSIZE - 1].set_next_om((ObjectMonitor*)NULL); - - // Element [0] is reserved for global list linkage - temp[0].set_allocation_state(ObjectMonitor::ChainMarker); - - // Consider carving out this thread's current request from the - // block in hand. This avoids some lock traffic and redundant - // list activity. - - prepend_block_to_lists(temp); - } -} - -// Place "m" on the caller's private per-thread om_free_list. -// In practice there's no need to clamp or limit the number of -// monitors on a thread's om_free_list as the only non-allocation time -// we'll call om_release() is to return a monitor to the free list after -// a CAS attempt failed. This doesn't allow unbounded #s of monitors to -// accumulate on a thread's free list. -// -// Key constraint: all ObjectMonitors on a thread's free list and the global -// free list must have their object field set to null. This prevents the -// scavenger -- deflate_monitor_list() -- from reclaiming them -// while we are trying to release them. - -void ObjectSynchronizer::om_release(Thread* self, ObjectMonitor* m, - bool from_per_thread_alloc) { - guarantee(m->header().value() == 0, "invariant"); - guarantee(m->object_peek() == NULL, "invariant"); - NoSafepointVerifier nsv; - - if ((m->is_busy() | m->_recursions) != 0) { - stringStream ss; - fatal("freeing in-use monitor: %s, recursions=" INTX_FORMAT, - m->is_busy_to_string(&ss), m->_recursions); - } - m->set_allocation_state(ObjectMonitor::Free); - // _next_om is used for both per-thread in-use and free lists so - // we have to remove 'm' from the in-use list first (as needed). - if (from_per_thread_alloc) { - // Need to remove 'm' from om_in_use_list. - ObjectMonitor* mid = NULL; - ObjectMonitor* next = NULL; - - // This list walk can race with another list walker or with async - // deflation so we have to worry about an ObjectMonitor being - // removed from this list while we are walking it. - - // Lock the list head to avoid racing with another list walker - // or with async deflation. - if ((mid = get_list_head_locked(&self->om_in_use_list)) == NULL) { - fatal("thread=" INTPTR_FORMAT " in-use list must not be empty.", p2i(self)); - } - next = unmarked_next(mid); - if (m == mid) { - // First special case: - // 'm' matches mid, is the list head and is locked. Switch the list - // head to next which unlocks the list head, but leaves the extracted - // mid locked. Release semantics not needed on this "unlock" since - // memory is already consistent due to the get_list_head_locked() - // above. - Atomic::store(&self->om_in_use_list, next); - } else if (m == next) { - // Second special case: - // 'm' matches next after the list head and we already have the list - // head locked so set mid to what we are extracting: - mid = next; - // Lock mid to prevent races with a list walker or an async - // deflater thread that's ahead of us. The locked list head - // prevents races from behind us. - om_lock(mid); - // Update next to what follows mid (if anything): - next = unmarked_next(mid); - // Switch next after the list head to new next which unlocks the - // list head, but leaves the extracted mid locked. Release semantics - // not needed on this "unlock" since memory is already consistent - // due to the get_list_head_locked() above. - self->om_in_use_list->set_next_om(next); - } else { - // We have to search the list to find 'm'. - guarantee(next != NULL, "thread=" INTPTR_FORMAT ": om_in_use_list=" INTPTR_FORMAT - " is too short.", p2i(self), p2i(self->om_in_use_list)); - // Our starting anchor is next after the list head which is the - // last ObjectMonitor we checked: - ObjectMonitor* anchor = next; - // Lock anchor to prevent races with a list walker or an async - // deflater thread that's ahead of us. The locked list head - // prevents races from behind us. - om_lock(anchor); - om_unlock(mid); // Unlock the list head now that anchor is locked. - while ((mid = unmarked_next(anchor)) != NULL) { - if (m == mid) { - // We found 'm' on the per-thread in-use list so extract it. - // Update next to what follows mid (if anything): - next = unmarked_next(mid); - // Switch next after the anchor to new next which unlocks the - // anchor, but leaves the extracted mid locked. Release semantics - // not needed on this "unlock" since memory is already consistent - // due to the om_unlock() above before entering the loop or the - // om_unlock() below before looping again. - anchor->set_next_om(next); - break; - } else { - // Lock the next anchor to prevent races with a list walker - // or an async deflater thread that's ahead of us. The locked - // current anchor prevents races from behind us. - om_lock(mid); - // Unlock current anchor now that next anchor is locked: - om_unlock(anchor); - anchor = mid; // Advance to new anchor and try again. - } - } - } - - if (mid == NULL) { - // Reached end of the list and didn't find 'm' so: - fatal("thread=" INTPTR_FORMAT " must find m=" INTPTR_FORMAT "on om_in_use_list=" - INTPTR_FORMAT, p2i(self), p2i(m), p2i(self->om_in_use_list)); - } - - // At this point mid is disconnected from the in-use list so - // its lock no longer has any effects on the in-use list. - Atomic::dec(&self->om_in_use_count); - // Unlock mid, but leave the next value for any lagging list - // walkers. It will get cleaned up when mid is prepended to - // the thread's free list: - om_unlock(mid); - } - - prepend_to_om_free_list(self, m); - guarantee(m->is_free(), "invariant"); -} - -// Return ObjectMonitors on a moribund thread's free and in-use -// lists to the appropriate global lists. The ObjectMonitors on the -// per-thread in-use list may still be in use by other threads. -// -// We currently call om_flush() from Threads::remove() before the -// thread has been excised from the thread list and is no longer a -// mutator. -// -// deflate_global_idle_monitors() and deflate_per_thread_idle_monitors() -// (in another thread) can run at the same time as om_flush() so we have -// to follow a careful protocol to prevent list corruption. - -void ObjectSynchronizer::om_flush(Thread* self) { - // Process the per-thread in-use list first to be consistent. - int in_use_count = 0; - ObjectMonitor* in_use_list = NULL; - ObjectMonitor* in_use_tail = NULL; - NoSafepointVerifier nsv; - - // This function can race with a list walker or with an async - // deflater thread so we lock the list head to prevent confusion. - // An async deflater thread checks to see if the target thread - // is exiting, but if it has made it past that check before we - // started exiting, then it is racing to get to the in-use list. - if ((in_use_list = get_list_head_locked(&self->om_in_use_list)) != NULL) { - // At this point, we have locked the in-use list head so a racing - // thread cannot come in after us. However, a racing thread could - // be ahead of us; we'll detect that and delay to let it finish. - // - // The thread is going away, however the ObjectMonitors on the - // om_in_use_list may still be in-use by other threads. Link - // them to in_use_tail, which will be linked into the global - // in-use list (om_list_globals._in_use_list) below. - // - // Account for the in-use list head before the loop since it is - // already locked (by this thread): - in_use_tail = in_use_list; - in_use_count++; - for (ObjectMonitor* cur_om = unmarked_next(in_use_list); cur_om != NULL;) { - if (is_locked(cur_om)) { - // cur_om is locked so there must be a racing walker or async - // deflater thread ahead of us so we'll give it a chance to finish. - while (is_locked(cur_om)) { - os::naked_short_sleep(1); - } - // Refetch the possibly changed next field and try again. - cur_om = unmarked_next(in_use_tail); - continue; - } - if (cur_om->object_peek() == NULL) { - // Two reasons for object() to be NULL here: - // 1) cur_om was deflated and the object ref was cleared while it - // was locked. We happened to see it just after it was unlocked - // (and added to the free list). - // 2) The object has been GC'ed so the association with object is - // already broken, but we don't want to do the deflation work now. - - // Refetch the possibly changed next field: - ObjectMonitor* in_use_next = unmarked_next(in_use_tail); - if (cur_om != in_use_next) { - // The NULL is because of async deflation so try again: - cur_om = in_use_next; - continue; - } - // Implied else: The NULL is because of GC, but we leave the - // node on the in-use list to be deflated after it has been - // moved to the global in-use list. - } - in_use_tail = cur_om; - in_use_count++; - cur_om = unmarked_next(cur_om); - } - guarantee(in_use_tail != NULL, "invariant"); -#ifdef ASSERT - int l_om_in_use_count = Atomic::load(&self->om_in_use_count); - assert(l_om_in_use_count == in_use_count, "in-use counts don't match: " - "l_om_in_use_count=%d, in_use_count=%d", l_om_in_use_count, in_use_count); -#endif - Atomic::store(&self->om_in_use_count, 0); - OrderAccess::storestore(); // Make sure counter update is seen first. - // Clear the in-use list head (which also unlocks it): - Atomic::store(&self->om_in_use_list, (ObjectMonitor*)NULL); - om_unlock(in_use_list); - } - - int free_count = 0; - ObjectMonitor* free_list = NULL; - ObjectMonitor* free_tail = NULL; - // This function can race with a list walker thread so we lock the - // list head to prevent confusion. - if ((free_list = get_list_head_locked(&self->om_free_list)) != NULL) { - // At this point, we have locked the free list head so a racing - // thread cannot come in after us. However, a racing thread could - // be ahead of us; we'll detect that and delay to let it finish. - // - // The thread is going away. Set 'free_tail' to the last per-thread free - // monitor which will be linked to om_list_globals._free_list below. - // - // Account for the free list head before the loop since it is - // already locked (by this thread): - free_tail = free_list; - free_count++; - for (ObjectMonitor* s = unmarked_next(free_list); s != NULL; s = unmarked_next(s)) { - if (is_locked(s)) { - // s is locked so there must be a racing walker thread ahead - // of us so we'll give it a chance to finish. - while (is_locked(s)) { - os::naked_short_sleep(1); - } - } - free_tail = s; - free_count++; - guarantee(s->object_peek() == NULL, "invariant"); - if (s->is_busy()) { - stringStream ss; - fatal("must be !is_busy: %s", s->is_busy_to_string(&ss)); - } - } - guarantee(free_tail != NULL, "invariant"); -#ifdef ASSERT - int l_om_free_count = Atomic::load(&self->om_free_count); - assert(l_om_free_count == free_count, "free counts don't match: " - "l_om_free_count=%d, free_count=%d", l_om_free_count, free_count); -#endif - Atomic::store(&self->om_free_count, 0); - OrderAccess::storestore(); // Make sure counter update is seen first. - Atomic::store(&self->om_free_list, (ObjectMonitor*)NULL); - om_unlock(free_list); - } - - if (free_tail != NULL) { - prepend_list_to_global_free_list(free_list, free_tail, free_count); - } - - if (in_use_tail != NULL) { - prepend_list_to_global_in_use_list(in_use_list, in_use_tail, in_use_count); - } - - LogStreamHandle(Debug, monitorinflation) lsh_debug; - LogStreamHandle(Info, monitorinflation) lsh_info; - LogStream* ls = NULL; - if (log_is_enabled(Debug, monitorinflation)) { - ls = &lsh_debug; - } else if ((free_count != 0 || in_use_count != 0) && - log_is_enabled(Info, monitorinflation)) { - ls = &lsh_info; - } - if (ls != NULL) { - ls->print_cr("om_flush: jt=" INTPTR_FORMAT ", free_count=%d" - ", in_use_count=%d" ", om_free_provision=%d", - p2i(self), free_count, in_use_count, self->om_free_provision); - } -} - static void post_monitor_inflate_event(EventJavaMonitorInflate* event, const oop obj, ObjectSynchronizer::InflateCause cause) { @@ -1818,7 +1257,6 @@ void ObjectSynchronizer::inflate_helper(oop obj) { markWord mark = obj->mark(); if (mark.has_monitor()) { ObjectMonitor* monitor = mark.monitor(); - assert(ObjectSynchronizer::verify_objmon_isinpool(monitor), "monitor=" INTPTR_FORMAT " is invalid", p2i(monitor)); markWord dmw = monitor->header(); assert(dmw.is_neutral(), "sanity check: header=" INTPTR_FORMAT, dmw.value()); return; @@ -1846,7 +1284,6 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, ObjectMonitor* inf = mark.monitor(); markWord dmw = inf->header(); assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value()); - assert(ObjectSynchronizer::verify_objmon_isinpool(inf), "monitor is invalid"); return inf; } @@ -1870,31 +1307,18 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, // ObjectMonitor into the mark. This was correct, but artificially lengthened // the interval in which INFLATING appeared in the mark, thus increasing // the odds of inflation contention. - // - // We now use per-thread private ObjectMonitor free lists. - // These list are reprovisioned from the global free list outside the - // critical INFLATING...ST interval. A thread can transfer - // multiple ObjectMonitors en-mass from the global free list to its local free list. - // This reduces coherency traffic and lock contention on the global free list. - // Using such local free lists, it doesn't matter if the om_alloc() call appears - // before or after the CAS(INFLATING) operation. - // See the comments in om_alloc(). LogStreamHandle(Trace, monitorinflation) lsh; if (mark.has_locker()) { - ObjectMonitor* m = om_alloc(self); + ObjectMonitor* m = new ObjectMonitor(object); // Optimistically prepare the ObjectMonitor - anticipate successful CAS // We do this before the CAS in order to minimize the length of time // in which INFLATING appears in the mark. - m->Recycle(); - m->_Responsible = NULL; - m->_SpinDuration = ObjectMonitor::Knob_SpinLimit; // Consider: maintain by type/class markWord cmp = object->cas_set_mark(markWord::INFLATING(), mark); if (cmp != mark) { - // om_release() will reset the allocation state from New to Free. - om_release(self, m, true); + delete m; continue; // Interference -- just retry } @@ -1941,8 +1365,7 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, // Note that a thread can inflate an object // that it has stack-locked -- as might happen in wait() -- directly // with CAS. That is, we can avoid the xchg-NULL .... ST idiom. - m->set_owner_from(NULL, DEFLATER_MARKER, mark.locker()); - m->set_object(object); + m->set_owner_from(NULL, mark.locker()); // TODO-FIXME: assert BasicLock->dhw != 0. // Must preserve store ordering. The monitor state must @@ -1953,9 +1376,7 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, // Once ObjectMonitor is configured and the object is associated // with the ObjectMonitor, it is safe to allow async deflation: - assert(m->is_new(), "freshly allocated monitor must be new"); - // Release semantics needed to keep allocation_state from floating up. - m->release_set_allocation_state(ObjectMonitor::Old); + _in_use_list.add(m); // Hopefully the performance counters are allocated on distinct cache lines // to avoid false sharing on MP systems ... @@ -1984,22 +1405,12 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, // Catch if the object's header is not neutral (not locked and // not marked is what we care about here). assert(mark.is_neutral(), "invariant: header=" INTPTR_FORMAT, mark.value()); - ObjectMonitor* m = om_alloc(self); + ObjectMonitor* m = new ObjectMonitor(object); // prepare m for installation - set monitor to initial state - m->Recycle(); m->set_header(mark); - // DEFLATER_MARKER is the only non-NULL value we should see here. - m->try_set_owner_from(DEFLATER_MARKER, NULL); - m->set_object(object); - m->_Responsible = NULL; - m->_SpinDuration = ObjectMonitor::Knob_SpinLimit; // consider: keep metastats by type/class if (object->cas_set_mark(markWord::encode(m), mark) != mark) { - m->set_header(markWord::zero()); - m->set_object(NULL); - m->Recycle(); - // om_release() will reset the allocation state from New to Free. - om_release(self, m, true); + delete m; m = NULL; continue; // interference - the markword changed - just retry. @@ -2009,10 +1420,7 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, // Once the ObjectMonitor is configured and object is associated // with the ObjectMonitor, it is safe to allow async deflation: - assert(m->is_new(), "freshly allocated monitor must be new"); - // Release semantics are not needed to keep allocation_state from - // floating up since cas_set_mark() takes care of it. - m->set_allocation_state(ObjectMonitor::Old); + _in_use_list.add(m); // Hopefully the performance counters are allocated on distinct // cache lines to avoid false sharing on MP systems ... @@ -2030,296 +1438,58 @@ ObjectMonitor* ObjectSynchronizer::inflate(Thread* self, oop object, } } - -// An async deflation request is registered with the ServiceThread -// and it is notified. -void ObjectSynchronizer::do_safepoint_work() { - assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - - log_debug(monitorinflation)("requesting async deflation of idle monitors."); - // Request deflation of idle monitors by the ServiceThread: - set_is_async_deflation_requested(true); - MonitorLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); - ml.notify_all(); - - if (log_is_enabled(Debug, monitorinflation)) { - // The VMThread calls do_final_audit_and_print_stats() which calls - // audit_and_print_stats() at the Info level at VM exit time. - ObjectSynchronizer::audit_and_print_stats(false /* on_exit */); - } -} - -// Deflate the specified ObjectMonitor if not in-use. Returns true if it -// was deflated and false otherwise. -// -// The async deflation protocol sets owner to DEFLATER_MARKER and -// makes contentions negative as signals to contending threads that -// an async deflation is in progress. There are a number of checks -// as part of the protocol to make sure that the calling thread has -// not lost the race to a contending thread. -// -// The ObjectMonitor has been successfully async deflated when: -// (contentions < 0) -// Contending threads that see that condition know to retry their operation. -// -bool ObjectSynchronizer::deflate_monitor(ObjectMonitor* mid, - ObjectMonitor** free_head_p, - ObjectMonitor** free_tail_p) { - // A newly allocated ObjectMonitor should not be seen here so we - // avoid an endless inflate/deflate cycle. - assert(mid->is_old(), "must be old: allocation_state=%d", - (int) mid->allocation_state()); - - if (mid->is_busy()) { - // Easy checks are first - the ObjectMonitor is busy so no deflation. - return false; +void ObjectSynchronizer::chk_for_block_req(JavaThread* self, const char* op_name, + const char* cnt_name, size_t cnt, + LogStream* ls, elapsedTimer* timer_p) { + if (!SafepointMechanism::should_process(self)) { + return; } - const oop obj = mid->object_peek(); - - if (obj == NULL) { - // If the object died, we can recycle the monitor without racing with - // Java threads. The GC already broke the association with the object. - mid->set_owner_from(NULL, DEFLATER_MARKER); - mid->_contentions = -max_jint; - } else { - // Set a NULL owner to DEFLATER_MARKER to force any contending thread - // through the slow path. This is just the first part of the async - // deflation dance. - if (mid->try_set_owner_from(NULL, DEFLATER_MARKER) != NULL) { - // The owner field is no longer NULL so we lost the race since the - // ObjectMonitor is now busy. - return false; - } - - if (mid->contentions() > 0 || mid->_waiters != 0) { - // Another thread has raced to enter the ObjectMonitor after - // mid->is_busy() above or has already entered and waited on - // it which makes it busy so no deflation. Restore owner to - // NULL if it is still DEFLATER_MARKER. - if (mid->try_set_owner_from(DEFLATER_MARKER, NULL) != DEFLATER_MARKER) { - // Deferred decrement for the JT EnterI() that cancelled the async deflation. - mid->add_to_contentions(-1); - } - return false; - } - - // Make a zero contentions field negative to force any contending threads - // to retry. This is the second part of the async deflation dance. - if (Atomic::cmpxchg(&mid->_contentions, (jint)0, -max_jint) != 0) { - // Contentions was no longer 0 so we lost the race since the - // ObjectMonitor is now busy. Restore owner to NULL if it is - // still DEFLATER_MARKER: - if (mid->try_set_owner_from(DEFLATER_MARKER, NULL) != DEFLATER_MARKER) { - // Deferred decrement for the JT EnterI() that cancelled the async deflation. - mid->add_to_contentions(-1); - } - return false; - } + // A safepoint/handshake has started. + if (ls != NULL) { + timer_p->stop(); + ls->print_cr("pausing %s: %s=" SIZE_FORMAT ", in_use_list stats: ceiling=" + SIZE_FORMAT ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, + op_name, cnt_name, cnt, in_use_list_ceiling(), + _in_use_list.count(), _in_use_list.max()); } - // Sanity checks for the races: - guarantee(mid->owner_is_DEFLATER_MARKER(), "must be deflater marker"); - guarantee(mid->contentions() < 0, "must be negative: contentions=%d", - mid->contentions()); - guarantee(mid->_waiters == 0, "must be 0: waiters=%d", mid->_waiters); - guarantee(mid->_cxq == NULL, "must be no contending threads: cxq=" - INTPTR_FORMAT, p2i(mid->_cxq)); - guarantee(mid->_EntryList == NULL, - "must be no entering threads: EntryList=" INTPTR_FORMAT, - p2i(mid->_EntryList)); - - if (obj != NULL) { - if (log_is_enabled(Trace, monitorinflation)) { - ResourceMark rm; - log_trace(monitorinflation)("deflate_monitor: object=" INTPTR_FORMAT - ", mark=" INTPTR_FORMAT ", type='%s'", - p2i(obj), obj->mark().value(), - obj->klass()->external_name()); - } - - // Install the old mark word if nobody else has already done it. - mid->install_displaced_markword_in_object(obj); + { + // Honor block request. + ThreadBlockInVM tbivm(self); } - mid->clear_common(); - assert(mid->object_peek() == NULL, "must be NULL: object=" INTPTR_FORMAT, - p2i(mid->object_peek())); - assert(mid->is_free(), "must be free: allocation_state=%d", - (int)mid->allocation_state()); - - // Move the deflated ObjectMonitor to the working free list - // defined by free_head_p and free_tail_p. - if (*free_head_p == NULL) { - // First one on the list. - *free_head_p = mid; - } - if (*free_tail_p != NULL) { - // We append to the list so the caller can use mid->_next_om - // to fix the linkages in its context. - ObjectMonitor* prevtail = *free_tail_p; - // prevtail should have been cleaned up by the caller: -#ifdef ASSERT - ObjectMonitor* l_next_om = unmarked_next(prevtail); - assert(l_next_om == NULL, "must be NULL: _next_om=" INTPTR_FORMAT, p2i(l_next_om)); -#endif - om_lock(prevtail); - prevtail->set_next_om(mid); // prevtail now points to mid (and is unlocked) + if (ls != NULL) { + ls->print_cr("resuming %s: in_use_list stats: ceiling=" SIZE_FORMAT + ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, op_name, + in_use_list_ceiling(), _in_use_list.count(), _in_use_list.max()); + timer_p->start(); } - *free_tail_p = mid; - - // At this point, mid->_next_om still refers to its current - // value and another ObjectMonitor's _next_om field still - // refers to this ObjectMonitor. Those linkages have to be - // cleaned up by the caller who has the complete context. - - // We leave owner == DEFLATER_MARKER and contentions < 0 - // to force any racing threads to retry. - return true; // Success, ObjectMonitor has been deflated. } -// Walk a given ObjectMonitor list and deflate idle ObjectMonitors. -// Returns the number of deflated ObjectMonitors. The given -// list could be a per-thread in-use list or the global in-use list. -// If self is a JavaThread and a safepoint has started, then we save state -// via saved_mid_in_use_p and return to the caller to honor the safepoint. -// -int ObjectSynchronizer::deflate_monitor_list(Thread* self, - ObjectMonitor** list_p, - int* count_p, - ObjectMonitor** free_head_p, - ObjectMonitor** free_tail_p, - ObjectMonitor** saved_mid_in_use_p) { - ObjectMonitor* cur_mid_in_use = NULL; - ObjectMonitor* mid = NULL; - ObjectMonitor* next = NULL; - ObjectMonitor* next_next = NULL; - int deflated_count = 0; - NoSafepointVerifier nsv; +// Walk the in-use list and deflate (at most MonitorDeflationMax) idle +// ObjectMonitors. Returns the number of deflated ObjectMonitors. +size_t ObjectSynchronizer::deflate_monitor_list(Thread *self, LogStream* ls, + elapsedTimer* timer_p) { + MonitorList::Iterator iter = _in_use_list.iterator(); + size_t deflated_count = 0; - // We use the more complicated lock-cur_mid_in_use-and-mid-as-we-go - // protocol because om_release() can do list deletions in parallel; - // this also prevents races with a list walker thread. We also - // lock-next-next-as-we-go to prevent an om_flush() that is behind - // this thread from passing us. - if (*saved_mid_in_use_p == NULL) { - // No saved state so start at the beginning. - // Lock the list head so we can possibly deflate it: - if ((mid = get_list_head_locked(list_p)) == NULL) { - return 0; // The list is empty so nothing to deflate. - } - next = unmarked_next(mid); - } else { - // We're restarting after a safepoint so restore the necessary state - // before we resume. - cur_mid_in_use = *saved_mid_in_use_p; - // Lock cur_mid_in_use so we can possibly update its - // next field to extract a deflated ObjectMonitor. - om_lock(cur_mid_in_use); - mid = unmarked_next(cur_mid_in_use); - if (mid == NULL) { - om_unlock(cur_mid_in_use); - *saved_mid_in_use_p = NULL; - return 0; // The remainder is empty so nothing more to deflate. - } - // Lock mid so we can possibly deflate it: - om_lock(mid); - next = unmarked_next(mid); - } - - while (true) { - // The current mid is locked at this point. If we have a - // cur_mid_in_use, then it is also locked at this point. - - if (next != NULL) { - // We lock next so that an om_flush() thread that is behind us - // cannot pass us when we unlock the current mid. - om_lock(next); - next_next = unmarked_next(next); + while (iter.has_next()) { + if (deflated_count >= (size_t)MonitorDeflationMax) { + break; } - - // Only try to deflate if mid is old (is not newly allocated and - // is not newly freed). - if (mid->is_old() && deflate_monitor(mid, free_head_p, free_tail_p)) { - // Deflation succeeded and already updated free_head_p and - // free_tail_p as needed. Finish the move to the local free list - // by unlinking mid from the global or per-thread in-use list. - if (cur_mid_in_use == NULL) { - // mid is the list head and it is locked. Switch the list head - // to next which is also locked (if not NULL) and also leave - // mid locked. Release semantics needed since not all code paths - // in deflate_monitor() ensure memory consistency. - Atomic::release_store(list_p, next); - } else { - ObjectMonitor* locked_next = mark_om_ptr(next); - // mid and cur_mid_in_use are locked. Switch cur_mid_in_use's - // next field to locked_next and also leave mid locked. - // Release semantics needed since not all code paths in - // deflate_monitor() ensure memory consistency. - cur_mid_in_use->release_set_next_om(locked_next); - } - // At this point mid is disconnected from the in-use list so - // its lock longer has any effects on in-use list. + ObjectMonitor* mid = iter.next(); + if (mid->deflate_monitor()) { deflated_count++; - Atomic::dec(count_p); - // mid is current tail in the free_head_p list so NULL terminate - // it (which also unlocks it). No release semantics needed since - // Atomic::dec() already provides it. - mid->set_next_om(NULL); - - // All the list management is done so move on to the next one: - mid = next; // mid keeps non-NULL next's locked state - next = next_next; - } else { - // mid is considered in-use if mid is not old or deflation did not - // succeed. A mid->is_new() node can be seen here when it is freshly - // returned by om_alloc() (and skips the deflation code path). - // A mid->is_old() node can be seen here when deflation failed. - // A mid->is_free() node can be seen here when a fresh node from - // om_alloc() is released by om_release() due to losing the race - // in inflate(). - - // All the list management is done so move on to the next one: - if (cur_mid_in_use != NULL) { - om_unlock(cur_mid_in_use); - } - // The next cur_mid_in_use keeps mid's lock state so - // that it is stable for a possible next field change. It - // cannot be modified by om_release() while it is locked. - cur_mid_in_use = mid; - mid = next; // mid keeps non-NULL next's locked state - next = next_next; - - if (self->is_Java_thread() && - SafepointMechanism::should_process(self->as_Java_thread()) && - // Acquire semantics are not needed on this list load since - // it is not dependent on the following load which does have - // acquire semantics. - cur_mid_in_use != Atomic::load(list_p) && cur_mid_in_use->is_old()) { - // If a safepoint has started and cur_mid_in_use is not the list - // head and is old, then it is safe to use as saved state. Return - // to the caller before blocking. - *saved_mid_in_use_p = cur_mid_in_use; - om_unlock(cur_mid_in_use); - if (mid != NULL) { - om_unlock(mid); - } - return deflated_count; - } - } - if (mid == NULL) { - if (cur_mid_in_use != NULL) { - om_unlock(cur_mid_in_use); - } - break; // Reached end of the list so nothing more to deflate. } - // The current mid's next field is locked at this point. If we have - // a cur_mid_in_use, then it is also locked at this point. + if (self->is_Java_thread()) { + // A JavaThread must check for a safepoint/handshake and honor it. + chk_for_block_req(self->as_Java_thread(), "deflation", "deflated_count", + deflated_count, ls, timer_p); + } } - // We finished the list without a safepoint starting so there's - // no need to save state. - *saved_mid_in_use_p = NULL; + return deflated_count; } @@ -2333,192 +1503,100 @@ class HandshakeForDeflation : public HandshakeClosure { } }; -// This function is called by the ServiceThread to deflate monitors. -// It is also called by do_final_audit_and_print_stats() by the VMThread. -void ObjectSynchronizer::deflate_idle_monitors() { +// This function is called by the MonitorDeflationThread to deflate +// ObjectMonitors. It is also called via do_final_audit_and_print_stats() +// by the VMThread. +size_t ObjectSynchronizer::deflate_idle_monitors() { Thread* self = Thread::current(); - // Deflate any global idle monitors. - deflate_global_idle_monitors(self); - - int count = 0; - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - if (Atomic::load(&jt->om_in_use_count) > 0 && !jt->is_exiting()) { - // This JavaThread is using ObjectMonitors so deflate any that - // are idle unless this JavaThread is exiting; do not race with - // ObjectSynchronizer::om_flush(). - deflate_per_thread_idle_monitors(self, jt); - count++; - } - } - if (count > 0) { - log_debug(monitorinflation)("did async deflation of idle monitors for %d thread(s).", count); - } - - log_info(monitorinflation)("async global_population=%d, global_in_use_count=%d, " - "global_free_count=%d, global_wait_count=%d", - Atomic::load(&om_list_globals._population), - Atomic::load(&om_list_globals._in_use_count), - Atomic::load(&om_list_globals._free_count), - Atomic::load(&om_list_globals._wait_count)); - - GVars.stw_random = os::random(); - if (self->is_Java_thread()) { // The async deflation request has been processed. _last_async_deflation_time_ns = os::javaTimeNanos(); set_is_async_deflation_requested(false); } - if (Atomic::load(&om_list_globals._wait_count) > 0) { - // There are deflated ObjectMonitors waiting for a handshake - // (or a safepoint) for safety. - - ObjectMonitor* list = Atomic::load(&om_list_globals._wait_list); - assert(list != NULL, "om_list_globals._wait_list must not be NULL"); - int count = Atomic::load(&om_list_globals._wait_count); - Atomic::store(&om_list_globals._wait_count, 0); - OrderAccess::storestore(); // Make sure counter update is seen first. - Atomic::store(&om_list_globals._wait_list, (ObjectMonitor*)NULL); - - // Find the tail for prepend_list_to_common(). No need to mark - // ObjectMonitors for this list walk since only the deflater - // thread manages the wait list. -#ifdef ASSERT - int l_count = 0; -#endif - ObjectMonitor* tail = NULL; - for (ObjectMonitor* n = list; n != NULL; n = unmarked_next(n)) { - tail = n; -#ifdef ASSERT - l_count++; -#endif - } - assert(count == l_count, "count=%d != l_count=%d", count, l_count); - - if (self->is_Java_thread()) { - // A JavaThread needs to handshake in order to safely free the - // monitors that were deflated in this cycle. - HandshakeForDeflation hfd_hc; - Handshake::execute(&hfd_hc); - } - - prepend_list_to_common(list, tail, count, &om_list_globals._free_list, - &om_list_globals._free_count); - - log_info(monitorinflation)("moved %d idle monitors from global waiting list to global free list", count); + LogStreamHandle(Debug, monitorinflation) lsh_debug; + LogStreamHandle(Info, monitorinflation) lsh_info; + LogStream* ls = NULL; + if (log_is_enabled(Debug, monitorinflation)) { + ls = &lsh_debug; + } else if (log_is_enabled(Info, monitorinflation)) { + ls = &lsh_info; } -} - -// Deflate global idle ObjectMonitors. -// -void ObjectSynchronizer::deflate_global_idle_monitors(Thread* self) { - deflate_common_idle_monitors(self, true /* is_global */, NULL /* target */); -} - -// Deflate the specified JavaThread's idle ObjectMonitors. -// -void ObjectSynchronizer::deflate_per_thread_idle_monitors(Thread* self, - JavaThread* target) { - deflate_common_idle_monitors(self, false /* !is_global */, target); -} -// Deflate global or per-thread idle ObjectMonitors. -// -void ObjectSynchronizer::deflate_common_idle_monitors(Thread* self, - bool is_global, - JavaThread* target) { - int deflated_count = 0; - ObjectMonitor* free_head_p = NULL; // Local SLL of scavenged ObjectMonitors - ObjectMonitor* free_tail_p = NULL; - ObjectMonitor* saved_mid_in_use_p = NULL; elapsedTimer timer; - - if (log_is_enabled(Info, monitorinflation)) { + if (ls != NULL) { + ls->print_cr("begin deflating: in_use_list stats: ceiling=" SIZE_FORMAT ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, + in_use_list_ceiling(), _in_use_list.count(), _in_use_list.max()); timer.start(); } - if (is_global) { - OM_PERFDATA_OP(MonExtant, set_value(Atomic::load(&om_list_globals._in_use_count))); - } else { - OM_PERFDATA_OP(MonExtant, inc(Atomic::load(&target->om_in_use_count))); - } + // Deflate some idle ObjectMonitors. + size_t deflated_count = deflate_monitor_list(self, ls, &timer); + if (deflated_count > 0 || is_final_audit()) { + // There are ObjectMonitors that have been deflated or this is the + // final audit and all the remaining ObjectMonitors have been + // deflated, BUT the MonitorDeflationThread blocked for the final + // safepoint during unlinking. - do { - int local_deflated_count; - if (is_global) { - local_deflated_count = - deflate_monitor_list(self, &om_list_globals._in_use_list, - &om_list_globals._in_use_count, - &free_head_p, &free_tail_p, - &saved_mid_in_use_p); - } else { - local_deflated_count = - deflate_monitor_list(self, &target->om_in_use_list, - &target->om_in_use_count, &free_head_p, - &free_tail_p, &saved_mid_in_use_p); - } - deflated_count += local_deflated_count; - - if (free_head_p != NULL) { - // Move the deflated ObjectMonitors to the global free list. - guarantee(free_tail_p != NULL && local_deflated_count > 0, "free_tail_p=" INTPTR_FORMAT ", local_deflated_count=%d", p2i(free_tail_p), local_deflated_count); - // Note: The target thread can be doing an om_alloc() that - // is trying to prepend an ObjectMonitor on its in-use list - // at the same time that we have deflated the current in-use - // list head and put it on the local free list. prepend_to_common() - // will detect the race and retry which avoids list corruption, - // but the next field in free_tail_p can flicker to marked - // and then unmarked while prepend_to_common() is sorting it - // all out. -#ifdef ASSERT - ObjectMonitor* l_next_om = unmarked_next(free_tail_p); - assert(l_next_om == NULL, "must be NULL: _next_om=" INTPTR_FORMAT, p2i(l_next_om)); -#endif - - prepend_list_to_global_wait_list(free_head_p, free_tail_p, local_deflated_count); + // Unlink deflated ObjectMonitors from the in-use list. + ResourceMark rm; + GrowableArray delete_list((int)deflated_count); + size_t unlinked_count = _in_use_list.unlink_deflated(self, ls, &timer, + &delete_list); + if (self->is_Java_thread()) { + if (ls != NULL) { + timer.stop(); + ls->print_cr("before handshaking: unlinked_count=" SIZE_FORMAT + ", in_use_list stats: ceiling=" SIZE_FORMAT ", count=" + SIZE_FORMAT ", max=" SIZE_FORMAT, + unlinked_count, in_use_list_ceiling(), + _in_use_list.count(), _in_use_list.max()); + } - OM_PERFDATA_OP(Deflations, inc(local_deflated_count)); - } + // A JavaThread needs to handshake in order to safely free the + // ObjectMonitors that were deflated in this cycle. + HandshakeForDeflation hfd_hc; + Handshake::execute(&hfd_hc); - if (saved_mid_in_use_p != NULL) { - // deflate_monitor_list() detected a safepoint starting. - timer.stop(); - { - if (is_global) { - log_debug(monitorinflation)("pausing deflation of global idle monitors for a safepoint."); - } else { - log_debug(monitorinflation)("jt=" INTPTR_FORMAT ": pausing deflation of per-thread idle monitors for a safepoint.", p2i(target)); - } - assert(self->is_Java_thread() && - SafepointMechanism::should_process(self->as_Java_thread()), - "sanity check"); - ThreadBlockInVM blocker(self->as_Java_thread()); - } - // Prepare for another loop after the safepoint. - free_head_p = NULL; - free_tail_p = NULL; - if (log_is_enabled(Info, monitorinflation)) { + if (ls != NULL) { + ls->print_cr("after handshaking: in_use_list stats: ceiling=" + SIZE_FORMAT ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, + in_use_list_ceiling(), _in_use_list.count(), _in_use_list.max()); timer.start(); } } - } while (saved_mid_in_use_p != NULL); - timer.stop(); - LogStreamHandle(Debug, monitorinflation) lsh_debug; - LogStreamHandle(Info, monitorinflation) lsh_info; - LogStream* ls = NULL; - if (log_is_enabled(Debug, monitorinflation)) { - ls = &lsh_debug; - } else if (deflated_count != 0 && log_is_enabled(Info, monitorinflation)) { - ls = &lsh_info; + // After the handshake, safely free the ObjectMonitors that were + // deflated in this cycle. + size_t deleted_count = 0; + for (ObjectMonitor* monitor: delete_list) { + delete monitor; + deleted_count++; + + if (self->is_Java_thread()) { + // A JavaThread must check for a safepoint/handshake and honor it. + chk_for_block_req(self->as_Java_thread(), "deletion", "deleted_count", + deleted_count, ls, &timer); + } + } } + if (ls != NULL) { - if (is_global) { - ls->print_cr("async-deflating global idle monitors, %3.7f secs, %d monitors", timer.seconds(), deflated_count); - } else { - ls->print_cr("jt=" INTPTR_FORMAT ": async-deflating per-thread idle monitors, %3.7f secs, %d monitors", p2i(target), timer.seconds(), deflated_count); + timer.stop(); + if (deflated_count != 0 || log_is_enabled(Debug, monitorinflation)) { + ls->print_cr("deflated " SIZE_FORMAT " monitors in %3.7f secs", + deflated_count, timer.seconds()); } + ls->print_cr("end deflating: in_use_list stats: ceiling=" SIZE_FORMAT ", count=" SIZE_FORMAT ", max=" SIZE_FORMAT, + in_use_list_ceiling(), _in_use_list.count(), _in_use_list.max()); } + + OM_PERFDATA_OP(MonExtant, set_value(_in_use_list.count())); + OM_PERFDATA_OP(Deflations, inc(deflated_count)); + + GVars.stw_random = os::random(); + + return deflated_count; } // Monitor cleanup on JavaThread::exit @@ -2596,6 +1674,29 @@ u_char* ObjectSynchronizer::get_gvars_stw_random_addr() { return (u_char*)&GVars.stw_random; } +// Do the final audit and print of ObjectMonitor stats; must be done +// by the VMThread at VM exit time. +void ObjectSynchronizer::do_final_audit_and_print_stats() { + assert(Thread::current()->is_VM_thread(), "sanity check"); + + if (is_final_audit()) { // Only do the audit once. + return; + } + set_is_final_audit(); + + if (log_is_enabled(Info, monitorinflation)) { + // Do a deflation in order to reduce the in-use monitor population + // that is reported by ObjectSynchronizer::log_in_use_monitor_details() + // which is called by ObjectSynchronizer::audit_and_print_stats(). + while (ObjectSynchronizer::deflate_idle_monitors() != 0) { + ; // empty + } + // The other audit_and_print_stats() call is done at the Debug + // level at a safepoint in ObjectSynchronizer::do_safepoint_work(). + ObjectSynchronizer::audit_and_print_stats(true /* on_exit */); + } +} + // This function can be called at a safepoint or it can be called when // we are trying to exit the VM. When we are trying to exit the VM, the // list walker functions can run in parallel with the other list @@ -2622,50 +1723,15 @@ void ObjectSynchronizer::audit_and_print_stats(bool on_exit) { } assert(ls != NULL, "sanity check"); - // Log counts for the global and per-thread monitor lists: - int chk_om_population = log_monitor_list_counts(ls); int error_cnt = 0; - ls->print_cr("Checking global lists:"); - - // Check om_list_globals._population: - if (Atomic::load(&om_list_globals._population) == chk_om_population) { - ls->print_cr("global_population=%d equals chk_om_population=%d", - Atomic::load(&om_list_globals._population), chk_om_population); - } else { - // With fine grained locks on the monitor lists, it is possible for - // log_monitor_list_counts() to return a value that doesn't match - // om_list_globals._population. So far a higher value has been - // seen in testing so something is being double counted by - // log_monitor_list_counts(). - ls->print_cr("WARNING: global_population=%d is not equal to " - "chk_om_population=%d", - Atomic::load(&om_list_globals._population), chk_om_population); - } - - // Check om_list_globals._in_use_list and om_list_globals._in_use_count: - chk_global_in_use_list_and_count(ls, &error_cnt); - - // Check om_list_globals._free_list and om_list_globals._free_count: - chk_global_free_list_and_count(ls, &error_cnt); - - // Check om_list_globals._wait_list and om_list_globals._wait_count: - chk_global_wait_list_and_count(ls, &error_cnt); - - ls->print_cr("Checking per-thread lists:"); - - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - // Check om_in_use_list and om_in_use_count: - chk_per_thread_in_use_list_and_count(jt, ls, &error_cnt); - - // Check om_free_list and om_free_count: - chk_per_thread_free_list_and_count(jt, ls, &error_cnt); - } + ls->print_cr("Checking in_use_list:"); + chk_in_use_list(ls, &error_cnt); if (error_cnt == 0) { - ls->print_cr("No errors found in monitor list checks."); + ls->print_cr("No errors found in in_use_list checks."); } else { - log_error(monitorinflation)("found monitor list errors: error_cnt=%d", error_cnt); + log_error(monitorinflation)("found in_use_list errors: error_cnt=%d", error_cnt); } if ((on_exit && log_is_enabled(Info, monitorinflation)) || @@ -2681,407 +1747,102 @@ void ObjectSynchronizer::audit_and_print_stats(bool on_exit) { guarantee(error_cnt == 0, "ERROR: found monitor list errors: error_cnt=%d", error_cnt); } -// Check a free monitor entry; log any errors. -void ObjectSynchronizer::chk_free_entry(JavaThread* jt, ObjectMonitor* n, - outputStream * out, int *error_cnt_p) { - stringStream ss; - if (n->is_busy()) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": free per-thread monitor must not be busy: %s", p2i(jt), - p2i(n), n->is_busy_to_string(&ss)); - } else { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": free global monitor " - "must not be busy: %s", p2i(n), n->is_busy_to_string(&ss)); - } - *error_cnt_p = *error_cnt_p + 1; - } - if (n->header().value() != 0) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": free per-thread monitor must have NULL _header " - "field: _header=" INTPTR_FORMAT, p2i(jt), p2i(n), - n->header().value()); - *error_cnt_p = *error_cnt_p + 1; - } - } - if (n->object_peek() != NULL) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": free per-thread monitor must have NULL _object " - "field: _object=" INTPTR_FORMAT, p2i(jt), p2i(n), - p2i(n->object_peek())); - } else { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": free global monitor " - "must have NULL _object field: _object=" INTPTR_FORMAT, - p2i(n), p2i(n->object_peek())); - } - *error_cnt_p = *error_cnt_p + 1; - } -} - -// Lock the next ObjectMonitor for traversal and unlock the current -// ObjectMonitor. Returns the next ObjectMonitor if there is one. -// Otherwise returns NULL (after unlocking the current ObjectMonitor). -// This function is used by the various list walker functions to -// safely walk a list without allowing an ObjectMonitor to be moved -// to another list in the middle of a walk. -static ObjectMonitor* lock_next_for_traversal(ObjectMonitor* cur) { - assert(is_locked(cur), "cur=" INTPTR_FORMAT " must be locked", p2i(cur)); - ObjectMonitor* next = unmarked_next(cur); - if (next == NULL) { // Reached the end of the list. - om_unlock(cur); - return NULL; - } - om_lock(next); // Lock next before unlocking current to keep - om_unlock(cur); // from being by-passed by another thread. - return next; -} +// Check the in_use_list; log the results of the checks. +void ObjectSynchronizer::chk_in_use_list(outputStream* out, int *error_cnt_p) { + size_t l_in_use_count = _in_use_list.count(); + size_t l_in_use_max = _in_use_list.max(); + out->print_cr("count=" SIZE_FORMAT ", max=" SIZE_FORMAT, l_in_use_count, + l_in_use_max); -// Check the global free list and count; log the results of the checks. -void ObjectSynchronizer::chk_global_free_list_and_count(outputStream * out, - int *error_cnt_p) { - int chk_om_free_count = 0; - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&om_list_globals._free_list)) != NULL) { - // Marked the global free list head so process the list. - while (true) { - chk_free_entry(NULL /* jt */, cur, out, error_cnt_p); - chk_om_free_count++; - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } + size_t ck_in_use_count = 0; + MonitorList::Iterator iter = _in_use_list.iterator(); + while (iter.has_next()) { + ObjectMonitor* mid = iter.next(); + chk_in_use_entry(mid, out, error_cnt_p); + ck_in_use_count++; } - int l_free_count = Atomic::load(&om_list_globals._free_count); - if (l_free_count == chk_om_free_count) { - out->print_cr("global_free_count=%d equals chk_om_free_count=%d", - l_free_count, chk_om_free_count); - } else { - // With fine grained locks on om_list_globals._free_list, it - // is possible for an ObjectMonitor to be prepended to - // om_list_globals._free_list after we started calculating - // chk_om_free_count so om_list_globals._free_count may not - // match anymore. - out->print_cr("WARNING: global_free_count=%d is not equal to " - "chk_om_free_count=%d", l_free_count, chk_om_free_count); - } -} -// Check the global wait list and count; log the results of the checks. -void ObjectSynchronizer::chk_global_wait_list_and_count(outputStream * out, - int *error_cnt_p) { - int chk_om_wait_count = 0; - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&om_list_globals._wait_list)) != NULL) { - // Marked the global wait list head so process the list. - while (true) { - // Rules for om_list_globals._wait_list are the same as for - // om_list_globals._free_list: - chk_free_entry(NULL /* jt */, cur, out, error_cnt_p); - chk_om_wait_count++; - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } - } - if (Atomic::load(&om_list_globals._wait_count) == chk_om_wait_count) { - out->print_cr("global_wait_count=%d equals chk_om_wait_count=%d", - Atomic::load(&om_list_globals._wait_count), chk_om_wait_count); + if (l_in_use_count == ck_in_use_count) { + out->print_cr("in_use_count=" SIZE_FORMAT " equals ck_in_use_count=" + SIZE_FORMAT, l_in_use_count, ck_in_use_count); } else { - out->print_cr("ERROR: global_wait_count=%d is not equal to " - "chk_om_wait_count=%d", - Atomic::load(&om_list_globals._wait_count), chk_om_wait_count); - *error_cnt_p = *error_cnt_p + 1; + out->print_cr("WARNING: in_use_count=" SIZE_FORMAT " is not equal to " + "ck_in_use_count=" SIZE_FORMAT, l_in_use_count, + ck_in_use_count); } -} -// Check the global in-use list and count; log the results of the checks. -void ObjectSynchronizer::chk_global_in_use_list_and_count(outputStream * out, - int *error_cnt_p) { - int chk_om_in_use_count = 0; - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&om_list_globals._in_use_list)) != NULL) { - // Marked the global in-use list head so process the list. - while (true) { - chk_in_use_entry(NULL /* jt */, cur, out, error_cnt_p); - chk_om_in_use_count++; - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } - } - int l_in_use_count = Atomic::load(&om_list_globals._in_use_count); - if (l_in_use_count == chk_om_in_use_count) { - out->print_cr("global_in_use_count=%d equals chk_om_in_use_count=%d", - l_in_use_count, chk_om_in_use_count); + size_t ck_in_use_max = _in_use_list.max(); + if (l_in_use_max == ck_in_use_max) { + out->print_cr("in_use_max=" SIZE_FORMAT " equals ck_in_use_max=" + SIZE_FORMAT, l_in_use_max, ck_in_use_max); } else { - // With fine grained locks on the monitor lists, it is possible for - // an exiting JavaThread to put its in-use ObjectMonitors on the - // global in-use list after chk_om_in_use_count is calculated above. - out->print_cr("WARNING: global_in_use_count=%d is not equal to chk_om_in_use_count=%d", - l_in_use_count, chk_om_in_use_count); + out->print_cr("WARNING: in_use_max=" SIZE_FORMAT " is not equal to " + "ck_in_use_max=" SIZE_FORMAT, l_in_use_max, ck_in_use_max); } } // Check an in-use monitor entry; log any errors. -void ObjectSynchronizer::chk_in_use_entry(JavaThread* jt, ObjectMonitor* n, - outputStream * out, int *error_cnt_p) { +void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, + int* error_cnt_p) { + if (n->owner_is_DEFLATER_MARKER()) { + // This should not happen, but if it does, it is not fatal. + out->print_cr("WARNING: monitor=" INTPTR_FORMAT ": in-use monitor is " + "deflated.", p2i(n)); + return; + } if (n->header().value() == 0) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": in-use per-thread monitor must have non-NULL _header " - "field.", p2i(jt), p2i(n)); - } else { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use global monitor " - "must have non-NULL _header field.", p2i(n)); - } + out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor must " + "have non-NULL _header field.", p2i(n)); *error_cnt_p = *error_cnt_p + 1; } const oop obj = n->object_peek(); if (obj != NULL) { const markWord mark = obj->mark(); if (!mark.has_monitor()) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": in-use per-thread monitor's object does not think " - "it has a monitor: obj=" INTPTR_FORMAT ", mark=" - INTPTR_FORMAT, p2i(jt), p2i(n), p2i(obj), mark.value()); - } else { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use global " - "monitor's object does not think it has a monitor: obj=" - INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n), - p2i(obj), mark.value()); - } + out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " + "object does not think it has a monitor: obj=" + INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n), + p2i(obj), mark.value()); *error_cnt_p = *error_cnt_p + 1; } ObjectMonitor* const obj_mon = mark.monitor(); if (n != obj_mon) { - if (jt != NULL) { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ", monitor=" INTPTR_FORMAT - ": in-use per-thread monitor's object does not refer " - "to the same monitor: obj=" INTPTR_FORMAT ", mark=" - INTPTR_FORMAT ", obj_mon=" INTPTR_FORMAT, p2i(jt), - p2i(n), p2i(obj), mark.value(), p2i(obj_mon)); - } else { - out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use global " - "monitor's object does not refer to the same monitor: obj=" - INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon=" - INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon)); - } + out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's " + "object does not refer to the same monitor: obj=" + INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon=" + INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon)); *error_cnt_p = *error_cnt_p + 1; } } } -// Check the thread's free list and count; log the results of the checks. -void ObjectSynchronizer::chk_per_thread_free_list_and_count(JavaThread *jt, - outputStream * out, - int *error_cnt_p) { - int chk_om_free_count = 0; - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&jt->om_free_list)) != NULL) { - // Marked the per-thread free list head so process the list. - while (true) { - chk_free_entry(jt, cur, out, error_cnt_p); - chk_om_free_count++; - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } - } - int l_om_free_count = Atomic::load(&jt->om_free_count); - if (l_om_free_count == chk_om_free_count) { - out->print_cr("jt=" INTPTR_FORMAT ": om_free_count=%d equals " - "chk_om_free_count=%d", p2i(jt), l_om_free_count, chk_om_free_count); - } else { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ": om_free_count=%d is not " - "equal to chk_om_free_count=%d", p2i(jt), l_om_free_count, - chk_om_free_count); - *error_cnt_p = *error_cnt_p + 1; - } -} - -// Check the thread's in-use list and count; log the results of the checks. -void ObjectSynchronizer::chk_per_thread_in_use_list_and_count(JavaThread *jt, - outputStream * out, - int *error_cnt_p) { - int chk_om_in_use_count = 0; - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&jt->om_in_use_list)) != NULL) { - // Marked the per-thread in-use list head so process the list. - while (true) { - chk_in_use_entry(jt, cur, out, error_cnt_p); - chk_om_in_use_count++; - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } - } - int l_om_in_use_count = Atomic::load(&jt->om_in_use_count); - if (l_om_in_use_count == chk_om_in_use_count) { - out->print_cr("jt=" INTPTR_FORMAT ": om_in_use_count=%d equals " - "chk_om_in_use_count=%d", p2i(jt), l_om_in_use_count, - chk_om_in_use_count); - } else { - out->print_cr("ERROR: jt=" INTPTR_FORMAT ": om_in_use_count=%d is not " - "equal to chk_om_in_use_count=%d", p2i(jt), l_om_in_use_count, - chk_om_in_use_count); - *error_cnt_p = *error_cnt_p + 1; - } -} - -// Do the final audit and print of ObjectMonitor stats; must be done -// by the VMThread (at VM exit time). -void ObjectSynchronizer::do_final_audit_and_print_stats() { - assert(Thread::current()->is_VM_thread(), "sanity check"); - - if (is_final_audit()) { // Only do the audit once. - return; - } - set_is_final_audit(); - - if (log_is_enabled(Info, monitorinflation)) { - // Do a deflation in order to reduce the in-use monitor population - // that is reported by ObjectSynchronizer::log_in_use_monitor_details() - // which is called by ObjectSynchronizer::audit_and_print_stats(). - ObjectSynchronizer::deflate_idle_monitors(); - // The other audit_and_print_stats() call is done at the Debug - // level at a safepoint in ObjectSynchronizer::do_safepoint_work(). - ObjectSynchronizer::audit_and_print_stats(true /* on_exit */); - } -} - -// Log details about ObjectMonitors on the in-use lists. The 'BHL' +// Log details about ObjectMonitors on the in_use_list. The 'BHL' // flags indicate why the entry is in-use, 'object' and 'object type' // indicate the associated object and its type. -void ObjectSynchronizer::log_in_use_monitor_details(outputStream * out) { +void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out) { stringStream ss; - if (Atomic::load(&om_list_globals._in_use_count) > 0) { - out->print_cr("In-use global monitor info:"); + if (_in_use_list.count() > 0) { + out->print_cr("In-use monitor info:"); out->print_cr("(B -> is_busy, H -> has hash code, L -> lock status)"); out->print_cr("%18s %s %18s %18s", "monitor", "BHL", "object", "object type"); out->print_cr("================== === ================== =================="); - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&om_list_globals._in_use_list)) != NULL) { - // Marked the global in-use list head so process the list. - while (true) { - const oop obj = cur->object_peek(); - const markWord mark = cur->header(); - ResourceMark rm; - out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(cur), - cur->is_busy() != 0, mark.hash() != 0, cur->owner() != NULL, - p2i(obj), obj == NULL ? "" : obj->klass()->external_name()); - if (cur->is_busy() != 0) { - out->print(" (%s)", cur->is_busy_to_string(&ss)); - ss.reset(); - } - out->cr(); - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } - } - } - } - - out->print_cr("In-use per-thread monitor info:"); - out->print_cr("(B -> is_busy, H -> has hash code, L -> lock status)"); - out->print_cr("%18s %18s %s %18s %18s", - "jt", "monitor", "BHL", "object", "object type"); - out->print_cr("================== ================== === ================== =================="); - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - ObjectMonitor* cur = NULL; - if ((cur = get_list_head_locked(&jt->om_in_use_list)) != NULL) { - // Marked the global in-use list head so process the list. - while (true) { - const oop obj = cur->object_peek(); - const markWord mark = cur->header(); - ResourceMark rm; - out->print(INTPTR_FORMAT " " INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT - " %s", p2i(jt), p2i(cur), cur->is_busy() != 0, - mark.hash() != 0, cur->owner() != NULL, p2i(obj), - obj == NULL ? "" : obj->klass()->external_name()); - if (cur->is_busy() != 0) { - out->print(" (%s)", cur->is_busy_to_string(&ss)); - ss.reset(); - } - out->cr(); - - cur = lock_next_for_traversal(cur); - if (cur == NULL) { - break; - } + MonitorList::Iterator iter = _in_use_list.iterator(); + while (iter.has_next()) { + ObjectMonitor* mid = iter.next(); + const oop obj = mid->object_peek(); + const markWord mark = mid->header(); + ResourceMark rm; + out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(mid), + mid->is_busy() != 0, mark.hash() != 0, mid->owner() != NULL, + p2i(obj), obj == NULL ? "" : obj->klass()->external_name()); + if (mid->is_busy() != 0) { + out->print(" (%s)", mid->is_busy_to_string(&ss)); + ss.reset(); } + out->cr(); } } out->flush(); } - -// Log counts for the global and per-thread monitor lists and return -// the population count. -int ObjectSynchronizer::log_monitor_list_counts(outputStream * out) { - int pop_count = 0; - out->print_cr("%18s %10s %10s %10s %10s", - "Global Lists:", "InUse", "Free", "Wait", "Total"); - out->print_cr("================== ========== ========== ========== =========="); - int l_in_use_count = Atomic::load(&om_list_globals._in_use_count); - int l_free_count = Atomic::load(&om_list_globals._free_count); - int l_wait_count = Atomic::load(&om_list_globals._wait_count); - out->print_cr("%18s %10d %10d %10d %10d", "", l_in_use_count, - l_free_count, l_wait_count, - Atomic::load(&om_list_globals._population)); - pop_count += l_in_use_count + l_free_count + l_wait_count; - - out->print_cr("%18s %10s %10s %10s", - "Per-Thread Lists:", "InUse", "Free", "Provision"); - out->print_cr("================== ========== ========== =========="); - - for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) { - int l_om_in_use_count = Atomic::load(&jt->om_in_use_count); - int l_om_free_count = Atomic::load(&jt->om_free_count); - out->print_cr(INTPTR_FORMAT " %10d %10d %10d", p2i(jt), - l_om_in_use_count, l_om_free_count, jt->om_free_provision); - pop_count += l_om_in_use_count + l_om_free_count; - } - return pop_count; -} - -#ifndef PRODUCT - -// Check if monitor belongs to the monitor cache -// The list is grow-only so it's *relatively* safe to traverse -// the list of extant blocks without taking a lock. - -int ObjectSynchronizer::verify_objmon_isinpool(ObjectMonitor *monitor) { - PaddedObjectMonitor* block = Atomic::load(&g_block_list); - while (block != NULL) { - assert(block->is_chainmarker(), "must be a block header"); - if (monitor > &block[0] && monitor < &block[_BLOCKSIZE]) { - address mon = (address)monitor; - address blk = (address)block; - size_t diff = mon - blk; - assert((diff % sizeof(PaddedObjectMonitor)) == 0, "must be aligned"); - return 1; - } - // unmarked_next() is not needed with g_block_list (no locking - // used with block linkage _next_om fields). - block = (PaddedObjectMonitor*)block->next_om(); - } - return 0; -} - -#endif diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp index 3c6efd80cf1aa..685561cf54b8d 100644 --- a/src/hotspot/share/runtime/synchronizer.hpp +++ b/src/hotspot/share/runtime/synchronizer.hpp @@ -31,19 +31,13 @@ #include "runtime/handles.hpp" #include "runtime/perfData.hpp" +class LogStream; class ObjectMonitor; class ThreadsList; -#ifndef OM_CACHE_LINE_SIZE -// Use DEFAULT_CACHE_LINE_SIZE if not already specified for -// the current build platform. -#define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE -#endif - -typedef PaddedEnd PaddedObjectMonitor; - class ObjectSynchronizer : AllStatic { friend class VMStructs; + public: typedef enum { owner_self, @@ -99,12 +93,6 @@ class ObjectSynchronizer : AllStatic { static intx complete_exit(Handle obj, TRAPS); static void reenter (Handle obj, intx recursions, TRAPS); - // thread-specific and global ObjectMonitor free list accessors - static ObjectMonitor* om_alloc(Thread* self); - static void om_release(Thread* self, ObjectMonitor* m, - bool FromPerThreadAlloc); - static void om_flush(Thread* self); - // Inflate light weight monitor to heavy weight monitor static ObjectMonitor* inflate(Thread* self, oop obj, const InflateCause cause); // This version is only for internal use @@ -127,23 +115,18 @@ class ObjectSynchronizer : AllStatic { static void monitors_iterate(MonitorClosure* m); // GC: we current use aggressive monitor deflation policy - // Basically we deflate all monitors that are not busy. - // An adaptive profile-based deflation policy could be used if needed - static void deflate_idle_monitors(); - static void deflate_global_idle_monitors(Thread* self); - static void deflate_per_thread_idle_monitors(Thread* self, - JavaThread* target); - static void deflate_common_idle_monitors(Thread* self, bool is_global, - JavaThread* target); - - // For a given in-use monitor list: global or per-thread, deflate idle - // monitors. - static int deflate_monitor_list(Thread* self, ObjectMonitor** list_p, - int* count_p, ObjectMonitor** free_head_p, - ObjectMonitor** free_tail_p, - ObjectMonitor** saved_mid_in_use_p); - static bool deflate_monitor(ObjectMonitor* mid, ObjectMonitor** free_head_p, - ObjectMonitor** free_tail_p); + // Basically we try to deflate all monitors that are not busy. + static size_t deflate_idle_monitors(); + + // Deflate idle monitors: + static void chk_for_block_req(JavaThread* self, const char* op_name, + const char* cnt_name, size_t cnt, LogStream* ls, + elapsedTimer* timer_p); + static size_t deflate_monitor_list(Thread* self, LogStream* ls, + elapsedTimer* timer_p); + static size_t in_use_list_ceiling(); + static void dec_in_use_list_ceiling(); + static void inc_in_use_list_ceiling(); static bool is_async_deflation_needed(); static bool is_async_deflation_requested() { return _is_async_deflation_requested; } static bool is_final_audit() { return _is_final_audit; } @@ -155,42 +138,19 @@ class ObjectSynchronizer : AllStatic { // debugging static void audit_and_print_stats(bool on_exit); - static void chk_free_entry(JavaThread* jt, ObjectMonitor* n, - outputStream * out, int *error_cnt_p); - static void chk_global_free_list_and_count(outputStream * out, - int *error_cnt_p); - static void chk_global_wait_list_and_count(outputStream * out, - int *error_cnt_p); - static void chk_global_in_use_list_and_count(outputStream * out, - int *error_cnt_p); - static void chk_in_use_entry(JavaThread* jt, ObjectMonitor* n, - outputStream * out, int *error_cnt_p); - static void chk_per_thread_in_use_list_and_count(JavaThread *jt, - outputStream * out, - int *error_cnt_p); - static void chk_per_thread_free_list_and_count(JavaThread *jt, - outputStream * out, - int *error_cnt_p); + static void chk_in_use_list(outputStream* out, int* error_cnt_p); + static void chk_in_use_entry(ObjectMonitor* n, outputStream* out, + int* error_cnt_p); static void do_final_audit_and_print_stats(); - static void log_in_use_monitor_details(outputStream * out); - static int log_monitor_list_counts(outputStream * out); - static int verify_objmon_isinpool(ObjectMonitor *addr) PRODUCT_RETURN0; - - static void do_safepoint_work(); + static void log_in_use_monitor_details(outputStream* out); private: friend class SynchronizerTest; - enum { _BLOCKSIZE = 128 }; - // global list of blocks of monitors - static PaddedObjectMonitor* g_block_list; static volatile bool _is_async_deflation_requested; static volatile bool _is_final_audit; static jlong _last_async_deflation_time_ns; - // Function to prepend new blocks to the appropriate lists: - static void prepend_block_to_lists(PaddedObjectMonitor* new_blk); - // Support for SynchronizerTest access to GVars fields: static u_char* get_gvars_addr(); static u_char* get_gvars_hc_sequence_addr(); diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 7bc6272d3d338..615a11514117d 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -81,6 +81,7 @@ #include "runtime/jniHandles.inline.hpp" #include "runtime/jniPeriodicChecker.hpp" #include "runtime/memprofiler.hpp" +#include "runtime/monitorDeflationThread.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/objectMonitor.hpp" #include "runtime/orderAccess.hpp" @@ -262,11 +263,6 @@ Thread::Thread() { _current_pending_monitor_is_from_java = true; _current_waiting_monitor = NULL; _current_pending_raw_monitor = NULL; - om_free_list = NULL; - om_free_count = 0; - om_free_provision = 32; - om_in_use_list = NULL; - om_in_use_count = 0; #ifdef ASSERT _visited_for_critical_count = false; @@ -3691,6 +3687,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // and other cleanups. Needs to start before the compilers start posting events. ServiceThread::initialize(); + // Start the monitor deflation thread: + MonitorDeflationThread::initialize(); + // initialize compiler(s) #if defined(COMPILER1) || COMPILER2_OR_JVMCI #if INCLUDE_JVMCI @@ -4233,6 +4232,9 @@ void Threads::add(JavaThread* p, bool force_daemon) { // Maintain fast thread list ThreadsSMRSupport::add_thread(p); + // Increase the ObjectMonitor ceiling for the new thread. + ObjectSynchronizer::inc_in_use_list_ceiling(); + // Possible GC point. Events::log(p, "Thread added: " INTPTR_FORMAT, p2i(p)); @@ -4241,19 +4243,14 @@ void Threads::add(JavaThread* p, bool force_daemon) { } void Threads::remove(JavaThread* p, bool is_daemon) { - - // Reclaim the ObjectMonitors from the om_in_use_list and om_free_list of the moribund thread. - ObjectSynchronizer::om_flush(p); - // Extra scope needed for Thread_lock, so we can check // that we do not remove thread without safepoint code notice { MonitorLocker ml(Threads_lock); - // We must flush any deferred card marks and other various GC barrier - // related buffers (e.g. G1 SATB buffer and G1 dirty card queue buffer) - // before removing a thread from the list of active threads. - // This must be done after ObjectSynchronizer::om_flush(), as GC barriers - // are used in om_flush(). + // BarrierSet state must be destroyed after the last thread transition + // before the thread terminates. Thread transitions result in calls to + // StackWatermarkSet::on_safepoint(), which performs GC processing, + // requiring the GC state to be alive. BarrierSet::barrier_set()->on_thread_detach(p); assert(ThreadsSMRSupport::get_java_thread_list()->includes(p), "p must be present"); @@ -4283,6 +4280,9 @@ void Threads::remove(JavaThread* p, bool is_daemon) { EscapeBarrier::thread_removed(p); } // unlock Threads_lock + // Reduce the ObjectMonitor ceiling for the exiting thread. + ObjectSynchronizer::dec_in_use_list_ceiling(); + // Since Events::log uses a lock, we grab it outside the Threads_lock Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p)); } diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index 6c6f6a3af177e..ee77c409d936f 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -416,14 +416,6 @@ class Thread: public ThreadShadow { // ObjectMonitor on which this thread called Object.wait() ObjectMonitor* _current_waiting_monitor; - // Per-thread ObjectMonitor lists: - public: - ObjectMonitor* om_free_list; // SLL of free ObjectMonitors - int om_free_count; // # on om_free_list - int om_free_provision; // # to try to allocate next - ObjectMonitor* om_in_use_list; // SLL of in-use ObjectMonitors - int om_in_use_count; // # on om_in_use_list - #ifdef ASSERT private: volatile uint64_t _visited_for_critical_count; @@ -485,6 +477,7 @@ class Thread: public ThreadShadow { virtual bool is_Compiler_thread() const { return false; } virtual bool is_Code_cache_sweeper_thread() const { return false; } virtual bool is_service_thread() const { return false; } + virtual bool is_monitor_deflation_thread() const { return false; } virtual bool is_hidden_from_external_view() const { return false; } virtual bool is_jvmti_agent_thread() const { return false; } // True iff the thread can perform GC operations at a safepoint. diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index ed9b0d3966600..903e4b36587fd 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -86,6 +86,7 @@ #include "runtime/globals.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/monitorDeflationThread.hpp" #include "runtime/notificationThread.hpp" #include "runtime/os.hpp" #include "runtime/perfMemory.hpp" @@ -887,7 +888,6 @@ typedef HashtableEntry KlassHashtableEntry; volatile_nonstatic_field(ObjectMonitor, _recursions, intx) \ nonstatic_field(BasicObjectLock, _lock, BasicLock) \ nonstatic_field(BasicObjectLock, _obj, oop) \ - static_field(ObjectSynchronizer, g_block_list, PaddedObjectMonitor*) \ \ /*********************/ \ /* Matcher (C2 only) */ \ @@ -1338,6 +1338,7 @@ typedef HashtableEntry KlassHashtableEntry; declare_type(WatcherThread, NonJavaThread) \ declare_type(JavaThread, Thread) \ declare_type(JvmtiAgentThread, JavaThread) \ + declare_type(MonitorDeflationThread, JavaThread) \ declare_type(ServiceThread, JavaThread) \ declare_type(NotificationThread, JavaThread) \ declare_type(CompilerThread, JavaThread) \ @@ -1462,7 +1463,6 @@ typedef HashtableEntry KlassHashtableEntry; /************/ \ \ declare_toplevel_type(ObjectMonitor) \ - declare_toplevel_type(PaddedObjectMonitor) \ declare_toplevel_type(ObjectSynchronizer) \ declare_toplevel_type(BasicLock) \ declare_toplevel_type(BasicObjectLock) \ @@ -1993,7 +1993,6 @@ typedef HashtableEntry KlassHashtableEntry; declare_toplevel_type(nmethod*) \ COMPILER2_PRESENT(declare_unsigned_integer_type(node_idx_t)) \ declare_toplevel_type(ObjectMonitor*) \ - declare_toplevel_type(PaddedObjectMonitor*) \ declare_toplevel_type(oop*) \ declare_toplevel_type(OopMapCache*) \ declare_toplevel_type(VMReg) \ @@ -2522,12 +2521,6 @@ typedef HashtableEntry KlassHashtableEntry; declare_constant(JNIHandleBlock::block_size_in_oops) \ \ /**********************/ \ - /* ObjectSynchronizer */ \ - /**********************/ \ - \ - declare_constant(ObjectSynchronizer::_BLOCKSIZE) \ - \ - /**********************/ \ /* PcDesc */ \ /**********************/ \ \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/MonitorDeflationThread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/MonitorDeflationThread.java new file mode 100644 index 0000000000000..8bd6492f880c4 --- /dev/null +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/MonitorDeflationThread.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package sun.jvm.hotspot.runtime; + +import java.io.*; + +import sun.jvm.hotspot.debugger.*; +import sun.jvm.hotspot.types.*; + +public class MonitorDeflationThread extends JavaThread { + public MonitorDeflationThread(Address addr) { + super(addr); + } + + public boolean isJavaThread() { return false; } + public boolean isHiddenFromExternalView() { return true; } + public boolean isMonitorDeflationThread() { return true; } + +} diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java index af18075d3b2ef..43ddf257d0413 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectMonitor.java @@ -103,10 +103,6 @@ public int contentions() { return contentionsField.getValue(addr); } - // FIXME - // oop* object_addr(); - // void set_object(oop obj); - // The following four either aren't expressed as typed fields in // vmStructs.cpp because they aren't strongly typed in the VM, or // would confuse the SA's type system. diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java index 2624773e4ca83..db42ca8dd2d68 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Thread.java @@ -121,6 +121,7 @@ public long allocatedBytes() { public boolean isJvmtiAgentThread() { return false; } public boolean isWatcherThread() { return false; } public boolean isServiceThread() { return false; } + public boolean isMonitorDeflationThread() { return false; } /** Memory operations */ public void oopsDo(AddressVisitor oopVisitor) { diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java index 80279b3d1c535..49a2a1964b1b3 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/Threads.java @@ -149,6 +149,7 @@ private static synchronized void initialize(TypeDataBase db) { } virtualConstructor.addMapping("JvmtiAgentThread", JvmtiAgentThread.class); virtualConstructor.addMapping("ServiceThread", ServiceThread.class); + virtualConstructor.addMapping("MonitorDeflationThread", MonitorDeflationThread.class); virtualConstructor.addMapping("NotificationThread", NotificationThread.class); } @@ -157,7 +158,7 @@ public Threads() { } /** NOTE: this returns objects of type JavaThread, CompilerThread, - JvmtiAgentThread, NotificationThread, and ServiceThread. + JvmtiAgentThread, NotificationThread, MonitorDeflationThread and ServiceThread. The latter four are subclasses of the former. Most operations (fetching the top frame, etc.) are only allowed to be performed on a "pure" JavaThread. For this reason, {@link @@ -188,7 +189,7 @@ public JavaThread createJavaThreadWrapper(Address threadAddr) { return thread; } catch (Exception e) { throw new RuntimeException("Unable to deduce type of thread from address " + threadAddr + - " (expected type JavaThread, CompilerThread, ServiceThread, JvmtiAgentThread or CodeCacheSweeperThread)", e); + " (expected type JavaThread, CompilerThread, MonitorDeflationThread, ServiceThread, JvmtiAgentThread or CodeCacheSweeperThread)", e); } } diff --git a/test/hotspot/gtest/runtime/test_objectMonitor.cpp b/test/hotspot/gtest/runtime/test_objectMonitor.cpp index 919c43a687310..33bec3253d190 100644 --- a/test/hotspot/gtest/runtime/test_objectMonitor.cpp +++ b/test/hotspot/gtest/runtime/test_objectMonitor.cpp @@ -27,9 +27,6 @@ #include "unittest.hpp" TEST_VM(ObjectMonitor, sanity) { - - EXPECT_EQ(0, ObjectMonitor::header_offset_in_bytes()) << "Offset for _header must be zero."; - uint cache_line_size = VM_Version::L1_data_cache_line_size(); if (cache_line_size != 0) { diff --git a/test/hotspot/jtreg/runtime/logging/SafepointCleanupTest.java b/test/hotspot/jtreg/runtime/logging/SafepointCleanupTest.java index 5ceaf0160e85d..5c445dc4f1c56 100644 --- a/test/hotspot/jtreg/runtime/logging/SafepointCleanupTest.java +++ b/test/hotspot/jtreg/runtime/logging/SafepointCleanupTest.java @@ -39,7 +39,6 @@ static void analyzeOutputOn(ProcessBuilder pb) throws Exception { OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldContain("[safepoint,cleanup]"); output.shouldContain("safepoint cleanup tasks"); - output.shouldContain("deflating idle monitors"); output.shouldContain("updating inline caches"); output.shouldContain("compilation policy safepoint handler"); output.shouldHaveExitValue(0); diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java index 2ebe4647113e0..eae15ea35e223 100644 --- a/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java +++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbPrintStatics.java @@ -59,7 +59,6 @@ public static void main(String[] args) throws Exception { "Abstract_VM_Version::_vm_major_version", "ClassLoaderDataGraph::_head", "JNIHandles::_weak_global_handles", "PerfMemory::_top", - "ObjectSynchronizer::g_block_list", "java_lang_Class::_oop_size_offset")); expStrMap.put("printstatics SystemDictionary", List.of( "Static fields of SystemDictionary", From 3c3469b9ceb6a8f6c6fe6902b69533b349f3cbca Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Wed, 11 Nov 2020 18:03:58 +0000 Subject: [PATCH 073/124] 8256020: Shenandoah: Don't resurrect objects during evacuation on AS_NO_KEEPALIVE Reviewed-by: shade --- .../shenandoah/shenandoahBarrierSet.inline.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index e9cd4a2b54f82..1388465328dab 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -106,14 +106,15 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj, T* load_addr) { if (!HasDecorator::value && obj != NULL && _heap->is_concurrent_weak_root_in_progress() && !_heap->marking_context()->is_marked(obj)) { - Thread* thr = Thread::current(); - if (thr->is_Java_thread()) { - return NULL; - } else { - // This path is sometimes (rarely) taken by GC threads. - // See e.g.: https://bugs.openjdk.java.net/browse/JDK-8237874 - return obj; - } + return NULL; + } + + // Prevent resurrection of unreachable objects that are visited during + // concurrent class-unloading. + if (HasDecorator::value && obj != NULL && + _heap->is_evacuation_in_progress() && + !_heap->marking_context()->is_marked(obj)) { + return obj; } oop fwd = load_reference_barrier(obj); From 96e0261041626d0db4ddb33bb2224333716dfa72 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Wed, 11 Nov 2020 18:07:08 +0000 Subject: [PATCH 074/124] 8256106: Bypass intrinsic/barrier when calling Reference.get() from Finalizer Reviewed-by: eosterlund --- .../share/classes/java/lang/ref/Finalizer.java | 2 +- .../share/classes/java/lang/ref/Reference.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/ref/Finalizer.java b/src/java.base/share/classes/java/lang/ref/Finalizer.java index 59c59c528be23..54b1283bb44a2 100644 --- a/src/java.base/share/classes/java/lang/ref/Finalizer.java +++ b/src/java.base/share/classes/java/lang/ref/Finalizer.java @@ -82,7 +82,7 @@ private void runFinalizer(JavaLangAccess jla) { } try { - Object finalizee = this.get(); + Object finalizee = this.getInactive(); if (finalizee != null && !(finalizee instanceof java.lang.Enum)) { jla.invokeFinalize(finalizee); diff --git a/src/java.base/share/classes/java/lang/ref/Reference.java b/src/java.base/share/classes/java/lang/ref/Reference.java index e655a85792b25..d7eb5ea883e43 100644 --- a/src/java.base/share/classes/java/lang/ref/Reference.java +++ b/src/java.base/share/classes/java/lang/ref/Reference.java @@ -342,6 +342,20 @@ public T get() { return this.referent; } + /** + * Load referent with strong semantics. Treating the referent + * as strong referent is ok when the Reference is inactive, + * because then the referent is switched to strong semantics + * anyway. + * + * This is only used from Finalizer to bypass the intrinsic, + * which might return a null referent, even though it is not + * null, and would subsequently not finalize the referent/finalizee. + */ + T getInactive() { + return this.referent; + } + /** * Tests if the referent of this reference object is {@code obj}. * Using a {@code null} {@code obj} returns {@code true} if the From bfa060f098e477fb45bc466f87fd66842161b73f Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Wed, 11 Nov 2020 19:12:55 +0000 Subject: [PATCH 075/124] 8256051: nmethod_entry_barrier stub miscalculates xmm spill size on x86_32 Reviewed-by: shade --- src/hotspot/cpu/x86/stubGenerator_x86_32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp index c0b2219dcec27..4bc3b0340b5fb 100644 --- a/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp +++ b/src/hotspot/cpu/x86/stubGenerator_x86_32.cpp @@ -3669,7 +3669,7 @@ class StubGenerator: public StubCodeGenerator { __ pusha(); // xmm0 and xmm1 may be used for passing float/double arguments - const int xmm_size = wordSize * 2; + const int xmm_size = wordSize * 4; const int xmm_spill_size = xmm_size * 2; __ subptr(rsp, xmm_spill_size); __ movdqu(Address(rsp, xmm_size * 1), xmm1); From 59965c17b40d03bbcbcd15b128f01c00aa6c16de Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 11 Nov 2020 19:52:18 +0000 Subject: [PATCH 076/124] 8256237: Zero: non-PCH build fails after JDK-8253064 Reviewed-by: zgu, dcubed --- src/hotspot/share/runtime/monitorDeflationThread.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/runtime/monitorDeflationThread.cpp b/src/hotspot/share/runtime/monitorDeflationThread.cpp index 0b1dd2587fdc4..3b629082e57bb 100644 --- a/src/hotspot/share/runtime/monitorDeflationThread.cpp +++ b/src/hotspot/share/runtime/monitorDeflationThread.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/javaClasses.hpp" +#include "memory/universe.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" From ccb48b72033ecbbf13729fe43b832017533ffa6b Mon Sep 17 00:00:00 2001 From: Corey Ashford Date: Wed, 11 Nov 2020 21:31:07 +0000 Subject: [PATCH 077/124] 8248188: Add IntrinsicCandidate and API for Base64 decoding 8248188: Add IntrinsicCandidate and API for Base64 decoding, add Power64LE intrinsic implementation. This patch set encompasses the following commits: Adds a new intrinsic candidate to the java.lang.Base64 class - decodeBlock(), and provides a flexible API for the intrinsic. The API is similar to the existing encodeBlock intrinsic. Adds the code in HotSpot to check and martial the new intrinsic's arguments to the arch-specific intrinsic implementation. Adds a Power64LE-specific implementation of the decodeBlock intrinsic. Adds a JMH microbenchmark for both Base64 encoding and encoding. Enhances the JTReg hotspot intrinsic "TestBase64.java" regression test to more fully test both decoding and encoding. Reviewed-by: rriggs, mdoerr, kvn --- src/hotspot/cpu/ppc/assembler_ppc.hpp | 23 + src/hotspot/cpu/ppc/assembler_ppc.inline.hpp | 10 + src/hotspot/cpu/ppc/stubGenerator_ppc.cpp | 485 +++++++++++- src/hotspot/cpu/ppc/vm_version_ppc.cpp | 7 + src/hotspot/share/classfile/vmIntrinsics.cpp | 1 + src/hotspot/share/classfile/vmIntrinsics.hpp | 6 + .../gc/shenandoah/c2/shenandoahSupport.cpp | 5 +- src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 1 + src/hotspot/share/opto/c2compiler.cpp | 1 + src/hotspot/share/opto/escape.cpp | 1 + src/hotspot/share/opto/library_call.cpp | 36 + src/hotspot/share/opto/library_call.hpp | 1 + src/hotspot/share/opto/runtime.cpp | 21 + src/hotspot/share/opto/runtime.hpp | 1 + src/hotspot/share/runtime/stubRoutines.cpp | 1 + src/hotspot/share/runtime/stubRoutines.hpp | 2 + src/hotspot/share/runtime/vmStructs.cpp | 1 + .../share/classes/java/util/Base64.java | 93 ++- .../hotspot/test/CheckGraalIntrinsics.java | 8 + .../intrinsics/base64/TestBase64.java | 133 +++- .../intrinsics/base64/longLineBaseEncode.txt | 100 +++ .../intrinsics/base64/longLineHEX.txt | 100 +++ .../intrinsics/base64/longLineMimeEncode.txt | 700 ++++++++++++++++++ .../intrinsics/base64/longLineUrlEncode.txt | 100 +++ .../bench/java/util/Base64VarLenDecode.java | 91 +++ 25 files changed, 1901 insertions(+), 27 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/base64/longLineBaseEncode.txt create mode 100644 test/hotspot/jtreg/compiler/intrinsics/base64/longLineHEX.txt create mode 100644 test/hotspot/jtreg/compiler/intrinsics/base64/longLineMimeEncode.txt create mode 100644 test/hotspot/jtreg/compiler/intrinsics/base64/longLineUrlEncode.txt create mode 100644 test/micro/org/openjdk/bench/java/util/Base64VarLenDecode.java diff --git a/src/hotspot/cpu/ppc/assembler_ppc.hpp b/src/hotspot/cpu/ppc/assembler_ppc.hpp index 05ab385fb4f55..3ddfcb0dc2ebf 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp @@ -338,6 +338,7 @@ class Assembler : public AbstractAssembler { MTCRF_OPCODE = (31u << OPCODE_SHIFT | 144u << 1), MFCR_OPCODE = (31u << OPCODE_SHIFT | 19u << 1), MCRF_OPCODE = (19u << OPCODE_SHIFT | 0u << 1), + MCRXRX_OPCODE = (31u << OPCODE_SHIFT | 576u << 1), SETB_OPCODE = (31u << OPCODE_SHIFT | 128u << 1), // condition register logic instructions @@ -519,6 +520,8 @@ class Assembler : public AbstractAssembler { LVSR_OPCODE = (31u << OPCODE_SHIFT | 38u << 1), // Vector-Scalar (VSX) instruction support. + LXV_OPCODE = (61u << OPCODE_SHIFT | 1u ), + STXV_OPCODE = (61u << OPCODE_SHIFT | 5u ), LXVD2X_OPCODE = (31u << OPCODE_SHIFT | 844u << 1), STXVD2X_OPCODE = (31u << OPCODE_SHIFT | 972u << 1), MTVSRD_OPCODE = (31u << OPCODE_SHIFT | 179u << 1), @@ -530,12 +533,16 @@ class Assembler : public AbstractAssembler { XXMRGHW_OPCODE = (60u << OPCODE_SHIFT | 18u << 3), XXMRGLW_OPCODE = (60u << OPCODE_SHIFT | 50u << 3), XXSPLTW_OPCODE = (60u << OPCODE_SHIFT | 164u << 2), + XXLAND_OPCODE = (60u << OPCODE_SHIFT | 130u << 3), XXLOR_OPCODE = (60u << OPCODE_SHIFT | 146u << 3), XXLXOR_OPCODE = (60u << OPCODE_SHIFT | 154u << 3), XXLEQV_OPCODE = (60u << OPCODE_SHIFT | 186u << 3), XVDIVSP_OPCODE = (60u << OPCODE_SHIFT | 88u << 3), XXBRD_OPCODE = (60u << OPCODE_SHIFT | 475u << 2 | 23u << 16), // XX2-FORM XXBRW_OPCODE = (60u << OPCODE_SHIFT | 475u << 2 | 15u << 16), // XX2-FORM + XXPERM_OPCODE = (60u << OPCODE_SHIFT | 26u << 3), + XXSEL_OPCODE = (60u << OPCODE_SHIFT | 3u << 4), + XXSPLTIB_OPCODE= (60u << OPCODE_SHIFT | 360u << 1), XVDIVDP_OPCODE = (60u << OPCODE_SHIFT | 120u << 3), XVABSSP_OPCODE = (60u << OPCODE_SHIFT | 409u << 2), XVABSDP_OPCODE = (60u << OPCODE_SHIFT | 473u << 2), @@ -592,6 +599,7 @@ class Assembler : public AbstractAssembler { VSPLTISH_OPCODE= (4u << OPCODE_SHIFT | 844u ), VSPLTISW_OPCODE= (4u << OPCODE_SHIFT | 908u ), + VPEXTD_OPCODE = (4u << OPCODE_SHIFT | 1421u ), VPERM_OPCODE = (4u << OPCODE_SHIFT | 43u ), VSEL_OPCODE = (4u << OPCODE_SHIFT | 42u ), @@ -1099,6 +1107,7 @@ class Assembler : public AbstractAssembler { static int frs( int x) { return opp_u_field(x, 10, 6); } static int frt( int x) { return opp_u_field(x, 10, 6); } static int fxm( int x) { return opp_u_field(x, 19, 12); } + static int imm8( int x) { return opp_u_field(uimm(x, 8), 20, 13); } static int l10( int x) { assert(x == 0 || x == 1, "must be 0 or 1"); return opp_u_field(x, 10, 10); } static int l14( int x) { return opp_u_field(x, 15, 14); } static int l15( int x) { return opp_u_field(x, 15, 15); } @@ -1165,14 +1174,20 @@ class Assembler : public AbstractAssembler { // Support Vector-Scalar (VSX) instructions. static int vsra( int x) { return opp_u_field(x & 0x1F, 15, 11) | opp_u_field((x & 0x20) >> 5, 29, 29); } static int vsrb( int x) { return opp_u_field(x & 0x1F, 20, 16) | opp_u_field((x & 0x20) >> 5, 30, 30); } + static int vsrc( int x) { return opp_u_field(x & 0x1F, 25, 21) | opp_u_field((x & 0x20) >> 5, 28, 28); } static int vsrs( int x) { return opp_u_field(x & 0x1F, 10, 6) | opp_u_field((x & 0x20) >> 5, 31, 31); } static int vsrt( int x) { return vsrs(x); } static int vsdm( int x) { return opp_u_field(x, 23, 22); } + static int vsrs_dq( int x) { return opp_u_field(x & 0x1F, 10, 6) | opp_u_field((x & 0x20) >> 5, 28, 28); } + static int vsrt_dq( int x) { return vsrs_dq(x); } static int vsra( VectorSRegister r) { return vsra(r->encoding());} static int vsrb( VectorSRegister r) { return vsrb(r->encoding());} + static int vsrc( VectorSRegister r) { return vsrc(r->encoding());} static int vsrs( VectorSRegister r) { return vsrs(r->encoding());} static int vsrt( VectorSRegister r) { return vsrt(r->encoding());} + static int vsrs_dq(VectorSRegister r) { return vsrs_dq(r->encoding());} + static int vsrt_dq(VectorSRegister r) { return vsrt_dq(r->encoding());} static int vsplt_uim( int x) { return opp_u_field(x, 15, 12); } // for vsplt* instructions static int vsplti_sim(int x) { return opp_u_field(x, 15, 11); } // for vsplti* instructions @@ -1675,6 +1690,7 @@ class Assembler : public AbstractAssembler { inline void mcrf( ConditionRegister crd, ConditionRegister cra); inline void mtcr( Register s); // >= Power9 + inline void mcrxrx(ConditionRegister cra); inline void setb( Register d, ConditionRegister cra); // Special purpose registers @@ -2119,6 +2135,7 @@ class Assembler : public AbstractAssembler { inline void vspltish( VectorRegister d, int si5); inline void vspltisw( VectorRegister d, int si5); inline void vperm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); + inline void vpextd( VectorRegister d, VectorRegister a, VectorRegister b); inline void vsel( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c); inline void vsl( VectorRegister d, VectorRegister a, VectorRegister b); inline void vsldoi( VectorRegister d, VectorRegister a, VectorRegister b, int ui4); @@ -2235,6 +2252,8 @@ class Assembler : public AbstractAssembler { inline void mfvscr( VectorRegister d); // Vector-Scalar (VSX) instructions. + inline void lxv( VectorSRegister d, int si16, Register a); + inline void stxv( VectorSRegister d, int si16, Register a); inline void lxvd2x( VectorSRegister d, Register a); inline void lxvd2x( VectorSRegister d, Register a, Register b); inline void stxvd2x( VectorSRegister d, Register a); @@ -2243,6 +2262,7 @@ class Assembler : public AbstractAssembler { inline void mfvrwz( Register a, VectorRegister d); inline void mtvrd( VectorRegister d, Register a); inline void mfvrd( Register a, VectorRegister d); + inline void xxperm( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xxpermdi( VectorSRegister d, VectorSRegister a, VectorSRegister b, int dm); inline void xxmrghw( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xxmrglw( VectorSRegister d, VectorSRegister a, VectorSRegister b); @@ -2256,6 +2276,9 @@ class Assembler : public AbstractAssembler { inline void xxleqv( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xxbrd( VectorSRegister d, VectorSRegister b); inline void xxbrw( VectorSRegister d, VectorSRegister b); + inline void xxland( VectorSRegister d, VectorSRegister a, VectorSRegister b); + inline void xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c); + inline void xxspltib( VectorSRegister d, int ui8); inline void xvdivsp( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xvdivdp( VectorSRegister d, VectorSRegister a, VectorSRegister b); inline void xvabssp( VectorSRegister d, VectorSRegister b); diff --git a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp index a351674b5ba92..b139bb957185e 100644 --- a/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/assembler_ppc.inline.hpp @@ -378,6 +378,9 @@ inline void Assembler::mfcr( Register d ) { emit_int32(MFCR_OPCODE | rt inline void Assembler::mcrf( ConditionRegister crd, ConditionRegister cra) { emit_int32(MCRF_OPCODE | bf(crd) | bfa(cra)); } inline void Assembler::mtcr( Register s) { Assembler::mtcrf(0xff, s); } +// Introduced in Power 9: +inline void Assembler::mcrxrx(ConditionRegister cra) + { emit_int32(MCRXRX_OPCODE | bf(cra)); } inline void Assembler::setb(Register d, ConditionRegister cra) { emit_int32(SETB_OPCODE | rt(d) | bfa(cra)); } @@ -776,6 +779,8 @@ inline void Assembler::lvsl( VectorRegister d, Register s1, Register s2) { emit inline void Assembler::lvsr( VectorRegister d, Register s1, Register s2) { emit_int32( LVSR_OPCODE | vrt(d) | ra0mem(s1) | rb(s2)); } // Vector-Scalar (VSX) instructions. +inline void Assembler::lxv( VectorSRegister d, int ui16, Register a) { assert(is_aligned(ui16, 16), "displacement must be a multiple of 16"); emit_int32( LXV_OPCODE | vsrt_dq(d) | ra0mem(a) | uimm(ui16, 16)); } +inline void Assembler::stxv( VectorSRegister d, int ui16, Register a) { assert(is_aligned(ui16, 16), "displacement must be a multiple of 16"); emit_int32( STXV_OPCODE | vsrs_dq(d) | ra0mem(a) | uimm(ui16, 16)); } inline void Assembler::lxvd2x( VectorSRegister d, Register s1) { emit_int32( LXVD2X_OPCODE | vsrt(d) | ra(0) | rb(s1)); } inline void Assembler::lxvd2x( VectorSRegister d, Register s1, Register s2) { emit_int32( LXVD2X_OPCODE | vsrt(d) | ra0mem(s1) | rb(s2)); } inline void Assembler::stxvd2x( VectorSRegister d, Register s1) { emit_int32( STXVD2X_OPCODE | vsrs(d) | ra(0) | rb(s1)); } @@ -784,7 +789,9 @@ inline void Assembler::mtvsrd( VectorSRegister d, Register a) { e inline void Assembler::mfvsrd( Register d, VectorSRegister a) { emit_int32( MFVSRD_OPCODE | vsrs(a) | ra(d)); } inline void Assembler::mtvsrwz( VectorSRegister d, Register a) { emit_int32( MTVSRWZ_OPCODE | vsrt(d) | ra(a)); } inline void Assembler::mfvsrwz( Register d, VectorSRegister a) { emit_int32( MFVSRWZ_OPCODE | vsrs(a) | ra(d)); } +inline void Assembler::xxspltib(VectorSRegister d, int ui8) { emit_int32( XXSPLTIB_OPCODE | vsrt(d) | imm8(ui8)); } inline void Assembler::xxspltw( VectorSRegister d, VectorSRegister b, int ui2) { emit_int32( XXSPLTW_OPCODE | vsrt(d) | vsrb(b) | xxsplt_uim(uimm(ui2,2))); } +inline void Assembler::xxland( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXLAND_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxlor( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXLOR_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxlxor( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXLXOR_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxleqv( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXLEQV_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } @@ -817,9 +824,11 @@ inline void Assembler::mtvrd( VectorRegister d, Register a) { em inline void Assembler::mfvrd( Register a, VectorRegister d) { emit_int32( MFVSRD_OPCODE | vsrt(d->to_vsr()) | ra(a)); } inline void Assembler::mtvrwz( VectorRegister d, Register a) { emit_int32( MTVSRWZ_OPCODE | vsrt(d->to_vsr()) | ra(a)); } inline void Assembler::mfvrwz( Register a, VectorRegister d) { emit_int32( MFVSRWZ_OPCODE | vsrt(d->to_vsr()) | ra(a)); } +inline void Assembler::xxperm( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXPERM_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxpermdi(VectorSRegister d, VectorSRegister a, VectorSRegister b, int dm) { emit_int32( XXPERMDI_OPCODE | vsrt(d) | vsra(a) | vsrb(b) | vsdm(dm)); } inline void Assembler::xxmrghw( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXMRGHW_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } inline void Assembler::xxmrglw( VectorSRegister d, VectorSRegister a, VectorSRegister b) { emit_int32( XXMRGHW_OPCODE | vsrt(d) | vsra(a) | vsrb(b)); } +inline void Assembler::xxsel( VectorSRegister d, VectorSRegister a, VectorSRegister b, VectorSRegister c) { emit_int32( XXSEL_OPCODE | vsrt(d) | vsra(a) | vsrb(b) | vsrc(c)); } // VSX Extended Mnemonics inline void Assembler::xxspltd( VectorSRegister d, VectorSRegister a, int x) { xxpermdi(d, a, a, x ? 3 : 0); } @@ -860,6 +869,7 @@ inline void Assembler::vspltisb(VectorRegister d, int si5) inline void Assembler::vspltish(VectorRegister d, int si5) { emit_int32( VSPLTISH_OPCODE| vrt(d) | vsplti_sim(simm(si5,5))); } inline void Assembler::vspltisw(VectorRegister d, int si5) { emit_int32( VSPLTISW_OPCODE| vrt(d) | vsplti_sim(simm(si5,5))); } inline void Assembler::vperm( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c){ emit_int32( VPERM_OPCODE | vrt(d) | vra(a) | vrb(b) | vrc(c)); } +inline void Assembler::vpextd( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VPEXTD_OPCODE| vrt(d) | vra(a) | vrb(b)); } inline void Assembler::vsel( VectorRegister d, VectorRegister a, VectorRegister b, VectorRegister c){ emit_int32( VSEL_OPCODE | vrt(d) | vra(a) | vrb(b) | vrc(c)); } inline void Assembler::vsl( VectorRegister d, VectorRegister a, VectorRegister b) { emit_int32( VSL_OPCODE | vrt(d) | vra(a) | vrb(b)); } inline void Assembler::vsldoi( VectorRegister d, VectorRegister a, VectorRegister b, int ui4) { emit_int32( VSLDOI_OPCODE| vrt(d) | vra(a) | vrb(b) | vsldoi_shb(uimm(ui4,4))); } diff --git a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp index 64e654cd40c30..9f6cdea66c117 100644 --- a/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp +++ b/src/hotspot/cpu/ppc/stubGenerator_ppc.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2019 SAP SE. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2020 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3544,6 +3544,480 @@ class StubGenerator: public StubCodeGenerator { return start; } +#ifdef VM_LITTLE_ENDIAN +// The following Base64 decode intrinsic is based on an algorithm outlined +// in here: +// http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html +// in the section titled "Vector lookup (pshufb with bitmask)" +// +// This implementation differs in the following ways: +// * Instead of Intel SSE instructions, Power AltiVec VMX and VSX instructions +// are used instead. It turns out that some of the vector operations +// needed in the algorithm require fewer AltiVec instructions. +// * The algorithm in the above mentioned paper doesn't handle the +// Base64-URL variant in RFC 4648. Adjustments to both the code and to two +// lookup tables are needed for this. +// * The "Pack" section of the code is a complete rewrite for Power because we +// can utilize better instructions for this step. +// + +// Offsets per group of Base64 characters +// Uppercase +#define UC (signed char)((-'A' + 0) & 0xff) +// Lowercase +#define LC (signed char)((-'a' + 26) & 0xff) +// Digits +#define DIG (signed char)((-'0' + 52) & 0xff) +// Plus sign (URL = 0) +#define PLS (signed char)((-'+' + 62) & 0xff) +// Hyphen (URL = 1) +#define HYP (signed char)((-'-' + 62) & 0xff) +// Slash (URL = 0) +#define SLS (signed char)((-'/' + 63) & 0xff) +// Underscore (URL = 1) +#define US (signed char)((-'_' + 63) & 0xff) + +#define VEC_ALIGN __attribute__ ((aligned(16))) + +// In little-endian mode, the lxv instruction loads the element at EA into +// element 15 of the the vector register, EA+1 goes into element 14, and so +// on. +// +// To make a look-up table easier to read, ARRAY_TO_LXV_ORDER reverses the +// order of the elements in a vector initialization. +#define ARRAY_TO_LXV_ORDER(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15) e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0 + + // + // Base64 decodeBlock intrinsic + address generate_base64_decodeBlock() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "base64_decodeBlock"); + address start = __ function_entry(); + + static const signed char VEC_ALIGN offsetLUT_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 0, PLS, DIG, UC, UC, LC, LC, + 0, 0, 0, 0, 0, 0, 0, 0 ) }; + + static const signed char VEC_ALIGN offsetLUT_URL_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 0, HYP, DIG, UC, UC, LC, LC, + 0, 0, 0, 0, 0, 0, 0, 0 ) }; + + static const unsigned char VEC_ALIGN maskLUT_val[16] = { + ARRAY_TO_LXV_ORDER( + /* 0 */ (unsigned char)0b10101000, + /* 1 .. 9 */ (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, + (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, + (unsigned char)0b11111000, + /* 10 */ (unsigned char)0b11110000, + /* 11 */ (unsigned char)0b01010100, + /* 12 .. 14 */ (unsigned char)0b01010000, (unsigned char)0b01010000, (unsigned char)0b01010000, + /* 15 */ (unsigned char)0b01010100 ) }; + + static const unsigned char VEC_ALIGN maskLUT_URL_val[16] = { + ARRAY_TO_LXV_ORDER( + /* 0 */ (unsigned char)0b10101000, + /* 1 .. 9 */ (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, + (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, + (unsigned char)0b11111000, + /* 10 */ (unsigned char)0b11110000, + /* 11 .. 12 */ (unsigned char)0b01010000, (unsigned char)0b01010000, + /* 13 */ (unsigned char)0b01010100, + /* 14 */ (unsigned char)0b01010000, + /* 15 */ (unsigned char)0b01110000 ) }; + + static const unsigned char VEC_ALIGN bitposLUT_val[16] = { + ARRAY_TO_LXV_ORDER( + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (unsigned char)0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) }; + + static const unsigned char VEC_ALIGN pack_lshift_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 6, 4, 2, 0, 6, 4, 2, 0, 6, 4, 2, 0, 6, 4, 2 ) }; + + static const unsigned char VEC_ALIGN pack_rshift_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0 ) }; + + // The first 4 index values are "don't care" because + // we only use the first 12 bytes of the vector, + // which are decoded from 16 bytes of Base64 characters. + static const unsigned char VEC_ALIGN pack_permute_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 0, 0, 0, + 0, 1, 2, + 4, 5, 6, + 8, 9, 10, + 12, 13, 14 ) }; + + static const unsigned char VEC_ALIGN p10_pack_permute_val[16] = { + ARRAY_TO_LXV_ORDER( + 0, 0, 0, 0, 7, 6, 5, 4, + 3, 2, 15, 14, 13, 12, 11, 10 ) }; + + // loop_unrolls needs to be a power of two so that the rounding can be + // done using a mask. + // + // The amount of loop unrolling was determined by running a benchmark + // that decodes a 20k block of Base64 data on a Power9 machine: + // loop_unrolls = 1 : + // (min, avg, max) = (108639.215, 110530.479, 110779.920), stdev = 568.437 + // loop_unrolls = 2 : + // (min, avg, max) = (108259.029, 110174.202, 110399.642), stdev = 561.729 + // loop_unrolls = 4 : + // (min, avg, max) = (106514.175, 108373.110, 108514.786), stdev = 392.237 + // loop_unrolls = 8 : + // (min, avg, max) = (106281.283, 108316.668, 108539.953), stdev = 553.938 + // loop_unrolls = 16 : + // (min, avg, max) = (108580.768, 110631.161, 110766.237), stdev = 430.510 + // + // Comparing only the max values, there's no reason to go past + // loop_unrolls = 1. Performance at loop_unrolls = 16 is similar but + // has the disadvantage of requiring a larger minimum block of data to + // work with. A value of 1 gives a minimum of (16 + 12) = 28 bytes + // before the intrinsic will decode any data. See the reason for the + // +12 in the following logic. + const unsigned loop_unrolls = 1; + + const unsigned vec_size = 16; // size of vector registers in bytes + const unsigned block_size = vec_size * loop_unrolls; // number of bytes to process in each pass through the loop + const unsigned block_size_shift = exact_log2(block_size); + + // According to the ELF V2 ABI, registers r3-r12 are volatile and available for use without save/restore + Register s = R3_ARG1; // source starting address of Base64 characters + Register sp = R4_ARG2; // source offset + Register sl = R5_ARG3; // source length = # of Base64 characters to be processed + Register d = R6_ARG4; // destination address + Register dp = R7_ARG5; // destination offset + Register isURL = R8_ARG6; // boolean, if non-zero indicates use of RFC 4648 base64url encoding + + // Local variables + Register const_ptr = R9; // used for loading constants + Register tmp_reg = R10; // used for speeding up load_constant_optimized() + + // Re-use R9 and R10 to avoid using non-volatile registers (requires save/restore) + Register out = R9; // moving out (destination) pointer + Register in = R10; // moving in (source) pointer + + // Volatile VSRS are 0..13, 32..51 (VR0..VR13) + // VR Constants + VectorRegister vec_0s = VR0; + VectorRegister vec_4s = VR1; + VectorRegister vec_8s = VR2; + VectorRegister vec_special_case_char = VR3; + VectorRegister pack_rshift = VR4; + VectorRegister pack_lshift = VR5; + // P10+ + VectorRegister vec_0x3fs = VR4; // safe to reuse pack_rshift's register + + // VSR Constants + VectorSRegister offsetLUT = VSR0; + VectorSRegister maskLUT = VSR1; + VectorSRegister bitposLUT = VSR2; + VectorSRegister vec_0xfs = VSR3; + VectorSRegister vec_special_case_offset = VSR4; + VectorSRegister pack_permute = VSR5; + + // Variables for lookup + // VR + VectorRegister input = VR6; + VectorRegister higher_nibble = VR7; + VectorRegister eq_special_case_char = VR8; + VectorRegister offsets = VR9; + VectorRegister non_match = VR10; + + // VSR + VectorSRegister bit = VSR6; + VectorSRegister lower_nibble = VSR7; + VectorSRegister M = VSR8; + + // Variables for pack + // VR + VectorRegister l = VR7; // reuse higher_nibble's register + VectorRegister r = VR8; // reuse eq_special_case_char's register + VectorRegister gathered = VR9; // reuse offsets's register + + Label not_URL, calculate_size, unrolled_loop_start, unrolled_loop_exit, return_zero; + + // The upper 32 bits of the non-pointer parameter registers are not + // guaranteed to be zero, so mask off those upper bits. + __ clrldi(sp, sp, 32); + __ clrldi(sl, sl, 32); + + // Don't handle the last 4 characters of the source, because this + // VSX-based algorithm doesn't handle padding characters. Also the + // vector code will always write 16 bytes of decoded data on each pass, + // but only the first 12 of those 16 bytes are valid data (16 base64 + // characters become 12 bytes of binary data), so for this reason we + // need to subtract an additional 8 bytes from the source length, in + // order not to write past the end of the destination buffer. The + // result of this subtraction implies that a Java function in the + // Base64 class will be used to process the last 12 characters. + __ sub(sl, sl, sp); + __ subi(sl, sl, 12); + + // Load CTR with the number of passes through the unrolled loop + // = sl >> block_size_shift. After the shift, if sl <= 0, there's too + // little data to be processed by this intrinsic. + __ srawi_(sl, sl, block_size_shift); + __ ble(CCR0, return_zero); + __ mtctr(sl); + + // Clear the other two parameter registers upper 32 bits. + __ clrldi(isURL, isURL, 32); + __ clrldi(dp, dp, 32); + + // Load constant vec registers that need to be loaded from memory + __ load_const_optimized(const_ptr, (address)&bitposLUT_val, tmp_reg); + __ lxv(bitposLUT, 0, const_ptr); + if (PowerArchitecturePPC64 >= 10) { + __ load_const_optimized(const_ptr, (address)&p10_pack_permute_val, tmp_reg); + } else { + __ load_const_optimized(const_ptr, (address)&pack_rshift_val, tmp_reg); + __ lxv(pack_rshift->to_vsr(), 0, const_ptr); + __ load_const_optimized(const_ptr, (address)&pack_lshift_val, tmp_reg); + __ lxv(pack_lshift->to_vsr(), 0, const_ptr); + __ load_const_optimized(const_ptr, (address)&pack_permute_val, tmp_reg); + } + __ lxv(pack_permute, 0, const_ptr); + + // Splat the constants that can use xxspltib + __ xxspltib(vec_0s->to_vsr(), 0); + __ xxspltib(vec_4s->to_vsr(), 4); + __ xxspltib(vec_8s->to_vsr(), 8); + __ xxspltib(vec_0xfs, 0xf); + if (PowerArchitecturePPC64 >= 10) { + __ xxspltib(vec_0x3fs->to_vsr(), 0x3f); + } + + // The rest of the constants use different values depending on the + // setting of isURL + __ cmpwi(CCR0, isURL, 0); + __ beq(CCR0, not_URL); + + // isURL != 0 (true) + __ load_const_optimized(const_ptr, (address)&offsetLUT_URL_val, tmp_reg); + __ lxv(offsetLUT, 0, const_ptr); + __ load_const_optimized(const_ptr, (address)&maskLUT_URL_val, tmp_reg); + __ lxv(maskLUT, 0, const_ptr); + __ xxspltib(vec_special_case_char->to_vsr(), '_'); + __ xxspltib(vec_special_case_offset, (unsigned char)US); + __ b(calculate_size); + + // isURL = 0 (false) + __ bind(not_URL); + __ load_const_optimized(const_ptr, (address)&offsetLUT_val, tmp_reg); + __ lxv(offsetLUT, 0, const_ptr); + __ load_const_optimized(const_ptr, (address)&maskLUT_val, tmp_reg); + __ lxv(maskLUT, 0, const_ptr); + __ xxspltib(vec_special_case_char->to_vsr(), '/'); + __ xxspltib(vec_special_case_offset, (unsigned char)SLS); + + __ bind(calculate_size); + + // out starts at d + dp + __ add(out, d, dp); + + // in starts at s + sp + __ add(in, s, sp); + + __ align(32); + __ bind(unrolled_loop_start); + for (unsigned unroll_cnt=0; unroll_cnt < loop_unrolls; unroll_cnt++) { + // We can use a static displacement in the load since it's always a + // multiple of 16, which is a requirement of lxv/stxv. This saves + // an addi instruction. + __ lxv(input->to_vsr(), unroll_cnt * 16, in); + // + // Lookup + // + // Isolate the upper 4 bits of each character by shifting it right 4 bits + __ vsrb(higher_nibble, input, vec_4s); + // Isolate the lower 4 bits by masking + __ xxland(lower_nibble, input->to_vsr(), vec_0xfs); + + // Get the offset (the value to subtract from the byte) by using + // a lookup table indexed by the upper 4 bits of the character + __ xxperm(offsets->to_vsr(), offsetLUT, higher_nibble->to_vsr()); + + // Find out which elements are the special case character (isURL ? '/' : '-') + __ vcmpequb(eq_special_case_char, input, vec_special_case_char); + + // For each character in the input which is a special case + // character, replace its offset with one that is special for that + // character. + __ xxsel(offsets->to_vsr(), offsets->to_vsr(), vec_special_case_offset, eq_special_case_char->to_vsr()); + + // Use the lower_nibble to select a mask "M" from the lookup table. + __ xxperm(M, maskLUT, lower_nibble); + + // "bit" is used to isolate which of the bits in M is relevant. + __ xxperm(bit, bitposLUT, higher_nibble->to_vsr()); + + // Each element of non_match correspond to one each of the 16 input + // characters. Those elements that become 0x00 after the xxland + // instuction are invalid Base64 characters. + __ xxland(non_match->to_vsr(), M, bit); + + // Compare each element to zero + // + // vmcmpequb_ sets the EQ bit of CCR6 if no elements compare equal. + // Any element comparing equal to zero means there is an error in + // that element. Note that the comparison result register + // non_match is not referenced again. Only CCR6-EQ matters. + __ vcmpequb_(non_match, non_match, vec_0s); + __ bne_predict_not_taken(CCR6, unrolled_loop_exit); + + // The Base64 characters had no errors, so add the offsets + __ vaddubm(input, input, offsets); + + // Pack + // + // In the tables below, b0, b1, .. b15 are the bytes of decoded + // binary data, the first line of each of the cells (except for + // the constants) uses the bit-field nomenclature from the + // above-linked paper, whereas the second line is more specific + // about which exact bits are present, and is constructed using the + // Power ISA 3.x document style, where: + // + // * The specifier after the colon depicts which bits are there. + // * The bit numbering is big endian style (bit 0 is the most + // significant). + // * || is a concatenate operator. + // * Strings of 0's are a field of zeros with the shown length, and + // likewise for strings of 1's. + + if (PowerArchitecturePPC64 >= 10) { + // Note that only e8..e15 are shown here because the extract bit + // pattern is the same in e0..e7. + // + // +===============+=============+======================+======================+=============+=============+======================+======================+=============+ + // | Vector | e8 | e9 | e10 | e11 | e12 | e13 | e14 | e15 | + // | Element | | | | | | | | | + // +===============+=============+======================+======================+=============+=============+======================+======================+=============+ + // | after vaddudb | 00hhhhhh | 00gggggg | 00ffffff | 00eeeeee | 00dddddd | 00cccccc | 00bbbbbb | 00aaaaaa | + // | | 00||b5:2..7 | 00||b4:4..7||b5:0..1 | 00||b3:6..7||b4:0..3 | 00||b3:0..5 | 00||b2:2..7 | 00||b1:4..7||b2:0..1 | 00||b0:6..7||b1:0..3 | 00||b0:0..5 | + // +---------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ + // | after xxbrd | 00aaaaaa | 00bbbbbb | 00cccccc | 00dddddd | 00eeeeee | 00ffffff | 00gggggg | 00hhhhhh | + // | | 00||b0:0..5 | 00||b0:6..7||b1:0..3 | 00||b1:4..7||b2:0..1 | 00||b2:2..7 | 00||b3:0..5 | 00||b3:6..7||b4:0..3 | 00||b4:4..7||b5:0..1 | 00||b5:2..7 | + // +---------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ + // | vec_0x3fs | 00111111 | 00111111 | 00111111 | 00111111 | 00111111 | 00111111 | 00111111 | 00111111 | + // +---------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ + // | after vpextd | 00000000 | 00000000 | aaaaaabb | bbbbcccc | ccdddddd | eeeeeeff | ffffgggg | gghhhhhh | + // | | 00000000 | 00000000 | b0:0..7 | b1:0..7 | b2:0..7 | b3:0..7 | b4:0..7 | b5:0..7 | + // +===============+=============+======================+======================+=============+=============+======================+======================+=============+ + + __ xxbrd(input->to_vsr(), input->to_vsr()); + __ vpextd(gathered, input, vec_0x3fs); + + // Final rearrangement of bytes into their correct positions. + // +==================+====+====+====+====+=====+=====+=====+=====+====+====+=====+=====+=====+=====+=====+=====+ + // | Vector | e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | e8 | e9 | e10 | e11 | e12 | e13 | e14 | e15 | + // | Elements | | | | | | | | | | | | | | | | | + // +==================+====+====+====+====+=====+=====+=====+=====+====+====+=====+=====+=====+=====+=====+=====+ + // | after vpextd | 0 | 0 | b6 | b7 | b8 | b9 | b10 | b11 | 0 | 0 | b0 | b1 | b2 | b3 | b4 | b5 | + // +------------------+----+----+----+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+-----+-----+ + // | p10_pack_permute | 0 | 0 | 0 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 15 | 14 | 13 | 12 | 11 | 10 | + // +------------------+----+----+----+----+-----+-----+-----+-----+----+----+-----+-----+-----+-----+-----+-----+ + // | after xxperm | 0 | 0 | 0 | 0 | b11 | b10 | b9 | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // +==================+====+====+====+====+=====+=====+=====+=====+====+====+=====+=====+=====+=====+=====+=====+ + + } else { + // Note that only e12..e15 are shown here because the shifting + // and OR'ing pattern replicates for e8..e11, e4..7, and + // e0..e3. + // + // +======================+=================+======================+======================+=============+ + // | Vector | e12 | e13 | e14 | e15 | + // | Element | | | | | + // +======================+=================+======================+======================+=============+ + // | after vaddubm | 00dddddd | 00cccccc | 00bbbbbb | 00aaaaaa | + // | | 00||b2:2..7 | 00||b1:4..7||b2:0..1 | 00||b0:6..7||b1:0..3 | 00||b0:0..5 | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | pack_lshift | | << 6 | << 4 | << 2 | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | l after vslb | 00dddddd | cc000000 | bbbb0000 | aaaaaa00 | + // | | 00||b2:2..7 | b2:0..1||000000 | b1:0..3||0000 | b0:0..5||00 | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | l after vslo | cc000000 | bbbb0000 | aaaaaa00 | 00000000 | + // | | b2:0..1||000000 | b1:0..3||0000 | b0:0..5||00 | 00000000 | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | pack_rshift | | >> 2 | >> 4 | | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | r after vsrb | 00dddddd | 0000cccc | 000000bb | 00aaaaaa | + // | | 00||b2:2..7 | 0000||b1:4..7 | 000000||b0:6..7 | 00||b0:0..5 | + // +----------------------+-----------------+----------------------+----------------------+-------------+ + // | gathered after xxlor | ccdddddd | bbbbcccc | aaaaaabb | 00aaaaaa | + // | | b2:0..7 | b1:0..7 | b0:0..7 | 00||b0:0..5 | + // +======================+=================+======================+======================+=============+ + // + // Note: there is a typo in the above-linked paper that shows the result of the gathering process is: + // [ddddddcc|bbbbcccc|aaaaaabb] + // but should be: + // [ccdddddd|bbbbcccc|aaaaaabb] + // + __ vslb(l, input, pack_lshift); + // vslo of vec_8s shifts the vector by one octet toward lower + // element numbers, discarding element 0. This means it actually + // shifts to the right (not left) according to the order of the + // table above. + __ vslo(l, l, vec_8s); + __ vsrb(r, input, pack_rshift); + __ xxlor(gathered->to_vsr(), l->to_vsr(), r->to_vsr()); + + // Final rearrangement of bytes into their correct positions. + // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ + // | Vector | e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | e8 | e9 | e10 | e11 | e12 | e13 | e14 | e15 | + // | Elements | | | | | | | | | | | | | | | | | + // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ + // | after xxlor | b11 | b10 | b9 | xx | b8 | b7 | b6 | xx | b5 | b4 | b3 | xx | b2 | b1 | b0 | xx | + // +--------------+------+------+------+------+-----+-----+----+----+----+----+-----+-----+-----+-----+-----+-----+ + // | pack_permute | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | + // +--------------+------+------+------+------+-----+-----+----+----+----+----+-----+-----+-----+-----+-----+-----+ + // | after xxperm | b11* | b11* | b11* | b11* | b11 | b10 | b9 | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | + // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ + // xx bytes are not used to form the final data + // b0..b15 are the decoded and reassembled 8-bit bytes of data + // b11 with asterisk is a "don't care", because these bytes will be + // overwritten on the next iteration. + } + __ xxperm(gathered->to_vsr(), gathered->to_vsr(), pack_permute); + + // We cannot use a static displacement on the store, since it's a + // multiple of 12, not 16. Note that this stxv instruction actually + // writes 16 bytes, even though only the first 12 are valid data. + __ stxv(gathered->to_vsr(), 0, out); + __ addi(out, out, 12); + } + __ addi(in, in, 16 * loop_unrolls); + __ bdnz(unrolled_loop_start); + + __ bind(unrolled_loop_exit); + + // Return the number of out bytes produced, which is (out - (d + dp)) == out - d - dp; + __ sub(R3_RET, out, d); + __ sub(R3_RET, R3_RET, dp); + + __ blr(); + + __ bind(return_zero); + __ li(R3_RET, 0); + __ blr(); + + return start; + } + +#undef UC +#undef LC +#undef DIG +#undef PLS +#undef HYP +#undef SLS +#undef US + +#endif // VM_LITTLE_ENDIAN + // Initialization void generate_initial() { // Generates all stubs and initializes the entry points @@ -3642,6 +4116,13 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_sha512_implCompress = generate_sha512_implCompress(false, "sha512_implCompress"); StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(true, "sha512_implCompressMB"); } + +#ifdef VM_LITTLE_ENDIAN + // Currently supported on PPC64LE only + if (UseBASE64Intrinsics) { + StubRoutines::_base64_decodeBlock = generate_base64_decodeBlock(); + } +#endif } public: diff --git a/src/hotspot/cpu/ppc/vm_version_ppc.cpp b/src/hotspot/cpu/ppc/vm_version_ppc.cpp index f64999d108aa7..6c91e7d04e8db 100644 --- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp +++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp @@ -139,6 +139,9 @@ void VM_Version::initialize() { if (FLAG_IS_DEFAULT(UseVectorByteReverseInstructionsPPC64)) { FLAG_SET_ERGO(UseVectorByteReverseInstructionsPPC64, true); } + if (FLAG_IS_DEFAULT(UseBASE64Intrinsics)) { + FLAG_SET_ERGO(UseBASE64Intrinsics, true); + } } else { if (UseCountTrailingZerosInstructionsPPC64) { warning("UseCountTrailingZerosInstructionsPPC64 specified, but needs at least Power9."); @@ -152,6 +155,10 @@ void VM_Version::initialize() { warning("UseVectorByteReverseInstructionsPPC64 specified, but needs at least Power9."); FLAG_SET_DEFAULT(UseVectorByteReverseInstructionsPPC64, false); } + if (UseBASE64Intrinsics) { + warning("UseBASE64Intrinsics specified, but needs at least Power9."); + FLAG_SET_DEFAULT(UseBASE64Intrinsics, false); + } } if (PowerArchitecturePPC64 >= 10) { diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index ea7f5da0b6cd5..ce3001b0fd99a 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -446,6 +446,7 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) { if (!UseGHASHIntrinsics) return true; break; case vmIntrinsics::_base64_encodeBlock: + case vmIntrinsics::_base64_decodeBlock: if (!UseBASE64Intrinsics) return true; break; case vmIntrinsics::_updateBytesCRC32C: diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index 249bbbc73953b..b76bc161782dd 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -448,6 +448,12 @@ class methodHandle; do_name(encodeBlock_name, "encodeBlock") \ do_signature(encodeBlock_signature, "([BII[BIZ)V") \ \ + /* support for java.util.Base64.Decoder*/ \ + do_class(java_util_Base64_Decoder, "java/util/Base64$Decoder") \ + do_intrinsic(_base64_decodeBlock, java_util_Base64_Decoder, decodeBlock_name, decodeBlock_signature, F_R) \ + do_name(decodeBlock_name, "decodeBlock") \ + do_signature(decodeBlock_signature, "([BII[BIZ)I") \ + \ /* support for com.sun.crypto.provider.GHASH */ \ do_class(com_sun_crypto_provider_ghash, "com/sun/crypto/provider/GHASH") \ do_intrinsic(_ghash_processBlocks, com_sun_crypto_provider_ghash, processBlocks_name, ghash_processBlocks_signature, F_S) \ diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index ed9c9b545cbf5..bd7f450443f75 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Red Hat, Inc. All rights reserved. + * Copyright (c) 2015, 2020, Red Hat, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -451,6 +451,9 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { "encodeBlock", { { TypeFunc::Parms, ShenandoahLoad }, { TypeFunc::Parms+3, ShenandoahStore }, { -1, ShenandoahNone }, { -1, ShenandoahNone}, { -1, ShenandoahNone}, { -1, ShenandoahNone} }, + "decodeBlock", + { { TypeFunc::Parms, ShenandoahLoad }, { TypeFunc::Parms+3, ShenandoahStore }, { -1, ShenandoahNone }, + { -1, ShenandoahNone}, { -1, ShenandoahNone}, { -1, ShenandoahNone} }, }; if (call->is_call_to_arraycopystub()) { diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp index 4db20d040e9d8..ccc611ca07b4b 100644 --- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp +++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp @@ -307,6 +307,7 @@ static_field(StubRoutines, _electronicCodeBook_decryptAESCrypt, address) \ static_field(StubRoutines, _counterMode_AESCrypt, address) \ static_field(StubRoutines, _base64_encodeBlock, address) \ + static_field(StubRoutines, _base64_decodeBlock, address) \ static_field(StubRoutines, _ghash_processBlocks, address) \ static_field(StubRoutines, _sha1_implCompress, address) \ static_field(StubRoutines, _sha1_implCompressMB, address) \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 5576f266bae32..34fc0a257d116 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -638,6 +638,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_vectorizedMismatch: case vmIntrinsics::_ghash_processBlocks: case vmIntrinsics::_base64_encodeBlock: + case vmIntrinsics::_base64_decodeBlock: case vmIntrinsics::_updateCRC32: case vmIntrinsics::_updateBytesCRC32: case vmIntrinsics::_updateByteBufferCRC32: diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 5742f4e862b67..ec5e0469e2df0 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -1074,6 +1074,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) { strcmp(call->as_CallLeaf()->_name, "counterMode_AESCrypt") == 0 || strcmp(call->as_CallLeaf()->_name, "ghash_processBlocks") == 0 || strcmp(call->as_CallLeaf()->_name, "encodeBlock") == 0 || + strcmp(call->as_CallLeaf()->_name, "decodeBlock") == 0 || strcmp(call->as_CallLeaf()->_name, "md5_implCompress") == 0 || strcmp(call->as_CallLeaf()->_name, "md5_implCompressMB") == 0 || strcmp(call->as_CallLeaf()->_name, "sha1_implCompress") == 0 || diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 9bb7ea8c03490..a9acf6733187f 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -574,6 +574,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { return inline_ghash_processBlocks(); case vmIntrinsics::_base64_encodeBlock: return inline_base64_encodeBlock(); + case vmIntrinsics::_base64_decodeBlock: + return inline_base64_decodeBlock(); case vmIntrinsics::_encodeISOArray: case vmIntrinsics::_encodeByteISOArray: @@ -6175,6 +6177,40 @@ bool LibraryCallKit::inline_base64_encodeBlock() { return true; } +bool LibraryCallKit::inline_base64_decodeBlock() { + address stubAddr; + const char *stubName; + assert(UseBASE64Intrinsics, "need Base64 intrinsics support"); + assert(callee()->signature()->size() == 6, "base64_decodeBlock has 6 parameters"); + stubAddr = StubRoutines::base64_decodeBlock(); + stubName = "decodeBlock"; + + if (!stubAddr) return false; + Node* base64obj = argument(0); + Node* src = argument(1); + Node* src_offset = argument(2); + Node* len = argument(3); + Node* dest = argument(4); + Node* dest_offset = argument(5); + Node* isURL = argument(6); + + src = must_be_not_null(src, true); + dest = must_be_not_null(dest, true); + + Node* src_start = array_element_address(src, intcon(0), T_BYTE); + assert(src_start, "source array is NULL"); + Node* dest_start = array_element_address(dest, intcon(0), T_BYTE); + assert(dest_start, "destination array is NULL"); + + Node* call = make_runtime_call(RC_LEAF, + OptoRuntime::base64_decodeBlock_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, src_offset, len, dest_start, dest_offset, isURL); + Node* result = _gvn.transform(new ProjNode(call, TypeFunc::Parms)); + set_result(result); + return true; +} + //------------------------------inline_digestBase_implCompress----------------------- // // Calculate MD5 for single-block byte[] array. diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index ff144d71b5ad2..476e1098fbfd3 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -276,6 +276,7 @@ class LibraryCallKit : public GraphKit { Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); bool inline_ghash_processBlocks(); bool inline_base64_encodeBlock(); + bool inline_base64_decodeBlock(); bool inline_digestBase_implCompress(vmIntrinsics::ID id); bool inline_digestBase_implCompressMB(int predicate); bool inline_digestBase_implCompressMB(Node* digestBaseObj, ciInstanceKlass* instklass, diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index f38a3a205d62e..b123d99dc8ce9 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -1194,6 +1194,27 @@ const TypeFunc* OptoRuntime::base64_encodeBlock_Type() { const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); return TypeFunc::make(domain, range); } +// Base64 decode function +const TypeFunc* OptoRuntime::base64_decodeBlock_Type() { + int argcnt = 6; + + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // src array + fields[argp++] = TypeInt::INT; // src offset + fields[argp++] = TypeInt::INT; // src length + fields[argp++] = TypePtr::NOTNULL; // dest array + fields[argp++] = TypeInt::INT; // dest offset + fields[argp++] = TypeInt::BOOL; // isURL + assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms + 0] = TypeInt::INT; // count of bytes written to dst + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 1, fields); + return TypeFunc::make(domain, range); +} //------------- Interpreter state access for on stack replacement const TypeFunc* OptoRuntime::osr_end_Type() { diff --git a/src/hotspot/share/opto/runtime.hpp b/src/hotspot/share/opto/runtime.hpp index 8f07b71de33db..653ce0e0ed652 100644 --- a/src/hotspot/share/opto/runtime.hpp +++ b/src/hotspot/share/opto/runtime.hpp @@ -292,6 +292,7 @@ class OptoRuntime : public AllStatic { static const TypeFunc* ghash_processBlocks_Type(); static const TypeFunc* base64_encodeBlock_Type(); + static const TypeFunc* base64_decodeBlock_Type(); static const TypeFunc* updateBytesCRC32_Type(); static const TypeFunc* updateBytesCRC32C_Type(); diff --git a/src/hotspot/share/runtime/stubRoutines.cpp b/src/hotspot/share/runtime/stubRoutines.cpp index 0656d0499c74f..40e3b522d7cbb 100644 --- a/src/hotspot/share/runtime/stubRoutines.cpp +++ b/src/hotspot/share/runtime/stubRoutines.cpp @@ -135,6 +135,7 @@ address StubRoutines::_electronicCodeBook_decryptAESCrypt = NULL; address StubRoutines::_counterMode_AESCrypt = NULL; address StubRoutines::_ghash_processBlocks = NULL; address StubRoutines::_base64_encodeBlock = NULL; +address StubRoutines::_base64_decodeBlock = NULL; address StubRoutines::_md5_implCompress = NULL; address StubRoutines::_md5_implCompressMB = NULL; diff --git a/src/hotspot/share/runtime/stubRoutines.hpp b/src/hotspot/share/runtime/stubRoutines.hpp index 287e7133dd983..9b6ae56963a4c 100644 --- a/src/hotspot/share/runtime/stubRoutines.hpp +++ b/src/hotspot/share/runtime/stubRoutines.hpp @@ -216,6 +216,7 @@ class StubRoutines: AllStatic { static address _counterMode_AESCrypt; static address _ghash_processBlocks; static address _base64_encodeBlock; + static address _base64_decodeBlock; static address _md5_implCompress; static address _md5_implCompressMB; @@ -398,6 +399,7 @@ class StubRoutines: AllStatic { static address counterMode_AESCrypt() { return _counterMode_AESCrypt; } static address ghash_processBlocks() { return _ghash_processBlocks; } static address base64_encodeBlock() { return _base64_encodeBlock; } + static address base64_decodeBlock() { return _base64_decodeBlock; } static address md5_implCompress() { return _md5_implCompress; } static address md5_implCompressMB() { return _md5_implCompressMB; } static address sha1_implCompress() { return _sha1_implCompress; } diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 903e4b36587fd..444c68c54b392 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -573,6 +573,7 @@ typedef HashtableEntry KlassHashtableEntry; static_field(StubRoutines, _counterMode_AESCrypt, address) \ static_field(StubRoutines, _ghash_processBlocks, address) \ static_field(StubRoutines, _base64_encodeBlock, address) \ + static_field(StubRoutines, _base64_decodeBlock, address) \ static_field(StubRoutines, _updateBytesCRC32, address) \ static_field(StubRoutines, _crc_table_adr, address) \ static_field(StubRoutines, _crc32c_table_addr, address) \ diff --git a/src/java.base/share/classes/java/util/Base64.java b/src/java.base/share/classes/java/util/Base64.java index a4a3f27522ef8..983b41e64dfff 100644 --- a/src/java.base/share/classes/java/util/Base64.java +++ b/src/java.base/share/classes/java/util/Base64.java @@ -741,6 +741,67 @@ private int decodedOutLength(byte[] src, int sp, int sl) { return 3 * (int) ((len + 3L) / 4) - paddings; } + /** + * Decodes base64 characters, and returns the number of data bytes + * written into the destination array. + * + * It is the fast path for full 4-byte to 3-byte decoding w/o errors. + * + * decodeBlock() can be overridden by an arch-specific intrinsic. + * decodeBlock can choose to decode all, none, or a variable-sized + * prefix of the src bytes. This allows the intrinsic to decode in + * chunks of the src that are of a favorable size for the specific + * processor it's running on. + * + * If the intrinsic function does not process all of the bytes in + * src, it must process a multiple of four of them, making the + * returned destination length a multiple of three. + * + * If any illegal base64 bytes are encountered in src by the + * intrinsic, the intrinsic must return the actual number of valid + * data bytes already written to dst. Note that the '=' pad + * character is treated as an illegal Base64 character by + * decodeBlock, so it will not process a block of 4 bytes + * containing pad characters. + * + * Given the parameters, no length check is possible on dst, so dst + * is assumed to be large enough to store the decoded bytes. + * + * @param src + * the source byte array of Base64 encoded bytes + * @param sp + * the offset into src array to begin reading + * @param sl + * the offset (exclusive) past the last byte to be converted. + * @param dst + * the destination byte array of decoded data bytes + * @param dp + * the offset into dst array to begin writing + * @param isURL + * boolean, when true decode RFC4648 URL-safe base64 characters + * @return the number of destination data bytes produced + */ + @IntrinsicCandidate + private int decodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) { + int[] base64 = isURL ? fromBase64URL : fromBase64; + int sl0 = sp + ((sl - sp) & ~0b11); + int new_dp = dp; + while (sp < sl0) { + int b1 = base64[src[sp++] & 0xff]; + int b2 = base64[src[sp++] & 0xff]; + int b3 = base64[src[sp++] & 0xff]; + int b4 = base64[src[sp++] & 0xff]; + if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte + return new_dp - dp; + } + int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4; + dst[new_dp++] = (byte)(bits0 >> 16); + dst[new_dp++] = (byte)(bits0 >> 8); + dst[new_dp++] = (byte)(bits0); + } + return new_dp - dp; + } + private int decode0(byte[] src, int sp, int sl, byte[] dst) { int[] base64 = isURL ? fromBase64URL : fromBase64; int dp = 0; @@ -748,24 +809,20 @@ private int decode0(byte[] src, int sp, int sl, byte[] dst) { int shiftto = 18; // pos of first byte of 4-byte atom while (sp < sl) { - if (shiftto == 18 && sp + 4 < sl) { // fast path - int sl0 = sp + ((sl - sp) & ~0b11); - while (sp < sl0) { - int b1 = base64[src[sp++] & 0xff]; - int b2 = base64[src[sp++] & 0xff]; - int b3 = base64[src[sp++] & 0xff]; - int b4 = base64[src[sp++] & 0xff]; - if ((b1 | b2 | b3 | b4) < 0) { // non base64 byte - sp -= 4; - break; - } - int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4; - dst[dp++] = (byte)(bits0 >> 16); - dst[dp++] = (byte)(bits0 >> 8); - dst[dp++] = (byte)(bits0); - } - if (sp >= sl) - break; + if (shiftto == 18 && sp < sl - 4) { // fast path + int dl = decodeBlock(src, sp, sl, dst, dp, isURL); + /* + * Calculate how many characters were processed by how many + * bytes of data were returned. + */ + int chars_decoded = (dl / 3) * 4; + + sp += chars_decoded; + dp += dl; + } + if (sp >= sl) { + // we're done + break; } int b = src[sp++] & 0xff; if ((b = base64[b]) < 0) { diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java index fe4034c768146..f98b11a9755cd 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java @@ -437,6 +437,14 @@ public CheckGraalIntrinsics() { "java/lang/Math.signum(D)D", "java/lang/Math.signum(F)F", "sun/security/provider/MD5.implCompress0([BI)V"); + if (config.useBase64Intrinsics()) { + // Currently implemented on ppc64le only, but could be implemented on others + add(toBeInvestigated, + "java/util/Base64$Decoder.decodeBlock([BII[BIZ)I"); + } else { + add(ignore, + "java/util/Base64$Decoder.decodeBlock([BII[BIZ)I"); + } } if (!config.inlineNotify()) { diff --git a/test/hotspot/jtreg/compiler/intrinsics/base64/TestBase64.java b/test/hotspot/jtreg/compiler/intrinsics/base64/TestBase64.java index 48baa5e5b8be4..45f2a2784fd7f 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/base64/TestBase64.java +++ b/test/hotspot/jtreg/compiler/intrinsics/base64/TestBase64.java @@ -47,10 +47,12 @@ import java.util.Base64.Decoder; import java.util.Base64.Encoder; import java.util.Objects; +import java.util.Random; import compiler.whitebox.CompilerWhiteBoxTest; import sun.hotspot.code.Compiler; import jtreg.SkippedException; +import jdk.test.lib.Utils; public class TestBase64 { static boolean checkOutput = Boolean.getBoolean("checkOutput"); @@ -59,15 +61,40 @@ public static void main(String[] args) throws Exception { if (!Compiler.isIntrinsicAvailable(CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION, "java.util.Base64$Encoder", "encodeBlock", byte[].class, int.class, int.class, byte[].class, int.class, boolean.class)) { throw new SkippedException("Base64 intrinsic is not available"); } - int iters = (args.length > 0 ? Integer.valueOf(args[0]) : 100000); + int iters = (args.length > 0 ? Integer.valueOf(args[0]) : 5_000); System.out.println(iters + " iterations"); - test0(Base64Type.BASIC, Base64.getEncoder(), Base64.getDecoder(),"plain.txt", "baseEncode.txt", iters); - test0(Base64Type.URLSAFE, Base64.getUrlEncoder(), Base64.getUrlDecoder(),"plain.txt", "urlEncode.txt", iters); - test0(Base64Type.MIME, Base64.getMimeEncoder(), Base64.getMimeDecoder(),"plain.txt", "mimeEncode.txt", iters); + initNonBase64Arrays(); + + warmup(); + + test0(FileType.ASCII, Base64Type.BASIC, Base64.getEncoder(), Base64.getDecoder(),"plain.txt", "baseEncode.txt", iters); + test0(FileType.ASCII, Base64Type.URLSAFE, Base64.getUrlEncoder(), Base64.getUrlDecoder(),"plain.txt", "urlEncode.txt", iters); + test0(FileType.ASCII, Base64Type.MIME, Base64.getMimeEncoder(), Base64.getMimeDecoder(),"plain.txt", "mimeEncode.txt", iters); + + test0(FileType.HEXASCII, Base64Type.BASIC, Base64.getEncoder(), Base64.getDecoder(),"longLineHEX.txt", "longLineBaseEncode.txt", iters); + test0(FileType.HEXASCII, Base64Type.URLSAFE, Base64.getUrlEncoder(), Base64.getUrlDecoder(),"longLineHEX.txt", "longLineUrlEncode.txt", iters); + test0(FileType.HEXASCII, Base64Type.MIME, Base64.getMimeEncoder(), Base64.getMimeDecoder(),"longLineHEX.txt", "longLineMimeEncode.txt", iters); + } + + private static void warmup() { + final int warmupCount = 20_000; + final int bufSize = 60; + byte[] srcBuf = new byte[bufSize]; + byte[] encBuf = new byte[(bufSize / 3) * 4]; + byte[] decBuf = new byte[bufSize]; + + ran.nextBytes(srcBuf); + + // This should be enough to get both encode and decode compiled on + // the highest tier. + for (int i = 0; i < warmupCount; i++) { + Base64.getEncoder().encode(srcBuf, encBuf); + Base64.getDecoder().decode(encBuf, decBuf); + } } - public static void test0(Base64Type type, Encoder encoder, Decoder decoder, String srcFile, String encodedFile, int numIterations) throws Exception { + public static void test0(FileType inputFileType, Base64Type type, Encoder encoder, Decoder decoder, String srcFile, String encodedFile, int numIterations) throws Exception { String[] srcLns = Files.readAllLines(Paths.get(SRCDIR, srcFile), DEF_CHARSET) .toArray(new String[0]); @@ -96,7 +123,18 @@ public static void test0(Base64Type type, Encoder encoder, Decoder decoder, Stri } } - byte[] srcArr = srcStr.getBytes(DEF_CHARSET); + byte[] srcArr; + switch (inputFileType) { + case ASCII: + srcArr = srcStr.getBytes(DEF_CHARSET); + break; + case HEXASCII: + srcArr = Utils.toByteArray(srcStr); + break; + default: + throw new IllegalStateException(); + } + byte[] encodedArr = encodedStr.getBytes(DEF_CHARSET); ByteBuffer srcBuf = ByteBuffer.wrap(srcArr); @@ -134,6 +172,25 @@ public static void test0(Base64Type type, Encoder encoder, Decoder decoder, Stri resArr = decoder.decode(encodedArr); assertEqual(resArr, srcArr); + // test that an illegal Base64 character is detected + if ((type != Base64Type.MIME) && (encodedArr.length > 0)) { + int bytePosToCorrupt = ran.nextInt(encodedArr.length); + byte orig = encodedArr[bytePosToCorrupt]; + encodedArr[bytePosToCorrupt] = getBadBase64Char(type); + boolean caught = false; + try { + // resArr is already allocated + len = decoder.decode(encodedArr, resArr); + } catch (IllegalArgumentException e) { + caught = true; + } + if (!caught) { + throw new RuntimeException(String.format("Decoder did not catch an illegal base64 character: 0x%02x at position: %d in encoded buffer of length %d", + encodedArr[bytePosToCorrupt], bytePosToCorrupt, encodedArr.length)); + } + encodedArr[bytePosToCorrupt] = orig; + } + // test ByteBuffer decode(ByteBuffer) limit = encodedBuf.limit(); resBuf = decoder.decode(encodedBuf); @@ -150,6 +207,11 @@ public static void test0(Base64Type type, Encoder encoder, Decoder decoder, Stri } } + // Data type in the input file + enum FileType { + ASCII, HEXASCII + } + // helper enum Base64Type { BASIC, URLSAFE, MIME @@ -160,6 +222,7 @@ enum Base64Type { private static final String DEF_EXCEPTION_MSG = "Assertion failed! The result is not same as expected\n"; private static final String DEFAULT_CRLF = "\r\n"; + private static final Random ran = new Random(1000); // Constant seed for repeatability private static void assertEqual(Object result, Object expect) { if (checkOutput) { @@ -177,4 +240,62 @@ private static void assertEqual(Object result, Object expect) { } } } + + // This array will contain all possible 8-bit values *except* those + // that are legal Base64 characters: A-Z a-z 0-9 + / = + private static final byte[] nonBase64 = new byte[256 - 65]; + + // This array will contain all possible 8-bit values *except* those + // that are legal URL-safe Base64 characters: A-Z a-z 0-9 - _ = + private static final byte[] nonBase64URL = new byte[256 - 65]; + + private static final byte[] legalBase64 = new byte[] { + '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', + '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', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '+', '/', '=' }; + + private static final byte[] legalBase64URL = new byte[] { + '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', + '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', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', + '-', '_', '=' }; + + private static final boolean contains(byte[] ary, byte b) { + for (int i = 0; i < ary.length; i++) { + if (ary[i] == b) { + return true; + } + } + return false; + } + + private static final void initNonBase64Arrays() { + int i0 = 0, i1 = 0; + for (int val = 0; val < 256; val++) { + if (! contains(legalBase64, (byte)val)) { + nonBase64[i0++] = (byte)val; + } + if (! contains(legalBase64URL, (byte)val)) { + nonBase64URL[i1++] = (byte)val; + } + } + } + + private static final byte getBadBase64Char(Base64Type b64Type) { + int ch = ran.nextInt(256 - 65); // 64 base64 characters, and one for the '=' padding character + switch (b64Type) { + case MIME: + case BASIC: + return nonBase64[ch]; + case URLSAFE: + return nonBase64URL[ch]; + default: + throw new InternalError("Internal test error: getBadBase64Char called with unknown Base64Type value"); + } + } } diff --git a/test/hotspot/jtreg/compiler/intrinsics/base64/longLineBaseEncode.txt b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineBaseEncode.txt new file mode 100644 index 0000000000000..5516ec5582e3e --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineBaseEncode.txt @@ -0,0 +1,100 @@ +AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w== +k6r4A8ytszE6K0LqJXLfYh11VVN9bmDoZmunjWCQFLEhOOipRcpF95DW8ROajitm3068+kPga3qd9jtNZptGQ3r/pXm1Q7GBCUcnnpyZlpEzqwh34VZEgAON3NIqAIEIWQj1/ACLtVG9OwMob3NbvEaRwp9+Y1wnFEynDf4c2ZFKsLgfEx2/PvMoH1AH0rsWZVGWpo9yLR/8bG3Iw4Roxxzj7OlW4828Zaals5k= +WBrp7Jho6FfByCFvW07pHqfW7cXeiPkgMwetaCNkpeMdkAAe8VnqqlqBIwqb1S4fh15FTeg0rVjSw3FFnFwQBzQQxKZfhWNi167Ma3QQpWviddFNWG9BuD15wNZXYbRviZcrOhYniS852XpNZZCD0rHkJXPxHaGHSIckH+rrCGlaoUengGScE9uGel3CRyyykz0hsILyuguG8ejPAHo8rlFHY6lZkrDF2CjabUTWDHIfK12bJLzIfDCiFY1e7GMAsfBw86aeHEgXxzQAJQyvboWaNj6TCIZwAo+o2alNIW4KAUXkBhAITY3I4psoHKSw/hvq1xmbv0l8SG/5X5Rct/4iOXLtHqjRJWM2dSwUmxp+F0kGqUbLKBFxLH+Y7HNPkXuNwSxelYPcPx3rtQVWakVKf/WElmzbyqVk1vrieYSEjDHwYJHQXH+3uU/xZdOeqgpr5QrXklLPOmFpcx/5Q0YBWpO5BusLpExygMEzPzvE0xS1H/WGEGA= +8dcsB7oE5RhpYaMTzZREu/3bNUUx+FCZsdmstvQguMpStzboCEyWBbKOyi6ptoc90PYRYKdFdG+8OsrJYAxhEdZueAkHXUTujl/1GZKPQHOWrsB2RzPB6a2U9zB+UHdHv5pZSYd6suFGZkamMQaReBILVq9oQwzPtrn3h+zTSznNhamm4FmkXVPyWJwpbzyf92v8oVyOjJkhTsyb2d8GVf4zQUZLKpniczSwLpr1JVM0impCh9bNML9wSuYFHKrJDHfneU3GjwMSjhOKSEd/qzvQ0hSt7ut87dXLVrIFBp0UpewsnIFVjoiYOQnr/iQdtnbPDBcRmTir1X/682kXLf1KA18NE/AyrVdcbyVJneA1/8uv64t5kUrl1wHhZFWKDXjfEAkvfjJFHB7Ik15XdE11aulXTT8LMQmRkTZdbk04KgIcAy85oLejVmGTI3/TtAhpHPreHzc1stz9z4TsSp0uUpx3a/ijpdIzUh6fFtDKdIHsETAPpQWMHs+NUUqtwCbqPOmow0XQu0AsVaupFhe3xEDw +SqsrwtQqIIX1C/YdoAlvMfhJ2TzBNSteN1hInPj8OQCtbyctDgo5KrrKgSTUOjwHcIxO4fDCuMTJJxKXbfRZKVe9FfsL25gYJIPojPFsr79Da5Nqli6qTTHwl3LRn80yHh51PnPniXM/vgMjNONtcYq4Ho2C42eDeqS5RIT1rTpIyH31pxxBtVOnOA0pNfdXs7KEXlv8Sn2GiuVH5Bb1MxvEhAO3tJ+vAh1vrGj8T4LmiubzUgmHl1O30PAaGhnUsu0EkGwo1RFasj4UE/YaOfHyIq8sYzflcrWt4uGrYHykLa5p+Qh38U4XxJaHJMpwRS5qxS+aCO4ryFQuzMh5avSIFMK/JmbTJWS/1g== +ZE3mfTaoaWmBECTNbd9nikvk3dFzaPW2nDCyS9pT+mv6w2T2XjEz001DnKhuQgCippWMoevRqu4pQowVfwhfkJ60me0EXTHQVBqLOi5NhDNWXwze3NP2r2mR6fMufG0BflIbtc5kL5LbzQACnKsbK5OnizdTTua2Zxvrj4pL7l+0Nwht6BinzYm/YotDVNx+tdZ0he6R/A0E01xRWqqA1bwvxc/U9O9W6YwtEhb2d9rXFm4MIwEgBLpwN97SOZB2Yoljgjr9CRSJPW9uVMutVy3QQYfG2IHEOKQ+9kCLct+oYSGBTfyw1DUXjJYfcRekmXQbg1125P9YNAJJp936kx9+kQoqhxF0xKO7WDgrwTP1/LKEt8fmPud0/rO+jDKSSCzdm8tmtvkDRMe/Zx0T3ZbAzHtWQLBL0rIgat+3oLBama9+Ascf7DsGl6YH/JCMiQmkBhnznTsi+GKUU5iVJxZDNGD3zJCqyAPs9kC04FcO5rUAszBJjlK73cyWGBbpAtnMdLXpkgFS+OcRaH+xosDNYdhpQ5/3hfDbFvkO0SqnNMunPp2csxd30uoK+OG6Anv6PZ7ESjUQBHS/L76jr0bH6l6k+XRT1kKkTyCbgnAdna+Tn/wfktk1HaVHATZMiBzQP7WBw/3NSsbV5j8pKZ691rBA3vh7GNbGdo+uITOgzTyNpeFU +T9aj/+YGEfNm8h1xwhh5tOn48lHVrytqbZBZjEf4hAV8R2kGozuLF/fWEZR6tVOtPfFPXaRQEhITuqRyTbJr84cLnNVxsUegeEOUX8jYZ3ChUFTI1z9n3YvSAmsWQi6OGy7crfMI5Gr3Vaea2Alrr16xW7UjyP9GsZxuH3okAshaEnL9yHZVJVoqIU6nmHJtb/zBkwG8bAhDPMpTKoBom+7PIeHXdp/ZlvO719GX8MPZfIBphpUQTmLIVAgWWQ/Rv7C0zNL+WWWSvgfBseOmR9Vu0oikTj0kA/M0tlcX8KG0X8nrUpWKgk2Pw/I= +ygSLUWJPLE9q5qHkLbcFJuMYsxkvaKMp8U22uDUMoi3BfzlQERGKErlfT23ToKJgVqD7FA/Sz1d/1NDKbvidAbFPUaEKUc+HSbZVFN8hkwwEWe++nRIaKGAuXE7EB1QsUyT+bdAPgTsVD9e2n/CjalSso0jl0xT7ZGOT57sBPyEwTowg1TXdfuCjFGzuAVQTMyM2xz70azluaEnQu60BpKDR+SndvJCRy73HUnNg45kZKiUK0l/R5ddWXddF5+fg +66UXSFBwzCBYQM5rq9I24X77GkWcGWo+gBoK56Irx6iPmoq5+pEh0MW40G+riwH9ztXACK3x/VXQI6N+PYJCqUn6grFJUJEh42iEwZSBKngOT7jLpNs7G/1SrDLFgZy7IAGAe3fM/h5s2QWoYWjHTvND6ZZ+wF8UCRjavpBFxufaJwkRGEf2Mc/zhT/S+6KgMfBHB7zErumW2SB+v+7WCTrl+1bA9dd1GkNtWozHkTLX8sY3QbOGDo/Igg== +GmRba6+KP0E1ves8fAr55tqJ3MVoouLC7qyXy5i6msrT6u558btPZJlf3U7Qfc5SjJknySBLS3N+cMNJ13tVVKn/mAkOBRxbFpApXL7KAHx+3mjX8MIgFifcLQ6PHXGL6Qp5t20WyKeSeuMf6VMgojXUYJOg1UnveBroaAXLLIDTVjjcPwm6X9wwdLyzrrLbKhwsTq0eZlr69NpZOYmwUd7Ww4zdVIxX4Iq6zg7cro4tFkgRUqnaCBTaPG/0h7maYDvOPaLVURxU9Baw3z5nfGcSs25GBm1kqRVMFKRd7aMS1aVuqHq1ZHCh2HR0y3y7ioKIq6UnDKYBpafUhs943vvAv/UCAnRXIbakEob8OEuSwbHIjaYW+mpLxBWkj1kfN+GeVt5irkI/1zJRVPvsn7LlEtDz6dFbHUcmyCdw6SQgKnqyIkKOMT+s/ttGj8a13xvMAxuZbYrrN6eLdKka2F22Lc/tGtDr8MZgDqg9njB4FQJZTf8nsRuuecOegfrkSiTxtJVpm9GugY3+8jM89pcDpdY1vP8jHfVMKaYI2/lFyWRQkIw6KqFB/DEjZJnTe5XOMXhc +yGpzF04SQsZxXyxQyM6xKElxrcKIq2eWp5aRH1XyR12rN0oSkktWn6DoUMbJBmT1IQRLQuFrlK6NwXKqMzGRlStbnlcPVzeCfGJJ+HhyQIu8vRDb9DzXIpIHkuhtlyLvVv4JqgFdStv/TZ1feH4zRxG0WPHYlcwbbptoMlCwwf/mh7joFnmItdJf+LF96exmyXbN4syzi0e+ZkpDqzx3/2BJvqk8aqPGvJKnF/7Q1lZgx75qp965cEa+Qk7yZ9O34HUNfWOEnKtYmmEsdDRBWzDgXeiEOlSIvbpyDT+wiQAAyJldtX5n7Y464E6yaRkF4K1xESinvLOQpXxinwftW2+BN765jfgqUfzFsND7xg65l+6LkTV5xz4XfwU9l8wkog6U1P1OYAkgwUe2MIKfCFo/APBDqz7Foy0hsNvDJN8yA+t3ogNaYtbCMxwP5INhXMf8t8iZp8zbXB5aI9I1Hl3P3TZsotkVLWO1ZzSPB0pPF8eR4oA4BR9y8QBN0HR7KLL9Db4xFymabnQZLB7H7KehJaD3hxgIKi35bJoc1TSscIJODvyam98vcTmk80+CBShwIy5JBDeh9L6OIij21wROrChPDrxp6e9Bl1E8Kb9pDkgHwycHmAc3tNl77aiMY8dWXsyIKVJxUwNg7fN7FsqTptsOuGijresSLbCM +d8qFLaYAQ+TX5Beyf85B0ViTq966wcFaiiupM8QSoa+06CEeLtlDQesMrT89jeV5LoC+EpHbROV+4e0KsFjIdWifGG7O9npd5gRC8rFI9f/mZvyOVNEZjmX19xlbntNFcqqM+F5bY5Yo6Jafeu6mSRxfp5KfZKNAjw== +y32UrDKLLehCJrsW5H771kgpOy91y7/V4gs7uwjo5XvVh6MbB7HP1lwG8lsHzS+p8Mxmc0pe7P5/tQkihlgMiqxb6M5xL+acXxwRlQXL5f6ruM9wBy4hL1It4glV3wfbdiN/Pob+Y1LuVSjrHb0hAHl2htAOR0qef3DUtZQoyd5vTHMZeuHF29Gv6r3AzYtyB6B9ai9FKwVNR0wj08pKyAxT9r2BEODxsgiCk4zr7IlUXnOajpOaq/odrKSiHpE2RpYgANW9s8oooAcAnDHl/rs5rB/62RxSEcXGqrjPNFuCwr3SePjh7XLBHf+iRF7t2rDuHTH2U4m7rsJRLKPVLpg7og== +H28+pYYbQT1WKC/83SROqJcjzOOXAOwSOAwVIYAhAvnXi/Wc0kg75Lghfj6njTxs4K2elpQr7TgU6IbXHFpf/M5pyBlIATpOogBcXmN5YOgmz2z7Qlzpf2AKDGqlJ2/72zXrZyB6GDUlfRmW6t68ufcmMnY2aQptmvhBpjjRmfEuWPHb6HJL1D8DzeGWN5qBy0R+P9me9OAvZ0vZBtN1LbMQXQVPa/iCYHmf+vBpwOF242+XTAOeUJP+sKChINTJqdlCfyqTtT46lEKYv/xtBQEqF9q9hxZI8sOeNu3s1PLkOoDtR9R1rm0ULnf3HaRId6C4+sqXspDp2TDPNYh9hUdtG5B6mhMaeo0QoRHvOGspLFTjXasjj9ztKnbYxWSC6WcsEWe8FdWLKfB/OKco5dq6FzqKOPZhUeax7SBs2NNvs+LjmBgIno3dlivf9cLBYv8dCvGP/yXjUXYStTsb6Ra5KtRKXhV3Ga+ljDdACpg+0RDSHS8k+UYN4FI09CKv7NZnN3+AMW+vY++zAyPyHpanHHlq/R402KmskmL4Wk+71cNB0FZ95cI2gP9Qw9fALq/CpeTumkxHD4cpie79TCtImwKE71K1FAvohI9rxtvO/w== +/a6fRfnC4h9ca/dudoc4h4RiCW3s6J83PIn5CwiWbEbHZk9ABkkVM9pf7hcwwrdzkcY7zF54LzV0vpKM/cZ463EL2oReeVAQxgg9XoQmy1ydekyvwE4iVgucLWeBddhEgOQJpMZ7ehi7NbATmzE9GljMNkeu3zDi5h1fXfO8JERg7OWA1n0YsGJb5PL+KbrLXWhSiCcCgtg5Rv+H788Y1pk0aV2cOdk2ADC6rizg3FmQcTqoB6tVDAj/DM9/hvmh79Uk6nsV4mWS7lf7ESWIjJlb0Mfkh5L3dddvu2Pdk57e006qv0tcu+mwOS0= +aS5ICgf0PPfd6s5SJPj08eBXCgOSwUhjp71QUHPR2RUVZVGjomoUZEHtQXEANyMHhRMq5zxjVI5aSKoyNysBmhJfNkW+aiyEo5t/Ra+YjZUBy3WrsoHsRFJWDkXBJkdP/cPR/zZbg6Uc9u4+4FQCNwUriSF+ksrKscy+wX+3r7+VXNnKSkQqsw== +Dx5tUKiGam8Xg8Q+5jxGyF+/ExC8YfHOKxlfgV5jZnkTc/BS3FjR7N0rFRfGhwwwCY2Z4dT8oyFQtr/LW2ZYgs0KnLGcrBMMd3viqn8vJGlAS+Z80e1lhFQU1mLXUJNWLlZ4fRC+ykZ+/esD3K6GjmfuBOsoUQOaUdHD8Xm6HDRP9W0qU6YLbCWFdRCIDIedAeFDwfviyI10Wk90PX3pvG5CyaGpeAAT38M3FuLq/8sc +BkQpZhAeXJj+Ymwaz4BpGv+mBywZaB0rNaW/baBue8IZN+oys2fbOEmUw03oZTheMiZs0E/4q7n8ko+UB/L8IAvGfyrLx4gMf2VHSexP4K/63X2DAewmDsRugH0BY8BCDBJn0z2GwgLJp0Ie/aho3gDp+7LhMBNo9v76C3wMhunV4Kfprqp6NdHC8cVrMUiayrZ5MHkQFOuZOBx6vJrOCQnkmQI7dGiak6NgWeuk09dezOh+xcCyyyA7lVo7YDC6rjTQto4dGcXQ6uz2x6wjZx4AbPVeYU/OJM52dL8heJ8TVYHTyDRdQqTS+8R5qgBXcYMBJMDnrqN622cXJez3TtVwABW8OYoMxGBdpEvHP3qrWGgGIn2Zljht1k5pkZYpUiPC8gTjI6HjV9QTW+Uotryj53ph0yMZFLSDglQdKDcwdHTgaZpdILZeV6Ofz1CEJl1Qg4XfDq2yfgj+betxcWJknLEcnfkVIfp0tV1IdCu47tzXVJDEmFK+w+aXynIINqlCcEthBkoHuWXEkmXQN4kcSzCE8nMEYIm0DuetbynkATBMh5v2PkYpV+nStnWhvebW6vU/w+hyIMI7L3AxtCSnNgd/e5WySQlSAyLHGjqOu3Brj2avVCRt9Fi8MARwS3ICdJGaXcnRQFgJPGw= +yRiVz9ElgvOOWY0RA8ur+TZpCvt4rRylTFQRMP7PXctgbm7Aafq/nJNj1TIPMUHalu/DuBYVezF8jBFXHTarsXJwoVVzWDMzZJPXuGu+8Wd0V5l1C2iJlwagYaaeHX1aGOahTqm0KQW2p+889cr9RCBWzB9kiSQMhYClIq2ipBMaY5hfF/FXC3eix+WvgootYw+eZ6zYFAQv2UB2Cmd9+iRuZrexNYzsrIhkpeRj5+ULyrFnGj2kNAM0SqHOFqM2w2d9XD3OSpomfjHvtG3OX2u0h1BOlWJzKLrRSNrk97SNviTPAbTYEpjRilOIjEikVSOiqA7elvDUHNI9c4F03Qrml4O3HDm0el9Mie+vmf8N2saSltHpTlEgebCB+lfH1o2HllCdobpP6t9lYyPD1bna2KTuOr8aoFQdi/4bFGh2oIBm/m/P +wshJPn6NEMzEMurNw7e6GS5lhXwEv6NDcIyrZQM+5q8WgLxBDDE2pL1X08RRQ/f8KJngMhCRlg8RSnPE0BAoFnXHJyqQ245QLn0ChdMTeube9BE65c6H5nKqjWTkwC2KmWhL9a0Hz8vtfdld+lNFXM58qRibbVFgOWzbmAGSBnJJhMrx/rnoK2q4OgHxLaoHvCUuAlhLnjyKkrg2GlfQNi/JwMvJkmZzaICxMWSvRdhn3yTLL8FG+riCH2yaqD+HjgBT/DCO5qDCjblsGA== +KgpWTkl/rTT8+i9nM+9ktpC1TIbOKTcAHm6c7qkthknIekGlpiaLF9CPElmx6S0zdLLSJbTvPkL3Zo4Evjj+ZvfpjyDPOQfo+5sayLFCjsRoJ0neXKFilDabTwHKb93GuYpyr/A/b5iJHCOxXqpumQAotAeHZUm5M7FUbgC9PR0vgRU5FR5I9EI/+g799SuLoA1XW3xRz8IqluUvYIO4vjuoZxE1j6t3dmvnGNxKDlgr2QVrJOUmkZGBM5dLHLZLVUcEAX1LQh0PyGTHqJCNcBzQvvOjfNePCqbe9rHnu0AQG6uKYVlxoHTLmRCWuc/Zm5gnlPkhNBthMIA1YGBC0dI+2rzhFSmvxDQKe/pO/aTzh111HV+qKBfsofD+y6iaOG74O6BX4p/dEgFnus3egAmAokUea4SWCT6ymRPgkw== +tSyf5DBTQASxPzCoIFE11zz4YCrDT5C9msp6H8MSa1PVP3+dJJ7gznbZC9uNDm4GM5YGCixvNKv8X36ZpNxdeLsOUsBe5sxPHD1fBJUQFCsm5EDC9A5mn/+Z43szll52TFh3uSnPHr+a+Jv28c6LBQuzBb9TLhIq98hExWhjwo+KzZc7hIVErIw5WfjZCptqRIYAeMdqZJkyjgjF9aTUFKcA66M/eHPpoEKeMjp4ONEtp9BXsjZ9Anu7q9KbdfnyOjee+0nCwWdVUVBjVrghdSZapnqHDxaONuyHlCbC3wQ5l8ApzcJmj4mJZVM4HsCHjZ+05esYgX2EWYtNiF8yyxIwV7iGWKyIX1VUlpa90fskJPuQY6WhUABxeKaVsFvqf2QI0tla//Pq0DEJiE5YY2/H8uH5Kj29iKcOhuJgxgeMKdnVLq+xoDWgD5isVlxpeDaDBGgpRb+UAbNrQaQ0dkSvixMOmNq3E9QOziAj7VKTNOKwSXd+dqziF8RjiZc8m9YPNXeq79JJPLOg5Q/0IYjxvsi5hBciW5VN2RjS7+sA +ksMB7XWQfsLlBx6VRcauBkprDv2k6kUN4rCt4L3MJiKkUjivaB+FOjPXtbCE6X+Mn98AaAbEwGllevQqcuN7GfbWuoTBMXU1zWLZCU/IFs/76dgmvCOKt+8HZL9vR9BjMpmY7a/zEE+0WedxK6QdHk8DJmQazQlnIrrcWA1NswVsVXg08XH/d6V3KybETbc4sQBrfulvlw8fh492KQ7506z68evc7lu3QW8Ia+48yNohjE5D9GBx1vl2gAnBtvvyEJC2pIy6CvnlogDk98dF5CkU0rL8HKAuHSM1LRQ4/0MIxPNLuvBqSsQUGUm34d+e1ZgFn4/0hkQzzvdpyshcIy+adra9ttIVsioo3PT8sJmE2LnEV3JvqP9pPwWvtcAPMb4Q2TdlXuAyr3s3iXNNsRJyusFA8b27rzC/5WYQOpxaAMIw4zSQBzKZYdvUFNOj3lkDiBLuHVMuPY93ubBfVi53HtAVzUHsCBYcF/g3W8shOne29IlRI+et+CzdADXzq752pl5bYi8jYKezV/BpeCxCnVlSvw1ZiL9k+lUyQm2R5T9l240PWPrP6fDl0MFc7MrVp7PWXPs2gelzyjNOalZ3Q6CnD3Q= +rbbBTbq/PKBZaJcY8oAlhcN9+eB5CFmVIzcGkQP1AaToh3mhcmr7DjXhMHdiMjE9V+q18qPvQ0F07vkJmKBucXW0E/nUyZzao4t8Xl4zS80U5lx4jeKd883v4RdtjxVCE9v+zj8z1osbhiSZvdf0kDAxYKeB6TdKDLkL0gGyGZf2tqj+aY4MfzRk4f20fRwkEfOWcFy8077LOrnUDqfcNJJQcBfcDUqGzLI6KZ20erXU/Q1bKEiSwmY+VOF3bU1ED/2Ab8UOOigA5kWzKCHohNgaAng4eo69EoLdP98XgOkEmP6Gh3+G+ok26Ed3j10wwt543FLqJGWR5yA0ZOQc1d2JJDmWZCuW5MsfKJ+87Dpwfu0hYtJy9f9NKDkOVhNbhgu7QCDrQrPg47+vGVlbPnEkt4Mz5YbCM5dyUljAN3nKPG52/pay1P1VOiSSvst3blp3/LNst6VBTWQXcb4Uaehsi3dm/1gVJ/Pnk67R7PcQ5jn1Q0m90JFoh+yZL6Ap5zmZ/fOpIZxIXEcILqhKHd23rQGGQ+l0C7324zEaPnse5lWbam2PHDis0bT+WSLGjFUDJEcpHcfimaDM8Q6N7nqBkTXS9Tn/fZC2irOFTMdbVOor3Q== +6+x49LzwwXKPWPdJbs5L1a+mcEkxu+sTuw/STC+Om9hrMdbQBv7DUJdW2NaAIzi0X0ktA8ysGKYGHYXad17LYTwtPz47nAUSlEYRjUiOKwTX9ekjMdI2QIQeVB6ow7NwPT5ihlM0V7AiOXXVm74c3A4lGu72+Oagf1yXSWOEHUpz4Zn0AlvN2/JqDZBhsWxcC3j3GOp9etTuV9CkKyDtkZ9iz8n1dmyXBq47WzWDKC2JBde7ZfRdgUkpT6io70E/CHRy01ABfcwzYAITb4ojkAbN4y8Zt3FZ6J9RkIfVk2z1HgYq8w== +CEp/ThwgBT1Mh8rEpEvg4s9ReZYW9oX9bnlUiNGQ+UsAysseNP7SZtSRUgXqhBNEcgT+DXT3EyUj/hsgG/r7kYlVULLpl70inHy7jop82rJHzOV4X1Ye9g0b0KiKtpGn+bBoCwTZQDNqyJMudmoBUTAUrdTtJ3bo25jKkhjAnorzMzEqZBHTGmYkoQKz8sxDY1ua7k+AWjFjVpxEI7XOQTSYeWMpkbN7an8cFZ7XUEu9bQBzS66gSX+gOLEO7+DihD97wJQVkFaLLtSbDx7H1XhSmjGhcAJwdcg+3AZBLUZTZ/fOIXOXi3WhxkzdCWv1ynBvIfuyCcthmP2jWt716X/bDnRaCF8iEzg= +VSiETcjwaNOhx1UchjMip37s4990wZd2QeZwOe32ovlvE22xyCMb7nqQeC6JqlsyijoBTntD3H65SQRc8A5cu41CRWaQ7XVcElLDjjbhDnF5htXn3+FDZBvkl3cVI7kFqcDHqIZ1YFRtQjtVtuGm1dyRXd9mkzZ7L7H9IbGiCjUut+L39Sdv7lmUHPlUvUxnT24Wh/p/cp+BK9arUyxStXyAmXgWTMPsaLY0gmG22Gz4+ECfFzDngKf9HdNhbRWqD5NIVoQLn58ZhUbZKekaQe5Rw+Hkf6M4++P82gO+vRrAxsufnVE1VMgA775zB/fk9wo8Vn+fIrhZQ2d12hQL959vEiZPICLa0umW4A+S160jWa3i7dCaf1MD9iUCsiim8NmP5tfNLJSC6teQ8ivyucfuWJ4/QroqZxaQb+UkxHXDkRVl5KKinCyvAJlZW+SKUt4zZWGZZE2xQkxzWgrOeakSj5mxaQR8FHUGu4N3JFF/J40tEqLPJJifJWWMJDttA0MnyoVmUu6Gm4hDbv/HcGdPU5mSq6yQ+U/lZDgpa/NzVkz9Ii6WOrGhVEMpDBul75p6VnQEt+ZT7uU8qu6bzuh0IVai+CacwA== +8PAtQpNwD10LqyIwPxJnPwN6GUnwisTTywZBxBQ5rW2poEjWlB994lzWjTYpieCOZnC+S5YYppEsLZP9VTJPrqfkkRGFFi1t5qaJntuIbkuDjEE2dNdH/HtMpvQkOCweh0jo4MW+NNdF1Fifqy3kXuye+Mf2lACjRquS0Q06g7/4M4vw66yKPtqh3+U42NO5XXFmQ2vvYMdbRfpmPKMQwt5lMNHedx5alC0Okj1hHZ2RU5LNdcRtqQu58Wt0rBfWep0tn0a81NheHurdVqMNGWHwpYcW7/InYbbXpUAyIvcY1i+7zNnWZK310d0/zWFFnhtZ/MoWfTo= +aZd33bIANMCvAASw0cm5vrVg/QMT37U57bFQOdJkySZu3ltVv4TrbG3XITrtXFPtP5CZox/aoC3Pg8D/1X5YcsS2vkyeeAVGIrFSeX20Prj4dkSv4zpHqdpH3wl/5g/FORLUB2Nbo2lwQkPZWzFz5Cc3OVYxgxQIIr8rBV5lltnmx8ApJk3Io3XocxGUHFVMm6ZubapT7Ahg3l5oI/ZQCRlvvZzMwX/HUTKd4Mmkyc0REtzoeOZByHOID4iM62zAUsCYPtpr8PbRcK0oucFi3Ni6YkZiCVgIU5cbT7omUNK4xyay3ur8eLTSu9GCbSdBN3nde6gbTjPVslP+dfvbO4uTGPTlrseVIJWPEBXHc3kXFePCOHtNrd1v+4fpIgC9I9a/G4eavfWmuDd3UoBvNGm8pO+oAYPgZD+L/k9SQejm4TDOAeqWAHTGFmNhNaSE0zmAsJHpTJTtsDmcCvJ2CL+fXPfdzcvMISYJNK4hJSgXRrRJsMAZDs5w1NWvaHxzovSIIbU+CQqdeRPmJabSgMKXxDhYz4ts5KZbDIWELb59B7MRd6QadcWKC3kSPDmllgz9Yb2CTswjFOlT8o9CXT//YxPNnc1ODUXaqzs7hig5k6keh0Ynt4eJZsFbbYAcUqlceQaWTlnbQMGPpvoW8qU0bM85cr1R5hx94/NhwggXjb/UGJp+ +80wlc5HYHczVzUNV7p2dzCi1YLz98GQ37PETXjLbwSqSlmWbHpToCzf7uRHg/PGhV2jS3f1devgsRLUaXX3EKpzXPFQskTjiXq21xUQ5eRIYF/8RXURp5ax6NiuyGcJsegkp3OFpMdd05n388kU2aQPgkhv/0lddRtYZ3JmFuT0NN2GcaWZq4O8sXllqr06DVuClEkEY8+TXn4fjplAfRUfIyDdjVY6iBCR7ZJjssR6Tu+jI+AAT3kdBXE94gI4h5el18oA1H1Ve20tI+yfpaXh2m8rTWbVBEFwsujUM547LdkTv0EXG0pM7NweQB7Je2hrUeLKCzJnBQ6geD5FwkXz34rtG7ss93y7pohT0wl7gp4OqdTZw/wxi7/JhzG0kOSr3s+gKF9+Y78C6F1xKjyLSy3av7+RvyhhG6NdBq+w+lsO+MApZm+sF7xxdcreKJBUXf9Ro8QCKdL30KbSNcZ+x1tokdMCMWG7E0xPnIL66godeWcAfihdtOvnnyg1fSQtJBJVD0U1pdZcZVCb6JcLuGqU5s0YoiPxqsNp5M0SkD/3qGKLQWzK34tV/jX+7yeEm63t3+CRNoiVIbqaNWlDJa/8ug0onENEG9toaSzUK2Jh7GrBCybvnrTOzjBnWi0B0WRaYMUY= +QzRaESG1gFRRfgvtBEaRnDpksG1aLTiz1sTs3B8USBH1Apb5eGSKTSVYFPDPNUhCwWGDt4zTI2w7N5INFvO2iPEmQSqem8eUsLY0i2oTgZObcmuyhyI25GnFc6hHI7H7y98t8Gm4Ak1EmC5FnOkEoAgHv8ztDXO3MII3sK/Zuiotghf9pHTxgGof92SguYw0A4fQEjMxnf3NF5IEw74d4hqxS340XAjqcQNgCUjffu7Ih3QW7BMr7Q1WpdQ= +KYGCWcHA9zb7H53ITtAuwD72UMV4S+NZXDKogEW2U91pGsWLvSZtmUZPEafrkiNqzMFYeC48i7WS3qM6QrcVwbALQHedaG64uZgKg3A+9WDzuBe/2py2t8DoNJmlKKKjEV009CQoWm631OnRv69FBlIGyg3tmNXYoJi9oW3Ktq8XJw8S4KqJKplh0pXlFRRETcnzFF5NU9a6y/pjfqm0UnpZOeHbub59+R5BPceW/NP/oPBX3Q5n8P107+vb/ceIaaedanP9aai8InZLGA1fNwGfmyhyce9fTzP3RHRaPA/sHHhb10k+o0y+QkoYUQO/iSBjmf5/25OZYngB+LqC7uTW4texXZclj0K+lJicnTll/2vUJ6Gsoswxrdku3GXfHF+d9nqdMCkcCVhczoPW +SrsnYzXFh35ZQCLOXnXyFAAypXj0OqlrHUbIk24DNeMF/PzlSTFPZaxBQF09tpLYW+H9F0xYZmZ4HWMMhyGDn/v3Xa2PkF0HGCKDQ+Gu0pDAYkIaaaAxU6V1U3S3piAGiEP00cWyPfS+NdNGRfVglwD/fH7iyjuZxB4nmBZjv7OzizopwIADwfDldpPRObJ1PW6xYR2j5EhrsE92e5wgmcOjI7JGqLFqhmgqJWkT6PcWibcNFvnaB6u29Jl/9jPyIIs95+N9K3K4/Ny5ir5WpG80zDNPDAcD0ZiuRcmFLtcb0NeAGIk9QOMqg87tGEMHStto1m/pzFpb0p7vLW8U7AXQpyV8WTE80IObNzV+W2Q/IddfAEEWhanobIIJmy6rIm1xiEb3fB7yc7uyJPxLOxAxDGDbL7w77qOg824C88z3mi9utw== +8woeitkW1lMYNs4rQYbCdrJXyRfjnJgG4U1vVDsn+jgV3zbQpIOSTp7mGkTJM5e1iTIo4JXsJ3DLlVh9+e/ly+tBVqSBKpD3wuoEAbFIyg/IB903Vt2ibxUwvByCwiL57un1kqwYWxXd6/sblp9jyIvDZz7evc0/Xa6ShM4VRHWKqX+8+JMwhsr2XIlHqUBV/eSAFcK9NtUdIEbf5wJIQKqz5j2yiejxNLYXKaDG82QdyNg2ldagK7mmmJuQYfca1Zs= +HSJ980GFCe+VJ73OWlj+j5bpcaGdfathRPlKua1ilQs7LpPOgxdw0Jzv6JrAbRTqA7hmINgTNJ0274ZM4zhwK8ifH5GPCeLC2TmOcSorYLlXosAG59/JXsQLceX3oNPfxIg9+A1Jc/L7UedEFX1KA96a0MjJ2IQRNmW/O76pLnj9SAGDRlJjUiU1S4sxQI9ZjmgQNCRGuJ80SLz6oJrwsRW4sBBWGP9b/XgUnK13AXaAklMFjv214hJlayLD+3QTDu18jCml6V3Z56ou6kFHhygmvMLkfyv5hwsZyfaoMlHaJmEjz+Yz7plqOYEvMBm24OQHbDyUpvJA1+HMxw== +pxpvVHmBrC/g8560mbPJYnhrKEegZezUq0zddJTlTwV66+HHb5Hy5z28ZyDfuCb2Ycg19QM2IJzz19bMkq060GFUAXlzUvrsmMFdYx+FZEyD1kQF7oiL/DxjwUITBH3ERjUEfEIs6iTUbzJv2NnRmW36MbbY+KwSP9d7cVhjRQkfeqcJuVI2xFuadyGMNuNJuhPM9j8mKFJBDrrjT5uUCgrMYK5xqXK/6/jNLQuAKbW52YNVOpe0SLsihU0ZdiAAKA== +ECHwvv7wwUY8yeDJqKsX+b/X4d4cQqd9CtANxtuOstu7L99A/XGkejGaer40PgBflSb4htpy5lFksJhas/PEc8hQ04cwn0aRoWdySDt4QEQyom5Rwf5D4Loel2ENTHf/PFgfnBVUinjPBI3Jh0jgbo7YJzlCfpUiTG2VrW1fokiJzzVZ69IgkDgE3ZFzVMEp69aWuMNxfEDgW3dr1YTqbFRkE/4A2wRAJ6yc7RgRs3+O35vhiSA6cBbOILhfFoVayQRA5NaCVDNnpidp562JJEMGN7u7cC+eJJ1tqIT0QE/xEs+nox4ImKPwtblGT62bLCKOZPYoZ3duzjcwnJ04y4k/+WWeAggQ4pkqyJQiqRREqauI+irTTI7eq34ST+B26oO7gKdVFaitr6h0KLU5ijvrnZHrFZJJNomfNs/FtIpkuAJrvqQecFv4P27ILk+MBipZMKLg4IuDjkwZVvsyL7FV8ALXn2QqAu1SyRHDmUzDF++fnOMdjCz5+pYZh5BS3syY5H4= +BZeEbiWvUhQEZFQwhjz20HRoF2wRxoAIqnJDBd5fzAJf01C/H7xjyr7MR36r3wEhm3rX6SJJKhuw7vZ/1WA7/tBWPEAth+kKLJSFMmmF7nA314ucVJjabRVBUost68asVgIJa93Ulztr0T2orLkIRVMaqCnENKJVMMHxNDOe5XeK1AyQoejanr9DnkR4TgvWgREc+0pHvYIoTrs52cHYenaoetO+kYcxjTbRt2rfCTcJvGYLNiqjPZGFbbRNU8mUzZrd6W1L8GhIHLQj/5pqdMEtBLhyUM64Td8x2pqvRrT6eiYaoxn7N95e5Zk1zXsq/rfSEZMotO0oVK5i3i6M0ZXh7/8Kf6LsJOaFhevHGAMf/8UpWFR8lK/xLAyCWyoUpllYh6YpdFe85k5xnnnlrTpikekT3NulyeouwwYodrVXHhC3p7t+y/xSXqngZKK6YjOv4Ya6uOyIoRzm27BYB3b//rDmRjdjS5tVSS1REPoPVU+hpF70LbkqbDTGBe40V8wUcZLt0ymFFjMihUvsvBmrpwfFjvJdr8nT0FGAfy6Y7MDfiiyYftCtdwJSb3+L3uMsHsQ2UGjT4fvwyCBFTjt8 +id9MnVt7OwflHvdD0PoaL+yPolMJZOk6o0lwjyOgk1a0DoCG5U8nX3yUFh7uH9Vf0pqakDT+zNNGWXOz9OAcuyBWJ7HM4reCm1MdsEyN3a3hze87j1xMOwiLIFl2/MQeE82+Vv5onC4KBGY30aNnHrNP7L+A1dreHrDfnClzidQjlvMlLX062S5JiIpODh7zTM4z+EUUaPMTHJR39atb+ELIasyfcyPxHu5WpVwZhMZvpn+uqJWzybZtshZmwlhnjYSgANuSTOWT7ypNKpMrAwGB1MO2VociKWbZaiwwKFhWs4JsmOGFaDfI7ki92EZE5NqTkBFMehGbWXQ= +O9PkIvTzJSOL45mI3ZF7qR9yFhvX8f3/VQ2m2BWU8AEnyyGh1Dct/X3Vt/8RaLu0/t2st9ewmNQBRNNU8GnrTy67lcJJilRyNeuITIypD9ZF44z4f/cDrn9QoGVrNfj5c+nLuJyEqV2zBIkTEHGKLRYXm13p160+t7Il4eMAZcTmsHZN4vDnRDxgET4W8Gssxev2YZY8/i+xrIuKJx+P5WTe4+IMFYCGuNGda/EHYtE66Ba3zH5gSMyjIQ== +c24++oNg+Im/NdEJ3OrNEruwIu9HfdTrAWqyPQO5Y+WJjYVLk9hT02e49dqtqpv+uL24EHHuwfC9LK66xiiDoGuzr+ZIhgKyEHvPPM0kUH+NVCxopuFZA4KtqSLi3eekNYO5DqMUVAx73LOzIKan7N3J62BmA5WHNt1Gg/jFG2adfs1/eb8/cfvgOoQ42bjhxjl8NeFWRzOLZpM7C2PFcI9uSgtBVuRwiRjBJxtjD8O8dvkse4fiM+hXB9iYHQyAaHHXBH+gpxikpRIs14CEFE/B2d1P/AuoVyntrMXD+KFbBfsBa5lpGmnysRtmIEf5QdJ7DRjjiw3ux76QHN5S0W2iv3tJC00ScZlVw2X6qZiSpfukEfcaAT/x2GJbLnGom2rcsuwKpXXw7Q2GGSAZrtih2DdCT3/2RZkquNLyioeXWu158MRroJLCiQ5IkOPwiYHaoMLUjUpSoEFG1mH3xK+OMUchOd6Tms4NA2FBGqybkMUzZ9V4PYKPPXi+6vFqUBdNJJdFrszGSJAwdy5LOwqtUQVrL/5QKrA7w/msTe2uz5/w/ZFKxmwngpuM3V2lv6L0y3mb9InKHpgpr6upbwbPlHSQ1sGkYpq4GKrjLhtVRuzDtpehHQE2p3V3Q5gEC9uNz0o= +vnYcIAnMVOZrsETmFEM2ZXf9AwRNrZ1aQoDnb8i4veAesJfbCinBnBzR5YYIUGC0pcgLEQovdMoBQ23P0xMoJpo4eYF72mtFcg3iZQbMxFgCo1Ik4hNDjXiS3KALWjCEE6f5idkobyNBmnxTJ7/orc53CGy4MDkjm+3Rm59f1RnwJAv6nFU/c74iOFRTMHsG0hDJnY1q+LU78c7CoN8RbRZpRGmYZiNa9FRwDooUhsxQqBtyK6AweoM76qObbaxK+W8ErA3pCltsuQ== +hurR/6xZTWQktuHJzvPQHzI1L77qSjZireIrM7yTVT+oCthbZey45UmpSyCTTI65oFvzZgW4QVn1fGYbdVqB4RwT0om7f/46n7oi1UcDGWjMbRUuP1Aks1/RYUkVjpNV7izm4vpPrNVKJJ2nWZzbcL0AwaBt7sw2dxIMYCNjyxJ5qI6EnxvI+frl6eY3jnp8p6ICkj1S15MJjIMJhMP+IiN/h6WdqjTBflfd8qYTXpH/JSuatkKjl18mFy1RzI7YbpJOVb6x+pdFuuVMxHu2ohz2EjoXUHcdohXA/bcOIWPHMr8tnZD4vFEoprhYb4ZH5AGmO5avVDr1LIpNTP2gx7B6OXXFa3RJC2zQ9NK5NRMFlU+1danqN7JeccBmH/6yr+jjNmUVGS6RSad3lvMmC0HWsFH8LTm9XuU6RehzO7SLx4XA +Ws286qPJyfLE0L08PTQyyu1J0r+Uu2cTjjRWVtlsH6Mk/SelGJvrZAbjfWaV8hcHopYIqdAZ5dT0tprK1PEG5++7i2579UmNauY7YAttJ+3ilnbzs34sBixXTmkhCZ+sIVT3Br6CyGwa5oSAxqD0OAzt8hjZwQ0+Bo3P8mdz35i2u/ItwlJeUcujGNSA4i7p17kEClWCWIqelaGfX/O1JsRiBfiQIbkN0seuOrQhgOvtlSepTUG5sJ6WcLBuK76oWffGGHzZfuGlmUsByfEqFSywvnbC4G5rLJDsxYuB2+YzguOEsL2TqWPAyprR78EH9pNS3wMmTCcciQ6H8Jw= +39Ntrh2h1//BzoX21kj5qWggCo7MQa2Us9aEMhQN+ngcYe+ose0R1jA7G80qYry2+vevfcvVyBp4d7qfxkwXxyzzWZP7H4e4ggNoY4xdGIcS3p+JbaflPqepnea9FqmUtKbNgf/hj2Tu49/nUmLVD+gIzrtVSNUx6LcaqbesdTujFdheXxbw1S/j0EWkbDLNSsaGumwpmRYEVaA+vToxyfyib4sbVtd5myXF6/yzxTgk9YcOx4QZZgymdbQRwgYxxiHU5zld/PP9O2YzLCkx2Wr9TCS6a1ctfvzEvfFFHXKMVN3q39947KVuX+Z4ezWX4+/5j5V9kJvlx/b0pr3HLAnFJjFXQth9N4PGaDwbirlDOGsMkM7ZXHpN0gZhV7gkZneeG4bC4qK8dZAAmdYYDjRx6M5maatMPYjfO1uC1TyZ8ALbQoWNQ2iqg8K5awHTrsUCfBYI6/UP0ofl1TkcW2Ieu5AoDqxMpB80nBe5A74zcQ1/XW3dXEa3jCVTJB3GX5Kc3FCf2VfPEX//WeaqxWXdzApN6pqqQYsxoan3iW3LlQx/dAMHYkCfE7EzHNoLfs6zX6OOaRl1NrWIYZ5Un0aWD3MDPlEjBdp2Ot9HLogaLlnfOnk= +u327jYnYtvS/TXW6AaWufVrd4BJwlmGt2+z1i1Uy4WiLoPHnH77JJ1jBH4VkoQezyPBhzayhwk/Rx6OC/0o3CllaAOzDUtPeMxUICGDAy+smwyacGnL/3huD1sTg2/wb+10OSn31lbDSkAGdZwyWirfsC6SqR8qntEd3IswEui1YlKmJWYkwZ+A14F6zCuW96v/0WCXkZoN/qAhjuI6nb8GdZ5k3F4MmE8OFFubavAKJO0Ej4MwYsYgqfg3M6nfEF0hGMYZJ1539T7eKsMS2afxzLhSVlSsLhpo92bTry85LiTppVSxZ1VMDXLHstAMJG7K6C9TA9gkEy69Is6bVaaZzfx9Kws0UG3SHSsciyCsWIX71np5wH70m6d31Ap8naKMeof3hH09UFuFbYOltqOil/4KfycfvqaT1f6ZzfTQ1sSoZMd0quY9O6hRzhVP/27v66IKiiMg+mi5+R2IhOtUdOT9ogFTf8KKUlXlJcZyjFCsm8WTKCl3mOLzDxeixvzWY0mXOceErgrYe1fRf24ZsBw== +rDnlz3X7Dn/KYL0mAnyyVAYaajrsNNcbdkBCNhMa9VtJ6CxwUkmrYeY1NAHvCzSJ/uIuoTBY16GH1icv7uh89BCIPGIhfc7u16JDLyBzZ0P2/Xd5DojoyJIvR3Aqmu/YesRH4iQnVV2vqC5PTpVM6IyTxl55nWtns+qym7i8CTHNKfH4ljWs+CnlFM1tAOBEsGM3Q2gIHsE9A+JQYioOe2MDMFNBz+Kiy7eodK1FLiHo2JSj98wki2uCuu3Z1dhoBEi7c0MPxgM/C5T2ucqEHvFlO0rylmjcjhBtfFZEbFLxDHuthxes3ndcBebQnHhRQbhBM1tw24Q9/MtcSVJ1q/0Ij4NuBEQFBl/bqjK8DivtLGj3V5AFRQAqfBqbrk3yXaVJ0SY4CXVH2AziVWmtHmwXWlkQynRvCXifAwKP7rCBIMdMOY7LGrwjpL5y5z6Tvl6tXwkWg2dh/1rp3y1l/agwiNvF3dWyL9/41uJECpRxzi2CZbr4jxvVxJ06czz/esCNROhhJFsZVXznxpnSzgzW+HiUjfwTDK3NnT5vWvpcMYWn4XxAQ8T9xhCLS5pIV/bZCw== +3O4cfet5fdn+4l/Rr4BbXVop+bS8NQO2wLVlBJB0jC9mjudgoRV63jmGX4kNq5J06tvuyUXHpoMhh7I4e7FWp/j9Q47/zc07FtaHi7GbXd668o+qMJSr1oR6633f/emPJRz0daorLsnSAuEDDJN/0S/hiw== +NXKh/iq104ruyhwEncN12oIDCiGRohErIWOVF5hEvIMgQ/XIJQlEcNnb2vigSagQVkPxCrblBd+YRhnNnsg32+Nua2EKkQ+umdoDAQ3z5jEGMLvzh8uhSttLr1uWNvxYYf+IjhmXHJ0Lt9SRjbH03YyL2wN9ye82w8pWGScJz3Zj7HisYCZOwstObypgtVz5ehxuNd5xW31UXd4n2Sa5PDQM4Q5m266Aatfu9kBqseVhK34KDI9uHQLmPe7xLqZocy4yH8NxXKpqoZVf6Dwwx6Ng9kYVOpBrBCA75DzfrOCO4OHgRYDAeoaTkQmzvkxBNojLQwBnT+c0L+foKR564mVcIy0D+odjyvjQyNqmmjjSyuFUU2+sMwDGbenHB2p4pBmYWQ== +3N9H9xFuBM0mqBMOwCC4XQ/speFNvyodqbGoQpNlnaCgfo629TMJLhc0AeT7OZ61ZGkE4ZCLwGHMQuayqU/Q9eDloRM4r8/jiEm8KW6tuHtb9964gg7CjVjJTzw2RPqPtHM+ydRdr8oXxUtyRnzm2B06AOJ2O6TK0FIaB0bXLitPsjjb7GyMgrO7T92oDmsy9pGTVna1SUJwFc5RfTU+MUt1C/nBjOnhK0ZFU9XVM6ow/FZAERxEylXfvk0fD7a5Ni/dMIddcHEcN0+uQA== +sUpqkHtFPzbH5kqwOzZI9raS0hdaa3Duc562K/5QA57Fd+nmpN/EZMiuqesXDsiyDbyWje9Ajs7Q9tmKnKhFIzsjYRaCO6T11BPVutaCLAI06OFG7of1ONEHD+d3HwEVVDOzt5QqH8YT9tENMIVrY4pwZcAKPqvRdl/bQq75wBTlqStdhp6tvPytacdMFMM3c01ScbhC33m3IeO3/ZyzWIYJtSa3Mo+TQFB7iD6jOPa5jrZG9UvntDHCcQwNyrtET3uDxFdEZH9ed9GfQ79Aw8XBlOHjuI0oxVbPp8BD1aasKryRqhNCIm0= +IaNFVhcAK+o4H9KsH7CrBvxkA1d4ObtekSTydOJck+aVLhoxPFD9Yz9eI/XJf3MbO9qi51TSvthd7m6QTWu0QX0/AheXmX5N4Q+LPL3heCnzeJVL+sUh5i9aBSqV4co5i8DyIMdtlu7oCUENnQhE1ngSV671A4+0WfUJ+xnydCozsQ2yHnOnluogfxyydWjBGk25VMFkkmNuF6aTy5Exy5539K67PCJ7NoLvaG1yPAEcdbVJMrE= +TEKVZVAJ6dBxu9RCSloafRX+XHKgrSMcxP4tyQPI1sgSW/RY1Hajkqvg/9kWRlvweDY/z1hRqI2PnpZLbVzaPOVfomqaYY50u5BlZqzyiYMosM+7ipPc14+2SEDhyvoIh/+6Rgc3CDhw0IyslMAR9+vvMsfnqx6NlTG+OYz3hKiO0cJklHLTk9ebmg3Pcu7yGjDIsXI5vY4m2p6px8HoPF2r+L5+pq0ST+wK1ZC6uAbsNaaO6P9p6CzjbUXQYHQpZ/Wdmoj+Qy104PbDo4G5BEA+VVx6WUY+v8Qk23ylUEYgc8z4Rdr+EW94mVsDTGglYsJcdUsMDw+W9ykzUqoqGDUKTS1z4h6QF2lkbxhP5XxTXwRgQ87KfIQHMGqMahSkfPr4DOExrM9/kERvJqfkRQOE7X+rJpzwpYGBV5S/HksmFQ89Y//DGU90ivlnQoh0ElHhLBgJJUeZMR1jXkcfQqpxLTJSWq1ujG71LFXnwyqMNWqmIsr44WJGUnE7vjgO6gguPhxjUjgs3WU1nO2XZB3AtmtmzKsdYMIvPZMwh5oe355GWJmBvBX7r6U+Ht6TRg== +OG9afPTY69JWvwmhkF31HjWd96yLs/HMHANgOfduETiOsGceYUkCbMISmpnhC3SjzHzvVLSWYZsGIkap+Ke8gDK02tki3KHZ41QUF2RgFIpfmwO49yQOoRy4/9vG5+dca2IXi2p4L4yqFI505XWbOJVpENmjiBU10rE2wayeuzwGuZ/z5baI1PaxP6dWJjVTRGxoiUjBUSBqyLcB5SORWU/vZJTBjCfr4EdLd0AQRjyGOrpmfbAFgyzhIEaXqJIV1nHl68rk7dl6l/cS6Zp7fMqXB2r8W13dPaQ8Qsh92Jl1OJ9rJar/tWRxy5O2jZewajQocorgUKCg04zTwx+LKxkTQMMTyUy50SAn7jnFgmhSkFpfQZ5g6dbogtF6one71D4w61OUJbdoVby/emkMgRd1XDWnA7HNqXbfDVPnqSe06OV9kSV3XpYUd4z2QPR8KpuZyBuZBr6Wvsns8R96wtsSgtJ0MSTetiZb7cqLrEh+7dtwFLsTh2MWBTduJKeHYVVDb7XB9mWrgsEXovZUBzzoFkjBfLFpVITki5Ak2o50Azk11Wmu8VdCjFXMHHhR+6KwdW1Fa8mIHlr7 +GTU3Ry3P3eJls/17k8nvDBgJZPvv5PRGnOz6+LDDUfqUXI0+C6E81THci3skNu2kphLe5SmscvKeyKGhHdSAN1p1Zt8/GdEBCdkAhJ8+UQlbrV2TFLWye7IiL03+PLsYTq6zEX9gGRvftlyemdov12mxq1pq6PQM4a34 +D224CzK2lhIgGTFWRf5PHInpM3FyndJagfrqDmSRXx/OFZZntcGovf1nPw2++KH0PGsT3/dHtVOYiLQM6qOeBuPH801RdvaUQsUekAqFdHxQL2NZxr+cfkJGaPUBgqaMzUumg35rQwaYI9163hfjCyhrVwsO9Tvvc7ieMTwPmPRBHqnAsZjyiDfdgSCNRncC6MTrj3KYsPSr8okJhL1Vnd2fn/G1QjnmSu+TlyWF87XvQPiYtrYBBMfyF+keSKpLjWmS78frBl9JA/HKe3D/vU+orlZ8k4an7/md8Ns9lyA6Wk8tD7kXCp/EbiunOSQvz9L4DZRH2u8ofIhZAusmrjPH8Njm3tj1QwS9dnCyqzTkX0phU4+76QIclztjJme8310R8Gt7b5SkKx0IMXLKiS5fWteKX2ih7yslGiNy9x8pKyTU7oKI8YByUUqinLNn+2k5DIOw9R+0Q/DOr/2zSS9jfsGlEFMaFm9puSC99UpHmWUgkBEf8n1j9kBqB+3ztj96yDfJRGQ8bcb22Uq9H7j/L9m0aQu2FB/QfoYZzmvYklq8mbAD4dIN8nhzOmtbKPwi882Bjiu2WB6dkJ1HDXqRPY63/R6pHbq8S59CAnAhCLst48hj +Cwh4+ixaG2D9PMqic2npYX5mIWMcIust5WkfjPPP4Ny+kmVFgUCFVLP9A51vgiGGBxN1DbGfoYF0VChJ3QidHTkl5+rbH/ckl+69HjR1xAR8r2TrkIyiIA/+bP7WQp/i/Kv+TPmvcGfCVEL4FtSihye2KwqzdkWTwhWdhfZbccIYxcZUjXvquyK5oVWB3yUhFyani59w6ShqMN8HS7i9wlFSkdJuLWWb9EYfWQDB16A/OqdlI3b4SsCbfssI9dkNVgaQPvuqDNR9uFSxqlc= +QPF8CLiB8EW64iWRLT9S0yygi4Q+LprnK4aNeZxN56oMPJsbM4nTRjTfSAhVql9wRHSz9pPgxbiZFdtQ6PdaM5ewkNQXKtuf7sRLnxwQY6jVgjJ/x8DIZLXsU0NmTnPPbPySPUbraJSMpcw5p6sBGoqmaYxFebh3xkfxH00whZzK71a8795zw+gxNSs8LZr3f0u8OjSKDUftWd0JGDIdFiVfH5lGfiDDST6h6MfN7eiV7/Svskx6KQUL8QpXZjip +ahFQ3AFxGH/Zr52T6XJuSkc1guCwG0OYgHbM9PNl/wOZYC4CmZQEXli/Lb688jmVgvOsRCV/dIPUgwdvAnN/sWy/tMUpTKQl6xwh25vf90U1hoD+OBTbv6CUB4x6EpETuqFEAuj6+0UzmPlOT+Qha125LVZcbG/Gvsft+QzzDAl/y5oKdL5ADlENad31OTs+C/RGG2vhNSrKRglpD+tsyIGrhlzbQBjF/VP+8PssDxIrbNtZVRGPxe1i5aGzizfEWwF7+hOIGZchG60QoGxky+/gkuR7xCEVYdl8/P66H1RpVVnIXns0+mZByhCSk6xUXWhMNUSb0z85xxPkHclanxG6DooldmvtWP/qdBRkJqJ3y9gkXjFoK+jFP8OGhrlN9Ft18cWEbZx1mSqGO1IEtY7jJ+wuCTBUELuQ4G3M8n56VQJBw6t+88/sZKe2uv4iKpSNDvfiVrYE1G27Ag+iiEBnuwkB4kOHoHLJUaxf56IeyY2AK+NAXJ0mkVgOZpPx6eDycVUc4G5uKmVKLaO4Izf+q7t2KosdwYq1BFGPqzvOaVCeFu/zfiH5EJWo+JHjEN/FEusuJ/kyF+AHP3Yg6xMPo6ghX+Rnpu0/kwIYNwl+a3zu+IwAmR3S5NODs2NbliT3qcU8FDuN8EjWs3GKSyooH0p1osh36gyDJduJPstEaXhhJA== +XdUnbYkvdk10EMPjohpvklkry+W6mcDdrzxRA6LphTV0q6te+W4RQc6iU4Ag9igeLFyQgrY2ZLMDkcm/Lf4cGJuB1EWiWaBSw9o1UV+jveBv1Thy9254nZpvCiZRa7QKM4CIk4hR/1Qs2S+B5/AEXy9CxrgTE06w8mad18v04DakNfoEcjfBDFxCKAcaYhqIHRDVC63AxXBp4uy2KmkH0B7yzw== +Hv8ukPJ5AKfZQCT1bfFtuwg4iD0sV1xg50Lxv7UYxw5ld+VaGWkSfy9nuaNMhprJ0DodvfM/TXXLVT3qUcVhcr/He6fKT8Sph4CHdm+wR6yDS0e74lRvJhQiS6T8YAKxsyW7hn8GzVvu/6QjPgPQPAbyORMzjgOT6zAUrAuWJU2zPesn5F6PItZ/DvUV4eFQ5e0TBw1veHyeAHhVlDL9ftvNrPD/9BWl6vLj6x20EIy31D2lHA3qtyobd0LQXyYEIPnkwFQPMXBoJMG5Q9e40jG/CZpfXwL2gqviQsLNzuVi0Tz+fANBVYAP3SLEmpTrXUwoCq1YuFovLvTQxTzkk+yPq9i8nA3TRZBNL9Hvn9J9snDlejxyVKfFZaJv78UL2QacSA8HSQ2d5vrIzkCyNxYydzVIwwtR/4dq4Ul47FHizBhULcU60MGGzCGv4mU= +o5B67mJ9MbZVfCR46H0i5hiQiwULJP0fxdCEjeG1q+dy9Ra3QfqyopOfAgLVzq+oCTGVpk665NSmCMQqW2K78y3JVTE0OxwK7XyW19htC5fC/W63QmSIyWMyzOc6w601hQaGPLniRJSy31muFPSVEkyvWB69TuYRnj1oboXuXmKNenC4F60yxOId3mqE58P8AZ6p +h34uu0gAimN6ZRmEN4VJRKu15v8Kb/lyRWNYT1eh23KzZKmk1ZUeiwHrd7eEwAp7cuuEyPLeWDdItB6Viic5VXbsRtZ4gIE/soAKCtIpjCZHZV3gh00HTtBZl964B5aM759E8+kuPdLx+0UX2Tn5+2DoX5+dEm6mU988k92Ek1t0OH4tMk1fPa7WH77UbxBJgTldKeyhOGEqKa67QAL+nc9S2U0McRO8L5EEkknZtSZmtb/o3tatw6SeeTXD+bio94lVRjmZ4riDRHp5AgRXoemsFNKoTAHI67jJUqGSuGPArjbScvwmm/7OEOqx76JpYwD7ByJGEchFe6lkD36TIgh1259ZtTb5XDMKZ7RsCnK9jvwq9b0MyS1NQLtPihu42TKti/PBtZfzlE+bh3OcGcjTtBqD7Jrex7mLh6BWkUwHEbBpMc+e0IRUFg== +ZfSpRj5h6WgqB+czKZMmNKLS42TEWznU+Gp72SHe1SuEkr4HC5IAKDx2IyUB/oChnut+tLE8QI899k9isZL4S4jaby5E/FJ/VTD4BhLiJtk24jEl44/Y9qH/sQuHWMqrlXRwZFw63pFmqGr2CTd/pwDtnpxlMkPzHGKXRpZQL1mTuIJAEmXmKhfJr1kwx5WgiXjC/rBVLAwuxlIGvTaShg9OsNiW5ODC7JJubnLCUhpD +BuuiW3zzGvA91aranVxSL1lRa/VWRLW8+30/ycuN25dpw2W5ViaoZpvH20wwiRqqmOZ6gBomxv/4LMfRBOJ1qjZY1VmzJvXrJsM0IBL2edNp9eezZVF4Y/VkC3O2MFTDzOrfBRimx07PrK1j8Cll8TCCG9ZSU3IscV1KGZxlbYv42FVX+WHqDjPunasQ1G5XC7yF9zdaPtnaREiatFxde12Twasw4xxVON8a0k0SFzpy95jGa4KnufX967ES4U8adEyrmSL3+cVmffXW0dFh962ugEjc8lmQO4C04qYhRPKS29t1+Qx5nWgSz1cT6vtSmPoPxBc7VNykXIw9iAZLs41tFU2GOXCyRCzERZBjlyQ4CWV6IpGiImrKcIpHBRsQYmTM9kVFmrnAczIqy2DyB6MQ0JzCR9XYNOh1V7nPntR9+02W/TqycHjlFZwLQvtuuD9lD58g02Q1iZ5vxnzbpv8l6DeDDQsOLOGGV1xxp7cnKNJuxOVpqsMrFTO5mdIDrCiK3h/MPpe0mMN1TVPwsUww6tYGzyO60oA5VpWlv/DPdlmoFN1TdyxeX1T183oWV3Ro78s3 +A+0YAIbigD/gzEbDZ8LH/P7RTAKYstBhfkytbMiuHharE685cYY/6UynYYdO8AlZKA/KR9oxX7I/TJA05or03nQXiiSzEvbJhDS3ORe1hL8/OHQvdS+yDsu1MIYqYzedtdl+5ige5UI1RSi8NIDjD9F61bD8gNjv+5HVXO2tWnXpCAxOKRuc4h54FDeGw6MReXyI29fXWiHX09pF63d7a1vFHn0hLIWWgwoaBBfAGFsBLDMefMp32Uzs/v1w/WE/V7rf4BN+XczLRaIWVPEKPFZD2RjcoHv8Wyq1xEUMU46AKjAFgp8c+eD2m1dZm1c6zfkUkmRX0g47a/7j7NgqwrFnPc46XcYUs3v5g7L4KI9KZaQDMG4/SPOjKvqYpUNE9UKpcoCcrGWH4HL4LokvMK0U2jjc9MVY5Kwnd2/ar30Y4+DfLbWdV2OH9AIxBnToHE5okIBTuUjL8sTSjvI6Gkue+vki1HqaQPBaHQtvL+qjjAhTRVyAFbxme/pWzbgeVynHJKzUwUI= +/d60170ufaShf50yGYRmiEYrf3jiJD3t13C1Rh6BQJhcytNZukonnxGAaovbyPEhN1b0TdUJ+osuOrVICzvPNHLgWmlwZOTPG+00YAm1B3IuYAzt67oYbYPhfu5GMENfYAYgXcF35B718bDANonMJaxB647h1ccf9RKxUqqexyOXq5j7oDBZ57V7Hwru/pfmxj01qqGVKBlniAbCKmN8YXlraG5jlMIUb5G48NGX9JaJFVVhWm+Q+uA7dP0xmoXtk2t8wr45ShAECLGQXxvd+CAxe7B7fxfP5jM12gyYhWTg/BJj4IOrCR0PWT0Wvo5MWVXS2gw78/VCeRrJy9zyNZ/uQCEndxwP3iHTaEF5i2XwPXWl6AAUELiix8UsbuVgQS5uPv+Rwn/FyuAxGAlKszLOl9ha3pSPMnQ3JAEjxmndFVD3YDE0SzzUTxiRIgq+cjFndJk4aFLYMCjPmlpo/rv61CBmEIKUW0d6Wn9KUZnGdCpGqbLb257z1FuwmQvZcoGMM5hBQhRbyWFqaeOQzpwpFSkU6FSWuraNjE+jxVATJA53PUEu5z7j3xyp4nc1pH1UmxF15rqaaau4Q2/aGeV/z51kk8QiH5N65kW2bEuATdkSfcckObmxVZiEmHnNNc34bAZtdLy+zEPPEdGsdM2/VJVGPMVL9rim7yWzX5O1 +sevB6dRZs0dXhYttmL38y/MepxKKCM6oo6VED+uN6j5nlMNNWlvcNXsQ+vWohnZOUjzNofKdDzWWwXhsE2w0EowLS9gr0XFRRq5Qm4munxQkfmFfNZF90DjBRxbKxtb0TdxOpWK3UcpwWU6e4acWWxkgZjkIpOHuNecQ8kWXonjCuMEPbhH9i7Rp +vEAP2l17gQetq7sO8pPaB/SxgqvyGr38LPJRyGXJ38lPtiuvTgXZukG9bVcwXzmih9I3ias8Q8TRspquj/rYgp2Y6WRZb7dNtV6W95BLiRReA6CuSWWodqYCV2cI059gy+TKWSI7xE2SF4/iS44SPzIqNFH+N6nQv0ilKVBp7f8J2kboNzBMUHsIbpVKrRet8AsLZunNttxM/hCRCra/414zKA0KGwam0ejhSJqX+gnUjhsjQD7bf9HDKkvrlmJpr4la7uA8quAhQh4dhZfDlethOKCsHpghaPF145TYHOhmZqerRPbHxOGFabfEp4wpiJM5/Gl3UwSbPyBHv+fzoU528wRyM1oU91qoyk2TTygmh+Cio7fDqjvsHHxSkCrsG21qS0ACtMiSvpUWQ36fBTXf8QyJIeHt0e4CBsRe0B4bDCAsAu0eFMtuAVV9N8G1Ucf7qaYr5SsYsVZ8axAFvprWqMPzjcDllRWqmy3trLhsK19L8V27HQLblM8cpldwbeYfgpPNVSl9xCqjJLxtD2VY/qMeJ4/HzJmYx7KOyyQuejBvgHE= +ja44eJcykHPFgYMtWaYvyg2f9eEm6mALCqWZhihXUpcKz2B/uifhO2lkvVAoFZgnbDy6GQggtzPA2XA5QgnNgeEhuE2Q6cvmE+LIsZYLwz1LOFiD7GirzEdXyrByLvOYbHXgTvEt/zFnT2ELtYELF3VEVrXyhlZkNhs9bgrY8aRdZwQqjU4HURuxakl7zPNTM//RJsc9+U+vs3lnTF8rQ89oj6lkvG03eWF++CfTVjyE3HwXxINADJUBQzX/sZn+yBydN6uML7tmnnH0NhEIcfWMrUm/DxLQgDKW0uiX0x2NeMN3pqQYIWuGOMVxw3iTonv1Ewm8hLf5Z3I9h3pgxmI2knDvAmqTPQG4L4LmwmzO2LtBI1tkEnB6zZypJ7TfJhlxyrGjYfNEFgCCn17BLKCvaVGcTddndoY3SUkPcjRRcDWhVoYXl6YwbQ/F7JgahYWXgKqLk4y4iZpiSIFaXtN4CHEdSTZrVM0zYAZSNFevCsos6oA5azu9rzxvZYD8Uv4WkizrDfiTHdOcZUNCITBZMxPXRz3qWB+WXkTzK6FPzofMjCB0yY4H+afGfioWQzv813LC7Xf5mhZhNOb6EqR9Ow== +mskTw14qQnBpuncGKwWdapkQD72baAtRiaq6Rens199jDHzSHzZp94C9O8Uzc7q+jNub+YofdvqwDXulNyeVyXp+n4h8R2aMTTv0f1ohCVguDwp34uS/sVroWhzPDmKltvYYG0Kc6LxT8V0UEQPDLWdewbPVU5cL30WEYR1DvuloddJEdEjDeB54w/2ncxpaRjATn6ePCRF+hfQMofDso36ujpBZRcTwE1dMLc1DPjBLZF+Q+AQyYesDa+011OqlGKyyhBZ/bp/Y2LqAv2pnFGV1wUvFTiHNqK4bLTkbrLsMEgM4QEIdE8cOKieRrWa3IHvkvazWYOS5KJSsG4wngON6qczymRbk+qZDoxPjRhuGiWTU+pWr+CLeYCvAd6FBuHEVQ+Ud/2Vg6Gm34UFv+iziVv7rh0gatJviuFT+z3ObkT5FCa4+hGd9X+8RLNX0+ZhW0LVPjoO2HuXh9R4wS9QytkgemMRltXPqHPknsB0V+cNK1H0T+9EnbCfAD0PboUU= +msymvy559eplp0A9WYxlhEkUDQKt2oM0PcJr9UrhukvUwaO0ir5LZk9qkQoRJvntmtmMGvbhl3SGRqbkqzIULipLyIhgYZAfUwJMQrvpmzwzMWrbqBrnUVwHTuLX/o4GLfaW6D4Lpg5099eSuNHmMgod/1Y/FRiUmsKdSej7ZlNnrZuh/sDpYF6HbwYGN2g6IqO2JIzC7g7rqNmSyJy8gV3Tq2MZgR9whrnfeUlty7wpHeYQWM3OPF4z9rlR75bmgOn6ZzR+IDPVFbwQpQFO80hI3+N6xziiw9sW5oZwfqB0LLR9XlbEn0x2uTPzPibtk5iZ1uCgu19EK0jjXFE40Uq5bw6K6oaMP4N8m8VTOB6hSZs0EPPres25F9mdph+nFK8aebVFZpGCRYbfwbSnCse0hqKbpNRps3xv1mEQmMll0s5jRw+NUSpeqhbWItKJdmLRkQtOZU8gCEObk1AjmTU1Eg== +JxAky0pnkPK77h0oQWQ1VL7+YnXIJYY3FVGIyKVaC8kET/MbOua+81HnHxwLZkFoOslCel1N7uf1HajTL10WV1Y0Nj3ebLcbTHuvV9ANml16p5+/UIUMvzZIX8e3nFV9jkIxPXhPpKy7iEkrnQhv6aYpkohQGiWesEgQ7P9foZAA5QFsfIoHcOJgUhAqcJOtynmUWhDCYO2EQ0G+CQNwEv7KJwflOwML5ni7/pTeyhb8L1wP05nBnqTJ4sw= +JCZcsEtb1ZhAo9LUVH3hx8y87Hp3NSQ9f2sB8gOj3/RMTjYVcAlKQny+sncXdaT/qxoROgVYwedcudhyzfILwZtKz8L5IZBHNN6YhkodWflIcN90XL34PMOXNhdLDw4g/eas1QCOHicTteenVqH2RuMis5vl7cEXbrOsl2PEjuxRXu+aqP819U6idsgszjE/lRchTl1PJqcs+Tflsi+nKx5PlGJhW+RNacOYSJxn7CVukFXAB3A3GJ7Vf7duNWGuRBYSHTR/AE4aD6d94kzA4NmuERE8zyWDXsrZnLgKcJMtG+UXrrGOHtIDpAjhBSPluCPkHcedJqMM1ZU23VzWMP6HgXXPVjVvY1pSfpEqn8LRkP5FjgKGya0UFPoSkdvcUe0v5JRdHIdKUWXxGSKszewSMqQUWnyd+0smCxaw3inmWVUBhvHHVhb1NLwhyZl9O36eQEXtKx4NozewRl2A9tlTnuvp4GkFcwl3M9BlGN+BvYKvF65rbd9FutCscqft20eamPFDN5NZ6Yqi/YrZuHrrua16a7vWQjhoFnO82+Xp4jZWieEq9NMKwSAPSMbLAI1eLBwD+qKnE78YUkx7qwfUrQMz0b4z8Xs4uxNGU757hBxYehNSMU35Ux5q7kiqdopVeW3XMR9S+Lt1fC5rKwYjogrCo9zPUF4= +/w041f32SN61MwGt9NoLLZYbAXFT3vnIOw1QfdwZIyuI/7I9Z3o3dSkJ+veQ+rn72MC84faPwNDhX3KBq7B1pASiqMWq7uNRyhilEq4pORTjFuMBz10EYsgCv2awbPzcUpgHyMproSpV4WpanvM996EmZzQ4ee2e363ta2+H3rsUT+iIr4JfGUH9yp4K +RDXgEmdfcm255Xb6zU6wEgKFbgUvTbHxbGLqa6SoPMJ5Etd+T+wNTmbG9rVox3nLK6HiuMZlhWyhoXX0xclqvaif8ivW4v4rB5GJcZU/S09dMraM+30S3IDo4yAp2onJreR6QFSa00RDGj/4jTonNgnwehx1yqmhuEPdOYAzIPMFkpHM+oqMfIYcHwb8ANaJktMcQEuYJFqfIa7fbiKiAt++Ae1np6OLX8+x2RL59xmR+xTFs6ZmphIuAbAHT9PvPiZkNRPppniBH3wLDEpSOnJyIZHU5sVuRA== +wYEJqKoBpXkesyRcCswEEQBIvx/rYtklJb0uCtYq+oe8oFU9OQXYMqo7DlOZDjzMlUqZPWUdTrGGHiZ1IKi1jne1H5QgLh92w0+kMAH209LSrac9IPHGeLa+KgAw2EvFs6hj+v4SoHnbSfZHE+p/2toSFBGfmmV8P8nzFJcWZNktHg9NbEc872crlFYePO/ZJhCkMrUmHoLzZmaoSnJwUhABc7v5I4YKiKWuHPWeYLVCf68Q9XiXBKcIkY7of4FFnKZi/68dOXmCyIuEE2ViF/6GFzvmqdlmBKhCmmMhmHCA8Z6VmE3E8obx5e5tA0nylgi6w4DJVfj0d8AqlNnZRHIe9WTOkGPNWVqdCGFKyi5N2OROa4CvPRp9IHsrEzJTbIqiY47NPRxJ60rMTuZH/P5FPW2BGNb96KuxY4r77KcxGGm+ydMa1fytLwtQoUNNIXGxphpZ7qXF2hDbB0qmhnWi7+fHZh1/jXUBH6LnjQTI/Ni6m9iCZchES+gJmUcaSP7QJ0TiEe2JRRMkJQKMNw+EXp/FGIihieCxVpq2ToguDvMulQK8gDXRW1yIpUI0e2vfuFQVqieKP67WzHYd53Btmg1WSMxxs8PVH5wsdJfce8rFV6hmra9NVuuPjKyuHByqOBuLv6NzJsftRyOHHBfRtwfXWnQZT4n6NWvdI/c= +0r/4nxpohIKGVw+fjYKPbJ3IKDgH+hZYL22Ym9nrQupOaNkEDXhsJ0MLXqhrw2esOHVP7usPaA7QVLQMVKUPIkQzkY4ylxRYjp+VOQfdjOn8ZfbnkwuW0tokPMhckOfrU+jebBWKiYSgucmIeSjFG4K59kYoI1CPlyykP5bgUz2YjOP5tOMrMpz2b9P3eE8zm11K3Z3OKCsVTaz6mHg0zkKZcxNMxTvzouCDt12DHyuwzBDsQoIRmobAOS/HngcDxfXh5sgubS2WNEOnfHlFh93dP6oyMiMAjoUUuzAQlqMUKsPJV08ANxkIBwqmPwfMb/+XDCgdLmr+5P4C6g6LsXVXK5uYduhLKlmte/ziE2spgEtXLnGRmYyXlDl1z3EMX1zhIvX+eNAHYiOyAbBppd9G3/K0MN9k7AaahpndnzDoyg/ykNMacrBkiZLofmXd3YzMA4XmyDLlJBaYR+yDv04J6l9f/Y6coj5n1iefeOOiFklIG7TNBddxxxM= +jeMxBT/R5Ny7NbNIZoLryQUDuG9r4cDWGUCYKodzcB9dfkDfc2mi4qzivZLCymkAD+M+IYI2k5gVCBo315C29ROXiL1uLVcjgrDLRNjbtH+IHmXfcH25NxOv9B34xp1yCu3aWtX8XMPEb752351BCEpASRqYPvdeHkAvBxPLl6usmMefBhwxjQ== +B+bCLn0AyxrexFzPu4tD7V/kZPURPTxNUt0NLTj9V7J276iFt272J8H7aAKiNMUuPkeTL9mgLfmmGrUX2Kb3Gceml7JEwxW7OVYv2m/naabGynnlFADdpIkWRUfO4GY/oxA7GNvSpJcgeg/bBOO4pJzLjqihZW5V32CevyDHevHXYGmRiwNxfv2p6YECcXswX2Pb/minLrBkjogQ7H7JbncxRCht+TNq1CiF1lX7YZe9wGT3yUuBdIKJ6Bu9USV9/acVOPVW6PIiBskOft56wMOZh2vi09VqjoQSH+XcRJ3y1VJjVi1yWnztPZQPiB3EAzjKUDbsCDbwMKlxRrr8uKkGUuZ/kvnRXVhnIQ== +Vadk/ZSgriYG3qCtpY2DFzip53hTOsw20oZYlcNxRKeyo71NBpSuyMftw+l3XZ0kiDTF/g2Tf8bMZuHsWKHv0w3O0isQbEqO75CsvowK0wf+NMf5gRUwzfjNDmSBKoBA+npeqrXCFVQbWeUQH/zA1tW3LBiNJ6WaiTUkqf3CwTAu2HhL736lO+6Dbbh8QHbO8Cr0Xo6D26s4Bq4r5ZFsorSffVG01xbiG+mgqOpVLnYo3mCTHpwMwzf4QFoh7qpo7ZXPXg4tLXnd94RrWP81uZu1iS0rKo4Xxbn4l99xDmzPetrez2mWKkokDJcurJd9gG16V+FmEBTnVCJBcMyOr/DQ7LhVQr9bFWnFouPxBi4CCGFw7H+zQMnZiHIiBfSIzglSxuH11E0qNW6kuJB6xqLZQJ+HuYWyQcXy6ZFwV5Tnlk+cDO9Okblr+4A3khnPk7W75OzB0qX5rVeS5gUG4wlQTxLG7azVINuQ+N/2ztWrvWI/j7HWoryoMz6ThHjccsv4F3AAwYs7utklhGko4E5SGViyQkrFbPmNCPvqr1wtBHtUjhE= +7pujXeDplvFZ36D+MM60UZgWfi/WmbWmjzv0txCESyHTIpI1igqDAe07VWZfUORAC7RdhJIRfjD8+n6t4vP7FOV+YLfKnoHU77bvYVUtEsEAC3KlgsFd2VNXRrqpI2L4qBNGhaRQLVSpGTJQL8jc2fY1ARVMQaRFaUPlIE4J82PkwtyUiCb/aistOwWFsedDSkwjqiRWaYf76Phi0liJ+9wYGJTBp+/BFke48NG0uK+Lunwfn95g/JotneIeMwUW6mPTUs8op1PZVrvY0r5BWne1p9m3M4gfX/fJVSPs5+vtKUt9K9/IJur7s6eDIJ2E/lHzUXB8JtnMsPqCwldO7g+FJ1j2HbzOfyQFuROWf0TpyNG4qHeUzm6X18v2PPS8aO4= +4wpFoiowrMZBZrXMOJxbijWUO2AstVInBtBPkCz42lfm+/TB3iZ/EjipIfKSaF+5ZF9pO3KDQ6oiuU0y+3zMPG1/3hMLL8WMVmIbYgD+6wB1e1U7tPkqWBl6nweTmUk1Nb7nXMBbj3adjuzF+XWqgXoSp+vg+H9+sI8dGXPgKtt+CeNVrouP9OxOmVzZblibaCXor3rfHzqARmN3ri/a28+BmmmTd07kI5RyF2rRX14+1pD5XP6z2tIhOE8Q5veJKiF5 +6LBo41Vd+dg+mhdN21H/y+1FZZ3FCIuAVXzW5SavdVhGHVTqf/SYZ2lNNNgG7vAQWKxPVT+7iRUI4HKreB6v/w5tKQ9WwK6aHnnQGxnr4kBB0z5mLxhFM3RFNuJv1D7HxwTr7JwWdiyd1ZtZ5Fzt2XJYKKuZoHL8PrU0a9NWuljB394dg5U3yQ99t8EzvpwoDW/6u+/Mt94PC0IQuuP1WW74W2iek1jAqryx64yzLOOwKBgJPKcY1+bAc5THd24Y/JWvAeEIvvbH/SOajWbKEyT0M3GTMe4mhA1/S1C08tWsIHlxJ4cDfURIe0KCIUPR/efamlMP/e9v7oqm+zL7E2LuE5zMwABfz+FlyKN9p1C6JVC+tv8VUg== +fNRLQsM7fviCeG3SDzMghX5bBn/HYsHoGi9WJ5nQcaEZxSxnEebgycc8mxGIbmp3kd6UFgtRebjeQyLy1+TjtQDvF08zJKv7xvcW+BtmbYIxyU8eRT35n1Y5ErHjgiIKo2JT0p9HkAhVyZJWM5BwDRtZHfQ8XDthj4aXErZWYPNs8bfzDSin/zxRs7Jt2BEfSkl3wIo40S7PHMxp+pVUnFLbwofCQ6sGU7ZctcipulOnoDY+SZhgKuddBIOynXFbwlI0xsADxZoeLJwX6szCKFegGLvn0tr9Mtw6Wze0FppvbqxOYsZoBl2HvaPonMUyHVWStw== +Hxza1FbiBO9TKO5qBcMR4RK+nhdI9sAaDKShWFBXv1xTTIq0fZh1WIektiLjKqtDgicznLo6DXcKw7AWBSjqj56W56sDNJbY7ZrrsOhErS3oSUQFja6I96zqYchMZYQqsq9oa2S5HNS/HnsJk85QKCQvb6Z4O72Gu2AnBIfYcYDlsROlAK20l3Kt0H9k9RbGLfma/1+/jvMPtuLr8Uyto8bDfuDyTvh2cnjuHbJ+2jTWo2CigWj4d0DBuaGMVYRaqhdGkC8lus5qLadgV2bKt7tUMpYnp3U8KkJYXTrv9Tyq +guJnfwo2qKl5xdGFzIr2wr6vXEhfa9HHA5t4R759m4SzwzRDoI8THYGzOrPcsfUrDGAmLOdLBzgJ2TxWmtpzabeeAa77TjMkCzqr7cQqE1rdhsgfFOR4lBd+fLQnN6pRLlQ9a+SV1UpEL4n50w5iO8SyhFhVqoVg4EnaLwFiD1zp71eOrZa9hIEeKRP2FieJfWBpDRizcEqs5e2lfnyROsuJ9/DCu/MbrJlrDMEPjwICsJ+3+PhieD8QvJijj3lZrvT/bQwaH4NHW6p+57AUtdkYumntG8gOmeDVDcDPpGy/kNR/nbSBp5HHmmybXiHbJoiX1RM5elwbmooiNw== +Ir3e5WjCJPT1KEM/ifZjWV7F8JA4ZHc/R0uFp9Tu9nbb0XZwis64A5UFbtWFftTNOVgx+SX9dVZIaE0iXRsZdZrfVCn7/WlfbT311ubZu1QknBRX1uMfxZA03n8uwzrqG5X4Qc2KIji+3COovf82Vwfb+hqhODjsrxYmxX0R84x+tOqje5ZTGV/+3omttDjkYtO4jamhM8CFhg/sKh8CicL0gp1ViOxfrzv8o5y++WCtdJRbWIrFUfjmScpUmqhlocG3T6NXJfGcDij53RPYrWW1fQPZioTa6oz4HaKcqIB0Q2RTGS6TxD6m9q0pjiIzVPcByxluEtwZfbwvpVl8xXo+jBIG8AenhqmGHQ+fSlWKd3+mz1xPukZcmpLj+zQCJDodYc8lu96La0diP4ETyYxYGZoDmyZtNwY+dgdIf2jMha/NLcBQ/t4TXZo8byBjObPhHaSCV64LYRkSuotmtmN/vCStSBaVfjMemPhbb8HjRcDilvvaOmfZ4LCYNzOLlbvctfjnHhrbTipjyaFOYR4jnrDSNUmS9RbmzdPSAGXkcXjLZNCfrnLVqxdTRiulwwNJpAHs2c+2WNTM95l5wTS7f+I+kiIVfAsjlsgt4jqomZFwI9pf9rAOOtBPcUruTy+TEGXCAos= +aGWYGdDrao6CKxS0weFdaBWGIXmJtwDbPTUrp4uz1hvcIZOC//zawj3Hkt+fPI65Mcywlkzjc0jmcSddn1YiFpWQ/AYZmS32vusXLHxHQXsw9DijDETzNEOEMdMFHyxBpcFeQUwJEs0FCB07WxTSg0PCcXbuxMy9Wvz381AGMZwp/dAwT0I6m1RLCwHOs8lwATvf4JwPiVDF3buyrT8nuFLNOlImOrbebarICqkI6Nmj6bl1MY01bK47i8pZCb736RcBsF8rQ+ZhtMs8VilAR+92bnfti8jSl9ZGKvdDreHE34FPVT/6N3aXIapLy9KEUCw4aiMMP7VzM8e+QVDGFKIh4YTyLeohl+fw3x5CNcefy1bF8dPZV5as2u+px1NDCEfGqW9AGgXcoO83woDMKidEVVIuBwGg7QRdZ0V2PMt+wFq9nE142J3oWrhasgD+S8byryIZXCYR+CGnsCV4YcgwLWkmFjBXlQjb9TTcAHNpsxxtfEaHLKFFAdNCtkFQkL5x4lmX5XCjLPBwZgqSCmrqimEGT/cYdbW1SykDms/7BAs96cRfXoZSTAWEimVuf/Voj6DbhNgdodyJ6BaW6bARAJg9MxQCQgWo2AS2dBOUi6Y/9QkkxDV4vcGyJe+A2RW+uw== +MSrkgEuWTbPJ6zP1+FK5eKxAbgS3Q8cm317dhR+zinDEVHmSU8h820EXmJ7emR8fsUXr9e63vfSCQV4Nf83sJCAcDHpfU7F8TuPFw9DY81k8yg9YM6gA1kcMrttCQo5e/h/siWxrbzTTWOsCUUu+1ObyIfecluJ4kP8cKUxCENVrokYLGxF4N2eoN8yr0V9wtGiAATDRoLFpo4M0j/A= +NH4fOI1OR1v59Uj3fSJKfAkui72/2BEir9ibR1bfR6fxey+UJgYwBPBahg+jChKWbC5FsZ3QSQR1cizmm95FuJLmaN5aB7DT9pQ2RvcxEEwg2AT+BQo2WUTHIZ5EkgK5CgCa/A+mB7nOBuHpiTHeN2E8ZYyGHRq/XRNrrbcIDU6CNf0aD2nd3FA9+T+dFZLz6ir8e9gmQi+qwQ+fY6GyWjHvwUid0U01qsmhWLPhk8fysdMBHii5GMc03DQac5DlrmGORJXxasRLonOsS3YRefPJB8wNbbQ452xsX09NC3VItWYDrDe2uO+a1w7LTZLCsq4TTFdgQscl5OnQ19QUQtgvd2Yw+B4rRFpefSmbHeUDYkJ46WaiU6dLIp2GQNDSm7VRruE03ta7b6c8sklx9NHlzYaANOf3Lg== +YnxTNZOAjUwGGlwf8VXw41HMkUjoMeiRIBsHH0hFhYWVElg1tFy5/rJCmNtpaDzfbzWByWIU/CnGa07vqYNJUDXVVjm9+x+q8qTq/Ey83OMMIC/L0YF5D3/WiajloJ7/fQga23KohEi95ipAZrlKADFSz43ilIgyB2wo1kh5POloR1+lMHbZFCVOVwiS1yeo++5jVjCe4Rn1+HzSyNxfp/sqmfwpwI0pje5fDPv0Tok4Fy8dpb/VqlXyroR30xpnUIE1IfaCdQm2BWbM3zKG7QVg5JJgqR8WX+yGKKi3jQj1m8rf +JaaRRRUtQz/XVzQEk/b6wgv1dNzyhYhVfXOBCSy5QEAk1dgqL/5OErh91I1TpHYOFtOGtGDONDfXnBusRcDy7xy8gm/2sVdnoX9hcAc5FyG1kzQir2FBHI/A9y/b+8Jnv/b39c1AvQxG4S+/ou9dxd89KBWyJ/X2qThalW7brfov6QyaymhzIz2E9B3DFIohlRoeNs5Q/RrPEk3XPYgdIDkmmj7Q4mI4dpT/GKHPV7n/imIbn36dSXwSC3KPO7Vw+s6BQGSpBkm6q3gUUiiEjjYZ9yGbRVfgcAK2w5QBQQBjfZLYuM3JTrKEMi3mr8TVKFbuayZ8lc7l9NumX3RElS0q8+BpYpW08NyAtdZhhsUu6maVmN7RuxDbR+Fn550zoZNhh3D0MFZ/6qaxSToiA7p0wIK8eLLuXQml1S990H/YqetkzhLFanf/SN2v9dYjtJt54EVzsmb5DWn+c7Vkfc91lH9mbDfuE8ZuwiuCK8D48zLIsp+2sh5xi7R35uos7wVku5H9qQLUnpZNXKg0s3kQbBgvXij+oNmLv+EsEXFs2WEm58vHzR6hTXDzXw== +34ZmaeDiWg7ZRQlFKK5GaeMUUgC+uLFNRuW/3FOgT84YvWq7jziHnjZhQBDduOcw8P2aeBG+wkaJ2oZ8gtgRc54YagQ3mgDOKDF/rplX8s7RmpuIUIa3iheXBJuy5o95uqixYYMkK1/PhC6Dl5AvX+e2aSZHCPL+mV1HqoLfJHfrb2kA1ce57AUhnpyOXOvn4dG3n7z8kAqUzgr+zODjIzLhuGcLZOpvRXfvB3GGDJc= +ZcakvlkL0Jwp9ToBI5Oa9Y2l5G1frcRFEtnvg5oUt9mLXL9WlPKdfJgHwwhv3Ij4ZT+tSiAMrcxQ1Qsmb5etRpE1uiINJx3Z9qDJJ1UmqfYveyuwEGvNQ+Ih3QK7oJu843lwXDc/w9GeqFd7xaUqTJ1h8t+FFCp8jC425kuLHQ== +6bWQzOtg9wjFWe9xfRpnQYPs6dcol500reqM/UPJq11EzMMXfkF5v88uOtDGw4NluDUdlk2AzswNbJzI+RsJZul1zQn9rYlUMWs8wwb9Y4sdXrx7wdEivGyB3GLnvWVFsbQLNyojRcJSzp5zrRh+20G3x1WN2y1BWpgxiSkBg92+z+6YqfzJnQ== +3Hyo9dYQezKq650s3x/B1JV9+B/XkMhNTdV64ayIArVQSvPxJilW8+kFUL0ErGYh/XR/kdAP1HB132wTi2UC1Cs533iFFXUOWtn9nhvwfnkJPXP7CWFiIsMihii5BwJa6bqwZJOqeddDcHYHuNf/8Bhm1V6KbBNL8NSnsYbsn35OfCWiYualyjinqRgYfNsmAZ3QM5bfWCbp4A2f+Lh8Kl6DxJrneAyUTm0m0l0EezrGuv7obZRRhFNl4yvbQJYH8Ms2byeYTEMKI8qVknz/hCyc7H3/ipCEDqUse+LTiIJh8SSMkMxrDZO5GIgNLq2TsER0qAsGKx4x4qhzWkBQY/BvHgsI+ExUo+y1dpT2I/Ncz1kfOEKuJBWlC0TzGwNhLucQdiZaNDO9M5So3hK7Mv6trGeWSWMX0vI1JA25BDwd87pRINz2SyHfQSni9KqvSK97wxhmcnHL7r7MJ3Au3GgHtgPsQy0eAODgDHGLV6xBYU6zeLJdya1pexnRj3n5OuOEkAu9FKF2vk4aqIxwKaYq4IkPQVFhfOBjb7WWl8U9DB+pkEx3Zr857nq0NDD5o8OwlCFp2JcaYWBLMSsLGDTUm5DMizfyMRue114SY6xwddNdFKpWvMzniIsFSTmlvG9SUTNT +tnS0UWj1P6auU6GNfRyu7tBLlj/thS3vtaabDNHuv0EwgILdg/ifVjh5HTqF2Os3ospzwz3uhOXie0jANaQE+6rUimcOJpfCfjrb+n5gXk7Yg/ykxEJRuV2Y2lUT6LQ0I3jd+nxMxxKlNlmrez3mu1VGM5pbMRPiM3Fu7AjozzveNsXOrG07ZvzeYqW+4rm4joiYU8SepR4cd7Y2Fr08OxWbJK9IvZuw/tCbx/oCe0w9bIyeri1f1EwV2VFa+hvwTDmDFBjn28WVnV1HJGhovfhdoIXtZvD5zVrisZ3gc/rSokJZwijjKSRwX85WtyvdMpZVzXHjed/Tu0KMNM2+SOCthaaKubgS1BGjXlKcwLQhOcllZRhijqh82Ve1VdUZ3npA/4EMniqrzj99DfMA/gJhzTMLJmsKiTYJxASPsxZRtPj+pla+fN/DZirYXEf/Srjr188/fxQV/mLPNROXDOiVra2G8r4r+gFEH5rAsjw4CdnKBx7A572zT1K8Kgm3NS54GMUL4lk3GSjbok1/f0xZ1HPuDFFvtuJUn4CpNCeEaUn7KB4iw6uJd+6JVAm+Jft2mVT7WXT311ypMwggRFxZYSCGysGitYTtFq+v4lZALGPIfC7OP7hIkTKNHkvkIjns4z1y3DkporCjyv6HICOrehW79XI/8CBAgc4VG/1lvVNhkjF8 +ze2ooQZtMOFlz+DZQ/OOHWXxTiMlEbBxSBqxzoico1yQ0KpSRFlIkCKriMKig/TOtoG0OF7/k/OXUf53r9jU9Axl72Xlce/1TKCZ4n2csCHw+4/DbCDBfwM8YOdigJqUy6pVhKrCLShrA00S/dYCPyRBOIVHvJD2994rvgPaiqTTEe+hNTYyWzk2tlRt+A2oht3q456J+0mjbROnpOJHOvXxY42MBT2Nqcf0h5OVxi2NGHlV30e9wp6nAgOonvq4X1yWYUGXnIrQFFYYOUGhWJupjNsWCNPls98krNdP0yn41B4jqlRKyjHp5hBy03rZrJDw/Biq2mlUUZl1b+P61v5gCzyn6Kqo9qPoDgk1PGJ/iXLTkQDGzSwzVsKcSYeTaoZ1OIl23G6FVL6tsE2/htSiw5lcNngCYgm41PEI9EKXm1xIh0A1wv9lws1dbm0IJHry +stiyr/C7OzhUsIkXsAhSFEc6TuQiQIRNYeMfJWCS58CzEeiSr+fl4tmZSAtk13JT/hM1ShFw/KWuArFfNk954sCeanR+/91vFW/YEls5JqqqKzkTgcsn79ZU4TRfEIFeKexzOxOYrRJrGOaC9Fo8WtX+zdPLHIvbQ1XeWWbujGOy7IuR7YiuqMlhjDfGJL8weX8XOmPKNAMXNRfmhZaH6W4R0KfwQSt5WAYs72hXKERz34oPMAKUMFI60AL5zAoGawmSkCHkqBqlgFBg+I5V/AtMKgtU5NMHnizmhF4FocK4nNa1CqjXAL8wAg== diff --git a/test/hotspot/jtreg/compiler/intrinsics/base64/longLineHEX.txt b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineHEX.txt new file mode 100644 index 0000000000000..9df466c13459e --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineHEX.txt @@ -0,0 +1,100 @@ +000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff +93aaf803ccadb3313a2b42ea2572df621d7555537d6e60e8666ba78d609014b12138e8a945ca45f790d6f1139a8e2b66df4ebcfa43e06b7a9df63b4d669b46437affa579b543b1810947279e9c99969133ab0877e1564480038ddcd22a0081085908f5fc008bb551bd3b03286f735bbc4691c29f7e635c27144ca70dfe1cd9914ab0b81f131dbf3ef3281f5007d2bb16655196a68f722d1ffc6c6dc8c38468c71ce3ece956e3cdbc65a6a5b399 +581ae9ec9868e857c1c8216f5b4ee91ea7d6edc5de88f9203307ad682364a5e31d90001ef159eaaa5a81230a9bd52e1f875e454de834ad58d2c371459c5c10073410c4a65f856362d7aecc6b7410a56be275d14d586f41b83d79c0d65761b46f89972b3a1627892f39d97a4d659083d2b1e42573f11da1874887241feaeb08695aa147a780649c13db867a5dc2472cb2933d21b082f2ba0b86f1e8cf007a3cae514763a95992b0c5d828da6d44d60c721f2b5d9b24bcc87c30a2158d5eec6300b1f070f3a69e1c4817c73400250caf6e859a363e93088670028fa8d9a94d216e0a0145e40610084d8dc8e29b281ca4b0fe1bead7199bbf497c486ff95f945cb7fe223972ed1ea8d1256336752c149b1a7e174906a946cb2811712c7f98ec734f917b8dc12c5e9583dc3f1debb505566a454a7ff584966cdbcaa564d6fae27984848c31f06091d05c7fb7b94ff165d39eaa0a6be50ad79252cf3a6169731ff94346015a93b906eb0ba44c7280c1333f3bc4d314b51ff5861060 +f1d72c07ba04e5186961a313cd9444bbfddb354531f85099b1d9acb6f420b8ca52b736e8084c9605b28eca2ea9b6873dd0f61160a745746fbc3acac9600c6111d66e7809075d44ee8e5ff519928f407396aec0764733c1e9ad94f7307e507747bf9a5949877ab2e1466646a631069178120b56af68430ccfb6b9f787ecd34b39cd85a9a6e059a45d53f2589c296f3c9ff76bfca15c8e8c99214ecc9bd9df0655fe3341464b2a99e27334b02e9af52553348a6a4287d6cd30bf704ae6051caac90c77e7794dc68f03128e138a48477fab3bd0d214adeeeb7cedd5cb56b205069d14a5ec2c9c81558e88983909ebfe241db676cf0c17119938abd57ffaf369172dfd4a035f0d13f032ad575c6f25499de035ffcbafeb8b79914ae5d701e164558a0d78df10092f7e32451c1ec8935e57744d756ae9574d3f0b31099191365d6e4d382a021c032f39a0b7a3566193237fd3b408691cfade1f3735b2dcfdcf84ec4a9d2e529c776bf8a3a5d233521e9f16d0ca7481ec11300fa5058c1ecf8d514aadc026ea3ce9a8c345d0bb402c55aba91617b7c440f0 +4aab2bc2d42a2085f50bf61da0096f31f849d93cc1352b5e3758489cf8fc3900ad6f272d0e0a392abaca8124d43a3c07708c4ee1f0c2b8c4c92712976df4592957bd15fb0bdb98182483e88cf16cafbf436b936a962eaa4d31f09772d19fcd321e1e753e73e789733fbe032334e36d718ab81e8d82e367837aa4b94484f5ad3a48c87df5a71c41b553a7380d2935f757b3b2845e5bfc4a7d868ae547e416f5331bc48403b7b49faf021d6fac68fc4f82e68ae6f35209879753b7d0f01a1a19d4b2ed04906c28d5115ab23e1413f61a39f1f222af2c6337e572b5ade2e1ab607ca42dae69f90877f14e17c4968724ca70452e6ac52f9a08ee2bc8542eccc8796af48814c2bf2666d32564bfd6 +644de67d36a86969811024cd6ddf678a4be4ddd17368f5b69c30b24bda53fa6bfac364f65e3133d34d439ca86e4200a2a6958ca1ebd1aaee29428c157f085f909eb499ed045d31d0541a8b3a2e4d8433565f0cdedcd3f6af6991e9f32e7c6d017e521bb5ce642f92dbcd00029cab1b2b93a78b37534ee6b6671beb8f8a4bee5fb437086de818a7cd89bf628b4354dc7eb5d67485ee91fc0d04d35c515aaa80d5bc2fc5cfd4f4ef56e98c2d1216f677dad7166e0c23012004ba7037ded2399076628963823afd0914893d6f6e54cbad572dd04187c6d881c438a43ef6408b72dfa86121814dfcb0d435178c961f7117a499741b835d76e4ff58340249a7ddfa931f7e910a2a871174c4a3bb58382bc133f5fcb284b7c7e63ee774feb3be8c3292482cdd9bcb66b6f90344c7bf671d13dd96c0cc7b5640b04bd2b2206adfb7a0b05a99af7e02c71fec3b0697a607fc908c8909a40619f39d3b22f862945398952716433460f7cc90aac803ecf640b4e0570ee6b500b330498e52bbddcc961816e902d9cc74b5e9920152f8e711687fb1a2c0cd61d869439ff785f0db16f90ed12aa734cba73e9d9cb31777d2ea0af8e1ba027bfa3d9ec44a35100474bf2fbea3af46c7ea5ea4f97453d642a44f209b82701d9daf939ffc1f92d9351da54701364c881cd03fb581c3fdcd4ac6d5e63f29299ebdd6b040def87b18d6c6768fae2133a0cd3c8da5e154 +4fd6a3ffe60611f366f21d71c21879b4e9f8f251d5af2b6a6d90598c47f884057c476906a33b8b17f7d611947ab553ad3df14f5da450121213baa4724db26bf3870b9cd571b147a07843945fc8d86770a15054c8d73f67dd8bd2026b16422e8e1b2edcadf308e46af755a79ad8096baf5eb15bb523c8ff46b19c6e1f7a2402c85a1272fdc87655255a2a214ea798726d6ffcc19301bc6c08433cca532a80689beecf21e1d7769fd996f3bbd7d197f0c3d97c80698695104e62c8540816590fd1bfb0b4ccd2fe596592be07c1b1e3a647d56ed288a44e3d2403f334b65717f0a1b45fc9eb52958a824d8fc3f2 +ca048b51624f2c4f6ae6a1e42db70526e318b3192f68a329f14db6b8350ca22dc17f395011118a12b95f4f6dd3a0a26056a0fb140fd2cf577fd4d0ca6ef89d01b14f51a10a51cf8749b65514df21930c0459efbe9d121a28602e5c4ec407542c5324fe6dd00f813b150fd7b69ff0a36a54aca348e5d314fb646393e7bb013f21304e8c20d535dd7ee0a3146cee015413332336c73ef46b396e6849d0bbad01a4a0d1f929ddbc9091cbbdc7527360e399192a250ad25fd1e5d7565dd745e7e7e0 +eba517485070cc205840ce6babd236e17efb1a459c196a3e801a0ae7a22bc7a88f9a8ab9fa9121d0c5b8d06fab8b01fdced5c008adf1fd55d023a37e3d8242a949fa82b149509121e36884c194812a780e4fb8cba4db3b1bfd52ac32c5819cbb2001807b77ccfe1e6cd905a86168c74ef343e9967ec05f140918dabe9045c6e7da2709111847f631cff3853fd2fba2a031f04707bcc4aee996d9207ebfeed6093ae5fb56c0f5d7751a436d5a8cc79132d7f2c63741b3860e8fc882 +1a645b6baf8a3f4135bdeb3c7c0af9e6da89dcc568a2e2c2eeac97cb98ba9acad3eaee79f1bb4f64995fdd4ed07dce528c9927c9204b4b737e70c349d77b5554a9ff98090e051c5b1690295cbeca007c7ede68d7f0c2201627dc2d0e8f1d718be90a79b76d16c8a7927ae31fe95320a235d46093a0d549ef781ae86805cb2c80d35638dc3f09ba5fdc3074bcb3aeb2db2a1c2c4ead1e665afaf4da593989b051ded6c38cdd548c57e08abace0edcae8e2d16481152a9da0814da3c6ff487b99a603bce3da2d5511c54f416b0df3e677c6712b36e46066d64a9154c14a45deda312d5a56ea87ab56470a1d87474cb7cbb8a8288aba5270ca601a5a7d486cf78defbc0bff50202745721b6a41286fc384b92c1b1c88da616fa6a4bc415a48f591f37e19e56de62ae423fd7325154fbec9fb2e512d0f3e9d15b1d4726c82770e924202a7ab222428e313facfedb468fc6b5df1bcc031b996d8aeb37a78b74a91ad85db62dcfed1ad0ebf0c6600ea83d9e30781502594dff27b11bae79c39e81fae44a24f1b495699bd1ae818dfef2333cf69703a5d635bcff231df54c29a608dbf945c96450908c3a2aa141fc31236499d37b95ce31785c +c86a73174e1242c6715f2c50c8ceb1284971adc288ab6796a796911f55f2475dab374a12924b569fa0e850c6c90664f521044b42e16b94ae8dc172aa333191952b5b9e570f5737827c6249f87872408bbcbd10dbf43cd722920792e86d9722ef56fe09aa015d4adbff4d9d5f787e334711b458f1d895cc1b6e9b683250b0c1ffe687b8e8167988b5d25ff8b17de9ec66c976cde2ccb38b47be664a43ab3c77ff6049bea93c6aa3c6bc92a717fed0d65660c7be6aa7deb97046be424ef267d3b7e0750d7d63849cab589a612c7434415b30e05de8843a5488bdba720d3fb0890000c8995db57e67ed8e3ae04eb2691905e0ad711128a7bcb390a57c629f07ed5b6f8137beb98df82a51fcc5b0d0fbc60eb997ee8b913579c73e177f053d97cc24a20e94d4fd4e600920c147b630829f085a3f00f043ab3ec5a32d21b0dbc324df3203eb77a2035a62d6c2331c0fe483615cc7fcb7c899a7ccdb5c1e5a23d2351e5dcfdd366ca2d9152d63b567348f074a4f17c791e28038051f72f1004dd0747b28b2fd0dbe3117299a6e74192c1ec7eca7a125a0f78718082a2df96c9a1cd534ac70824e0efc9a9bdf2f7139a4f34f82052870232e490437a1f4be8e2228f6d7044eac284f0ebc69e9ef4197513c29bf690e4807c32707980737b4d97beda88c63c7565ecc88295271530360edf37b16ca93a6db0eb868a3adeb122db08c +77ca852da60043e4d7e417b27fce41d15893abdebac1c15a8a2ba933c412a1afb4e8211e2ed94341eb0cad3f3d8de5792e80be1291db44e57ee1ed0ab058c875689f186ecef67a5de60442f2b148f5ffe666fc8e54d1198e65f5f7195b9ed34572aa8cf85e5b639628e8969f7aeea6491c5fa7929f64a3408f +cb7d94ac328b2de84226bb16e47efbd648293b2f75cbbfd5e20b3bbb08e8e57bd587a31b07b1cfd65c06f25b07cd2fa9f0cc66734a5eecfe7fb5092286580c8aac5be8ce712fe69c5f1c119505cbe5feabb8cf70072e212f522de20955df07db76237f3e86fe6352ee5528eb1dbd2100797686d00e474a9e7f70d4b59428c9de6f4c73197ae1c5dbd1afeabdc0cd8b7207a07d6a2f452b054d474c23d3ca4ac80c53f6bd8110e0f1b20882938cebec89545e739a8e939aabfa1daca4a21e913646962000d5bdb3ca28a007009c31e5febb39ac1ffad91c5211c5c6aab8cf345b82c2bdd278f8e1ed72c11dffa2445eeddab0ee1d31f65389bbaec2512ca3d52e983ba2 +1f6f3ea5861b413d56282ffcdd244ea89723cce39700ec12380c1521802102f9d78bf59cd2483be4b8217e3ea78d3c6ce0ad9e96942bed3814e886d71c5a5ffcce69c81948013a4ea2005c5e637960e826cf6cfb425ce97f600a0c6aa5276ffbdb35eb67207a1835257d1996eadebcb9f726327636690a6d9af841a638d199f12e58f1dbe8724bd43f03cde196379a81cb447e3fd99ef4e02f674bd906d3752db3105d054f6bf88260799ffaf069c0e176e36f974c039e5093feb0a0a120d4c9a9d9427f2a93b53e3a944298bffc6d05012a17dabd871648f2c39e36edecd4f2e43a80ed47d475ae6d142e77f71da44877a0b8faca97b290e9d930cf35887d85476d1b907a9a131a7a8d10a111ef386b292c54e35dab238fdced2a76d8c56482e9672c1167bc15d58b29f07f38a728e5daba173a8a38f66151e6b1ed206cd8d36fb3e2e39818089e8ddd962bdff5c2c162ff1d0af18fff25e3517612b53b1be916b92ad44a5e157719afa58c37400a983ed110d21d2f24f9460de05234f422afecd667377f80316faf63efb30323f21e96a71c796afd1e34d8a9ac9262f85a4fbbd5c341d0567de5c23680ff50c3d7c02eafc2a5e4ee9a4c470f872989eefd4c2b489b0284ef52b5140be8848f6bc6dbceff +fdae9f45f9c2e21f5c6bf76e768738878462096dece89f373c89f90b08966c46c7664f4006491533da5fee1730c2b77391c63bcc5e782f3574be928cfdc678eb710bda845e795010c6083d5e8426cb5c9d7a4cafc04e22560b9c2d678175d84480e409a4c67b7a18bb35b0139b313d1a58cc3647aedf30e2e61d5f5df3bc244460ece580d67d18b0625be4f2fe29bacb5d685288270282d83946ff87efcf18d69934695d9c39d9360030baae2ce0dc5990713aa807ab550c08ff0ccf7f86f9a1efd524ea7b15e26592ee57fb1125888c995bd0c7e48792f775d76fbb63dd939eded34eaabf4b5cbbe9b0392d +692e480a07f43cf7ddeace5224f8f4f1e0570a0392c14863a7bd505073d1d915156551a3a26a146441ed41710037230785132ae73c63548e5a48aa32372b019a125f3645be6a2c84a39b7f45af988d9501cb75abb281ec4452560e45c126474ffdc3d1ff365b83a51cf6ee3ee0540237052b89217e92cacab1ccbec17fb7afbf955cd9ca4a442ab3 +0f1e6d50a8866a6f1783c43ee63c46c85fbf1310bc61f1ce2b195f815e6366791373f052dc58d1ecdd2b1517c6870c30098d99e1d4fca32150b6bfcb5b665882cd0a9cb19cac130c777be2aa7f2f2469404be67cd1ed65845414d662d75093562e56787d10beca467efdeb03dcae868e67ee04eb2851039a51d1c3f179ba1c344ff56d2a53a60b6c25857510880c879d01e143c1fbe2c88d745a4f743d7de9bc6e42c9a1a9780013dfc33716e2eaffcb1c +06442966101e5c98fe626c1acf80691affa6072c19681d2b35a5bf6da06e7bc21937ea32b367db384994c34de865385e32266cd04ff8abb9fc928f9407f2fc200bc67f2acbc7880c7f654749ec4fe0affadd7d8301ec260ec46e807d0163c0420c1267d33d86c202c9a7421efda868de00e9fbb2e1301368f6fefa0b7c0c86e9d5e0a7e9aeaa7a35d1c2f1c56b31489acab67930791014eb99381c7abc9ace0909e499023b74689a93a36059eba4d3d75ecce87ec5c0b2cb203b955a3b6030baae34d0b68e1d19c5d0eaecf6c7ac23671e006cf55e614fce24ce7674bf21789f135581d3c8345d42a4d2fbc479aa005771830124c0e7aea37adb671725ecf74ed5700015bc398a0cc4605da44bc73f7aab586806227d9996386dd64e699196295223c2f204e323a1e357d4135be528b6bca3e77a61d3231914b48382541d2837307474e0699a5d20b65e57a39fcf5084265d508385df0eadb27e08fe6deb717162649cb11c9df91521fa74b55d48742bb8eedcd75490c49852bec3e697ca720836a942704b61064a07b965c49265d037891c4b3084f273046089b40ee7ad6f29e401304c879bf63e462957e9d2b675a1bde6d6eaf53fc3e87220c23b2f7031b424a736077f7b95b24909520322c71a3a8ebb706b8f66af54246df458bc3004704b720274919a5dc9d14058093c6c +c91895cfd12582f38e598d1103cbabf936690afb78ad1ca54c541130fecf5dcb606e6ec069fabf9c9363d5320f3141da96efc3b816157b317c8c11571d36abb17270a155735833336493d7b86bbef167745799750b68899706a061a69e1d7d5a18e6a14ea9b42905b6a7ef3cf5cafd442056cc1f6489240c8580a522ada2a4131a63985f17f1570b77a2c7e5af828a2d630f9e67acd814042fd940760a677dfa246e66b7b1358cecac8864a5e463e7e50bcab1671a3da43403344aa1ce16a336c3677d5c3dce4a9a267e31efb46dce5f6bb487504e95627328bad148dae4f7b48dbe24cf01b4d81298d18a53888c48a45523a2a80ede96f0d41cd23d738174dd0ae69783b71c39b47a5f4c89efaf99ff0ddac69296d1e94e512079b081fa57c7d68d8796509da1ba4feadf656323c3d5b9dad8a4ee3abf1aa0541d8bfe1b146876a08066fe6fcf +c2c8493e7e8d10ccc432eacdc3b7ba192e65857c04bfa343708cab65033ee6af1680bc410c3136a4bd57d3c45143f7fc2899e0321091960f114a73c4d010281675c7272a90db8e502e7d0285d3137ae6def4113ae5ce87e672aa8d64e4c02d8a99684bf5ad07cfcbed7dd95dfa53455cce7ca9189b6d5160396cdb98019206724984caf1feb9e82b6ab83a01f12daa07bc252e02584b9e3c8a92b8361a57d0362fc9c0cbc99266736880b13164af45d867df24cb2fc146fab8821f6c9aa83f878e0053fc308ee6a0c28db96c18 +2a0a564e497fad34fcfa2f6733ef64b690b54c86ce2937001e6e9ceea92d8649c87a41a5a6268b17d08f1259b1e92d3374b2d225b4ef3e42f7668e04be38fe66f7e98f20cf3907e8fb9b1ac8b1428ec4682749de5ca16294369b4f01ca6fddc6b98a72aff03f6f98891c23b15eaa6e990028b407876549b933b1546e00bd3d1d2f811539151e48f4423ffa0efdf52b8ba00d575b7c51cfc22a96e52f6083b8be3ba86711358fab77766be718dc4a0e582bd9056b24e52691918133974b1cb64b554704017d4b421d0fc864c7a8908d701cd0bef3a37cd78f0aa6def6b1e7bb40101bab8a615971a074cb991096b9cfd99b982794f921341b61308035606042d1d23edabce11529afc4340a7bfa4efda4f3875d751d5faa2817eca1f0fecba89a386ef83ba057e29fdd120167bacdde800980a2451e6b8496093eb29913e093 +b52c9fe430534004b13f30a8205135d73cf8602ac34f90bd9aca7a1fc3126b53d53f7f9d249ee0ce76d90bdb8d0e6e063396060a2c6f34abfc5f7e99a4dc5d78bb0e52c05ee6cc4f1c3d5f049510142b26e440c2f40e669fff99e37b33965e764c5877b929cf1ebf9af89bf6f1ce8b050bb305bf532e122af7c844c56863c28f8acd973b848544ac8c3959f8d90a9b6a44860078c76a6499328e08c5f5a4d414a700eba33f7873e9a0429e323a7838d12da7d057b2367d027bbbabd29b75f9f23a379efb49c2c1675551506356b82175265aa67a870f168e36ec879426c2df043997c029cdc2668f89896553381ec0878d9fb4e5eb18817d84598b4d885f32cb123057b88658ac885f55549696bdd1fb2424fb9063a5a150007178a695b05bea7f6408d2d95afff3ead03109884e58636fc7f2e1f92a3dbd88a70e86e260c6078c29d9d52eafb1a035a00f98ac565c6978368304682945bf9401b36b41a4347644af8b130e98dab713d40ece2023ed529334e2b049777e76ace217c46389973c9bd60f3577aaefd2493cb3a0e50ff42188f1bec8b98417225b954dd918d2efeb00 +92c301ed75907ec2e5071e9545c6ae064a6b0efda4ea450de2b0ade0bdcc2622a45238af681f853a33d7b5b084e97f8c9fdf006806c4c069657af42a72e37b19f6d6ba84c1317535cd62d9094fc816cffbe9d826bc238ab7ef0764bf6f47d063329998edaff3104fb459e7712ba41d1e4f0326641acd096722badc580d4db3056c557834f171ff77a5772b26c44db738b1006b7ee96f970f1f878f76290ef9d3acfaf1ebdcee5bb7416f086bee3cc8da218c4e43f46071d6f9768009c1b6fbf21090b6a48cba0af9e5a200e4f7c745e42914d2b2fc1ca02e1d23352d1438ff4308c4f34bbaf06a4ac4141949b7e1df9ed598059f8ff4864433cef769cac85c232f9a76b6bdb6d215b22a28dcf4fcb09984d8b9c457726fa8ff693f05afb5c00f31be10d937655ee032af7b3789734db11272bac140f1bdbbaf30bfe566103a9c5a00c230e3349007329961dbd414d3a3de59038812ee1d532e3d8f77b9b05f562e771ed015cd41ec08161c17f8375bcb213a77b6f4895123e7adf82cdd0035f3abbe76a65e5b622f2360a7b357f069782c429d5952bf0d5988bf64fa5532426d91e53f65db8d0f58facfe9f0e5d0c15ceccad5a7b3d65cfb3681e973ca334e6a567743a0a70f74 +adb6c14dbabf3ca059689718f2802585c37df9e0790859952337069103f501a4e88779a1726afb0e35e130776232313d57eab5f2a3ef434174eef90998a06e7175b413f9d4c99cdaa38b7c5e5e334bcd14e65c788de29df3cdefe1176d8f154213dbfece3f33d68b1b862499bdd7f490303160a781e9374a0cb90bd201b21997f6b6a8fe698e0c7f3464e1fdb47d1c2411f396705cbcd3becb3ab9d40ea7dc3492507017dc0d4a86ccb23a299db47ab5d4fd0d5b284892c2663e54e1776d4d440ffd806fc50e3a2800e645b32821e884d81a0278387a8ebd1282dd3fdf1780e90498fe86877f86fa8936e847778f5d30c2de78dc52ea246591e7203464e41cd5dd89243996642b96e4cb1f289fbcec3a707eed2162d272f5ff4d28390e56135b860bbb4020eb42b3e0e3bfaf19595b3e7124b78333e586c23397725258c03779ca3c6e76fe96b2d4fd553a2492becb776e5a77fcb36cb7a5414d641771be1469e86c8b7766ff581527f3e793aed1ecf710e639f54349bdd0916887ec992fa029e73999fdf3a9219c485c47082ea84a1dddb7ad018643e9740bbdf6e3311a3e7b1ee6559b6a6d8f1c38acd1b4fe5922c68c55032447291dc7e299a0ccf10e8dee7a819135d2f539ff7d90b68ab3854cc75b54ea2bdd +ebec78f4bcf0c1728f58f7496ece4bd5afa6704931bbeb13bb0fd24c2f8e9bd86b31d6d006fec3509756d8d6802338b45f492d03ccac18a6061d85da775ecb613c2d3f3e3b9c05129446118d488e2b04d7f5e92331d23640841e541ea8c3b3703d3e6286533457b0223975d59bbe1cdc0e251aeef6f8e6a07f5c974963841d4a73e199f4025bcddbf26a0d9061b16c5c0b78f718ea7d7ad4ee57d0a42b20ed919f62cfc9f5766c9706ae3b5b3583282d8905d7bb65f45d8149294fa8a8ef413f087472d350017dcc336002136f8a239006cde32f19b77159e89f519087d5936cf51e062af3 +084a7f4e1c20053d4c87cac4a44be0e2cf51799616f685fd6e795488d190f94b00cacb1e34fed266d4915205ea8413447204fe0d74f7132523fe1b201bfafb91895550b2e997bd229c7cbb8e8a7cdab247cce5785f561ef60d1bd0a88ab691a7f9b0680b04d940336ac8932e766a01513014add4ed2776e8db98ca9218c09e8af333312a6411d31a6624a102b3f2cc43635b9aee4f805a3163569c4423b5ce41349879632991b37b6a7f1c159ed7504bbd6d00734baea0497fa038b10eefe0e2843f7bc0941590568b2ed49b0f1ec7d578529a31a170027075c83edc06412d465367f7ce2173978b75a1c64cdd096bf5ca706f21fbb209cb6198fda35adef5e97fdb0e745a085f221338 +5528844dc8f068d3a1c7551c863322a77eece3df74c1977641e67039edf6a2f96f136db1c8231bee7a90782e89aa5b328a3a014e7b43dc7eb949045cf00e5cbb8d42456690ed755c1252c38e36e10e717986d5e7dfe143641be497771523b905a9c0c7a8867560546d423b55b6e1a6d5dc915ddf6693367b2fb1fd21b1a20a352eb7e2f7f5276fee59941cf954bd4c674f6e1687fa7f729f812bd6ab532c52b57c809978164cc3ec68b6348261b6d86cf8f8409f1730e780a7fd1dd3616d15aa0f934856840b9f9f198546d929e91a41ee51c3e1e47fa338fbe3fcda03bebd1ac0c6cb9f9d513554c800efbe7307f7e4f70a3c567f9f22b859436775da140bf79f6f12264f2022dad2e996e00f92d7ad2359ade2edd09a7f5303f62502b228a6f0d98fe6d7cd2c9482ead790f22bf2b9c7ee589e3f42ba2a6716906fe524c475c3911565e4a2a29c2caf0099595be48a52de33656199644db1424c735a0ace79a9128f99b169047c147506bb837724517f278d2d12a2cf24989f25658c243b6d034327ca856652ee869b88436effc770674f539992abac90f94fe56438296bf373564cfd222e963ab1a15443290c1ba5ef9a7a567404b7e653eee53caaee9bcee8742156a2f8269cc0 +f0f02d4293700f5d0bab22303f12673f037a1949f08ac4d3cb0641c41439ad6da9a048d6941f7de25cd68d362989e08e6670be4b9618a6912c2d93fd55324faea7e4911185162d6de6a6899edb886e4b838c413674d747fc7b4ca6f424382c1e8748e8e0c5be34d745d4589fab2de45eec9ef8c7f69400a346ab92d10d3a83bff8338bf0ebac8a3edaa1dfe538d8d3b95d7166436bef60c75b45fa663ca310c2de6530d1de771e5a942d0e923d611d9d915392cd75c46da90bb9f16b74ac17d67a9d2d9f46bcd4d85e1eeadd56a30d1961f0a58716eff22761b6d7a5403222f718d62fbbccd9d664adf5d1dd3fcd61459e1b59fcca167d3a +699777ddb20034c0af0004b0d1c9b9beb560fd0313dfb539edb15039d264c9266ede5b55bf84eb6c6dd7213aed5c53ed3f9099a31fdaa02dcf83c0ffd57e5872c4b6be4c9e78054622b152797db43eb8f87644afe33a47a9da47df097fe60fc53912d407635ba369704243d95b3173e4273739563183140822bf2b055e6596d9e6c7c029264dc8a375e87311941c554c9ba66e6daa53ec0860de5e6823f65009196fbd9cccc17fc751329de0c9a4c9cd1112dce878e641c873880f888ceb6cc052c0983eda6bf0f6d170ad28b9c162dcd8ba62466209580853971b4fba2650d2b8c726b2deeafc78b4d2bbd1826d27413779dd7ba81b4e33d5b253fe75fbdb3b8b9318f4e5aec79520958f1015c773791715e3c2387b4daddd6ffb87e92200bd23d6bf1b879abdf5a6b8377752806f3469bca4efa80183e0643f8bfe4f5241e8e6e130ce01ea960074c616636135a484d33980b091e94c94edb0399c0af27608bf9f5cf7ddcdcbcc21260934ae2125281746b449b0c0190ece70d4d5af687c73a2f48821b53e090a9d7913e625a6d280c297c43858cf8b6ce4a65b0c85842dbe7d07b31177a41a75c58a0b79123c39a5960cfd61bd824ecc2314e953f28f425d3fff6313cd9dcd4e0d45daab3b3b86283993a91e874627b7878966c15b6d801c52a95c7906964e59db40c18fa6fa16f2a5346ccf3972bd51e61c7de3f361c208178dbfd4189a7e +f34c257391d81dccd5cd4355ee9d9dcc28b560bcfdf06437ecf1135e32dbc12a9296659b1e94e80b37fbb911e0fcf1a15768d2ddfd5d7af82c44b51a5d7dc42a9cd73c542c9138e25eadb5c5443979121817ff115d4469e5ac7a362bb219c26c7a0929dce16931d774e67dfcf245366903e0921bffd2575d46d619dc9985b93d0d37619c69666ae0ef2c5e596aaf4e8356e0a5124118f3e4d79f87e3a6501f4547c8c83763558ea204247b6498ecb11e93bbe8c8f80013de47415c4f78808e21e5e975f280351f555edb4b48fb27e96978769bcad359b541105c2cba350ce78ecb7644efd045c6d2933b37079007b25eda1ad478b282cc99c143a81e0f9170917cf7e2bb46eecb3ddf2ee9a214f4c25ee0a783aa753670ff0c62eff261cc6d24392af7b3e80a17df98efc0ba175c4a8f22d2cb76afefe46fca1846e8d741abec3e96c3be300a599beb05ef1c5d72b78a2415177fd468f1008a74bdf429b48d719fb1d6da2474c08c586ec4d313e720beba82875e59c01f8a176d3af9e7ca0d5f490b49049543d14d697597195426fa25c2ee1aa539b3462888fc6ab0da793344a40ffdea18a2d05b32b7e2d57f8d7fbbc9e126eb7b77f8244da225486ea68d5a50c96bff2e834a2710d106f6da1a4b350ad8987b1ab042c9bbe7ad33b38c19d68b40745916983146 +43345a1121b58054517e0bed0446919c3a64b06d5a2d38b3d6c4ecdc1f144811f50296f978648a4d255814f0cf354842c16183b78cd3236c3b37920d16f3b688f126412a9e9bc794b0b6348b6a1381939b726bb2872236e469c573a84723b1fbcbdf2df069b8024d44982e459ce904a00807bfcced0d73b7308237b0afd9ba2a2d8217fda474f1806a1ff764a0b98c340387d01233319dfdcd179204c3be1de21ab14b7e345c08ea7103600948df7eeec8877416ec132bed0d56a5d4 +29818259c1c0f736fb1f9dc84ed02ec03ef650c5784be3595c32a88045b653dd691ac58bbd266d99464f11a7eb92236accc158782e3c8bb592dea33a42b715c1b00b40779d686eb8b9980a83703ef560f3b817bfda9cb6b7c0e83499a528a2a3115d34f424285a6eb7d4e9d1bfaf45065206ca0ded98d5d8a098bda16dcab6af17270f12e0aa892a9961d295e51514444dc9f3145e4d53d6bacbfa637ea9b4527a5939e1dbb9be7df91e413dc796fcd3ffa0f057dd0e67f0fd74efebdbfdc78869a79d6a73fd69a8bc22764b180d5f37019f9b287271ef5f4f33f744745a3c0fec1c785bd7493ea34cbe424a185103bf89206399fe7fdb9399627801f8ba82eee4d6e2d7b15d97258f42be94989c9d3965ff6bd427a1aca2cc31add92edc65df1c5f9df67a9d30291c09585cce83d6 +4abb276335c5877e594022ce5e75f2140032a578f43aa96b1d46c8936e0335e305fcfce549314f65ac41405d3db692d85be1fd174c586666781d630c8721839ffbf75dad8f905d0718228343e1aed290c062421a69a03153a5755374b7a620068843f4d1c5b23df4be35d34645f5609700ff7c7ee2ca3b99c41e27981663bfb3b38b3a29c08003c1f0e57693d139b2753d6eb1611da3e4486bb04f767b9c2099c3a323b246a8b16a86682a256913e8f71689b70d16f9da07abb6f4997ff633f2208b3de7e37d2b72b8fcdcb98abe56a46f34cc334f0c0703d198ae45c9852ed71bd0d78018893d40e32a83ceed1843074adb68d66fe9cc5a5bd29eef2d6f14ec05d0a7257c59313cd0839b37357e5b643f21d75f00411685a9e86c82099b2eab226d718846f77c1ef273bbb224fc4b3b10310c60db2fbc3beea3a0f36e02f3ccf79a2f6eb7 +f30a1e8ad916d6531836ce2b4186c276b257c917e39c9806e14d6f543b27fa3815df36d0a483924e9ee61a44c93397b5893228e095ec2770cb95587df9efe5cbeb4156a4812a90f7c2ea0401b148ca0fc807dd3756dda26f1530bc1c82c222f9eee9f592ac185b15ddebfb1b969f63c88bc3673edebdcd3f5dae9284ce1544758aa97fbcf8933086caf65c8947a94055fde48015c2bd36d51d2046dfe7024840aab3e63db289e8f134b61729a0c6f3641dc8d83695d6a02bb9a6989b9061f71ad59b +1d227df3418509ef9527bdce5a58fe8f96e971a19d7dab6144f94ab9ad62950b3b2e93ce831770d09cefe89ac06d14ea03b86620d813349d36ef864ce338702bc89f1f918f09e2c2d9398e712a2b60b957a2c006e7dfc95ec40b71e5f7a0d3dfc4883df80d4973f2fb51e744157d4a03de9ad0c8c9d884113665bf3bbea92e78fd4801834652635225354b8b31408f598e6810342446b89f3448bcfaa09af0b115b8b0105618ff5bfd78149cad770176809253058efdb5e212656b22c3fb74130eed7c8c29a5e95dd9e7aa2eea4147872826bcc2e47f2bf9870b19c9f6a83251da266123cfe633ee996a39812f3019b6e0e4076c3c94a6f240d7e1ccc7 +a71a6f547981ac2fe0f39eb499b3c962786b2847a065ecd4ab4cdd7494e54f057aebe1c76f91f2e73dbc6720dfb826f661c835f50336209cf3d7d6cc92ad3ad0615401797352faec98c15d631f85644c83d64405ee888bfc3c63c14213047dc44635047c422cea24d46f326fd8d9d1996dfa31b6d8f8ac123fd77b71586345091f7aa709b95236c45b9a77218c36e349ba13ccf63f262852410ebae34f9b940a0acc60ae71a972bfebf8cd2d0b8029b5b9d983553a97b448bb22854d1976200028 +1021f0befef0c1463cc9e0c9a8ab17f9bfd7e1de1c42a77d0ad00dc6db8eb2dbbb2fdf40fd71a47a319a7abe343e005f9526f886da72e65164b0985ab3f3c473c850d387309f4691a16772483b78404432a26e51c1fe43e0ba1e97610d4c77ff3c581f9c15548a78cf048dc98748e06e8ed82739427e95224c6d95ad6d5fa24889cf3559ebd220903804dd917354c129ebd696b8c3717c40e05b776bd584ea6c546413fe00db044027ac9ced1811b37f8edf9be189203a7016ce20b85f16855ac90440e4d682543367a62769e7ad8924430637bbbb702f9e249d6da884f4404ff112cfa7a31e0898a3f0b5b9464fad9b2c228e64f62867776ece37309c9d38cb893ff9659e020810e2992ac89422a91444a9ab88fa2ad34c8edeab7e124fe076ea83bb80a75515a8adafa87428b5398a3beb9d91eb15924936899f36cfc5b48a64b8026bbea41e705bf83f6ec82e4f8c062a5930a2e0e08b838e4c1956fb322fb155f002d79f642a02ed52c911c3994cc317ef9f9ce31d8c2cf9fa9619879052decc98e47e +0597846e25af521404645430863cf6d07468176c11c68008aa724305de5fcc025fd350bf1fbc63cabecc477eabdf01219b7ad7e922492a1bb0eef67fd5603bfed0563c402d87e90a2c9485326985ee7037d78b9c5498da6d1541528b2debc6ac5602096bddd4973b6bd13da8acb90845531aa829c434a25530c1f134339ee5778ad40c90a1e8da9ebf439e44784e0bd681111cfb4a47bd82284ebb39d9c1d87a76a87ad3be9187318d36d1b76adf093709bc660b362aa33d91856db44d53c994cd9adde96d4bf068481cb423ff9a6a74c12d04b87250ceb84ddf31da9aaf46b4fa7a261aa319fb37de5ee59935cd7b2afeb7d2119328b4ed2854ae62de2e8cd195e1efff0a7fa2ec24e68585ebc718031fffc52958547c94aff12c0c825b2a14a6595887a6297457bce64e719e79e5ad3a6291e913dcdba5c9ea2ec3062876b5571e10b7a7bb7ecbfc525ea9e064a2ba6233afe186bab8ec88a11ce6dbb0580776fffeb0e64637634b9b55492d5110fa0f554fa1a45ef42db92a6c34c605ee3457cc147192edd32985163322854becbc19aba707c58ef25dafc9d3d051807f2e98ecc0df8a2c987ed0ad7702526f7f8bdee32c1ec4365068d3e1fbf0c820454e3b7c +89df4c9d5b7b3b07e51ef743d0fa1a2fec8fa2530964e93aa349708f23a09356b40e8086e54f275f7c94161eee1fd55fd29a9a9034feccd3465973b3f4e01cbb205627b1cce2b7829b531db04c8dddade1cdef3b8f5c4c3b088b205976fcc41e13cdbe56fe689c2e0a046637d1a3671eb34fecbf80d5dade1eb0df9c297389d42396f3252d7d3ad92e49888a4e0e1ef34cce33f8451468f3131c9477f5ab5bf842c86acc9f7323f11eee56a55c1984c66fa67faea895b3c9b66db21666c258678d84a000db924ce593ef2a4d2a932b030181d4c3b65687222966d96a2c30285856b3826c98e1856837c8ee48bdd84644e4da9390114c7a119b5974 +3bd3e422f4f325238be39988dd917ba91f72161bd7f1fdff550da6d81594f00127cb21a1d4372dfd7dd5b7ff1168bbb4feddacb7d7b098d40144d354f069eb4f2ebb95c2498a547235eb884c8ca90fd645e38cf87ff703ae7f50a0656b35f8f973e9cbb89c84a95db304891310718a2d16179b5de9d7ad3eb7b225e1e30065c4e6b0764de2f0e7443c60113e16f06b2cc5ebf661963cfe2fb1ac8b8a271f8fe564dee3e20c158086b8d19d6bf10762d13ae816b7cc7e6048cca321 +736e3efa8360f889bf35d109dceacd12bbb022ef477dd4eb016ab23d03b963e5898d854b93d853d367b8f5daadaa9bfeb8bdb81071eec1f0bd2caebac62883a06bb3afe6488602b2107bcf3ccd24507f8d542c68a6e1590382ada922e2dde7a43583b90ea314540c7bdcb3b320a6a7ecddc9eb606603958736dd4683f8c51b669d7ecd7f79bf3f71fbe03a8438d9b8e1c6397c35e15647338b66933b0b63c5708f6e4a0b4156e4708918c1271b630fc3bc76f92c7b87e233e85707d8981d0c806871d7047fa0a718a4a5122cd78084144fc1d9dd4ffc0ba85729edacc5c3f8a15b05fb016b99691a69f2b11b662047f941d27b0d18e38b0deec7be901cde52d16da2bf7b490b4d12719955c365faa99892a5fba411f71a013ff1d8625b2e71a89b6adcb2ec0aa575f0ed0d86192019aed8a1d837424f7ff645992ab8d2f28a87975aed79f0c46ba092c2890e4890e3f08981daa0c2d48d4a52a04146d661f7c4af8e31472139de939ace0d0361411aac9b90c53367d5783d828f3d78beeaf16a50174d249745aeccc6489030772e4b3b0aad51056b2ffe502ab03bc3f9ac4dedaecf9ff0fd914ac66c27829b8cdd5da5bfa2f4cb799bf489ca1e9829afaba96f06cf947490d6c1a4629ab818aae32e1b5546ecc3b697a11d0136a775774398040bdb8dcf4a +be761c2009cc54e66bb044e61443366577fd03044dad9d5a4280e76fc8b8bde01eb097db0a29c19c1cd1e586085060b4a5c80b110a2f74ca01436dcfd31328269a3879817bda6b45720de26506ccc45802a35224e213438d7892dca00b5a308413a7f989d9286f23419a7c5327bfe8adce77086cb83039239bedd19b9f5fd519f0240bfa9c553f73be22385453307b06d210c99d8d6af8b53bf1cec2a0df116d166944699866235af454700e8a1486cc50a81b722ba0307a833beaa39b6dac4af96f04ac0de90a5b6cb9 +86ead1ffac594d6424b6e1c9cef3d01f32352fbeea4a3662ade22b33bc93553fa80ad85b65ecb8e549a94b20934c8eb9a05bf36605b84159f57c661b755a81e11c13d289bb7ffe3a9fba22d547031968cc6d152e3f5024b35fd16149158e9355ee2ce6e2fa4facd54a249da7599cdb70bd00c1a06deecc3677120c602363cb1279a88e849f1bc8f9fae5e9e6378e7a7ca7a202923d52d793098c830984c3fe22237f87a59daa34c17e57ddf2a6135e91ff252b9ab642a3975f26172d51cc8ed86e924e55beb1fa9745bae54cc47bb6a21cf6123a1750771da215c0fdb70e2163c732bf2d9d90f8bc5128a6b8586f8647e401a63b96af543af52c8a4d4cfda0c7b07a3975c56b74490b6cd0f4d2b9351305954fb575a9ea37b25e71c0661ffeb2afe8e3366515192e9149a77796f3260b41d6b051fc2d39bd5ee53a45e8733bb48bc785c0 +5acdbceaa3c9c9f2c4d0bd3c3d3432caed49d2bf94bb67138e345656d96c1fa324fd27a5189beb6406e37d6695f21707a29608a9d019e5d4f4b69acad4f106e7efbb8b6e7bf5498d6ae63b600b6d27ede29676f3b37e2c062c574e6921099fac2154f706be82c86c1ae68480c6a0f4380cedf218d9c10d3e068dcff26773df98b6bbf22dc2525e51cba318d480e22ee9d7b9040a5582588a9e95a19f5ff3b526c46205f89021b90dd2c7ae3ab42180ebed9527a94d41b9b09e9670b06e2bbea859f7c6187cd97ee1a5994b01c9f12a152cb0be76c2e06e6b2c90ecc58b81dbe63382e384b0bd93a963c0ca9ad1efc107f69352df03264c271c890e87f09c +dfd36dae1da1d7ffc1ce85f6d648f9a968200a8ecc41ad94b3d68432140dfa781c61efa8b1ed11d6303b1bcd2a62bcb6faf7af7dcbd5c81a7877ba9fc64c17c72cf35993fb1f87b8820368638c5d188712de9f896da7e53ea7a99de6bd16a994b4a6cd81ffe18f64eee3dfe75262d50fe808cebb5548d531e8b71aa9b7ac753ba315d85e5f16f0d52fe3d045a46c32cd4ac686ba6c2999160455a03ebd3a31c9fca26f8b1b56d7799b25c5ebfcb3c53824f5870ec78419660ca675b411c20631c621d4e7395dfcf3fd3b66332c2931d96afd4c24ba6b572d7efcc4bdf1451d728c54ddeadfdf78eca56e5fe6787b3597e3eff98f957d909be5c7f6f4a6bdc72c09c526315742d87d3783c6683c1b8ab943386b0c90ced95c7a4dd2066157b82466779e1b86c2e2a2bc75900099d6180e3471e8ce6669ab4c3d88df3b5b82d53c99f002db42858d4368aa83c2b96b01d3aec5027c1608ebf50fd287e5d5391c5b621ebb90280eac4ca41f349c17b903be33710d7f5d6ddd5c46b78c2553241dc65f929cdc509fd957cf117fff59e6aac565ddcc0a4dea9aaa418b31a1a9f7896dcb950c7f74030762409f13b1331cda0b7eceb35fa38e69197536b588619e549f46960f73033e512305da763adf472e881a2e59df3a79 +bb7dbb8d89d8b6f4bf4d75ba01a5ae7d5adde012709661addbecf58b5532e1688ba0f1e71fbec92758c11f8564a107b3c8f061cdaca1c24fd1c7a382ff4a370a595a00ecc352d3de3315080860c0cbeb26c3269c1a72ffde1b83d6c4e0dbfc1bfb5d0e4a7df595b0d290019d670c968ab7ec0ba4aa47caa7b4477722cc04ba2d5894a98959893067e035e05eb30ae5bdeafff45825e466837fa80863b88ea76fc19d67993717832613c38516e6dabc02893b4123e0cc18b1882a7e0dccea77c4174846318649d79dfd4fb78ab0c4b669fc732e1495952b0b869a3dd9b4ebcbce4b893a69552c59d553035cb1ecb403091bb2ba0bd4c0f60904cbaf48b3a6d569a6737f1f4ac2cd141b74874ac722c82b16217ef59e9e701fbd26e9ddf5029f2768a31ea1fde11f4f5416e15b60e96da8e8a5ff829fc9c7efa9a4f57fa6737d3435b12a1931dd2ab98f4eea14738553ffdbbbfae882a288c83e9a2e7e4762213ad51d393f688054dff0a294957949719ca3142b26f164ca0a5de638bcc3c5e8b1bf3598d265ce71e12b82b61ed5f45fdb866c07 +ac39e5cf75fb0e7fca60bd26027cb254061a6a3aec34d71b76404236131af55b49e82c705249ab61e6353401ef0b3489fee22ea13058d7a187d6272feee87cf410883c62217dceeed7a2432f20736743f6fd77790e88e8c8922f47702a9aefd87ac447e22427555dafa82e4f4e954ce88c93c65e799d6b67b3eab29bb8bc0931cd29f1f89635acf829e514cd6d00e044b063374368081ec13d03e250622a0e7b6303305341cfe2a2cbb7a874ad452e21e8d894a3f7cc248b6b82baedd9d5d8680448bb73430fc6033f0b94f6b9ca841ef1653b4af29668dc8e106d7c56446c52f10c7bad8717acde775c05e6d09c785141b841335b70db843dfccb5c495275abfd088f836e044405065fdbaa32bc0e2bed2c68f757900545002a7c1a9bae4df25da549d12638097547d80ce25569ad1e6c175a5910ca746f09789f03028feeb08120c74c398ecb1abc23a4be72e73e93be5ead5f0916836761ff5ae9df2d65fda83088dbc5ddd5b22fdff8d6e2440a9471ce2d8265baf88f1bd5c49d3a733cff7ac08d44e861245b19557ce7c699d2ce0cd6f878948dfc130cadcd9d3e6f5afa5c3185a7e17c4043c4fdc6108b4b9a4857f6d90b +dcee1c7deb797dd9fee25fd1af805b5d5a29f9b4bc3503b6c0b5650490748c2f668ee760a1157ade39865f890dab9274eadbeec945c7a6832187b2387bb156a7f8fd438effcdcd3b16d6878bb19b5ddebaf28faa3094abd6847aeb7ddffde98f251cf475aa2b2ec9d202e1030c937fd12fe18b +3572a1fe2ab5d38aeeca1c049dc375da82030a2191a2112b216395179844bc832043f5c825094470d9dbdaf8a049a8105643f10ab6e505df984619cd9ec837dbe36e6b610a910fae99da03010df3e6310630bbf387cba14adb4baf5b9636fc5861ff888e19971c9d0bb7d4918db1f4dd8c8bdb037dc9ef36c3ca56192709cf7663ec78ac60264ec2cb4e6f2a60b55cf97a1c6e35de715b7d545dde27d926b93c340ce10e66dbae806ad7eef6406ab1e5612b7e0a0c8f6e1d02e63deef12ea668732e321fc3715caa6aa1955fe83c30c7a360f646153a906b04203be43cdface08ee0e1e04580c07a86939109b3be4c413688cb4300674fe7342fe7e8291e7ae2655c232d03fa8763caf8d0c8daa69a38d2cae154536fac3300c66de9c7076a78a4199859 +dcdf47f7116e04cd26a8130ec020b85d0feca5e14dbf2a1da9b1a84293659da0a07e8eb6f533092e173401e4fb399eb5646904e1908bc061cc42e6b2a94fd0f5e0e5a11338afcfe38849bc296eadb87b5bf7deb8820ec28d58c94f3c3644fa8fb4733ec9d45dafca17c54b72467ce6d81d3a00e2763ba4cad0521a0746d72e2b4fb238dbec6c8c82b3bb4fdda80e6b32f691935676b549427015ce517d353e314b750bf9c18ce9e12b464553d5d533aa30fc5640111c44ca55dfbe4d1f0fb6b9362fdd30875d70711c374fae40 +b14a6a907b453f36c7e64ab03b3648f6b692d2175a6b70ee739eb62bfe50039ec577e9e6a4dfc464c8aea9eb170ec8b20dbc968def408eced0f6d98a9ca845233b236116823ba4f5d413d5bad6822c0234e8e146ee87f538d1070fe7771f01155433b3b7942a1fc613f6d10d30856b638a7065c00a3eabd1765fdb42aef9c014e5a92b5d869eadbcfcad69c74c14c337734d5271b842df79b721e3b7fd9cb3588609b526b7328f9340507b883ea338f6b98eb646f54be7b431c2710c0dcabb444f7b83c45744647f5e77d19f43bf40c3c5c194e1e3b88d28c556cfa7c043d5a6ac2abc91aa1342226d +21a3455617002bea381fd2ac1fb0ab06fc6403577839bb5e9124f274e25c93e6952e1a313c50fd633f5e23f5c97f731b3bdaa2e754d2bed85dee6e904d6bb4417d3f021797997e4de10f8b3cbde17829f378954bfac521e62f5a052a95e1ca398bc0f220c76d96eee809410d9d0844d6781257aef5038fb459f509fb19f2742a33b10db21e73a796ea207f1cb27568c11a4db954c16492636e17a693cb9131cb9e77f4aebb3c227b3682ef686d723c011c75b54932b1 +4c4295655009e9d071bbd4424a5a1a7d15fe5c72a0ad231cc4fe2dc903c8d6c8125bf458d476a392abe0ffd916465bf078363fcf5851a88d8f9e964b6d5cda3ce55fa26a9a618e74bb906566acf2898328b0cfbb8a93dcd78fb64840e1cafa0887ffba460737083870d08cac94c011f7ebef32c7e7ab1e8d9531be398cf784a88ed1c2649472d393d79b9a0dcf72eef21a30c8b17239bd8e26da9ea9c7c1e83c5dabf8be7ea6ad124fec0ad590bab806ec35a68ee8ff69e82ce36d45d060742967f59d9a88fe432d74e0f6c3a381b904403e555c7a59463ebfc424db7ca550462073ccf845dafe116f78995b034c682562c25c754b0c0f0f96f7293352aa2a18350a4d2d73e21e901769646f184fe57c535f046043ceca7c8407306a8c6a14a47cfaf80ce131accf7f90446f26a7e4450384ed7fab269cf0a581815794bf1e4b26150f3d63ffc3194f748af9674288741251e12c1809254799311d635e471f42aa712d32525aad6e8c6ef52c55e7c32a8c356aa622caf8e1624652713bbe380eea082e3e1c6352382cdd65359ced97641dc0b66b66ccab1d60c22f3d9330879a1edf9e46589981bc15fbafa53e1ede9346 +386f5a7cf4d8ebd256bf09a1905df51e359df7ac8bb3f1cc1c036039f76e11388eb0671e6149026cc2129a99e10b74a3cc7cef54b496619b062246a9f8a7bc8032b4dad922dca1d9e35414176460148a5f9b03b8f7240ea11cb8ffdbc6e7e75c6b62178b6a782f8caa148e74e5759b38956910d9a3881535d2b136c1ac9ebb3c06b99ff3e5b688d4f6b13fa756263553446c688948c151206ac8b701e52391594fef6494c18c27ebe0474b774010463c863aba667db005832ce1204697a89215d671e5ebcae4edd97a97f712e99a7b7cca97076afc5b5ddd3da43c42c87dd89975389f6b25aaffb56471cb93b68d97b06a3428728ae050a0a0d38cd3c31f8b2b191340c313c94cb9d12027ee39c5826852905a5f419e60e9d6e882d17aa277bbd43e30eb539425b76855bcbf7a690c8117755c35a703b1cda976df0d53e7a927b4e8e57d9125775e9614778cf640f47c2a9b99c81b9906be96bec9ecf11f7ac2db1282d2743124deb6265bedca8bac487eeddb7014bb1387631605376e24a7876155436fb5c1f665ab82c117a2f654073ce81648c17cb1695484e48b9024da8e74033935d569aef157428c55cc1c7851fba2b0756d456bc9881e5afb +193537472dcfdde265b3fd7b93c9ef0c180964fbefe4f4469cecfaf8b0c351fa945c8d3e0ba13cd531dc8b7b2436eda4a612dee529ac72f29ec8a1a11dd480375a7566df3f19d10109d900849f3e51095bad5d9314b5b27bb2222f4dfe3cbb184eaeb3117f60191bdfb65c9e99da2fd769b1ab5a6ae8f40ce1adf8 +0f6db80b32b696122019315645fe4f1c89e93371729dd25a81faea0e64915f1fce159667b5c1a8bdfd673f0dbef8a1f43c6b13dff747b5539888b40ceaa39e06e3c7f34d5176f69442c51e900a85747c502f6359c6bf9c7e424668f50182a68ccd4ba6837e6b43069823dd7ade17e30b286b570b0ef53bef73b89e313c0f98f4411ea9c0b198f28837dd81208d467702e8c4eb8f7298b0f4abf2890984bd559ddd9f9ff1b54239e64aef93972585f3b5ef40f898b6b60104c7f217e91e48aa4b8d6992efc7eb065f4903f1ca7b70ffbd4fa8ae567c9386a7eff99df0db3d97203a5a4f2d0fb9170a9fc46e2ba739242fcfd2f80d9447daef287c885902eb26ae33c7f0d8e6ded8f54304bd7670b2ab34e45f4a61538fbbe9021c973b632667bcdf5d11f06b7b6f94a42b1d083172ca892e5f5ad78a5f68a1ef2b251a2372f71f292b24d4ee8288f18072514aa29cb367fb69390c83b0f51fb443f0ceaffdb3492f637ec1a510531a166f69b920bdf54a4799652090111ff27d63f6406a07edf3b63f7ac837c944643c6dc6f6d94abd1fb8ff2fd9b4690bb6141fd07e8619ce6bd8925abc99b003e1d20df278733a6b5b28fc22f3cd818e2bb6581e9d909d470d7a913d8eb7fd1ea91dbabc4b9f4202702108bb2de3c863 +0b0878fa2c5a1b60fd3ccaa27369e9617e6621631c22eb2de5691f8cf3cfe0dcbe92654581408554b3fd039d6f8221860713750db19fa18174542849dd089d1d3925e7eadb1ff72497eebd1e3475c4047caf64eb908ca2200ffe6cfed6429fe2fcabfe4cf9af7067c25442f816d4a28727b62b0ab3764593c2159d85f65b71c218c5c6548d7beabb22b9a15581df25211726a78b9f70e9286a30df074bb8bdc2515291d26e2d659bf4461f5900c1d7a03f3aa7652376f84ac09b7ecb08f5d90d5606903efbaa0cd47db854b1aa57 +40f17c08b881f045bae225912d3f52d32ca08b843e2e9ae72b868d799c4de7aa0c3c9b1b3389d34634df480855aa5f704474b3f693e0c5b89915db50e8f75a3397b090d4172adb9feec44b9f1c1063a8d582327fc7c0c864b5ec5343664e73cf6cfc923d46eb68948ca5cc39a7ab011a8aa6698c4579b877c647f11f4d30859ccaef56bcefde73c3e831352b3c2d9af77f4bbc3a348a0d47ed59dd0918321d16255f1f99467e20c3493ea1e8c7cdede895eff4afb24c7a29050bf10a576638a9 +6a1150dc0171187fd9af9d93e9726e4a473582e0b01b43988076ccf4f365ff0399602e029994045e58bf2dbebcf2399582f3ac44257f7483d483076f02737fb16cbfb4c5294ca425eb1c21db9bdff745358680fe3814dbbfa094078c7a129113baa14402e8fafb453398f94e4fe4216b5db92d565c6c6fc6bec7edf90cf30c097fcb9a0a74be400e510d69ddf5393b3e0bf4461b6be1352aca4609690feb6cc881ab865cdb4018c5fd53fef0fb2c0f122b6cdb5955118fc5ed62e5a1b38b37c45b017bfa13881997211bad10a06c64cbefe092e47bc4211561d97cfcfeba1f54695559c85e7b34fa6641ca109293ac545d684c35449bd33f39c713e41dc95a9f11ba0e8a25766bed58ffea74146426a277cbd8245e31682be8c53fc38686b94df45b75f1c5846d9c75992a863b5204b58ee327ec2e09305410bb90e06dccf27e7a550241c3ab7ef3cfec64a7b6bafe222a948d0ef7e256b604d46dbb020fa2884067bb0901e24387a072c951ac5fe7a21ec98d802be3405c9d2691580e6693f1e9e0f271551ce06e6e2a654a2da3b82337feabbb762a8b1dc18ab504518fab3bce69509e16eff37e21f91095a8f891e310dfc512eb2e27f93217e0073f7620eb130fa3a8215fe467a6ed3f93021837097e6b7ceef88c00991dd2e4d383b3635b9624f7a9c53c143b8df048d6b3718a4b2a281f4a75a2c877ea0c8325db893ecb4469786124 +5dd5276d892f764d7410c3e3a21a6f92592bcbe5ba99c0ddaf3c5103a2e9853574abab5ef96e1141cea2538020f6281e2c5c9082b63664b30391c9bf2dfe1c189b81d445a259a052c3da35515fa3bde06fd53872f76e789d9a6f0a26516bb40a338088938851ff542cd92f81e7f0045f2f42c6b813134eb0f2669dd7cbf4e036a435fa047237c10c5c4228071a621a881d10d50badc0c57069e2ecb62a6907d01ef2cf +1eff2e90f27900a7d94024f56df16dbb0838883d2c575c60e742f1bfb518c70e6577e55a1969127f2f67b9a34c869ac9d03a1dbdf33f4d75cb553dea51c56172bfc77ba7ca4fc4a9878087766fb047ac834b47bbe2546f2614224ba4fc6002b1b325bb867f06cd5beeffa4233e03d03c06f23913338e0393eb3014ac0b96254db33deb27e45e8f22d67f0ef515e1e150e5ed13070d6f787c9e0078559432fd7edbcdacf0fff415a5eaf2e3eb1db4108cb7d43da51c0deab72a1b7742d05f260420f9e4c0540f31706824c1b943d7b8d231bf099a5f5f02f682abe242c2cdcee562d13cfe7c034155800fdd22c49a94eb5d4c280aad58b85a2f2ef4d0c53ce493ec8fabd8bc9c0dd345904d2fd1ef9fd27db270e57a3c7254a7c565a26fefc50bd9069c480f07490d9de6fac8ce40b2371632773548c30b51ff876ae14978ec51e2cc18542dc53ad0c186cc21afe265 +a3907aee627d31b6557c2478e87d22e618908b050b24fd1fc5d0848de1b5abe772f516b741fab2a2939f0202d5ceafa8093195a64ebae4d4a608c42a5b62bbf32dc95531343b1c0aed7c96d7d86d0b97c2fd6eb7426488c96332cce73ac3ad358506863cb9e24494b2df59ae14f495124caf581ebd4ee6119e3d686e85ee5e628d7a70b817ad32c4e21dde6a84e7c3fc019ea9 +877e2ebb48008a637a65198437854944abb5e6ff0a6ff9724563584f57a1db72b364a9a4d5951e8b01eb77b784c00a7b72eb84c8f2de583748b41e958a27395576ec46d67880813fb2800a0ad2298c2647655de0874d074ed05997deb807968cef9f44f3e92e3dd2f1fb4517d939f9fb60e85f9f9d126ea653df3c93dd84935b74387e2d324d5f3daed61fbed46f104981395d29eca138612a29aebb4002fe9dcf52d94d0c7113bc2f91049249d9b52666b5bfe8ded6adc3a49e7935c3f9b8a8f78955463999e2b883447a79020457a1e9ac14d2a84c01c8ebb8c952a192b863c0ae36d272fc269bfece10eab1efa2696300fb07224611c8457ba9640f7e93220875db9f59b536f95c330a67b46c0a72bd8efc2af5bd0cc92d4d40bb4f8a1bb8d932ad8bf3c1b597f3944f9b87739c19c8d3b41a83ec9adec7b98b87a056914c0711b06931cf9ed0845416 +65f4a9463e61e9682a07e73329932634a2d2e364c45b39d4f86a7bd921ded52b8492be070b9200283c76232501fe80a19eeb7eb4b13c408f3df64f62b192f84b88da6f2e44fc527f5530f80612e226d936e23125e38fd8f6a1ffb10b8758caab957470645c3ade9166a86af609377fa700ed9e9c653243f31c62974696502f5993b882401265e62a17c9af5930c795a08978c2feb0552c0c2ec65206bd3692860f4eb0d896e4e0c2ec926e6e72c2521a43 +06eba25b7cf31af03dd5aada9d5c522f59516bf55644b5bcfb7d3fc9cb8ddb9769c365b95626a8669bc7db4c30891aaa98e67a801a26c6fff82cc7d104e275aa3658d559b326f5eb26c3342012f679d369f5e7b365517863f5640b73b63054c3cceadf0518a6c74ecfacad63f02965f130821bd65253722c715d4a199c656d8bf8d85557f961ea0e33ee9dab10d46e570bbc85f7375a3ed9da44489ab45c5d7b5d93c1ab30e31c5538df1ad24d12173a72f798c66b82a7b9f5fdebb112e14f1a744cab9922f7f9c5667df5d6d1d161f7adae8048dcf259903b80b4e2a62144f292dbdb75f90c799d6812cf5713eafb5298fa0fc4173b54dca45c8c3d88064bb38d6d154d863970b2442cc445906397243809657a2291a2226aca708a47051b106264ccf645459ab9c073322acb60f207a310d09cc247d5d834e87557b9cf9ed47dfb4d96fd3ab27078e5159c0b42fb6eb83f650f9f20d36435899e6fc67cdba6ff25e837830d0b0e2ce186575c71a7b72728d26ec4e569aac32b1533b999d203ac288ade1fcc3e97b498c3754d53f0b14c30ead606cf23bad280395695a5bff0cf7659a814dd53772c5e5f54f5f37a16577468efcb37 +03ed180086e2803fe0cc46c367c2c7fcfed14c0298b2d0617e4cad6cc8ae1e16ab13af3971863fe94ca761874ef00959280fca47da315fb23f4c9034e68af4de74178a24b312f6c98434b73917b584bf3f38742f752fb20ecbb530862a63379db5d97ee6281ee542354528bc3480e30fd17ad5b0fc80d8effb91d55cedad5a75e9080c4e291b9ce21e78143786c3a311797c88dbd7d75a21d7d3da45eb777b6b5bc51e7d212c8596830a1a0417c0185b012c331e7cca77d94cecfefd70fd613f57badfe0137e5dcccb45a21654f10a3c5643d918dca07bfc5b2ab5c4450c538e802a3005829f1cf9e0f69b57599b573acdf914926457d20e3b6bfee3ecd82ac2b1673dce3a5dc614b37bf983b2f8288f4a65a403306e3f48f3a32afa98a54344f542a972809cac6587e072f82e892f30ad14da38dcf4c558e4ac27776fdaaf7d18e3e0df2db59d576387f402310674e81c4e68908053b948cbf2c4d28ef23a1a4b9efaf922d47a9a40f05a1d0b6f2feaa38c0853455c8015bc667bfa56cdb81e5729c724acd4c142 +fddeb4d7bd2e7da4a17f9d3219846688462b7f78e2243dedd770b5461e8140985ccad359ba4a279f11806a8bdbc8f1213756f44dd509fa8b2e3ab5480b3bcf3472e05a697064e4cf1bed346009b507722e600cedebba186d83e17eee4630435f6006205dc177e41ef5f1b0c03689cc25ac41eb8ee1d5c71ff512b152aa9ec72397ab98fba03059e7b57b1f0aeefe97e6c63d35aaa1952819678806c22a637c61796b686e6394c2146f91b8f0d197f496891555615a6f90fae03b74fd319a85ed936b7cc2be394a100408b1905f1bddf820317bb07b7f17cfe63335da0c988564e0fc1263e083ab091d0f593d16be8e4c5955d2da0c3bf3f542791ac9cbdcf2359fee402127771c0fde21d36841798b65f03d75a5e8001410b8a2c7c52c6ee560412e6e3eff91c27fc5cae03118094ab332ce97d85ade948f327437240123c669dd1550f76031344b3cd44f1891220abe7231677499386852d83028cf9a5a68febbfad420661082945b477a5a7f4a5199c6742a46a9b2dbdb9ef3d45bb0990bd972818c33984142145bc9616a69e390ce9c29152914e85496bab68d8c4fa3c55013240e773d412ee73ee3df1ca9e27735a47d549b1175e6ba9a69abb8436fda19e57fcf9d6493c4221f937ae645b66c4b804dd9127dc72439b9b15598849879cd35cdf86c066d74bcbecc43cf11d1ac74cdbf5495463cc54bf6b8a6ef25b35f93b5 +b1ebc1e9d459b34757858b6d98bdfccbf31ea7128a08cea8a3a5440feb8dea3e6794c34d5a5bdc357b10faf5a886764e523ccda1f29d0f3596c1786c136c34128c0b4bd82bd1715146ae509b89ae9f14247e615f35917dd038c14716cac6d6f44ddc4ea562b751ca70594e9ee1a7165b1920663908a4e1ee35e710f24597a278c2b8c10f6e11fd8bb469 +bc400fda5d7b8107adabbb0ef293da07f4b182abf21abdfc2cf251c865c9dfc94fb62baf4e05d9ba41bd6d57305f39a287d23789ab3c43c4d1b29aae8ffad8829d98e964596fb74db55e96f7904b89145e03a0ae4965a876a602576708d39f60cbe4ca59223bc44d92178fe24b8e123f322a3451fe37a9d0bf48a5295069edff09da46e837304c507b086e954aad17adf00b0b66e9cdb6dc4cfe10910ab6bfe35e33280d0a1b06a6d1e8e1489a97fa09d48e1b23403edb7fd1c32a4beb966269af895aeee03caae021421e1d8597c395eb6138a0ac1e982168f175e394d81ce86666a7ab44f6c7c4e18569b7c4a78c29889339fc697753049b3f2047bfe7f3a14e76f30472335a14f75aa8ca4d934f282687e0a2a3b7c3aa3bec1c7c52902aec1b6d6a4b4002b4c892be9516437e9f0535dff10c8921e1edd1ee0206c45ed01e1b0c202c02ed1e14cb6e01557d37c1b551c7fba9a62be52b18b1567c6b1005be9ad6a8c3f38dc0e59515aa9b2dedacb86c2b5f4bf15dbb1d02db94cf1ca657706de61f8293cd55297dc42aa324bc6d0f6558fea31e278fc7cc9998c7b28ecb242e7a306f8071 +8dae387897329073c581832d59a62fca0d9ff5e126ea600b0aa59986285752970acf607fba27e13b6964bd50281598276c3cba190820b733c0d970394209cd81e121b84d90e9cbe613e2c8b1960bc33d4b385883ec68abcc4757cab0722ef3986c75e04ef12dff31674f610bb5810b17754456b5f2865664361b3d6e0ad8f1a45d67042a8d4e07511bb16a497bccf35333ffd126c73df94fafb379674c5f2b43cf688fa964bc6d3779617ef827d3563c84dc7c17c483400c95014335ffb199fec81c9d37ab8c2fbb669e71f436110871f58cad49bf0f12d0803296d2e897d31d8d78c377a6a418216b8638c571c37893a27bf51309bc84b7f967723d877a60c662369270ef026a933d01b82f82e6c26cced8bb41235b6412707acd9ca927b4df261971cab1a361f3441600829f5ec12ca0af69519c4dd76776863749490f7234517035a156861797a6306d0fc5ec981a85859780aa8b938cb8899a6248815a5ed37808711d49366b54cd336006523457af0aca2cea80396b3bbdaf3c6f6580fc52fe16922ceb0df8931dd39c6543422130593313d7473dea581f965e44f32ba14fce87cc8c2074c98e07f9a7c67e2a16433bfcd772c2ed77f99a166134e6fa12a47d3b +9ac913c35e2a427069ba77062b059d6a99100fbd9b680b5189aaba45e9ecd7df630c7cd21f3669f780bd3bc53373babe8cdb9bf98a1f76fab00d7ba5372795c97a7e9f887c47668c4d3bf47f5a2109582e0f0a77e2e4bfb15ae85a1ccf0e62a5b6f6181b429ce8bc53f15d141103c32d675ec1b3d553970bdf4584611d43bee96875d2447448c3781e78c3fda7731a5a4630139fa78f09117e85f40ca1f0eca37eae8e905945c4f013574c2dcd433e304b645f90f8043261eb036bed35d4eaa518acb284167f6e9fd8d8ba80bf6a67146575c14bc54e21cda8ae1b2d391bacbb0c12033840421d13c70e2a2791ad66b7207be4bdacd660e4b92894ac1b8c2780e37aa9ccf29916e4faa643a313e3461b868964d4fa95abf822de602bc077a141b8711543e51dff6560e869b7e1416ffa2ce256feeb87481ab49be2b854fecf739b913e4509ae3e84677d5fef112cd5f4f99856d0b54f8e83b61ee5e1f51e304bd432b6481e98c465b573ea1cf927b01d15f9c34ad47d13fbd1276c27c00f43dba145 +9acca6bf2e79f5ea65a7403d598c658449140d02adda83343dc26bf54ae1ba4bd4c1a3b48abe4b664f6a910a1126f9ed9ad98c1af6e197748646a6e4ab32142e2a4bc8886061901f53024c42bbe99b3c33316adba81ae7515c074ee2d7fe8e062df696e83e0ba60e74f7d792b8d1e6320a1dff563f1518949ac29d49e8fb665367ad9ba1fec0e9605e876f060637683a22a3b6248cc2ee0eeba8d992c89cbc815dd3ab6319811f7086b9df79496dcbbc291de61058cdce3c5e33f6b951ef96e680e9fa67347e2033d515bc10a5014ef34848dfe37ac738a2c3db16e686707ea0742cb47d5e56c49f4c76b933f33e26ed939899d6e0a0bb5f442b48e35c5138d14ab96f0e8aea868c3f837c9bc553381ea1499b3410f3eb7acdb917d99da61fa714af1a79b5456691824586dfc1b4a70ac7b486a29ba4d469b37c6fd6611098c965d2ce63470f8d512a5eaa16d622d2897662d1910b4e654f2008439b93502399353512 +271024cb4a6790f2bbee1d2841643554befe6275c8258637155188c8a55a0bc9044ff31b3ae6bef351e71f1c0b6641683ac9427a5d4deee7f51da8d32f5d16575634363dde6cb71b4c7baf57d00d9a5d7aa79fbf50850cbf36485fc7b79c557d8e42313d784fa4acbb88492b9d086fe9a6299288501a259eb04810ecff5fa19000e5016c7c8a0770e26052102a7093adca79945a10c260ed844341be09037012feca2707e53b030be678bbfe94deca16fc2f5c0fd399c19ea4c9e2cc +24265cb04b5bd59840a3d2d4547de1c7ccbcec7a7735243d7f6b01f203a3dff44c4e361570094a427cbeb2771775a4ffab1a113a0558c1e75cb9d872cdf20bc19b4acfc2f921904734de98864a1d59f94870df745cbdf83cc39736174b0f0e20fde6acd5008e1e2713b5e7a756a1f646e322b39be5edc1176eb3ac9763c48eec515eef9aa8ff35f54ea276c82cce313f9517214e5d4f26a72cf937e5b22fa72b1e4f9462615be44d69c398489c67ec256e9055c0077037189ed57fb76e3561ae4416121d347f004e1a0fa77de24cc0e0d9ae11113ccf25835ecad99cb80a70932d1be517aeb18e1ed203a408e10523e5b823e41dc79d26a30cd59536dd5cd630fe878175cf56356f635a527e912a9fc2d190fe458e0286c9ad1414fa1291dbdc51ed2fe4945d1c874a5165f11922accdec1232a4145a7c9dfb4b260b16b0de29e659550186f1c75616f534bc21c9997d3b7e9e4045ed2b1e0da337b0465d80f6d9539eebe9e0690573097733d06518df81bd82af17ae6b6ddf45bad0ac72a7eddb479a98f143379359e98aa2fd8ad9b87aebb9ad7a6bbbd64238681673bcdbe5e9e2365689e12af4d30ac1200f48c6cb008d5e2c1c03faa2a713bf18524c7bab07d4ad0333d1be33f17b38bb134653be7b841c587a1352314df9531e6aee48aa768a55796dd7311f52f8bb757c2e6b2b0623a20ac2a3dccf505e +ff0d38d5fdf648deb53301adf4da0b2d961b017153def9c83b0d507ddc19232b88ffb23d677a37752909faf790fab9fbd8c0bce1f68fc0d0e15f7281abb075a404a2a8c5aaeee351ca18a512ae293914e316e301cf5d0462c802bf66b06cfcdc529807c8ca6ba12a55e16a5a9ef33df7a12667343879ed9edfaded6b6f87debb144fe888af825f1941fdca9e0a +4435e012675f726db9e576facd4eb01202856e052f4db1f16c62ea6ba4a83cc27912d77e4fec0d4e66c6f6b568c779cb2ba1e2b8c665856ca1a175f4c5c96abda89ff22bd6e2fe2b07918971953f4b4f5d32b68cfb7d12dc80e8e32029da89c9ade47a40549ad344431a3ff88d3a273609f07a1c75caa9a1b843dd39803320f3059291ccfa8a8c7c861c1f06fc00d68992d31c404b98245a9f21aedf6e22a202dfbe01ed67a7a38b5fcfb1d912f9f71991fb14c5b3a666a6122e01b0074fd3ef3e26643513e9a678811f7c0b0c4a523a72722191d4e6c56e44 +c18109a8aa01a5791eb3245c0acc04110048bf1feb62d92525bd2e0ad62afa87bca0553d3905d832aa3b0e53990e3ccc954a993d651d4eb1861e267520a8b58e77b51f94202e1f76c34fa43001f6d3d2d2ada73d20f1c678b6be2a0030d84bc5b3a863fafe12a079db49f64713ea7fdada1214119f9a657c3fc9f314971664d92d1e0f4d6c473cef672b94561e3cefd92610a432b5261e82f36666a84a727052100173bbf923860a88a5ae1cf59e60b5427faf10f5789704a708918ee87f81459ca662ffaf1d397982c88b8413656217fe86173be6a9d96604a8429a6321987080f19e95984dc4f286f1e5ee6d0349f29608bac380c955f8f477c02a94d9d944721ef564ce9063cd595a9d08614aca2e4dd8e44e6b80af3d1a7d207b2b1332536c8aa2638ecd3d1c49eb4acc4ee647fcfe453d6d8118d6fde8abb1638afbeca7311869bec9d31ad5fcad2f0b50a1434d2171b1a61a59eea5c5da10db074aa68675a2efe7c7661d7f8d75011fa2e78d04c8fcd8ba9bd88265c8444be80999471a48fed02744e211ed8945132425028c370f845e9fc51888a189e0b1569ab64e882e0ef32e9502bc8035d15b5c88a542347b6bdfb85415aa278a3faed6cc761de7706d9a0d5648cc71b3c3d51f9c2c7497dc7bcac557a866adaf4d56eb8f8cacae1c1caa381b8bbfa37326c7ed4723871c17d1b707d75a74194f89fa356bdd23f7 +d2bff89f1a68848286570f9f8d828f6c9dc8283807fa16582f6d989bd9eb42ea4e68d9040d786c27430b5ea86bc367ac38754feeeb0f680ed054b40c54a50f224433918e329714588e9f953907dd8ce9fc65f6e7930b96d2da243cc85c90e7eb53e8de6c158a8984a0b9c9887928c51b82b9f6462823508f972ca43f96e0533d988ce3f9b4e32b329cf66fd3f7784f339b5d4add9dce282b154dacfa987834ce429973134cc53bf3a2e083b75d831f2bb0cc10ec4282119a86c0392fc79e0703c5f5e1e6c82e6d2d963443a77c794587dddd3faa323223008e8514bb301096a3142ac3c9574f00371908070aa63f07cc6fff970c281d2e6afee4fe02ea0e8bb175572b9b9876e84b2a59ad7bfce2136b29804b572e7191998c97943975cf710c5f5ce122f5fe78d0076223b201b069a5df46dff2b430df64ec069a8699dd9f30e8ca0ff290d31a72b0648992e87e65dddd8ccc0385e6c832e524169847ec83bf4e09ea5f5ffd8e9ca23e67d6279f78e3a21649481bb4cd05d771c713 +8de331053fd1e4dcbb35b3486682ebc90503b86f6be1c0d61940982a8773701f5d7e40df7369a2e2ace2bd92c2ca69000fe33e218236939815081a37d790b6f5139788bd6e2d572382b0cb44d8dbb47f881e65df707db93713aff41df8c69d720aedda5ad5fc5cc3c46fbe76df9d41084a40491a983ef75e1e402f0713cb97abac98c79f061c318d +07e6c22e7d00cb1adec45ccfbb8b43ed5fe464f5113d3c4d52dd0d2d38fd57b276efa885b76ef627c1fb6802a234c52e3e47932fd9a02df9a61ab517d8a6f719c7a697b244c315bb39562fda6fe769a6c6ca79e51400dda489164547cee0663fa3103b18dbd2a497207a0fdb04e3b8a49ccb8ea8a1656e55df609ebf20c77af1d76069918b03717efda9e98102717b305f63dbfe68a72eb0648e8810ec7ec96e773144286df9336ad42885d655fb6197bdc064f7c94b81748289e81bbd51257dfda71538f556e8f22206c90e7ede7ac0c399876be2d3d56a8e84121fe5dc449df2d55263562d725a7ced3d940f881dc40338ca5036ec0836f030a97146bafcb8a90652e67f92f9d15d586721 +55a764fd94a0ae2606dea0ada58d831738a9e778533acc36d2865895c37144a7b2a3bd4d0694aec8c7edc3e9775d9d248834c5fe0d937fc6cc66e1ec58a1efd30dced22b106c4a8eef90acbe8c0ad307fe34c7f9811530cdf8cd0e64812a8040fa7a5eaab5c215541b59e5101ffcc0d6d5b72c188d27a59a893524a9fdc2c1302ed8784bef7ea53bee836db87c4076cef02af45e8e83dbab3806ae2be5916ca2b49f7d51b4d716e21be9a0a8ea552e7628de60931e9c0cc337f8405a21eeaa68ed95cf5e0e2d2d79ddf7846b58ff35b99bb5892d2b2a8e17c5b9f897df710e6ccf7adadecf69962a4a240c972eac977d806d7a57e1661014e754224170cc8eaff0d0ecb85542bf5b1569c5a2e3f1062e02086170ec7fb340c9d988722205f488ce0952c6e1f5d44d2a356ea4b8907ac6a2d9409f87b985b241c5f2e991705794e7964f9c0cef4e91b96bfb80379219cf93b5bbe4ecc1d2a5f9ad5792e60506e309504f12c6edacd520db90f8dff6ced5abbd623f8fb1d6a2bca8333e938478dc72cbf8177000c18b3bbad925846928e04e521958b2424ac56cf98d08fbeaaf5c2d047b548e11 +ee9ba35de0e996f159dfa0fe30ceb45198167e2fd699b5a68f3bf4b710844b21d32292358a0a8301ed3b55665f50e4400bb45d8492117e30fcfa7eade2f3fb14e57e60b7ca9e81d4efb6ef61552d12c1000b72a582c15dd9535746baa92362f8a8134685a4502d54a91932502fc8dcd9f63501154c41a4456943e5204e09f363e4c2dc948826ff6a2b2d3b0585b1e7434a4c23aa24566987fbe8f862d25889fbdc181894c1a7efc11647b8f0d1b4b8af8bba7c1f9fde60fc9a2d9de21e330516ea63d352cf28a753d956bbd8d2be415a77b5a7d9b733881f5ff7c95523ece7ebed294b7d2bdfc826eafbb3a783209d84fe51f351707c26d9ccb0fa82c2574eee0f852758f61dbcce7f2405b913967f44e9c8d1b8a87794ce6e97d7cbf63cf4bc68ee +e30a45a22a30acc64166b5cc389c5b8a35943b602cb5522706d04f902cf8da57e6fbf4c1de267f1238a921f292685fb9645f693b728343aa22b94d32fb7ccc3c6d7fde130b2fc58c56621b6200feeb00757b553bb4f92a58197a9f079399493535bee75cc05b8f769d8eecc5f975aa817a12a7ebe0f87f7eb08f1d1973e02adb7e09e355ae8b8ff4ec4e995cd96e589b6825e8af7adf1f3a80466377ae2fdadbcf819a6993774ee4239472176ad15f5e3ed690f95cfeb3dad221384f10e6f7892a2179 +e8b068e3555df9d83e9a174ddb51ffcbed45659dc5088b80557cd6e526af7558461d54ea7ff49867694d34d806eef01058ac4f553fbb891508e072ab781eafff0e6d290f56c0ae9a1e79d01b19ebe24041d33e662f184533744536e26fd43ec7c704ebec9c16762c9dd59b59e45cedd9725828ab99a072fc3eb5346bd356ba58c1dfde1d839537c90f7db7c133be9c280d6ffabbefccb7de0f0b4210bae3f5596ef85b689e9358c0aabcb1eb8cb32ce3b02818093ca718d7e6c07394c7776e18fc95af01e108bef6c7fd239a8d66ca1324f433719331ee26840d7f4b50b4f2d5ac2079712787037d44487b42822143d1fde7da9a530ffdef6fee8aa6fb32fb1362ee139cccc0005fcfe165c8a37da750ba2550beb6ff1552 +7cd44b42c33b7ef882786dd20f3320857e5b067fc762c1e81a2f562799d071a119c52c6711e6e0c9c73c9b11886e6a7791de94160b5179b8de4322f2d7e4e3b500ef174f3324abfbc6f716f81b666d8231c94f1e453df99f563912b1e382220aa36253d29f47900855c992563390700d1b591df43c5c3b618f869712b65660f36cf1b7f30d28a7ff3c51b3b26dd8111f4a4977c08a38d12ecf1ccc69fa95549c52dbc287c243ab0653b65cb5c8a9ba53a7a0363e4998602ae75d0483b29d715bc25234c6c003c59a1e2c9c17eaccc22857a018bbe7d2dafd32dc3a5b37b4169a6f6eac4e62c668065d87bda3e89cc5321d5592b7 +1f1cdad456e204ef5328ee6a05c311e112be9e1748f6c01a0ca4a1585057bf5c534c8ab47d98755887a4b622e32aab438227339cba3a0d770ac3b0160528ea8f9e96e7ab033496d8ed9aebb0e844ad2de84944058dae88f7acea61c84c65842ab2af686b64b91cd4bf1e7b0993ce5028242f6fa6783bbd86bb60270487d87180e5b113a500adb49772add07f64f516c62df99aff5fbf8ef30fb6e2ebf14cada3c6c37ee0f24ef8767278ee1db27eda34d6a360a28168f87740c1b9a18c55845aaa1746902f25bace6a2da7605766cab7bb54329627a7753c2a42585d3aeff53caa +82e2677f0a36a8a979c5d185cc8af6c2beaf5c485f6bd1c7039b7847be7d9b84b3c33443a08f131d81b33ab3dcb1f52b0c60262ce74b073809d93c569ada7369b79e01aefb4e33240b3aabedc42a135add86c81f14e47894177e7cb42737aa512e543d6be495d54a442f89f9d30e623bc4b2845855aa8560e049da2f01620f5ce9ef578ead96bd84811e2913f61627897d60690d18b3704aace5eda57e7c913acb89f7f0c2bbf31bac996b0cc10f8f0202b09fb7f8f862783f10bc98a38f7959aef4ff6d0c1a1f83475baa7ee7b014b5d918ba69ed1bc80e99e0d50dc0cfa46cbf90d47f9db481a791c79a6c9b5e21db268897d513397a5c1b9a8a2237 +22bddee568c224f4f528433f89f663595ec5f0903864773f474b85a7d4eef676dbd176708aceb80395056ed5857ed4cd395831f925fd755648684d225d1b19759adf5429fbfd695f6d3df5d6e6d9bb54249c1457d6e31fc59034de7f2ec33aea1b95f841cd8a2238bedc23a8bdff365707dbfa1aa13838ecaf1626c57d11f38c7eb4eaa37b9653195ffede89adb438e462d3b88da9a133c085860fec2a1f0289c2f4829d5588ec5faf3bfca39cbef960ad74945b588ac551f8e649ca549aa865a1c1b74fa35725f19c0e28f9dd13d8ad65b57d03d98a84daea8cf81da29ca88074436453192e93c43ea6f6ad298e223354f701cb196e12dc197dbc2fa5597cc57a3e8c1206f007a786a9861d0f9f4a558a777fa6cf5c4fba465c9a92e3fb3402243a1d61cf25bbde8b6b47623f8113c98c58199a039b266d37063e7607487f68cc85afcd2dc050fede135d9a3c6f206339b3e11da48257ae0b611912ba8b66b6637fbc24ad4816957e331e98f85b6fc1e345c0e296fbda3a67d9e0b09837338b95bbdcb5f8e71e1adb4e2a63c9a14e611e239eb0d2354992f516e6cdd3d20065e47178cb64d09fae72d5ab1753462ba5c30349a401ecd9cfb658d4ccf79979c134bb7fe23e9222157c0b2396c82de23aa899917023da5ff6b00e3ad04f714aee4f2f931065c2028b +68659819d0eb6a8e822b14b4c1e15d681586217989b700db3d352ba78bb3d61bdc219382fffcdac23dc792df9f3c8eb931ccb0964ce37348e671275d9f5622169590fc0619992df6beeb172c7c47417b30f438a30c44f334438431d3051f2c41a5c15e414c0912cd05081d3b5b14d28343c27176eec4ccbd5afcf7f35006319c29fdd0304f423a9b544b0b01ceb3c970013bdfe09c0f8950c5ddbbb2ad3f27b852cd3a52263ab6de6daac80aa908e8d9a3e9b975318d356cae3b8bca5909bef7e91701b05f2b43e661b4cb3c56294047ef766e77ed8bc8d297d6462af743ade1c4df814f553ffa37769721aa4bcbd284502c386a230c3fb57333c7be4150c614a221e184f22dea2197e7f0df1e4235c79fcb56c5f1d3d95796acdaefa9c753430847c6a96f401a05dca0ef37c280cc2a274455522e0701a0ed045d6745763ccb7ec05abd9c4d78d89de85ab85ab200fe4bc6f2af22195c2611f821a7b0257861c8302d69261630579508dbf534dc007369b31c6d7c46872ca14501d342b6415090be71e25997e570a32cf070660a920a6aea8a61064ff71875b5b54b29039acffb040b3de9c45f5e86524c05848a656e7ff5688fa0db84d81da1dc89e81696e9b01100983d3314024205a8d804b67413948ba63ff50924c43578bdc1b225ef80d915bebb +312ae4804b964db3c9eb33f5f852b978ac406e04b743c726df5edd851fb38a70c454799253c87cdb4117989ede991f1fb145ebf5eeb7bdf482415e0d7fcdec24201c0c7a5f53b17c4ee3c5c3d0d8f3593cca0f5833a800d6470caedb42428e5efe1fec896c6b6f34d358eb02514bbed4e6f221f79c96e27890ff1c294c4210d56ba2460b1b11783767a837ccabd15f70b468800130d1a0b169a383348ff0 +347e1f388d4e475bf9f548f77d224a7c092e8bbdbfd81122afd89b4756df47a7f17b2f9426063004f05a860fa30a12966c2e45b19dd0490475722ce69bde45b892e668de5a07b0d3f6943646f731104c20d804fe050a365944c7219e449202b90a009afc0fa607b9ce06e1e98931de37613c658c861d1abf5d136badb7080d4e8235fd1a0f69dddc503df93f9d1592f3ea2afc7bd826422faac10f9f63a1b25a31efc1489dd14d35aac9a158b3e193c7f2b1d3011e28b918c734dc341a7390e5ae618e4495f16ac44ba273ac4b761179f3c907cc0d6db438e76c6c5f4f4d0b7548b56603ac37b6b8ef9ad70ecb4d92c2b2ae134c576042c725e4e9d0d7d41442d82f776630f81e2b445a5e7d299b1de503624278e966a253a74b229d8640d0d29bb551aee134ded6bb6fa73cb24971f4d1e5cd868034e7f72e +627c533593808d4c061a5c1ff155f0e351cc9148e831e891201b071f4845858595125835b45cb9feb24298db69683cdf6f3581c96214fc29c66b4eefa983495035d55639bdfb1faaf2a4eafc4cbcdce30c202fcbd181790f7fd689a8e5a09eff7d081adb72a88448bde62a4066b94a003152cf8de2948832076c28d648793ce968475fa53076d914254e570892d727a8fbee6356309ee119f5f87cd2c8dc5fa7fb2a99fc29c08d298dee5f0cfbf44e8938172f1da5bfd5aa55f2ae8477d31a6750813521f6827509b60566ccdf3286ed0560e49260a91f165fec8628a8b78d08f59bcadf +25a69145152d433fd757340493f6fac20bf574dcf28588557d7381092cb9404024d5d82a2ffe4e12b87dd48d53a4760e16d386b460ce3437d79c1bac45c0f2ef1cbc826ff6b15767a17f617007391721b5933422af61411c8fc0f72fdbfbc267bff6f7f5cd40bd0c46e12fbfa2ef5dc5df3d2815b227f5f6a9385a956edbadfa2fe90c9aca6873233d84f41dc3148a21951a1e36ce50fd1acf124dd73d881d2039269a3ed0e262387694ff18a1cf57b9ff8a621b9f7e9d497c120b728f3bb570face814064a90649baab78145228848e3619f7219b4557e07002b6c394014100637d92d8b8cdc94eb284322de6afc4d52856ee6b267c95cee5f4dba65f7444952d2af3e0696295b4f0dc80b5d66186c52eea669598ded1bb10db47e167e79d33a193618770f430567feaa6b1493a2203ba74c082bc78b2ee5d09a5d52f7dd07fd8a9eb64ce12c56a77ff48ddaff5d623b49b79e04573b266f90d69fe73b5647dcf75947f666c37ee13c66ec22b822bc0f8f332c8b29fb6b21e718bb477e6ea2cef0564bb91fda902d49e964d5ca834b379106c182f5e28fea0d98bbfe12c11716cd96126e7cbc7cd1ea14d70f35f +df866669e0e25a0ed945094528ae4669e3145200beb8b14d46e5bfdc53a04fce18bd6abb8f38879e36614010ddb8e730f0fd9a7811bec24689da867c82d811739e186a04379a00ce28317fae9957f2ced19a9b885086b78a1797049bb2e68f79baa8b16183242b5fcf842e8397902f5fe7b669264708f2fe995d47aa82df2477eb6f6900d5c7b9ec05219e9c8e5cebe7e1d1b79fbcfc900a94ce0afecce0e32332e1b8670b64ea6f4577ef0771860c97 +65c6a4be590bd09c29f53a0123939af58da5e46d5fadc44512d9ef839a14b7d98b5cbf5694f29d7c9807c3086fdc88f8653fad4a200cadcc50d50b266f97ad469135ba220d271dd9f6a0c9275526a9f62f7b2bb0106bcd43e221dd02bba09bbce379705c373fc3d19ea8577bc5a52a4c9d61f2df85142a7c8c2e36e64b8b1d +e9b590cceb60f708c559ef717d1a674183ece9d728979d34adea8cfd43c9ab5d44ccc3177e4179bfcf2e3ad0c6c38365b8351d964d80cecc0d6c9cc8f91b0966e975cd09fdad8954316b3cc306fd638b1d5ebc7bc1d122bc6c81dc62e7bd6545b1b40b372a2345c252ce9e73ad187edb41b7c7558ddb2d415a983189290183ddbecfee98a9fcc99d +dc7ca8f5d6107b32aaeb9d2cdf1fc1d4957df81fd790c84d4dd57ae1ac8802b5504af3f1262956f3e90550bd04ac6621fd747f91d00fd47075df6c138b6502d42b39df788515750e5ad9fd9e1bf07e79093d73fb09616222c3228628b907025ae9bab06493aa79d743707607b8d7fff01866d55e8a6c134bf0d4a7b186ec9f7e4e7c25a262e6a5ca38a7a918187cdb26019dd03396df5826e9e00d9ff8b87c2a5e83c49ae7780c944e6d26d25d047b3ac6bafee86d9451845365e32bdb409607f0cb366f27984c430a23ca95927cff842c9cec7dff8a90840ea52c7be2d3888261f1248c90cc6b0d93b918880d2ead93b04474a80b062b1e31e2a8735a405063f06f1e0b08f84c54a3ecb57694f623f35ccf591f3842ae2415a50b44f31b03612ee71076265a3433bd3394a8de12bb32feadac6796496317d2f235240db9043c1df3ba5120dcf64b21df4129e2f4aaaf48af7bc318667271cbeebecc27702edc6807b603ec432d1e00e0e00c718b57ac41614eb378b25dc9ad697b19d18f79f93ae384900bbd14a176be4e1aa88c7029a62ae0890f4151617ce0636fb59697c53d0c1fa9904c7766bf39ee7ab43430f9a3c3b0942169d8971a61604b312b0b1834d49b90cc8b37f2311b9ed75e1263ac7075d35d14aa56bccce7888b054939a5bc6f52513353 +b674b45168f53fa6ae53a18d7d1caeeed04b963fed852defb5a69b0cd1eebf41308082dd83f89f5638791d3a85d8eb37a2ca73c33dee84e5e27b48c035a404fbaad48a670e2697c27e3adbfa7e605e4ed883fca4c44251b95d98da5513e8b4342378ddfa7c4cc712a53659ab7b3de6bb5546339a5b3113e233716eec08e8cf3bde36c5ceac6d3b66fcde62a5bee2b9b88e889853c49ea51e1c77b63616bd3c3b159b24af48bd9bb0fed09bc7fa027b4c3d6c8c9eae2d5fd44c15d9515afa1bf04c39831418e7dbc5959d5d47246868bdf85da085ed66f0f9cd5ae2b19de073fad2a24259c228e32924705fce56b72bdd329655cd71e379dfd3bb428c34cdbe48e0ad85a68ab9b812d411a35e529cc0b42139c9656518628ea87cd957b555d519de7a40ff810c9e2aabce3f7d0df300fe0261cd330b266b0a893609c4048fb31651b4f8fea656be7cdfc3662ad85c47ff4ab8ebd7cf3f7f1415fe62cf3513970ce895adad86f2be2bfa01441f9ac0b23c3809d9ca071ec0e7bdb34f52bc2a09b7352e7818c50be259371928dba24d7f7f4c59d473ee0c516fb6e2549f80a93427846949fb281e22c3ab8977ee895409be25fb769954fb5974f7d75ca9330820445c59612086cac1a2b584ed16afafe256402c63c87c2ece3fb84891328d1e4be42239ece33d72dc3929a2b0a3cafe872023ab7a15bbf5723ff0204081ce151bfd65bd536192317c +cdeda8a1066d30e165cfe0d943f38e1d65f14e232511b071481ab1ce889ca35c90d0aa524459489022ab88c2a283f4ceb681b4385eff93f39751fe77afd8d4f40c65ef65e571eff54ca099e27d9cb021f0fb8fc36c20c17f033c60e762809a94cbaa5584aac22d286b034d12fdd6023f2441388547bc90f6f7de2bbe03da8aa4d311efa13536325b3936b6546df80da886ddeae39e89fb49a36d13a7a4e2473af5f1638d8c053d8da9c7f4879395c62d8d187955df47bdc29ea70203a89efab85f5c966141979c8ad01456183941a1589ba98cdb1608d3e5b3df24acd74fd329f8d41e23aa544aca31e9e61072d37ad9ac90f0fc18aada69545199756fe3fad6fe600b3ca7e8aaa8f6a3e80e09353c627f8972d39100c6cd2c3356c29c4987936a8675388976dc6e8554beadb04dbf86d4a2c3995c3678026209b8d4f108f442979b5c48874035c2ff65c2cd5d6e6d08247af2 +b2d8b2aff0bb3b3854b08917b0085214473a4ee42240844d61e31f256092e7c0b311e892afe7e5e2d999480b64d77253fe13354a1170fca5ae02b15f364f79e2c09e6a747effdd6f156fd8125b3926aaaa2b391381cb27efd654e1345f10815e29ec733b1398ad126b18e682f45a3c5ad5fecdd3cb1c8bdb4355de5966ee8c63b2ec8b91ed88aea8c9618c37c624bf30797f173a63ca3403173517e6859687e96e11d0a7f0412b7958062cef6857284473df8a0f30029430523ad002f9cc0a066b09929021e4a81aa5805060f88e55fc0b4c2a0b54e4d3079e2ce6845e05a1c2b89cd6b50aa8d700bf3002 diff --git a/test/hotspot/jtreg/compiler/intrinsics/base64/longLineMimeEncode.txt b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineMimeEncode.txt new file mode 100644 index 0000000000000..a6ea357a7dd3a --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineMimeEncode.txt @@ -0,0 +1,700 @@ +AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4 +OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3Bx +cnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmq +q6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj +5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w== + +k6r4A8ytszE6K0LqJXLfYh11VVN9bmDoZmunjWCQFLEhOOipRcpF95DW8ROajitm3068+kPga3qd +9jtNZptGQ3r/pXm1Q7GBCUcnnpyZlpEzqwh34VZEgAON3NIqAIEIWQj1/ACLtVG9OwMob3NbvEaR +wp9+Y1wnFEynDf4c2ZFKsLgfEx2/PvMoH1AH0rsWZVGWpo9yLR/8bG3Iw4Roxxzj7OlW4828Zaal +s5k= + +WBrp7Jho6FfByCFvW07pHqfW7cXeiPkgMwetaCNkpeMdkAAe8VnqqlqBIwqb1S4fh15FTeg0rVjS +w3FFnFwQBzQQxKZfhWNi167Ma3QQpWviddFNWG9BuD15wNZXYbRviZcrOhYniS852XpNZZCD0rHk +JXPxHaGHSIckH+rrCGlaoUengGScE9uGel3CRyyykz0hsILyuguG8ejPAHo8rlFHY6lZkrDF2Cja +bUTWDHIfK12bJLzIfDCiFY1e7GMAsfBw86aeHEgXxzQAJQyvboWaNj6TCIZwAo+o2alNIW4KAUXk +BhAITY3I4psoHKSw/hvq1xmbv0l8SG/5X5Rct/4iOXLtHqjRJWM2dSwUmxp+F0kGqUbLKBFxLH+Y +7HNPkXuNwSxelYPcPx3rtQVWakVKf/WElmzbyqVk1vrieYSEjDHwYJHQXH+3uU/xZdOeqgpr5QrX +klLPOmFpcx/5Q0YBWpO5BusLpExygMEzPzvE0xS1H/WGEGA= + +8dcsB7oE5RhpYaMTzZREu/3bNUUx+FCZsdmstvQguMpStzboCEyWBbKOyi6ptoc90PYRYKdFdG+8 +OsrJYAxhEdZueAkHXUTujl/1GZKPQHOWrsB2RzPB6a2U9zB+UHdHv5pZSYd6suFGZkamMQaReBIL +Vq9oQwzPtrn3h+zTSznNhamm4FmkXVPyWJwpbzyf92v8oVyOjJkhTsyb2d8GVf4zQUZLKpniczSw +Lpr1JVM0impCh9bNML9wSuYFHKrJDHfneU3GjwMSjhOKSEd/qzvQ0hSt7ut87dXLVrIFBp0Upews +nIFVjoiYOQnr/iQdtnbPDBcRmTir1X/682kXLf1KA18NE/AyrVdcbyVJneA1/8uv64t5kUrl1wHh +ZFWKDXjfEAkvfjJFHB7Ik15XdE11aulXTT8LMQmRkTZdbk04KgIcAy85oLejVmGTI3/TtAhpHPre +Hzc1stz9z4TsSp0uUpx3a/ijpdIzUh6fFtDKdIHsETAPpQWMHs+NUUqtwCbqPOmow0XQu0AsVaup +Fhe3xEDw + +SqsrwtQqIIX1C/YdoAlvMfhJ2TzBNSteN1hInPj8OQCtbyctDgo5KrrKgSTUOjwHcIxO4fDCuMTJ +JxKXbfRZKVe9FfsL25gYJIPojPFsr79Da5Nqli6qTTHwl3LRn80yHh51PnPniXM/vgMjNONtcYq4 +Ho2C42eDeqS5RIT1rTpIyH31pxxBtVOnOA0pNfdXs7KEXlv8Sn2GiuVH5Bb1MxvEhAO3tJ+vAh1v +rGj8T4LmiubzUgmHl1O30PAaGhnUsu0EkGwo1RFasj4UE/YaOfHyIq8sYzflcrWt4uGrYHykLa5p ++Qh38U4XxJaHJMpwRS5qxS+aCO4ryFQuzMh5avSIFMK/JmbTJWS/1g== + +ZE3mfTaoaWmBECTNbd9nikvk3dFzaPW2nDCyS9pT+mv6w2T2XjEz001DnKhuQgCippWMoevRqu4p +QowVfwhfkJ60me0EXTHQVBqLOi5NhDNWXwze3NP2r2mR6fMufG0BflIbtc5kL5LbzQACnKsbK5On +izdTTua2Zxvrj4pL7l+0Nwht6BinzYm/YotDVNx+tdZ0he6R/A0E01xRWqqA1bwvxc/U9O9W6Ywt +Ehb2d9rXFm4MIwEgBLpwN97SOZB2Yoljgjr9CRSJPW9uVMutVy3QQYfG2IHEOKQ+9kCLct+oYSGB +Tfyw1DUXjJYfcRekmXQbg1125P9YNAJJp936kx9+kQoqhxF0xKO7WDgrwTP1/LKEt8fmPud0/rO+ +jDKSSCzdm8tmtvkDRMe/Zx0T3ZbAzHtWQLBL0rIgat+3oLBama9+Ascf7DsGl6YH/JCMiQmkBhnz +nTsi+GKUU5iVJxZDNGD3zJCqyAPs9kC04FcO5rUAszBJjlK73cyWGBbpAtnMdLXpkgFS+OcRaH+x +osDNYdhpQ5/3hfDbFvkO0SqnNMunPp2csxd30uoK+OG6Anv6PZ7ESjUQBHS/L76jr0bH6l6k+XRT +1kKkTyCbgnAdna+Tn/wfktk1HaVHATZMiBzQP7WBw/3NSsbV5j8pKZ691rBA3vh7GNbGdo+uITOg +zTyNpeFU + +T9aj/+YGEfNm8h1xwhh5tOn48lHVrytqbZBZjEf4hAV8R2kGozuLF/fWEZR6tVOtPfFPXaRQEhIT +uqRyTbJr84cLnNVxsUegeEOUX8jYZ3ChUFTI1z9n3YvSAmsWQi6OGy7crfMI5Gr3Vaea2Alrr16x +W7UjyP9GsZxuH3okAshaEnL9yHZVJVoqIU6nmHJtb/zBkwG8bAhDPMpTKoBom+7PIeHXdp/ZlvO7 +19GX8MPZfIBphpUQTmLIVAgWWQ/Rv7C0zNL+WWWSvgfBseOmR9Vu0oikTj0kA/M0tlcX8KG0X8nr +UpWKgk2Pw/I= + +ygSLUWJPLE9q5qHkLbcFJuMYsxkvaKMp8U22uDUMoi3BfzlQERGKErlfT23ToKJgVqD7FA/Sz1d/ +1NDKbvidAbFPUaEKUc+HSbZVFN8hkwwEWe++nRIaKGAuXE7EB1QsUyT+bdAPgTsVD9e2n/CjalSs +o0jl0xT7ZGOT57sBPyEwTowg1TXdfuCjFGzuAVQTMyM2xz70azluaEnQu60BpKDR+SndvJCRy73H +UnNg45kZKiUK0l/R5ddWXddF5+fg + +66UXSFBwzCBYQM5rq9I24X77GkWcGWo+gBoK56Irx6iPmoq5+pEh0MW40G+riwH9ztXACK3x/VXQ +I6N+PYJCqUn6grFJUJEh42iEwZSBKngOT7jLpNs7G/1SrDLFgZy7IAGAe3fM/h5s2QWoYWjHTvND +6ZZ+wF8UCRjavpBFxufaJwkRGEf2Mc/zhT/S+6KgMfBHB7zErumW2SB+v+7WCTrl+1bA9dd1GkNt +WozHkTLX8sY3QbOGDo/Igg== + +GmRba6+KP0E1ves8fAr55tqJ3MVoouLC7qyXy5i6msrT6u558btPZJlf3U7Qfc5SjJknySBLS3N+ +cMNJ13tVVKn/mAkOBRxbFpApXL7KAHx+3mjX8MIgFifcLQ6PHXGL6Qp5t20WyKeSeuMf6VMgojXU +YJOg1UnveBroaAXLLIDTVjjcPwm6X9wwdLyzrrLbKhwsTq0eZlr69NpZOYmwUd7Ww4zdVIxX4Iq6 +zg7cro4tFkgRUqnaCBTaPG/0h7maYDvOPaLVURxU9Baw3z5nfGcSs25GBm1kqRVMFKRd7aMS1aVu +qHq1ZHCh2HR0y3y7ioKIq6UnDKYBpafUhs943vvAv/UCAnRXIbakEob8OEuSwbHIjaYW+mpLxBWk +j1kfN+GeVt5irkI/1zJRVPvsn7LlEtDz6dFbHUcmyCdw6SQgKnqyIkKOMT+s/ttGj8a13xvMAxuZ +bYrrN6eLdKka2F22Lc/tGtDr8MZgDqg9njB4FQJZTf8nsRuuecOegfrkSiTxtJVpm9GugY3+8jM8 +9pcDpdY1vP8jHfVMKaYI2/lFyWRQkIw6KqFB/DEjZJnTe5XOMXhc + +yGpzF04SQsZxXyxQyM6xKElxrcKIq2eWp5aRH1XyR12rN0oSkktWn6DoUMbJBmT1IQRLQuFrlK6N +wXKqMzGRlStbnlcPVzeCfGJJ+HhyQIu8vRDb9DzXIpIHkuhtlyLvVv4JqgFdStv/TZ1feH4zRxG0 +WPHYlcwbbptoMlCwwf/mh7joFnmItdJf+LF96exmyXbN4syzi0e+ZkpDqzx3/2BJvqk8aqPGvJKn +F/7Q1lZgx75qp965cEa+Qk7yZ9O34HUNfWOEnKtYmmEsdDRBWzDgXeiEOlSIvbpyDT+wiQAAyJld +tX5n7Y464E6yaRkF4K1xESinvLOQpXxinwftW2+BN765jfgqUfzFsND7xg65l+6LkTV5xz4XfwU9 +l8wkog6U1P1OYAkgwUe2MIKfCFo/APBDqz7Foy0hsNvDJN8yA+t3ogNaYtbCMxwP5INhXMf8t8iZ +p8zbXB5aI9I1Hl3P3TZsotkVLWO1ZzSPB0pPF8eR4oA4BR9y8QBN0HR7KLL9Db4xFymabnQZLB7H +7KehJaD3hxgIKi35bJoc1TSscIJODvyam98vcTmk80+CBShwIy5JBDeh9L6OIij21wROrChPDrxp +6e9Bl1E8Kb9pDkgHwycHmAc3tNl77aiMY8dWXsyIKVJxUwNg7fN7FsqTptsOuGijresSLbCM + +d8qFLaYAQ+TX5Beyf85B0ViTq966wcFaiiupM8QSoa+06CEeLtlDQesMrT89jeV5LoC+EpHbROV+ +4e0KsFjIdWifGG7O9npd5gRC8rFI9f/mZvyOVNEZjmX19xlbntNFcqqM+F5bY5Yo6Jafeu6mSRxf +p5KfZKNAjw== + +y32UrDKLLehCJrsW5H771kgpOy91y7/V4gs7uwjo5XvVh6MbB7HP1lwG8lsHzS+p8Mxmc0pe7P5/ +tQkihlgMiqxb6M5xL+acXxwRlQXL5f6ruM9wBy4hL1It4glV3wfbdiN/Pob+Y1LuVSjrHb0hAHl2 +htAOR0qef3DUtZQoyd5vTHMZeuHF29Gv6r3AzYtyB6B9ai9FKwVNR0wj08pKyAxT9r2BEODxsgiC +k4zr7IlUXnOajpOaq/odrKSiHpE2RpYgANW9s8oooAcAnDHl/rs5rB/62RxSEcXGqrjPNFuCwr3S +ePjh7XLBHf+iRF7t2rDuHTH2U4m7rsJRLKPVLpg7og== + +H28+pYYbQT1WKC/83SROqJcjzOOXAOwSOAwVIYAhAvnXi/Wc0kg75Lghfj6njTxs4K2elpQr7TgU +6IbXHFpf/M5pyBlIATpOogBcXmN5YOgmz2z7Qlzpf2AKDGqlJ2/72zXrZyB6GDUlfRmW6t68ufcm +MnY2aQptmvhBpjjRmfEuWPHb6HJL1D8DzeGWN5qBy0R+P9me9OAvZ0vZBtN1LbMQXQVPa/iCYHmf ++vBpwOF242+XTAOeUJP+sKChINTJqdlCfyqTtT46lEKYv/xtBQEqF9q9hxZI8sOeNu3s1PLkOoDt +R9R1rm0ULnf3HaRId6C4+sqXspDp2TDPNYh9hUdtG5B6mhMaeo0QoRHvOGspLFTjXasjj9ztKnbY +xWSC6WcsEWe8FdWLKfB/OKco5dq6FzqKOPZhUeax7SBs2NNvs+LjmBgIno3dlivf9cLBYv8dCvGP +/yXjUXYStTsb6Ra5KtRKXhV3Ga+ljDdACpg+0RDSHS8k+UYN4FI09CKv7NZnN3+AMW+vY++zAyPy +HpanHHlq/R402KmskmL4Wk+71cNB0FZ95cI2gP9Qw9fALq/CpeTumkxHD4cpie79TCtImwKE71K1 +FAvohI9rxtvO/w== + +/a6fRfnC4h9ca/dudoc4h4RiCW3s6J83PIn5CwiWbEbHZk9ABkkVM9pf7hcwwrdzkcY7zF54LzV0 +vpKM/cZ463EL2oReeVAQxgg9XoQmy1ydekyvwE4iVgucLWeBddhEgOQJpMZ7ehi7NbATmzE9GljM +Nkeu3zDi5h1fXfO8JERg7OWA1n0YsGJb5PL+KbrLXWhSiCcCgtg5Rv+H788Y1pk0aV2cOdk2ADC6 +rizg3FmQcTqoB6tVDAj/DM9/hvmh79Uk6nsV4mWS7lf7ESWIjJlb0Mfkh5L3dddvu2Pdk57e006q +v0tcu+mwOS0= + +aS5ICgf0PPfd6s5SJPj08eBXCgOSwUhjp71QUHPR2RUVZVGjomoUZEHtQXEANyMHhRMq5zxjVI5a +SKoyNysBmhJfNkW+aiyEo5t/Ra+YjZUBy3WrsoHsRFJWDkXBJkdP/cPR/zZbg6Uc9u4+4FQCNwUr +iSF+ksrKscy+wX+3r7+VXNnKSkQqsw== + +Dx5tUKiGam8Xg8Q+5jxGyF+/ExC8YfHOKxlfgV5jZnkTc/BS3FjR7N0rFRfGhwwwCY2Z4dT8oyFQ +tr/LW2ZYgs0KnLGcrBMMd3viqn8vJGlAS+Z80e1lhFQU1mLXUJNWLlZ4fRC+ykZ+/esD3K6Gjmfu +BOsoUQOaUdHD8Xm6HDRP9W0qU6YLbCWFdRCIDIedAeFDwfviyI10Wk90PX3pvG5CyaGpeAAT38M3 +FuLq/8sc + +BkQpZhAeXJj+Ymwaz4BpGv+mBywZaB0rNaW/baBue8IZN+oys2fbOEmUw03oZTheMiZs0E/4q7n8 +ko+UB/L8IAvGfyrLx4gMf2VHSexP4K/63X2DAewmDsRugH0BY8BCDBJn0z2GwgLJp0Ie/aho3gDp ++7LhMBNo9v76C3wMhunV4Kfprqp6NdHC8cVrMUiayrZ5MHkQFOuZOBx6vJrOCQnkmQI7dGiak6Ng +Weuk09dezOh+xcCyyyA7lVo7YDC6rjTQto4dGcXQ6uz2x6wjZx4AbPVeYU/OJM52dL8heJ8TVYHT +yDRdQqTS+8R5qgBXcYMBJMDnrqN622cXJez3TtVwABW8OYoMxGBdpEvHP3qrWGgGIn2Zljht1k5p +kZYpUiPC8gTjI6HjV9QTW+Uotryj53ph0yMZFLSDglQdKDcwdHTgaZpdILZeV6Ofz1CEJl1Qg4Xf +Dq2yfgj+betxcWJknLEcnfkVIfp0tV1IdCu47tzXVJDEmFK+w+aXynIINqlCcEthBkoHuWXEkmXQ +N4kcSzCE8nMEYIm0DuetbynkATBMh5v2PkYpV+nStnWhvebW6vU/w+hyIMI7L3AxtCSnNgd/e5Wy +SQlSAyLHGjqOu3Brj2avVCRt9Fi8MARwS3ICdJGaXcnRQFgJPGw= + +yRiVz9ElgvOOWY0RA8ur+TZpCvt4rRylTFQRMP7PXctgbm7Aafq/nJNj1TIPMUHalu/DuBYVezF8 +jBFXHTarsXJwoVVzWDMzZJPXuGu+8Wd0V5l1C2iJlwagYaaeHX1aGOahTqm0KQW2p+889cr9RCBW +zB9kiSQMhYClIq2ipBMaY5hfF/FXC3eix+WvgootYw+eZ6zYFAQv2UB2Cmd9+iRuZrexNYzsrIhk +peRj5+ULyrFnGj2kNAM0SqHOFqM2w2d9XD3OSpomfjHvtG3OX2u0h1BOlWJzKLrRSNrk97SNviTP +AbTYEpjRilOIjEikVSOiqA7elvDUHNI9c4F03Qrml4O3HDm0el9Mie+vmf8N2saSltHpTlEgebCB ++lfH1o2HllCdobpP6t9lYyPD1bna2KTuOr8aoFQdi/4bFGh2oIBm/m/P + +wshJPn6NEMzEMurNw7e6GS5lhXwEv6NDcIyrZQM+5q8WgLxBDDE2pL1X08RRQ/f8KJngMhCRlg8R +SnPE0BAoFnXHJyqQ245QLn0ChdMTeube9BE65c6H5nKqjWTkwC2KmWhL9a0Hz8vtfdld+lNFXM58 +qRibbVFgOWzbmAGSBnJJhMrx/rnoK2q4OgHxLaoHvCUuAlhLnjyKkrg2GlfQNi/JwMvJkmZzaICx +MWSvRdhn3yTLL8FG+riCH2yaqD+HjgBT/DCO5qDCjblsGA== + +KgpWTkl/rTT8+i9nM+9ktpC1TIbOKTcAHm6c7qkthknIekGlpiaLF9CPElmx6S0zdLLSJbTvPkL3 +Zo4Evjj+ZvfpjyDPOQfo+5sayLFCjsRoJ0neXKFilDabTwHKb93GuYpyr/A/b5iJHCOxXqpumQAo +tAeHZUm5M7FUbgC9PR0vgRU5FR5I9EI/+g799SuLoA1XW3xRz8IqluUvYIO4vjuoZxE1j6t3dmvn +GNxKDlgr2QVrJOUmkZGBM5dLHLZLVUcEAX1LQh0PyGTHqJCNcBzQvvOjfNePCqbe9rHnu0AQG6uK +YVlxoHTLmRCWuc/Zm5gnlPkhNBthMIA1YGBC0dI+2rzhFSmvxDQKe/pO/aTzh111HV+qKBfsofD+ +y6iaOG74O6BX4p/dEgFnus3egAmAokUea4SWCT6ymRPgkw== + +tSyf5DBTQASxPzCoIFE11zz4YCrDT5C9msp6H8MSa1PVP3+dJJ7gznbZC9uNDm4GM5YGCixvNKv8 +X36ZpNxdeLsOUsBe5sxPHD1fBJUQFCsm5EDC9A5mn/+Z43szll52TFh3uSnPHr+a+Jv28c6LBQuz +Bb9TLhIq98hExWhjwo+KzZc7hIVErIw5WfjZCptqRIYAeMdqZJkyjgjF9aTUFKcA66M/eHPpoEKe +Mjp4ONEtp9BXsjZ9Anu7q9KbdfnyOjee+0nCwWdVUVBjVrghdSZapnqHDxaONuyHlCbC3wQ5l8Ap +zcJmj4mJZVM4HsCHjZ+05esYgX2EWYtNiF8yyxIwV7iGWKyIX1VUlpa90fskJPuQY6WhUABxeKaV +sFvqf2QI0tla//Pq0DEJiE5YY2/H8uH5Kj29iKcOhuJgxgeMKdnVLq+xoDWgD5isVlxpeDaDBGgp +Rb+UAbNrQaQ0dkSvixMOmNq3E9QOziAj7VKTNOKwSXd+dqziF8RjiZc8m9YPNXeq79JJPLOg5Q/0 +IYjxvsi5hBciW5VN2RjS7+sA + +ksMB7XWQfsLlBx6VRcauBkprDv2k6kUN4rCt4L3MJiKkUjivaB+FOjPXtbCE6X+Mn98AaAbEwGll +evQqcuN7GfbWuoTBMXU1zWLZCU/IFs/76dgmvCOKt+8HZL9vR9BjMpmY7a/zEE+0WedxK6QdHk8D +JmQazQlnIrrcWA1NswVsVXg08XH/d6V3KybETbc4sQBrfulvlw8fh492KQ7506z68evc7lu3QW8I +a+48yNohjE5D9GBx1vl2gAnBtvvyEJC2pIy6CvnlogDk98dF5CkU0rL8HKAuHSM1LRQ4/0MIxPNL +uvBqSsQUGUm34d+e1ZgFn4/0hkQzzvdpyshcIy+adra9ttIVsioo3PT8sJmE2LnEV3JvqP9pPwWv +tcAPMb4Q2TdlXuAyr3s3iXNNsRJyusFA8b27rzC/5WYQOpxaAMIw4zSQBzKZYdvUFNOj3lkDiBLu +HVMuPY93ubBfVi53HtAVzUHsCBYcF/g3W8shOne29IlRI+et+CzdADXzq752pl5bYi8jYKezV/Bp +eCxCnVlSvw1ZiL9k+lUyQm2R5T9l240PWPrP6fDl0MFc7MrVp7PWXPs2gelzyjNOalZ3Q6CnD3Q= + +rbbBTbq/PKBZaJcY8oAlhcN9+eB5CFmVIzcGkQP1AaToh3mhcmr7DjXhMHdiMjE9V+q18qPvQ0F0 +7vkJmKBucXW0E/nUyZzao4t8Xl4zS80U5lx4jeKd883v4RdtjxVCE9v+zj8z1osbhiSZvdf0kDAx +YKeB6TdKDLkL0gGyGZf2tqj+aY4MfzRk4f20fRwkEfOWcFy8077LOrnUDqfcNJJQcBfcDUqGzLI6 +KZ20erXU/Q1bKEiSwmY+VOF3bU1ED/2Ab8UOOigA5kWzKCHohNgaAng4eo69EoLdP98XgOkEmP6G +h3+G+ok26Ed3j10wwt543FLqJGWR5yA0ZOQc1d2JJDmWZCuW5MsfKJ+87Dpwfu0hYtJy9f9NKDkO +VhNbhgu7QCDrQrPg47+vGVlbPnEkt4Mz5YbCM5dyUljAN3nKPG52/pay1P1VOiSSvst3blp3/LNs +t6VBTWQXcb4Uaehsi3dm/1gVJ/Pnk67R7PcQ5jn1Q0m90JFoh+yZL6Ap5zmZ/fOpIZxIXEcILqhK +Hd23rQGGQ+l0C7324zEaPnse5lWbam2PHDis0bT+WSLGjFUDJEcpHcfimaDM8Q6N7nqBkTXS9Tn/ +fZC2irOFTMdbVOor3Q== + +6+x49LzwwXKPWPdJbs5L1a+mcEkxu+sTuw/STC+Om9hrMdbQBv7DUJdW2NaAIzi0X0ktA8ysGKYG +HYXad17LYTwtPz47nAUSlEYRjUiOKwTX9ekjMdI2QIQeVB6ow7NwPT5ihlM0V7AiOXXVm74c3A4l +Gu72+Oagf1yXSWOEHUpz4Zn0AlvN2/JqDZBhsWxcC3j3GOp9etTuV9CkKyDtkZ9iz8n1dmyXBq47 +WzWDKC2JBde7ZfRdgUkpT6io70E/CHRy01ABfcwzYAITb4ojkAbN4y8Zt3FZ6J9RkIfVk2z1HgYq +8w== + +CEp/ThwgBT1Mh8rEpEvg4s9ReZYW9oX9bnlUiNGQ+UsAysseNP7SZtSRUgXqhBNEcgT+DXT3EyUj +/hsgG/r7kYlVULLpl70inHy7jop82rJHzOV4X1Ye9g0b0KiKtpGn+bBoCwTZQDNqyJMudmoBUTAU +rdTtJ3bo25jKkhjAnorzMzEqZBHTGmYkoQKz8sxDY1ua7k+AWjFjVpxEI7XOQTSYeWMpkbN7an8c +FZ7XUEu9bQBzS66gSX+gOLEO7+DihD97wJQVkFaLLtSbDx7H1XhSmjGhcAJwdcg+3AZBLUZTZ/fO +IXOXi3WhxkzdCWv1ynBvIfuyCcthmP2jWt716X/bDnRaCF8iEzg= + +VSiETcjwaNOhx1UchjMip37s4990wZd2QeZwOe32ovlvE22xyCMb7nqQeC6JqlsyijoBTntD3H65 +SQRc8A5cu41CRWaQ7XVcElLDjjbhDnF5htXn3+FDZBvkl3cVI7kFqcDHqIZ1YFRtQjtVtuGm1dyR +Xd9mkzZ7L7H9IbGiCjUut+L39Sdv7lmUHPlUvUxnT24Wh/p/cp+BK9arUyxStXyAmXgWTMPsaLY0 +gmG22Gz4+ECfFzDngKf9HdNhbRWqD5NIVoQLn58ZhUbZKekaQe5Rw+Hkf6M4++P82gO+vRrAxsuf +nVE1VMgA775zB/fk9wo8Vn+fIrhZQ2d12hQL959vEiZPICLa0umW4A+S160jWa3i7dCaf1MD9iUC +siim8NmP5tfNLJSC6teQ8ivyucfuWJ4/QroqZxaQb+UkxHXDkRVl5KKinCyvAJlZW+SKUt4zZWGZ +ZE2xQkxzWgrOeakSj5mxaQR8FHUGu4N3JFF/J40tEqLPJJifJWWMJDttA0MnyoVmUu6Gm4hDbv/H +cGdPU5mSq6yQ+U/lZDgpa/NzVkz9Ii6WOrGhVEMpDBul75p6VnQEt+ZT7uU8qu6bzuh0IVai+Cac +wA== + +8PAtQpNwD10LqyIwPxJnPwN6GUnwisTTywZBxBQ5rW2poEjWlB994lzWjTYpieCOZnC+S5YYppEs +LZP9VTJPrqfkkRGFFi1t5qaJntuIbkuDjEE2dNdH/HtMpvQkOCweh0jo4MW+NNdF1Fifqy3kXuye ++Mf2lACjRquS0Q06g7/4M4vw66yKPtqh3+U42NO5XXFmQ2vvYMdbRfpmPKMQwt5lMNHedx5alC0O +kj1hHZ2RU5LNdcRtqQu58Wt0rBfWep0tn0a81NheHurdVqMNGWHwpYcW7/InYbbXpUAyIvcY1i+7 +zNnWZK310d0/zWFFnhtZ/MoWfTo= + +aZd33bIANMCvAASw0cm5vrVg/QMT37U57bFQOdJkySZu3ltVv4TrbG3XITrtXFPtP5CZox/aoC3P +g8D/1X5YcsS2vkyeeAVGIrFSeX20Prj4dkSv4zpHqdpH3wl/5g/FORLUB2Nbo2lwQkPZWzFz5Cc3 +OVYxgxQIIr8rBV5lltnmx8ApJk3Io3XocxGUHFVMm6ZubapT7Ahg3l5oI/ZQCRlvvZzMwX/HUTKd +4Mmkyc0REtzoeOZByHOID4iM62zAUsCYPtpr8PbRcK0oucFi3Ni6YkZiCVgIU5cbT7omUNK4xyay +3ur8eLTSu9GCbSdBN3nde6gbTjPVslP+dfvbO4uTGPTlrseVIJWPEBXHc3kXFePCOHtNrd1v+4fp +IgC9I9a/G4eavfWmuDd3UoBvNGm8pO+oAYPgZD+L/k9SQejm4TDOAeqWAHTGFmNhNaSE0zmAsJHp +TJTtsDmcCvJ2CL+fXPfdzcvMISYJNK4hJSgXRrRJsMAZDs5w1NWvaHxzovSIIbU+CQqdeRPmJabS +gMKXxDhYz4ts5KZbDIWELb59B7MRd6QadcWKC3kSPDmllgz9Yb2CTswjFOlT8o9CXT//YxPNnc1O +DUXaqzs7hig5k6keh0Ynt4eJZsFbbYAcUqlceQaWTlnbQMGPpvoW8qU0bM85cr1R5hx94/NhwggX +jb/UGJp+ + +80wlc5HYHczVzUNV7p2dzCi1YLz98GQ37PETXjLbwSqSlmWbHpToCzf7uRHg/PGhV2jS3f1devgs +RLUaXX3EKpzXPFQskTjiXq21xUQ5eRIYF/8RXURp5ax6NiuyGcJsegkp3OFpMdd05n388kU2aQPg +khv/0lddRtYZ3JmFuT0NN2GcaWZq4O8sXllqr06DVuClEkEY8+TXn4fjplAfRUfIyDdjVY6iBCR7 +ZJjssR6Tu+jI+AAT3kdBXE94gI4h5el18oA1H1Ve20tI+yfpaXh2m8rTWbVBEFwsujUM547LdkTv +0EXG0pM7NweQB7Je2hrUeLKCzJnBQ6geD5FwkXz34rtG7ss93y7pohT0wl7gp4OqdTZw/wxi7/Jh +zG0kOSr3s+gKF9+Y78C6F1xKjyLSy3av7+RvyhhG6NdBq+w+lsO+MApZm+sF7xxdcreKJBUXf9Ro +8QCKdL30KbSNcZ+x1tokdMCMWG7E0xPnIL66godeWcAfihdtOvnnyg1fSQtJBJVD0U1pdZcZVCb6 +JcLuGqU5s0YoiPxqsNp5M0SkD/3qGKLQWzK34tV/jX+7yeEm63t3+CRNoiVIbqaNWlDJa/8ug0on +ENEG9toaSzUK2Jh7GrBCybvnrTOzjBnWi0B0WRaYMUY= + +QzRaESG1gFRRfgvtBEaRnDpksG1aLTiz1sTs3B8USBH1Apb5eGSKTSVYFPDPNUhCwWGDt4zTI2w7 +N5INFvO2iPEmQSqem8eUsLY0i2oTgZObcmuyhyI25GnFc6hHI7H7y98t8Gm4Ak1EmC5FnOkEoAgH +v8ztDXO3MII3sK/Zuiotghf9pHTxgGof92SguYw0A4fQEjMxnf3NF5IEw74d4hqxS340XAjqcQNg +CUjffu7Ih3QW7BMr7Q1WpdQ= + +KYGCWcHA9zb7H53ITtAuwD72UMV4S+NZXDKogEW2U91pGsWLvSZtmUZPEafrkiNqzMFYeC48i7WS +3qM6QrcVwbALQHedaG64uZgKg3A+9WDzuBe/2py2t8DoNJmlKKKjEV009CQoWm631OnRv69FBlIG +yg3tmNXYoJi9oW3Ktq8XJw8S4KqJKplh0pXlFRRETcnzFF5NU9a6y/pjfqm0UnpZOeHbub59+R5B +PceW/NP/oPBX3Q5n8P107+vb/ceIaaedanP9aai8InZLGA1fNwGfmyhyce9fTzP3RHRaPA/sHHhb +10k+o0y+QkoYUQO/iSBjmf5/25OZYngB+LqC7uTW4texXZclj0K+lJicnTll/2vUJ6Gsoswxrdku +3GXfHF+d9nqdMCkcCVhczoPW + +SrsnYzXFh35ZQCLOXnXyFAAypXj0OqlrHUbIk24DNeMF/PzlSTFPZaxBQF09tpLYW+H9F0xYZmZ4 +HWMMhyGDn/v3Xa2PkF0HGCKDQ+Gu0pDAYkIaaaAxU6V1U3S3piAGiEP00cWyPfS+NdNGRfVglwD/ +fH7iyjuZxB4nmBZjv7OzizopwIADwfDldpPRObJ1PW6xYR2j5EhrsE92e5wgmcOjI7JGqLFqhmgq +JWkT6PcWibcNFvnaB6u29Jl/9jPyIIs95+N9K3K4/Ny5ir5WpG80zDNPDAcD0ZiuRcmFLtcb0NeA +GIk9QOMqg87tGEMHStto1m/pzFpb0p7vLW8U7AXQpyV8WTE80IObNzV+W2Q/IddfAEEWhanobIIJ +my6rIm1xiEb3fB7yc7uyJPxLOxAxDGDbL7w77qOg824C88z3mi9utw== + +8woeitkW1lMYNs4rQYbCdrJXyRfjnJgG4U1vVDsn+jgV3zbQpIOSTp7mGkTJM5e1iTIo4JXsJ3DL +lVh9+e/ly+tBVqSBKpD3wuoEAbFIyg/IB903Vt2ibxUwvByCwiL57un1kqwYWxXd6/sblp9jyIvD +Zz7evc0/Xa6ShM4VRHWKqX+8+JMwhsr2XIlHqUBV/eSAFcK9NtUdIEbf5wJIQKqz5j2yiejxNLYX +KaDG82QdyNg2ldagK7mmmJuQYfca1Zs= + +HSJ980GFCe+VJ73OWlj+j5bpcaGdfathRPlKua1ilQs7LpPOgxdw0Jzv6JrAbRTqA7hmINgTNJ02 +74ZM4zhwK8ifH5GPCeLC2TmOcSorYLlXosAG59/JXsQLceX3oNPfxIg9+A1Jc/L7UedEFX1KA96a +0MjJ2IQRNmW/O76pLnj9SAGDRlJjUiU1S4sxQI9ZjmgQNCRGuJ80SLz6oJrwsRW4sBBWGP9b/XgU +nK13AXaAklMFjv214hJlayLD+3QTDu18jCml6V3Z56ou6kFHhygmvMLkfyv5hwsZyfaoMlHaJmEj +z+Yz7plqOYEvMBm24OQHbDyUpvJA1+HMxw== + +pxpvVHmBrC/g8560mbPJYnhrKEegZezUq0zddJTlTwV66+HHb5Hy5z28ZyDfuCb2Ycg19QM2IJzz +19bMkq060GFUAXlzUvrsmMFdYx+FZEyD1kQF7oiL/DxjwUITBH3ERjUEfEIs6iTUbzJv2NnRmW36 +MbbY+KwSP9d7cVhjRQkfeqcJuVI2xFuadyGMNuNJuhPM9j8mKFJBDrrjT5uUCgrMYK5xqXK/6/jN +LQuAKbW52YNVOpe0SLsihU0ZdiAAKA== + +ECHwvv7wwUY8yeDJqKsX+b/X4d4cQqd9CtANxtuOstu7L99A/XGkejGaer40PgBflSb4htpy5lFk +sJhas/PEc8hQ04cwn0aRoWdySDt4QEQyom5Rwf5D4Loel2ENTHf/PFgfnBVUinjPBI3Jh0jgbo7Y +JzlCfpUiTG2VrW1fokiJzzVZ69IgkDgE3ZFzVMEp69aWuMNxfEDgW3dr1YTqbFRkE/4A2wRAJ6yc +7RgRs3+O35vhiSA6cBbOILhfFoVayQRA5NaCVDNnpidp562JJEMGN7u7cC+eJJ1tqIT0QE/xEs+n +ox4ImKPwtblGT62bLCKOZPYoZ3duzjcwnJ04y4k/+WWeAggQ4pkqyJQiqRREqauI+irTTI7eq34S +T+B26oO7gKdVFaitr6h0KLU5ijvrnZHrFZJJNomfNs/FtIpkuAJrvqQecFv4P27ILk+MBipZMKLg +4IuDjkwZVvsyL7FV8ALXn2QqAu1SyRHDmUzDF++fnOMdjCz5+pYZh5BS3syY5H4= + +BZeEbiWvUhQEZFQwhjz20HRoF2wRxoAIqnJDBd5fzAJf01C/H7xjyr7MR36r3wEhm3rX6SJJKhuw +7vZ/1WA7/tBWPEAth+kKLJSFMmmF7nA314ucVJjabRVBUost68asVgIJa93Ulztr0T2orLkIRVMa +qCnENKJVMMHxNDOe5XeK1AyQoejanr9DnkR4TgvWgREc+0pHvYIoTrs52cHYenaoetO+kYcxjTbR +t2rfCTcJvGYLNiqjPZGFbbRNU8mUzZrd6W1L8GhIHLQj/5pqdMEtBLhyUM64Td8x2pqvRrT6eiYa +oxn7N95e5Zk1zXsq/rfSEZMotO0oVK5i3i6M0ZXh7/8Kf6LsJOaFhevHGAMf/8UpWFR8lK/xLAyC +WyoUpllYh6YpdFe85k5xnnnlrTpikekT3NulyeouwwYodrVXHhC3p7t+y/xSXqngZKK6YjOv4Ya6 +uOyIoRzm27BYB3b//rDmRjdjS5tVSS1REPoPVU+hpF70LbkqbDTGBe40V8wUcZLt0ymFFjMihUvs +vBmrpwfFjvJdr8nT0FGAfy6Y7MDfiiyYftCtdwJSb3+L3uMsHsQ2UGjT4fvwyCBFTjt8 + +id9MnVt7OwflHvdD0PoaL+yPolMJZOk6o0lwjyOgk1a0DoCG5U8nX3yUFh7uH9Vf0pqakDT+zNNG +WXOz9OAcuyBWJ7HM4reCm1MdsEyN3a3hze87j1xMOwiLIFl2/MQeE82+Vv5onC4KBGY30aNnHrNP +7L+A1dreHrDfnClzidQjlvMlLX062S5JiIpODh7zTM4z+EUUaPMTHJR39atb+ELIasyfcyPxHu5W +pVwZhMZvpn+uqJWzybZtshZmwlhnjYSgANuSTOWT7ypNKpMrAwGB1MO2VociKWbZaiwwKFhWs4Js +mOGFaDfI7ki92EZE5NqTkBFMehGbWXQ= + +O9PkIvTzJSOL45mI3ZF7qR9yFhvX8f3/VQ2m2BWU8AEnyyGh1Dct/X3Vt/8RaLu0/t2st9ewmNQB +RNNU8GnrTy67lcJJilRyNeuITIypD9ZF44z4f/cDrn9QoGVrNfj5c+nLuJyEqV2zBIkTEHGKLRYX +m13p160+t7Il4eMAZcTmsHZN4vDnRDxgET4W8Gssxev2YZY8/i+xrIuKJx+P5WTe4+IMFYCGuNGd +a/EHYtE66Ba3zH5gSMyjIQ== + +c24++oNg+Im/NdEJ3OrNEruwIu9HfdTrAWqyPQO5Y+WJjYVLk9hT02e49dqtqpv+uL24EHHuwfC9 +LK66xiiDoGuzr+ZIhgKyEHvPPM0kUH+NVCxopuFZA4KtqSLi3eekNYO5DqMUVAx73LOzIKan7N3J +62BmA5WHNt1Gg/jFG2adfs1/eb8/cfvgOoQ42bjhxjl8NeFWRzOLZpM7C2PFcI9uSgtBVuRwiRjB +JxtjD8O8dvkse4fiM+hXB9iYHQyAaHHXBH+gpxikpRIs14CEFE/B2d1P/AuoVyntrMXD+KFbBfsB +a5lpGmnysRtmIEf5QdJ7DRjjiw3ux76QHN5S0W2iv3tJC00ScZlVw2X6qZiSpfukEfcaAT/x2GJb +LnGom2rcsuwKpXXw7Q2GGSAZrtih2DdCT3/2RZkquNLyioeXWu158MRroJLCiQ5IkOPwiYHaoMLU +jUpSoEFG1mH3xK+OMUchOd6Tms4NA2FBGqybkMUzZ9V4PYKPPXi+6vFqUBdNJJdFrszGSJAwdy5L +OwqtUQVrL/5QKrA7w/msTe2uz5/w/ZFKxmwngpuM3V2lv6L0y3mb9InKHpgpr6upbwbPlHSQ1sGk +Ypq4GKrjLhtVRuzDtpehHQE2p3V3Q5gEC9uNz0o= + +vnYcIAnMVOZrsETmFEM2ZXf9AwRNrZ1aQoDnb8i4veAesJfbCinBnBzR5YYIUGC0pcgLEQovdMoB +Q23P0xMoJpo4eYF72mtFcg3iZQbMxFgCo1Ik4hNDjXiS3KALWjCEE6f5idkobyNBmnxTJ7/orc53 +CGy4MDkjm+3Rm59f1RnwJAv6nFU/c74iOFRTMHsG0hDJnY1q+LU78c7CoN8RbRZpRGmYZiNa9FRw +DooUhsxQqBtyK6AweoM76qObbaxK+W8ErA3pCltsuQ== + +hurR/6xZTWQktuHJzvPQHzI1L77qSjZireIrM7yTVT+oCthbZey45UmpSyCTTI65oFvzZgW4QVn1 +fGYbdVqB4RwT0om7f/46n7oi1UcDGWjMbRUuP1Aks1/RYUkVjpNV7izm4vpPrNVKJJ2nWZzbcL0A +waBt7sw2dxIMYCNjyxJ5qI6EnxvI+frl6eY3jnp8p6ICkj1S15MJjIMJhMP+IiN/h6WdqjTBflfd +8qYTXpH/JSuatkKjl18mFy1RzI7YbpJOVb6x+pdFuuVMxHu2ohz2EjoXUHcdohXA/bcOIWPHMr8t +nZD4vFEoprhYb4ZH5AGmO5avVDr1LIpNTP2gx7B6OXXFa3RJC2zQ9NK5NRMFlU+1danqN7JeccBm +H/6yr+jjNmUVGS6RSad3lvMmC0HWsFH8LTm9XuU6RehzO7SLx4XA + +Ws286qPJyfLE0L08PTQyyu1J0r+Uu2cTjjRWVtlsH6Mk/SelGJvrZAbjfWaV8hcHopYIqdAZ5dT0 +tprK1PEG5++7i2579UmNauY7YAttJ+3ilnbzs34sBixXTmkhCZ+sIVT3Br6CyGwa5oSAxqD0OAzt +8hjZwQ0+Bo3P8mdz35i2u/ItwlJeUcujGNSA4i7p17kEClWCWIqelaGfX/O1JsRiBfiQIbkN0seu +OrQhgOvtlSepTUG5sJ6WcLBuK76oWffGGHzZfuGlmUsByfEqFSywvnbC4G5rLJDsxYuB2+YzguOE +sL2TqWPAyprR78EH9pNS3wMmTCcciQ6H8Jw= + +39Ntrh2h1//BzoX21kj5qWggCo7MQa2Us9aEMhQN+ngcYe+ose0R1jA7G80qYry2+vevfcvVyBp4 +d7qfxkwXxyzzWZP7H4e4ggNoY4xdGIcS3p+JbaflPqepnea9FqmUtKbNgf/hj2Tu49/nUmLVD+gI +zrtVSNUx6LcaqbesdTujFdheXxbw1S/j0EWkbDLNSsaGumwpmRYEVaA+vToxyfyib4sbVtd5myXF +6/yzxTgk9YcOx4QZZgymdbQRwgYxxiHU5zld/PP9O2YzLCkx2Wr9TCS6a1ctfvzEvfFFHXKMVN3q +39947KVuX+Z4ezWX4+/5j5V9kJvlx/b0pr3HLAnFJjFXQth9N4PGaDwbirlDOGsMkM7ZXHpN0gZh +V7gkZneeG4bC4qK8dZAAmdYYDjRx6M5maatMPYjfO1uC1TyZ8ALbQoWNQ2iqg8K5awHTrsUCfBYI +6/UP0ofl1TkcW2Ieu5AoDqxMpB80nBe5A74zcQ1/XW3dXEa3jCVTJB3GX5Kc3FCf2VfPEX//Weaq +xWXdzApN6pqqQYsxoan3iW3LlQx/dAMHYkCfE7EzHNoLfs6zX6OOaRl1NrWIYZ5Un0aWD3MDPlEj +Bdp2Ot9HLogaLlnfOnk= + +u327jYnYtvS/TXW6AaWufVrd4BJwlmGt2+z1i1Uy4WiLoPHnH77JJ1jBH4VkoQezyPBhzayhwk/R +x6OC/0o3CllaAOzDUtPeMxUICGDAy+smwyacGnL/3huD1sTg2/wb+10OSn31lbDSkAGdZwyWirfs +C6SqR8qntEd3IswEui1YlKmJWYkwZ+A14F6zCuW96v/0WCXkZoN/qAhjuI6nb8GdZ5k3F4MmE8OF +FubavAKJO0Ej4MwYsYgqfg3M6nfEF0hGMYZJ1539T7eKsMS2afxzLhSVlSsLhpo92bTry85LiTpp +VSxZ1VMDXLHstAMJG7K6C9TA9gkEy69Is6bVaaZzfx9Kws0UG3SHSsciyCsWIX71np5wH70m6d31 +Ap8naKMeof3hH09UFuFbYOltqOil/4KfycfvqaT1f6ZzfTQ1sSoZMd0quY9O6hRzhVP/27v66IKi +iMg+mi5+R2IhOtUdOT9ogFTf8KKUlXlJcZyjFCsm8WTKCl3mOLzDxeixvzWY0mXOceErgrYe1fRf +24ZsBw== + +rDnlz3X7Dn/KYL0mAnyyVAYaajrsNNcbdkBCNhMa9VtJ6CxwUkmrYeY1NAHvCzSJ/uIuoTBY16GH +1icv7uh89BCIPGIhfc7u16JDLyBzZ0P2/Xd5DojoyJIvR3Aqmu/YesRH4iQnVV2vqC5PTpVM6IyT +xl55nWtns+qym7i8CTHNKfH4ljWs+CnlFM1tAOBEsGM3Q2gIHsE9A+JQYioOe2MDMFNBz+Kiy7eo +dK1FLiHo2JSj98wki2uCuu3Z1dhoBEi7c0MPxgM/C5T2ucqEHvFlO0rylmjcjhBtfFZEbFLxDHut +hxes3ndcBebQnHhRQbhBM1tw24Q9/MtcSVJ1q/0Ij4NuBEQFBl/bqjK8DivtLGj3V5AFRQAqfBqb +rk3yXaVJ0SY4CXVH2AziVWmtHmwXWlkQynRvCXifAwKP7rCBIMdMOY7LGrwjpL5y5z6Tvl6tXwkW +g2dh/1rp3y1l/agwiNvF3dWyL9/41uJECpRxzi2CZbr4jxvVxJ06czz/esCNROhhJFsZVXznxpnS +zgzW+HiUjfwTDK3NnT5vWvpcMYWn4XxAQ8T9xhCLS5pIV/bZCw== + +3O4cfet5fdn+4l/Rr4BbXVop+bS8NQO2wLVlBJB0jC9mjudgoRV63jmGX4kNq5J06tvuyUXHpoMh +h7I4e7FWp/j9Q47/zc07FtaHi7GbXd668o+qMJSr1oR6633f/emPJRz0daorLsnSAuEDDJN/0S/h +iw== + +NXKh/iq104ruyhwEncN12oIDCiGRohErIWOVF5hEvIMgQ/XIJQlEcNnb2vigSagQVkPxCrblBd+Y +RhnNnsg32+Nua2EKkQ+umdoDAQ3z5jEGMLvzh8uhSttLr1uWNvxYYf+IjhmXHJ0Lt9SRjbH03YyL +2wN9ye82w8pWGScJz3Zj7HisYCZOwstObypgtVz5ehxuNd5xW31UXd4n2Sa5PDQM4Q5m266Aatfu +9kBqseVhK34KDI9uHQLmPe7xLqZocy4yH8NxXKpqoZVf6Dwwx6Ng9kYVOpBrBCA75DzfrOCO4OHg +RYDAeoaTkQmzvkxBNojLQwBnT+c0L+foKR564mVcIy0D+odjyvjQyNqmmjjSyuFUU2+sMwDGbenH +B2p4pBmYWQ== + +3N9H9xFuBM0mqBMOwCC4XQ/speFNvyodqbGoQpNlnaCgfo629TMJLhc0AeT7OZ61ZGkE4ZCLwGHM +QuayqU/Q9eDloRM4r8/jiEm8KW6tuHtb9964gg7CjVjJTzw2RPqPtHM+ydRdr8oXxUtyRnzm2B06 +AOJ2O6TK0FIaB0bXLitPsjjb7GyMgrO7T92oDmsy9pGTVna1SUJwFc5RfTU+MUt1C/nBjOnhK0ZF +U9XVM6ow/FZAERxEylXfvk0fD7a5Ni/dMIddcHEcN0+uQA== + +sUpqkHtFPzbH5kqwOzZI9raS0hdaa3Duc562K/5QA57Fd+nmpN/EZMiuqesXDsiyDbyWje9Ajs7Q +9tmKnKhFIzsjYRaCO6T11BPVutaCLAI06OFG7of1ONEHD+d3HwEVVDOzt5QqH8YT9tENMIVrY4pw +ZcAKPqvRdl/bQq75wBTlqStdhp6tvPytacdMFMM3c01ScbhC33m3IeO3/ZyzWIYJtSa3Mo+TQFB7 +iD6jOPa5jrZG9UvntDHCcQwNyrtET3uDxFdEZH9ed9GfQ79Aw8XBlOHjuI0oxVbPp8BD1aasKryR +qhNCIm0= + +IaNFVhcAK+o4H9KsH7CrBvxkA1d4ObtekSTydOJck+aVLhoxPFD9Yz9eI/XJf3MbO9qi51TSvthd +7m6QTWu0QX0/AheXmX5N4Q+LPL3heCnzeJVL+sUh5i9aBSqV4co5i8DyIMdtlu7oCUENnQhE1ngS +V671A4+0WfUJ+xnydCozsQ2yHnOnluogfxyydWjBGk25VMFkkmNuF6aTy5Exy5539K67PCJ7NoLv +aG1yPAEcdbVJMrE= + +TEKVZVAJ6dBxu9RCSloafRX+XHKgrSMcxP4tyQPI1sgSW/RY1Hajkqvg/9kWRlvweDY/z1hRqI2P +npZLbVzaPOVfomqaYY50u5BlZqzyiYMosM+7ipPc14+2SEDhyvoIh/+6Rgc3CDhw0IyslMAR9+vv +Msfnqx6NlTG+OYz3hKiO0cJklHLTk9ebmg3Pcu7yGjDIsXI5vY4m2p6px8HoPF2r+L5+pq0ST+wK +1ZC6uAbsNaaO6P9p6CzjbUXQYHQpZ/Wdmoj+Qy104PbDo4G5BEA+VVx6WUY+v8Qk23ylUEYgc8z4 +Rdr+EW94mVsDTGglYsJcdUsMDw+W9ykzUqoqGDUKTS1z4h6QF2lkbxhP5XxTXwRgQ87KfIQHMGqM +ahSkfPr4DOExrM9/kERvJqfkRQOE7X+rJpzwpYGBV5S/HksmFQ89Y//DGU90ivlnQoh0ElHhLBgJ +JUeZMR1jXkcfQqpxLTJSWq1ujG71LFXnwyqMNWqmIsr44WJGUnE7vjgO6gguPhxjUjgs3WU1nO2X +ZB3AtmtmzKsdYMIvPZMwh5oe355GWJmBvBX7r6U+Ht6TRg== + +OG9afPTY69JWvwmhkF31HjWd96yLs/HMHANgOfduETiOsGceYUkCbMISmpnhC3SjzHzvVLSWYZsG +Ikap+Ke8gDK02tki3KHZ41QUF2RgFIpfmwO49yQOoRy4/9vG5+dca2IXi2p4L4yqFI505XWbOJVp +ENmjiBU10rE2wayeuzwGuZ/z5baI1PaxP6dWJjVTRGxoiUjBUSBqyLcB5SORWU/vZJTBjCfr4EdL +d0AQRjyGOrpmfbAFgyzhIEaXqJIV1nHl68rk7dl6l/cS6Zp7fMqXB2r8W13dPaQ8Qsh92Jl1OJ9r +Jar/tWRxy5O2jZewajQocorgUKCg04zTwx+LKxkTQMMTyUy50SAn7jnFgmhSkFpfQZ5g6dbogtF6 +one71D4w61OUJbdoVby/emkMgRd1XDWnA7HNqXbfDVPnqSe06OV9kSV3XpYUd4z2QPR8KpuZyBuZ +Br6Wvsns8R96wtsSgtJ0MSTetiZb7cqLrEh+7dtwFLsTh2MWBTduJKeHYVVDb7XB9mWrgsEXovZU +BzzoFkjBfLFpVITki5Ak2o50Azk11Wmu8VdCjFXMHHhR+6KwdW1Fa8mIHlr7 + +GTU3Ry3P3eJls/17k8nvDBgJZPvv5PRGnOz6+LDDUfqUXI0+C6E81THci3skNu2kphLe5SmscvKe +yKGhHdSAN1p1Zt8/GdEBCdkAhJ8+UQlbrV2TFLWye7IiL03+PLsYTq6zEX9gGRvftlyemdov12mx +q1pq6PQM4a34 + +D224CzK2lhIgGTFWRf5PHInpM3FyndJagfrqDmSRXx/OFZZntcGovf1nPw2++KH0PGsT3/dHtVOY +iLQM6qOeBuPH801RdvaUQsUekAqFdHxQL2NZxr+cfkJGaPUBgqaMzUumg35rQwaYI9163hfjCyhr +VwsO9Tvvc7ieMTwPmPRBHqnAsZjyiDfdgSCNRncC6MTrj3KYsPSr8okJhL1Vnd2fn/G1QjnmSu+T +lyWF87XvQPiYtrYBBMfyF+keSKpLjWmS78frBl9JA/HKe3D/vU+orlZ8k4an7/md8Ns9lyA6Wk8t +D7kXCp/EbiunOSQvz9L4DZRH2u8ofIhZAusmrjPH8Njm3tj1QwS9dnCyqzTkX0phU4+76QIclztj +Jme8310R8Gt7b5SkKx0IMXLKiS5fWteKX2ih7yslGiNy9x8pKyTU7oKI8YByUUqinLNn+2k5DIOw +9R+0Q/DOr/2zSS9jfsGlEFMaFm9puSC99UpHmWUgkBEf8n1j9kBqB+3ztj96yDfJRGQ8bcb22Uq9 +H7j/L9m0aQu2FB/QfoYZzmvYklq8mbAD4dIN8nhzOmtbKPwi882Bjiu2WB6dkJ1HDXqRPY63/R6p +Hbq8S59CAnAhCLst48hj + +Cwh4+ixaG2D9PMqic2npYX5mIWMcIust5WkfjPPP4Ny+kmVFgUCFVLP9A51vgiGGBxN1DbGfoYF0 +VChJ3QidHTkl5+rbH/ckl+69HjR1xAR8r2TrkIyiIA/+bP7WQp/i/Kv+TPmvcGfCVEL4FtSihye2 +KwqzdkWTwhWdhfZbccIYxcZUjXvquyK5oVWB3yUhFyani59w6ShqMN8HS7i9wlFSkdJuLWWb9EYf +WQDB16A/OqdlI3b4SsCbfssI9dkNVgaQPvuqDNR9uFSxqlc= + +QPF8CLiB8EW64iWRLT9S0yygi4Q+LprnK4aNeZxN56oMPJsbM4nTRjTfSAhVql9wRHSz9pPgxbiZ +FdtQ6PdaM5ewkNQXKtuf7sRLnxwQY6jVgjJ/x8DIZLXsU0NmTnPPbPySPUbraJSMpcw5p6sBGoqm +aYxFebh3xkfxH00whZzK71a8795zw+gxNSs8LZr3f0u8OjSKDUftWd0JGDIdFiVfH5lGfiDDST6h +6MfN7eiV7/Svskx6KQUL8QpXZjip + +ahFQ3AFxGH/Zr52T6XJuSkc1guCwG0OYgHbM9PNl/wOZYC4CmZQEXli/Lb688jmVgvOsRCV/dIPU +gwdvAnN/sWy/tMUpTKQl6xwh25vf90U1hoD+OBTbv6CUB4x6EpETuqFEAuj6+0UzmPlOT+Qha125 +LVZcbG/Gvsft+QzzDAl/y5oKdL5ADlENad31OTs+C/RGG2vhNSrKRglpD+tsyIGrhlzbQBjF/VP+ +8PssDxIrbNtZVRGPxe1i5aGzizfEWwF7+hOIGZchG60QoGxky+/gkuR7xCEVYdl8/P66H1RpVVnI +Xns0+mZByhCSk6xUXWhMNUSb0z85xxPkHclanxG6DooldmvtWP/qdBRkJqJ3y9gkXjFoK+jFP8OG +hrlN9Ft18cWEbZx1mSqGO1IEtY7jJ+wuCTBUELuQ4G3M8n56VQJBw6t+88/sZKe2uv4iKpSNDvfi +VrYE1G27Ag+iiEBnuwkB4kOHoHLJUaxf56IeyY2AK+NAXJ0mkVgOZpPx6eDycVUc4G5uKmVKLaO4 +Izf+q7t2KosdwYq1BFGPqzvOaVCeFu/zfiH5EJWo+JHjEN/FEusuJ/kyF+AHP3Yg6xMPo6ghX+Rn +pu0/kwIYNwl+a3zu+IwAmR3S5NODs2NbliT3qcU8FDuN8EjWs3GKSyooH0p1osh36gyDJduJPstE +aXhhJA== + +XdUnbYkvdk10EMPjohpvklkry+W6mcDdrzxRA6LphTV0q6te+W4RQc6iU4Ag9igeLFyQgrY2ZLMD +kcm/Lf4cGJuB1EWiWaBSw9o1UV+jveBv1Thy9254nZpvCiZRa7QKM4CIk4hR/1Qs2S+B5/AEXy9C +xrgTE06w8mad18v04DakNfoEcjfBDFxCKAcaYhqIHRDVC63AxXBp4uy2KmkH0B7yzw== + +Hv8ukPJ5AKfZQCT1bfFtuwg4iD0sV1xg50Lxv7UYxw5ld+VaGWkSfy9nuaNMhprJ0DodvfM/TXXL +VT3qUcVhcr/He6fKT8Sph4CHdm+wR6yDS0e74lRvJhQiS6T8YAKxsyW7hn8GzVvu/6QjPgPQPAby +ORMzjgOT6zAUrAuWJU2zPesn5F6PItZ/DvUV4eFQ5e0TBw1veHyeAHhVlDL9ftvNrPD/9BWl6vLj +6x20EIy31D2lHA3qtyobd0LQXyYEIPnkwFQPMXBoJMG5Q9e40jG/CZpfXwL2gqviQsLNzuVi0Tz+ +fANBVYAP3SLEmpTrXUwoCq1YuFovLvTQxTzkk+yPq9i8nA3TRZBNL9Hvn9J9snDlejxyVKfFZaJv +78UL2QacSA8HSQ2d5vrIzkCyNxYydzVIwwtR/4dq4Ul47FHizBhULcU60MGGzCGv4mU= + +o5B67mJ9MbZVfCR46H0i5hiQiwULJP0fxdCEjeG1q+dy9Ra3QfqyopOfAgLVzq+oCTGVpk665NSm +CMQqW2K78y3JVTE0OxwK7XyW19htC5fC/W63QmSIyWMyzOc6w601hQaGPLniRJSy31muFPSVEkyv +WB69TuYRnj1oboXuXmKNenC4F60yxOId3mqE58P8AZ6p + +h34uu0gAimN6ZRmEN4VJRKu15v8Kb/lyRWNYT1eh23KzZKmk1ZUeiwHrd7eEwAp7cuuEyPLeWDdI +tB6Viic5VXbsRtZ4gIE/soAKCtIpjCZHZV3gh00HTtBZl964B5aM759E8+kuPdLx+0UX2Tn5+2Do +X5+dEm6mU988k92Ek1t0OH4tMk1fPa7WH77UbxBJgTldKeyhOGEqKa67QAL+nc9S2U0McRO8L5EE +kknZtSZmtb/o3tatw6SeeTXD+bio94lVRjmZ4riDRHp5AgRXoemsFNKoTAHI67jJUqGSuGPArjbS +cvwmm/7OEOqx76JpYwD7ByJGEchFe6lkD36TIgh1259ZtTb5XDMKZ7RsCnK9jvwq9b0MyS1NQLtP +ihu42TKti/PBtZfzlE+bh3OcGcjTtBqD7Jrex7mLh6BWkUwHEbBpMc+e0IRUFg== + +ZfSpRj5h6WgqB+czKZMmNKLS42TEWznU+Gp72SHe1SuEkr4HC5IAKDx2IyUB/oChnut+tLE8QI89 +9k9isZL4S4jaby5E/FJ/VTD4BhLiJtk24jEl44/Y9qH/sQuHWMqrlXRwZFw63pFmqGr2CTd/pwDt +npxlMkPzHGKXRpZQL1mTuIJAEmXmKhfJr1kwx5WgiXjC/rBVLAwuxlIGvTaShg9OsNiW5ODC7JJu +bnLCUhpD + +BuuiW3zzGvA91aranVxSL1lRa/VWRLW8+30/ycuN25dpw2W5ViaoZpvH20wwiRqqmOZ6gBomxv/4 +LMfRBOJ1qjZY1VmzJvXrJsM0IBL2edNp9eezZVF4Y/VkC3O2MFTDzOrfBRimx07PrK1j8Cll8TCC +G9ZSU3IscV1KGZxlbYv42FVX+WHqDjPunasQ1G5XC7yF9zdaPtnaREiatFxde12Twasw4xxVON8a +0k0SFzpy95jGa4KnufX967ES4U8adEyrmSL3+cVmffXW0dFh962ugEjc8lmQO4C04qYhRPKS29t1 ++Qx5nWgSz1cT6vtSmPoPxBc7VNykXIw9iAZLs41tFU2GOXCyRCzERZBjlyQ4CWV6IpGiImrKcIpH +BRsQYmTM9kVFmrnAczIqy2DyB6MQ0JzCR9XYNOh1V7nPntR9+02W/TqycHjlFZwLQvtuuD9lD58g +02Q1iZ5vxnzbpv8l6DeDDQsOLOGGV1xxp7cnKNJuxOVpqsMrFTO5mdIDrCiK3h/MPpe0mMN1TVPw +sUww6tYGzyO60oA5VpWlv/DPdlmoFN1TdyxeX1T183oWV3Ro78s3 + +A+0YAIbigD/gzEbDZ8LH/P7RTAKYstBhfkytbMiuHharE685cYY/6UynYYdO8AlZKA/KR9oxX7I/ +TJA05or03nQXiiSzEvbJhDS3ORe1hL8/OHQvdS+yDsu1MIYqYzedtdl+5ige5UI1RSi8NIDjD9F6 +1bD8gNjv+5HVXO2tWnXpCAxOKRuc4h54FDeGw6MReXyI29fXWiHX09pF63d7a1vFHn0hLIWWgwoa +BBfAGFsBLDMefMp32Uzs/v1w/WE/V7rf4BN+XczLRaIWVPEKPFZD2RjcoHv8Wyq1xEUMU46AKjAF +gp8c+eD2m1dZm1c6zfkUkmRX0g47a/7j7NgqwrFnPc46XcYUs3v5g7L4KI9KZaQDMG4/SPOjKvqY +pUNE9UKpcoCcrGWH4HL4LokvMK0U2jjc9MVY5Kwnd2/ar30Y4+DfLbWdV2OH9AIxBnToHE5okIBT +uUjL8sTSjvI6Gkue+vki1HqaQPBaHQtvL+qjjAhTRVyAFbxme/pWzbgeVynHJKzUwUI= + +/d60170ufaShf50yGYRmiEYrf3jiJD3t13C1Rh6BQJhcytNZukonnxGAaovbyPEhN1b0TdUJ+osu +OrVICzvPNHLgWmlwZOTPG+00YAm1B3IuYAzt67oYbYPhfu5GMENfYAYgXcF35B718bDANonMJaxB +647h1ccf9RKxUqqexyOXq5j7oDBZ57V7Hwru/pfmxj01qqGVKBlniAbCKmN8YXlraG5jlMIUb5G4 +8NGX9JaJFVVhWm+Q+uA7dP0xmoXtk2t8wr45ShAECLGQXxvd+CAxe7B7fxfP5jM12gyYhWTg/BJj +4IOrCR0PWT0Wvo5MWVXS2gw78/VCeRrJy9zyNZ/uQCEndxwP3iHTaEF5i2XwPXWl6AAUELiix8Us +buVgQS5uPv+Rwn/FyuAxGAlKszLOl9ha3pSPMnQ3JAEjxmndFVD3YDE0SzzUTxiRIgq+cjFndJk4 +aFLYMCjPmlpo/rv61CBmEIKUW0d6Wn9KUZnGdCpGqbLb257z1FuwmQvZcoGMM5hBQhRbyWFqaeOQ +zpwpFSkU6FSWuraNjE+jxVATJA53PUEu5z7j3xyp4nc1pH1UmxF15rqaaau4Q2/aGeV/z51kk8Qi +H5N65kW2bEuATdkSfcckObmxVZiEmHnNNc34bAZtdLy+zEPPEdGsdM2/VJVGPMVL9rim7yWzX5O1 + +sevB6dRZs0dXhYttmL38y/MepxKKCM6oo6VED+uN6j5nlMNNWlvcNXsQ+vWohnZOUjzNofKdDzWW +wXhsE2w0EowLS9gr0XFRRq5Qm4munxQkfmFfNZF90DjBRxbKxtb0TdxOpWK3UcpwWU6e4acWWxkg +ZjkIpOHuNecQ8kWXonjCuMEPbhH9i7Rp + +vEAP2l17gQetq7sO8pPaB/SxgqvyGr38LPJRyGXJ38lPtiuvTgXZukG9bVcwXzmih9I3ias8Q8TR +spquj/rYgp2Y6WRZb7dNtV6W95BLiRReA6CuSWWodqYCV2cI059gy+TKWSI7xE2SF4/iS44SPzIq +NFH+N6nQv0ilKVBp7f8J2kboNzBMUHsIbpVKrRet8AsLZunNttxM/hCRCra/414zKA0KGwam0ejh +SJqX+gnUjhsjQD7bf9HDKkvrlmJpr4la7uA8quAhQh4dhZfDlethOKCsHpghaPF145TYHOhmZqer +RPbHxOGFabfEp4wpiJM5/Gl3UwSbPyBHv+fzoU528wRyM1oU91qoyk2TTygmh+Cio7fDqjvsHHxS +kCrsG21qS0ACtMiSvpUWQ36fBTXf8QyJIeHt0e4CBsRe0B4bDCAsAu0eFMtuAVV9N8G1Ucf7qaYr +5SsYsVZ8axAFvprWqMPzjcDllRWqmy3trLhsK19L8V27HQLblM8cpldwbeYfgpPNVSl9xCqjJLxt +D2VY/qMeJ4/HzJmYx7KOyyQuejBvgHE= + +ja44eJcykHPFgYMtWaYvyg2f9eEm6mALCqWZhihXUpcKz2B/uifhO2lkvVAoFZgnbDy6GQggtzPA +2XA5QgnNgeEhuE2Q6cvmE+LIsZYLwz1LOFiD7GirzEdXyrByLvOYbHXgTvEt/zFnT2ELtYELF3VE +VrXyhlZkNhs9bgrY8aRdZwQqjU4HURuxakl7zPNTM//RJsc9+U+vs3lnTF8rQ89oj6lkvG03eWF+ ++CfTVjyE3HwXxINADJUBQzX/sZn+yBydN6uML7tmnnH0NhEIcfWMrUm/DxLQgDKW0uiX0x2NeMN3 +pqQYIWuGOMVxw3iTonv1Ewm8hLf5Z3I9h3pgxmI2knDvAmqTPQG4L4LmwmzO2LtBI1tkEnB6zZyp +J7TfJhlxyrGjYfNEFgCCn17BLKCvaVGcTddndoY3SUkPcjRRcDWhVoYXl6YwbQ/F7JgahYWXgKqL +k4y4iZpiSIFaXtN4CHEdSTZrVM0zYAZSNFevCsos6oA5azu9rzxvZYD8Uv4WkizrDfiTHdOcZUNC +ITBZMxPXRz3qWB+WXkTzK6FPzofMjCB0yY4H+afGfioWQzv813LC7Xf5mhZhNOb6EqR9Ow== + +mskTw14qQnBpuncGKwWdapkQD72baAtRiaq6Rens199jDHzSHzZp94C9O8Uzc7q+jNub+Yofdvqw +DXulNyeVyXp+n4h8R2aMTTv0f1ohCVguDwp34uS/sVroWhzPDmKltvYYG0Kc6LxT8V0UEQPDLWde +wbPVU5cL30WEYR1DvuloddJEdEjDeB54w/2ncxpaRjATn6ePCRF+hfQMofDso36ujpBZRcTwE1dM +Lc1DPjBLZF+Q+AQyYesDa+011OqlGKyyhBZ/bp/Y2LqAv2pnFGV1wUvFTiHNqK4bLTkbrLsMEgM4 +QEIdE8cOKieRrWa3IHvkvazWYOS5KJSsG4wngON6qczymRbk+qZDoxPjRhuGiWTU+pWr+CLeYCvA +d6FBuHEVQ+Ud/2Vg6Gm34UFv+iziVv7rh0gatJviuFT+z3ObkT5FCa4+hGd9X+8RLNX0+ZhW0LVP +joO2HuXh9R4wS9QytkgemMRltXPqHPknsB0V+cNK1H0T+9EnbCfAD0PboUU= + +msymvy559eplp0A9WYxlhEkUDQKt2oM0PcJr9UrhukvUwaO0ir5LZk9qkQoRJvntmtmMGvbhl3SG +RqbkqzIULipLyIhgYZAfUwJMQrvpmzwzMWrbqBrnUVwHTuLX/o4GLfaW6D4Lpg5099eSuNHmMgod +/1Y/FRiUmsKdSej7ZlNnrZuh/sDpYF6HbwYGN2g6IqO2JIzC7g7rqNmSyJy8gV3Tq2MZgR9whrnf +eUlty7wpHeYQWM3OPF4z9rlR75bmgOn6ZzR+IDPVFbwQpQFO80hI3+N6xziiw9sW5oZwfqB0LLR9 +XlbEn0x2uTPzPibtk5iZ1uCgu19EK0jjXFE40Uq5bw6K6oaMP4N8m8VTOB6hSZs0EPPres25F9md +ph+nFK8aebVFZpGCRYbfwbSnCse0hqKbpNRps3xv1mEQmMll0s5jRw+NUSpeqhbWItKJdmLRkQtO +ZU8gCEObk1AjmTU1Eg== + +JxAky0pnkPK77h0oQWQ1VL7+YnXIJYY3FVGIyKVaC8kET/MbOua+81HnHxwLZkFoOslCel1N7uf1 +HajTL10WV1Y0Nj3ebLcbTHuvV9ANml16p5+/UIUMvzZIX8e3nFV9jkIxPXhPpKy7iEkrnQhv6aYp +kohQGiWesEgQ7P9foZAA5QFsfIoHcOJgUhAqcJOtynmUWhDCYO2EQ0G+CQNwEv7KJwflOwML5ni7 +/pTeyhb8L1wP05nBnqTJ4sw= + +JCZcsEtb1ZhAo9LUVH3hx8y87Hp3NSQ9f2sB8gOj3/RMTjYVcAlKQny+sncXdaT/qxoROgVYwedc +udhyzfILwZtKz8L5IZBHNN6YhkodWflIcN90XL34PMOXNhdLDw4g/eas1QCOHicTteenVqH2RuMi +s5vl7cEXbrOsl2PEjuxRXu+aqP819U6idsgszjE/lRchTl1PJqcs+Tflsi+nKx5PlGJhW+RNacOY +SJxn7CVukFXAB3A3GJ7Vf7duNWGuRBYSHTR/AE4aD6d94kzA4NmuERE8zyWDXsrZnLgKcJMtG+UX +rrGOHtIDpAjhBSPluCPkHcedJqMM1ZU23VzWMP6HgXXPVjVvY1pSfpEqn8LRkP5FjgKGya0UFPoS +kdvcUe0v5JRdHIdKUWXxGSKszewSMqQUWnyd+0smCxaw3inmWVUBhvHHVhb1NLwhyZl9O36eQEXt +Kx4NozewRl2A9tlTnuvp4GkFcwl3M9BlGN+BvYKvF65rbd9FutCscqft20eamPFDN5NZ6Yqi/YrZ +uHrrua16a7vWQjhoFnO82+Xp4jZWieEq9NMKwSAPSMbLAI1eLBwD+qKnE78YUkx7qwfUrQMz0b4z +8Xs4uxNGU757hBxYehNSMU35Ux5q7kiqdopVeW3XMR9S+Lt1fC5rKwYjogrCo9zPUF4= + +/w041f32SN61MwGt9NoLLZYbAXFT3vnIOw1QfdwZIyuI/7I9Z3o3dSkJ+veQ+rn72MC84faPwNDh +X3KBq7B1pASiqMWq7uNRyhilEq4pORTjFuMBz10EYsgCv2awbPzcUpgHyMproSpV4WpanvM996Em +ZzQ4ee2e363ta2+H3rsUT+iIr4JfGUH9yp4K + +RDXgEmdfcm255Xb6zU6wEgKFbgUvTbHxbGLqa6SoPMJ5Etd+T+wNTmbG9rVox3nLK6HiuMZlhWyh +oXX0xclqvaif8ivW4v4rB5GJcZU/S09dMraM+30S3IDo4yAp2onJreR6QFSa00RDGj/4jTonNgnw +ehx1yqmhuEPdOYAzIPMFkpHM+oqMfIYcHwb8ANaJktMcQEuYJFqfIa7fbiKiAt++Ae1np6OLX8+x +2RL59xmR+xTFs6ZmphIuAbAHT9PvPiZkNRPppniBH3wLDEpSOnJyIZHU5sVuRA== + +wYEJqKoBpXkesyRcCswEEQBIvx/rYtklJb0uCtYq+oe8oFU9OQXYMqo7DlOZDjzMlUqZPWUdTrGG +HiZ1IKi1jne1H5QgLh92w0+kMAH209LSrac9IPHGeLa+KgAw2EvFs6hj+v4SoHnbSfZHE+p/2toS +FBGfmmV8P8nzFJcWZNktHg9NbEc872crlFYePO/ZJhCkMrUmHoLzZmaoSnJwUhABc7v5I4YKiKWu +HPWeYLVCf68Q9XiXBKcIkY7of4FFnKZi/68dOXmCyIuEE2ViF/6GFzvmqdlmBKhCmmMhmHCA8Z6V +mE3E8obx5e5tA0nylgi6w4DJVfj0d8AqlNnZRHIe9WTOkGPNWVqdCGFKyi5N2OROa4CvPRp9IHsr +EzJTbIqiY47NPRxJ60rMTuZH/P5FPW2BGNb96KuxY4r77KcxGGm+ydMa1fytLwtQoUNNIXGxphpZ +7qXF2hDbB0qmhnWi7+fHZh1/jXUBH6LnjQTI/Ni6m9iCZchES+gJmUcaSP7QJ0TiEe2JRRMkJQKM +Nw+EXp/FGIihieCxVpq2ToguDvMulQK8gDXRW1yIpUI0e2vfuFQVqieKP67WzHYd53Btmg1WSMxx +s8PVH5wsdJfce8rFV6hmra9NVuuPjKyuHByqOBuLv6NzJsftRyOHHBfRtwfXWnQZT4n6NWvdI/c= + +0r/4nxpohIKGVw+fjYKPbJ3IKDgH+hZYL22Ym9nrQupOaNkEDXhsJ0MLXqhrw2esOHVP7usPaA7Q +VLQMVKUPIkQzkY4ylxRYjp+VOQfdjOn8ZfbnkwuW0tokPMhckOfrU+jebBWKiYSgucmIeSjFG4K5 +9kYoI1CPlyykP5bgUz2YjOP5tOMrMpz2b9P3eE8zm11K3Z3OKCsVTaz6mHg0zkKZcxNMxTvzouCD +t12DHyuwzBDsQoIRmobAOS/HngcDxfXh5sgubS2WNEOnfHlFh93dP6oyMiMAjoUUuzAQlqMUKsPJ +V08ANxkIBwqmPwfMb/+XDCgdLmr+5P4C6g6LsXVXK5uYduhLKlmte/ziE2spgEtXLnGRmYyXlDl1 +z3EMX1zhIvX+eNAHYiOyAbBppd9G3/K0MN9k7AaahpndnzDoyg/ykNMacrBkiZLofmXd3YzMA4Xm +yDLlJBaYR+yDv04J6l9f/Y6coj5n1iefeOOiFklIG7TNBddxxxM= + +jeMxBT/R5Ny7NbNIZoLryQUDuG9r4cDWGUCYKodzcB9dfkDfc2mi4qzivZLCymkAD+M+IYI2k5gV +CBo315C29ROXiL1uLVcjgrDLRNjbtH+IHmXfcH25NxOv9B34xp1yCu3aWtX8XMPEb752351BCEpA +SRqYPvdeHkAvBxPLl6usmMefBhwxjQ== + +B+bCLn0AyxrexFzPu4tD7V/kZPURPTxNUt0NLTj9V7J276iFt272J8H7aAKiNMUuPkeTL9mgLfmm +GrUX2Kb3Gceml7JEwxW7OVYv2m/naabGynnlFADdpIkWRUfO4GY/oxA7GNvSpJcgeg/bBOO4pJzL +jqihZW5V32CevyDHevHXYGmRiwNxfv2p6YECcXswX2Pb/minLrBkjogQ7H7JbncxRCht+TNq1CiF +1lX7YZe9wGT3yUuBdIKJ6Bu9USV9/acVOPVW6PIiBskOft56wMOZh2vi09VqjoQSH+XcRJ3y1VJj +Vi1yWnztPZQPiB3EAzjKUDbsCDbwMKlxRrr8uKkGUuZ/kvnRXVhnIQ== + +Vadk/ZSgriYG3qCtpY2DFzip53hTOsw20oZYlcNxRKeyo71NBpSuyMftw+l3XZ0kiDTF/g2Tf8bM +ZuHsWKHv0w3O0isQbEqO75CsvowK0wf+NMf5gRUwzfjNDmSBKoBA+npeqrXCFVQbWeUQH/zA1tW3 +LBiNJ6WaiTUkqf3CwTAu2HhL736lO+6Dbbh8QHbO8Cr0Xo6D26s4Bq4r5ZFsorSffVG01xbiG+mg +qOpVLnYo3mCTHpwMwzf4QFoh7qpo7ZXPXg4tLXnd94RrWP81uZu1iS0rKo4Xxbn4l99xDmzPetre +z2mWKkokDJcurJd9gG16V+FmEBTnVCJBcMyOr/DQ7LhVQr9bFWnFouPxBi4CCGFw7H+zQMnZiHIi +BfSIzglSxuH11E0qNW6kuJB6xqLZQJ+HuYWyQcXy6ZFwV5Tnlk+cDO9Okblr+4A3khnPk7W75OzB +0qX5rVeS5gUG4wlQTxLG7azVINuQ+N/2ztWrvWI/j7HWoryoMz6ThHjccsv4F3AAwYs7utklhGko +4E5SGViyQkrFbPmNCPvqr1wtBHtUjhE= + +7pujXeDplvFZ36D+MM60UZgWfi/WmbWmjzv0txCESyHTIpI1igqDAe07VWZfUORAC7RdhJIRfjD8 ++n6t4vP7FOV+YLfKnoHU77bvYVUtEsEAC3KlgsFd2VNXRrqpI2L4qBNGhaRQLVSpGTJQL8jc2fY1 +ARVMQaRFaUPlIE4J82PkwtyUiCb/aistOwWFsedDSkwjqiRWaYf76Phi0liJ+9wYGJTBp+/BFke4 +8NG0uK+Lunwfn95g/JotneIeMwUW6mPTUs8op1PZVrvY0r5BWne1p9m3M4gfX/fJVSPs5+vtKUt9 +K9/IJur7s6eDIJ2E/lHzUXB8JtnMsPqCwldO7g+FJ1j2HbzOfyQFuROWf0TpyNG4qHeUzm6X18v2 +PPS8aO4= + +4wpFoiowrMZBZrXMOJxbijWUO2AstVInBtBPkCz42lfm+/TB3iZ/EjipIfKSaF+5ZF9pO3KDQ6oi +uU0y+3zMPG1/3hMLL8WMVmIbYgD+6wB1e1U7tPkqWBl6nweTmUk1Nb7nXMBbj3adjuzF+XWqgXoS +p+vg+H9+sI8dGXPgKtt+CeNVrouP9OxOmVzZblibaCXor3rfHzqARmN3ri/a28+BmmmTd07kI5Ry +F2rRX14+1pD5XP6z2tIhOE8Q5veJKiF5 + +6LBo41Vd+dg+mhdN21H/y+1FZZ3FCIuAVXzW5SavdVhGHVTqf/SYZ2lNNNgG7vAQWKxPVT+7iRUI +4HKreB6v/w5tKQ9WwK6aHnnQGxnr4kBB0z5mLxhFM3RFNuJv1D7HxwTr7JwWdiyd1ZtZ5Fzt2XJY +KKuZoHL8PrU0a9NWuljB394dg5U3yQ99t8EzvpwoDW/6u+/Mt94PC0IQuuP1WW74W2iek1jAqryx +64yzLOOwKBgJPKcY1+bAc5THd24Y/JWvAeEIvvbH/SOajWbKEyT0M3GTMe4mhA1/S1C08tWsIHlx +J4cDfURIe0KCIUPR/efamlMP/e9v7oqm+zL7E2LuE5zMwABfz+FlyKN9p1C6JVC+tv8VUg== + +fNRLQsM7fviCeG3SDzMghX5bBn/HYsHoGi9WJ5nQcaEZxSxnEebgycc8mxGIbmp3kd6UFgtRebje +QyLy1+TjtQDvF08zJKv7xvcW+BtmbYIxyU8eRT35n1Y5ErHjgiIKo2JT0p9HkAhVyZJWM5BwDRtZ +HfQ8XDthj4aXErZWYPNs8bfzDSin/zxRs7Jt2BEfSkl3wIo40S7PHMxp+pVUnFLbwofCQ6sGU7Zc +tcipulOnoDY+SZhgKuddBIOynXFbwlI0xsADxZoeLJwX6szCKFegGLvn0tr9Mtw6Wze0FppvbqxO +YsZoBl2HvaPonMUyHVWStw== + +Hxza1FbiBO9TKO5qBcMR4RK+nhdI9sAaDKShWFBXv1xTTIq0fZh1WIektiLjKqtDgicznLo6DXcK +w7AWBSjqj56W56sDNJbY7ZrrsOhErS3oSUQFja6I96zqYchMZYQqsq9oa2S5HNS/HnsJk85QKCQv +b6Z4O72Gu2AnBIfYcYDlsROlAK20l3Kt0H9k9RbGLfma/1+/jvMPtuLr8Uyto8bDfuDyTvh2cnju +HbJ+2jTWo2CigWj4d0DBuaGMVYRaqhdGkC8lus5qLadgV2bKt7tUMpYnp3U8KkJYXTrv9Tyq + +guJnfwo2qKl5xdGFzIr2wr6vXEhfa9HHA5t4R759m4SzwzRDoI8THYGzOrPcsfUrDGAmLOdLBzgJ +2TxWmtpzabeeAa77TjMkCzqr7cQqE1rdhsgfFOR4lBd+fLQnN6pRLlQ9a+SV1UpEL4n50w5iO8Sy +hFhVqoVg4EnaLwFiD1zp71eOrZa9hIEeKRP2FieJfWBpDRizcEqs5e2lfnyROsuJ9/DCu/MbrJlr +DMEPjwICsJ+3+PhieD8QvJijj3lZrvT/bQwaH4NHW6p+57AUtdkYumntG8gOmeDVDcDPpGy/kNR/ +nbSBp5HHmmybXiHbJoiX1RM5elwbmooiNw== + +Ir3e5WjCJPT1KEM/ifZjWV7F8JA4ZHc/R0uFp9Tu9nbb0XZwis64A5UFbtWFftTNOVgx+SX9dVZI +aE0iXRsZdZrfVCn7/WlfbT311ubZu1QknBRX1uMfxZA03n8uwzrqG5X4Qc2KIji+3COovf82Vwfb ++hqhODjsrxYmxX0R84x+tOqje5ZTGV/+3omttDjkYtO4jamhM8CFhg/sKh8CicL0gp1ViOxfrzv8 +o5y++WCtdJRbWIrFUfjmScpUmqhlocG3T6NXJfGcDij53RPYrWW1fQPZioTa6oz4HaKcqIB0Q2RT +GS6TxD6m9q0pjiIzVPcByxluEtwZfbwvpVl8xXo+jBIG8AenhqmGHQ+fSlWKd3+mz1xPukZcmpLj ++zQCJDodYc8lu96La0diP4ETyYxYGZoDmyZtNwY+dgdIf2jMha/NLcBQ/t4TXZo8byBjObPhHaSC +V64LYRkSuotmtmN/vCStSBaVfjMemPhbb8HjRcDilvvaOmfZ4LCYNzOLlbvctfjnHhrbTipjyaFO +YR4jnrDSNUmS9RbmzdPSAGXkcXjLZNCfrnLVqxdTRiulwwNJpAHs2c+2WNTM95l5wTS7f+I+kiIV +fAsjlsgt4jqomZFwI9pf9rAOOtBPcUruTy+TEGXCAos= + +aGWYGdDrao6CKxS0weFdaBWGIXmJtwDbPTUrp4uz1hvcIZOC//zawj3Hkt+fPI65Mcywlkzjc0jm +cSddn1YiFpWQ/AYZmS32vusXLHxHQXsw9DijDETzNEOEMdMFHyxBpcFeQUwJEs0FCB07WxTSg0PC +cXbuxMy9Wvz381AGMZwp/dAwT0I6m1RLCwHOs8lwATvf4JwPiVDF3buyrT8nuFLNOlImOrbebarI +CqkI6Nmj6bl1MY01bK47i8pZCb736RcBsF8rQ+ZhtMs8VilAR+92bnfti8jSl9ZGKvdDreHE34FP +VT/6N3aXIapLy9KEUCw4aiMMP7VzM8e+QVDGFKIh4YTyLeohl+fw3x5CNcefy1bF8dPZV5as2u+p +x1NDCEfGqW9AGgXcoO83woDMKidEVVIuBwGg7QRdZ0V2PMt+wFq9nE142J3oWrhasgD+S8byryIZ +XCYR+CGnsCV4YcgwLWkmFjBXlQjb9TTcAHNpsxxtfEaHLKFFAdNCtkFQkL5x4lmX5XCjLPBwZgqS +CmrqimEGT/cYdbW1SykDms/7BAs96cRfXoZSTAWEimVuf/Voj6DbhNgdodyJ6BaW6bARAJg9MxQC +QgWo2AS2dBOUi6Y/9QkkxDV4vcGyJe+A2RW+uw== + +MSrkgEuWTbPJ6zP1+FK5eKxAbgS3Q8cm317dhR+zinDEVHmSU8h820EXmJ7emR8fsUXr9e63vfSC +QV4Nf83sJCAcDHpfU7F8TuPFw9DY81k8yg9YM6gA1kcMrttCQo5e/h/siWxrbzTTWOsCUUu+1Oby +IfecluJ4kP8cKUxCENVrokYLGxF4N2eoN8yr0V9wtGiAATDRoLFpo4M0j/A= + +NH4fOI1OR1v59Uj3fSJKfAkui72/2BEir9ibR1bfR6fxey+UJgYwBPBahg+jChKWbC5FsZ3QSQR1 +cizmm95FuJLmaN5aB7DT9pQ2RvcxEEwg2AT+BQo2WUTHIZ5EkgK5CgCa/A+mB7nOBuHpiTHeN2E8 +ZYyGHRq/XRNrrbcIDU6CNf0aD2nd3FA9+T+dFZLz6ir8e9gmQi+qwQ+fY6GyWjHvwUid0U01qsmh +WLPhk8fysdMBHii5GMc03DQac5DlrmGORJXxasRLonOsS3YRefPJB8wNbbQ452xsX09NC3VItWYD +rDe2uO+a1w7LTZLCsq4TTFdgQscl5OnQ19QUQtgvd2Yw+B4rRFpefSmbHeUDYkJ46WaiU6dLIp2G +QNDSm7VRruE03ta7b6c8sklx9NHlzYaANOf3Lg== + +YnxTNZOAjUwGGlwf8VXw41HMkUjoMeiRIBsHH0hFhYWVElg1tFy5/rJCmNtpaDzfbzWByWIU/CnG +a07vqYNJUDXVVjm9+x+q8qTq/Ey83OMMIC/L0YF5D3/WiajloJ7/fQga23KohEi95ipAZrlKADFS +z43ilIgyB2wo1kh5POloR1+lMHbZFCVOVwiS1yeo++5jVjCe4Rn1+HzSyNxfp/sqmfwpwI0pje5f +DPv0Tok4Fy8dpb/VqlXyroR30xpnUIE1IfaCdQm2BWbM3zKG7QVg5JJgqR8WX+yGKKi3jQj1m8rf + +JaaRRRUtQz/XVzQEk/b6wgv1dNzyhYhVfXOBCSy5QEAk1dgqL/5OErh91I1TpHYOFtOGtGDONDfX +nBusRcDy7xy8gm/2sVdnoX9hcAc5FyG1kzQir2FBHI/A9y/b+8Jnv/b39c1AvQxG4S+/ou9dxd89 +KBWyJ/X2qThalW7brfov6QyaymhzIz2E9B3DFIohlRoeNs5Q/RrPEk3XPYgdIDkmmj7Q4mI4dpT/ +GKHPV7n/imIbn36dSXwSC3KPO7Vw+s6BQGSpBkm6q3gUUiiEjjYZ9yGbRVfgcAK2w5QBQQBjfZLY +uM3JTrKEMi3mr8TVKFbuayZ8lc7l9NumX3RElS0q8+BpYpW08NyAtdZhhsUu6maVmN7RuxDbR+Fn +550zoZNhh3D0MFZ/6qaxSToiA7p0wIK8eLLuXQml1S990H/YqetkzhLFanf/SN2v9dYjtJt54EVz +smb5DWn+c7Vkfc91lH9mbDfuE8ZuwiuCK8D48zLIsp+2sh5xi7R35uos7wVku5H9qQLUnpZNXKg0 +s3kQbBgvXij+oNmLv+EsEXFs2WEm58vHzR6hTXDzXw== + +34ZmaeDiWg7ZRQlFKK5GaeMUUgC+uLFNRuW/3FOgT84YvWq7jziHnjZhQBDduOcw8P2aeBG+wkaJ +2oZ8gtgRc54YagQ3mgDOKDF/rplX8s7RmpuIUIa3iheXBJuy5o95uqixYYMkK1/PhC6Dl5AvX+e2 +aSZHCPL+mV1HqoLfJHfrb2kA1ce57AUhnpyOXOvn4dG3n7z8kAqUzgr+zODjIzLhuGcLZOpvRXfv +B3GGDJc= + +ZcakvlkL0Jwp9ToBI5Oa9Y2l5G1frcRFEtnvg5oUt9mLXL9WlPKdfJgHwwhv3Ij4ZT+tSiAMrcxQ +1Qsmb5etRpE1uiINJx3Z9qDJJ1UmqfYveyuwEGvNQ+Ih3QK7oJu843lwXDc/w9GeqFd7xaUqTJ1h +8t+FFCp8jC425kuLHQ== + +6bWQzOtg9wjFWe9xfRpnQYPs6dcol500reqM/UPJq11EzMMXfkF5v88uOtDGw4NluDUdlk2AzswN +bJzI+RsJZul1zQn9rYlUMWs8wwb9Y4sdXrx7wdEivGyB3GLnvWVFsbQLNyojRcJSzp5zrRh+20G3 +x1WN2y1BWpgxiSkBg92+z+6YqfzJnQ== + +3Hyo9dYQezKq650s3x/B1JV9+B/XkMhNTdV64ayIArVQSvPxJilW8+kFUL0ErGYh/XR/kdAP1HB1 +32wTi2UC1Cs533iFFXUOWtn9nhvwfnkJPXP7CWFiIsMihii5BwJa6bqwZJOqeddDcHYHuNf/8Bhm +1V6KbBNL8NSnsYbsn35OfCWiYualyjinqRgYfNsmAZ3QM5bfWCbp4A2f+Lh8Kl6DxJrneAyUTm0m +0l0EezrGuv7obZRRhFNl4yvbQJYH8Ms2byeYTEMKI8qVknz/hCyc7H3/ipCEDqUse+LTiIJh8SSM +kMxrDZO5GIgNLq2TsER0qAsGKx4x4qhzWkBQY/BvHgsI+ExUo+y1dpT2I/Ncz1kfOEKuJBWlC0Tz +GwNhLucQdiZaNDO9M5So3hK7Mv6trGeWSWMX0vI1JA25BDwd87pRINz2SyHfQSni9KqvSK97wxhm +cnHL7r7MJ3Au3GgHtgPsQy0eAODgDHGLV6xBYU6zeLJdya1pexnRj3n5OuOEkAu9FKF2vk4aqIxw +KaYq4IkPQVFhfOBjb7WWl8U9DB+pkEx3Zr857nq0NDD5o8OwlCFp2JcaYWBLMSsLGDTUm5DMizfy +MRue114SY6xwddNdFKpWvMzniIsFSTmlvG9SUTNT + +tnS0UWj1P6auU6GNfRyu7tBLlj/thS3vtaabDNHuv0EwgILdg/ifVjh5HTqF2Os3ospzwz3uhOXi +e0jANaQE+6rUimcOJpfCfjrb+n5gXk7Yg/ykxEJRuV2Y2lUT6LQ0I3jd+nxMxxKlNlmrez3mu1VG +M5pbMRPiM3Fu7AjozzveNsXOrG07ZvzeYqW+4rm4joiYU8SepR4cd7Y2Fr08OxWbJK9IvZuw/tCb +x/oCe0w9bIyeri1f1EwV2VFa+hvwTDmDFBjn28WVnV1HJGhovfhdoIXtZvD5zVrisZ3gc/rSokJZ +wijjKSRwX85WtyvdMpZVzXHjed/Tu0KMNM2+SOCthaaKubgS1BGjXlKcwLQhOcllZRhijqh82Ve1 +VdUZ3npA/4EMniqrzj99DfMA/gJhzTMLJmsKiTYJxASPsxZRtPj+pla+fN/DZirYXEf/Srjr188/ +fxQV/mLPNROXDOiVra2G8r4r+gFEH5rAsjw4CdnKBx7A572zT1K8Kgm3NS54GMUL4lk3GSjbok1/ +f0xZ1HPuDFFvtuJUn4CpNCeEaUn7KB4iw6uJd+6JVAm+Jft2mVT7WXT311ypMwggRFxZYSCGysGi +tYTtFq+v4lZALGPIfC7OP7hIkTKNHkvkIjns4z1y3DkporCjyv6HICOrehW79XI/8CBAgc4VG/1l +vVNhkjF8 + +ze2ooQZtMOFlz+DZQ/OOHWXxTiMlEbBxSBqxzoico1yQ0KpSRFlIkCKriMKig/TOtoG0OF7/k/OX +Uf53r9jU9Axl72Xlce/1TKCZ4n2csCHw+4/DbCDBfwM8YOdigJqUy6pVhKrCLShrA00S/dYCPyRB +OIVHvJD2994rvgPaiqTTEe+hNTYyWzk2tlRt+A2oht3q456J+0mjbROnpOJHOvXxY42MBT2Nqcf0 +h5OVxi2NGHlV30e9wp6nAgOonvq4X1yWYUGXnIrQFFYYOUGhWJupjNsWCNPls98krNdP0yn41B4j +qlRKyjHp5hBy03rZrJDw/Biq2mlUUZl1b+P61v5gCzyn6Kqo9qPoDgk1PGJ/iXLTkQDGzSwzVsKc +SYeTaoZ1OIl23G6FVL6tsE2/htSiw5lcNngCYgm41PEI9EKXm1xIh0A1wv9lws1dbm0IJHry + +stiyr/C7OzhUsIkXsAhSFEc6TuQiQIRNYeMfJWCS58CzEeiSr+fl4tmZSAtk13JT/hM1ShFw/KWu +ArFfNk954sCeanR+/91vFW/YEls5JqqqKzkTgcsn79ZU4TRfEIFeKexzOxOYrRJrGOaC9Fo8WtX+ +zdPLHIvbQ1XeWWbujGOy7IuR7YiuqMlhjDfGJL8weX8XOmPKNAMXNRfmhZaH6W4R0KfwQSt5WAYs +72hXKERz34oPMAKUMFI60AL5zAoGawmSkCHkqBqlgFBg+I5V/AtMKgtU5NMHnizmhF4FocK4nNa1 +CqjXAL8wAg== + diff --git a/test/hotspot/jtreg/compiler/intrinsics/base64/longLineUrlEncode.txt b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineUrlEncode.txt new file mode 100644 index 0000000000000..bf9c20693e317 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/base64/longLineUrlEncode.txt @@ -0,0 +1,100 @@ +AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w== +k6r4A8ytszE6K0LqJXLfYh11VVN9bmDoZmunjWCQFLEhOOipRcpF95DW8ROajitm3068-kPga3qd9jtNZptGQ3r_pXm1Q7GBCUcnnpyZlpEzqwh34VZEgAON3NIqAIEIWQj1_ACLtVG9OwMob3NbvEaRwp9-Y1wnFEynDf4c2ZFKsLgfEx2_PvMoH1AH0rsWZVGWpo9yLR_8bG3Iw4Roxxzj7OlW4828Zaals5k= +WBrp7Jho6FfByCFvW07pHqfW7cXeiPkgMwetaCNkpeMdkAAe8VnqqlqBIwqb1S4fh15FTeg0rVjSw3FFnFwQBzQQxKZfhWNi167Ma3QQpWviddFNWG9BuD15wNZXYbRviZcrOhYniS852XpNZZCD0rHkJXPxHaGHSIckH-rrCGlaoUengGScE9uGel3CRyyykz0hsILyuguG8ejPAHo8rlFHY6lZkrDF2CjabUTWDHIfK12bJLzIfDCiFY1e7GMAsfBw86aeHEgXxzQAJQyvboWaNj6TCIZwAo-o2alNIW4KAUXkBhAITY3I4psoHKSw_hvq1xmbv0l8SG_5X5Rct_4iOXLtHqjRJWM2dSwUmxp-F0kGqUbLKBFxLH-Y7HNPkXuNwSxelYPcPx3rtQVWakVKf_WElmzbyqVk1vrieYSEjDHwYJHQXH-3uU_xZdOeqgpr5QrXklLPOmFpcx_5Q0YBWpO5BusLpExygMEzPzvE0xS1H_WGEGA= +8dcsB7oE5RhpYaMTzZREu_3bNUUx-FCZsdmstvQguMpStzboCEyWBbKOyi6ptoc90PYRYKdFdG-8OsrJYAxhEdZueAkHXUTujl_1GZKPQHOWrsB2RzPB6a2U9zB-UHdHv5pZSYd6suFGZkamMQaReBILVq9oQwzPtrn3h-zTSznNhamm4FmkXVPyWJwpbzyf92v8oVyOjJkhTsyb2d8GVf4zQUZLKpniczSwLpr1JVM0impCh9bNML9wSuYFHKrJDHfneU3GjwMSjhOKSEd_qzvQ0hSt7ut87dXLVrIFBp0UpewsnIFVjoiYOQnr_iQdtnbPDBcRmTir1X_682kXLf1KA18NE_AyrVdcbyVJneA1_8uv64t5kUrl1wHhZFWKDXjfEAkvfjJFHB7Ik15XdE11aulXTT8LMQmRkTZdbk04KgIcAy85oLejVmGTI3_TtAhpHPreHzc1stz9z4TsSp0uUpx3a_ijpdIzUh6fFtDKdIHsETAPpQWMHs-NUUqtwCbqPOmow0XQu0AsVaupFhe3xEDw +SqsrwtQqIIX1C_YdoAlvMfhJ2TzBNSteN1hInPj8OQCtbyctDgo5KrrKgSTUOjwHcIxO4fDCuMTJJxKXbfRZKVe9FfsL25gYJIPojPFsr79Da5Nqli6qTTHwl3LRn80yHh51PnPniXM_vgMjNONtcYq4Ho2C42eDeqS5RIT1rTpIyH31pxxBtVOnOA0pNfdXs7KEXlv8Sn2GiuVH5Bb1MxvEhAO3tJ-vAh1vrGj8T4LmiubzUgmHl1O30PAaGhnUsu0EkGwo1RFasj4UE_YaOfHyIq8sYzflcrWt4uGrYHykLa5p-Qh38U4XxJaHJMpwRS5qxS-aCO4ryFQuzMh5avSIFMK_JmbTJWS_1g== +ZE3mfTaoaWmBECTNbd9nikvk3dFzaPW2nDCyS9pT-mv6w2T2XjEz001DnKhuQgCippWMoevRqu4pQowVfwhfkJ60me0EXTHQVBqLOi5NhDNWXwze3NP2r2mR6fMufG0BflIbtc5kL5LbzQACnKsbK5OnizdTTua2Zxvrj4pL7l-0Nwht6BinzYm_YotDVNx-tdZ0he6R_A0E01xRWqqA1bwvxc_U9O9W6YwtEhb2d9rXFm4MIwEgBLpwN97SOZB2Yoljgjr9CRSJPW9uVMutVy3QQYfG2IHEOKQ-9kCLct-oYSGBTfyw1DUXjJYfcRekmXQbg1125P9YNAJJp936kx9-kQoqhxF0xKO7WDgrwTP1_LKEt8fmPud0_rO-jDKSSCzdm8tmtvkDRMe_Zx0T3ZbAzHtWQLBL0rIgat-3oLBama9-Ascf7DsGl6YH_JCMiQmkBhnznTsi-GKUU5iVJxZDNGD3zJCqyAPs9kC04FcO5rUAszBJjlK73cyWGBbpAtnMdLXpkgFS-OcRaH-xosDNYdhpQ5_3hfDbFvkO0SqnNMunPp2csxd30uoK-OG6Anv6PZ7ESjUQBHS_L76jr0bH6l6k-XRT1kKkTyCbgnAdna-Tn_wfktk1HaVHATZMiBzQP7WBw_3NSsbV5j8pKZ691rBA3vh7GNbGdo-uITOgzTyNpeFU +T9aj_-YGEfNm8h1xwhh5tOn48lHVrytqbZBZjEf4hAV8R2kGozuLF_fWEZR6tVOtPfFPXaRQEhITuqRyTbJr84cLnNVxsUegeEOUX8jYZ3ChUFTI1z9n3YvSAmsWQi6OGy7crfMI5Gr3Vaea2Alrr16xW7UjyP9GsZxuH3okAshaEnL9yHZVJVoqIU6nmHJtb_zBkwG8bAhDPMpTKoBom-7PIeHXdp_ZlvO719GX8MPZfIBphpUQTmLIVAgWWQ_Rv7C0zNL-WWWSvgfBseOmR9Vu0oikTj0kA_M0tlcX8KG0X8nrUpWKgk2Pw_I= +ygSLUWJPLE9q5qHkLbcFJuMYsxkvaKMp8U22uDUMoi3BfzlQERGKErlfT23ToKJgVqD7FA_Sz1d_1NDKbvidAbFPUaEKUc-HSbZVFN8hkwwEWe--nRIaKGAuXE7EB1QsUyT-bdAPgTsVD9e2n_CjalSso0jl0xT7ZGOT57sBPyEwTowg1TXdfuCjFGzuAVQTMyM2xz70azluaEnQu60BpKDR-SndvJCRy73HUnNg45kZKiUK0l_R5ddWXddF5-fg +66UXSFBwzCBYQM5rq9I24X77GkWcGWo-gBoK56Irx6iPmoq5-pEh0MW40G-riwH9ztXACK3x_VXQI6N-PYJCqUn6grFJUJEh42iEwZSBKngOT7jLpNs7G_1SrDLFgZy7IAGAe3fM_h5s2QWoYWjHTvND6ZZ-wF8UCRjavpBFxufaJwkRGEf2Mc_zhT_S-6KgMfBHB7zErumW2SB-v-7WCTrl-1bA9dd1GkNtWozHkTLX8sY3QbOGDo_Igg== +GmRba6-KP0E1ves8fAr55tqJ3MVoouLC7qyXy5i6msrT6u558btPZJlf3U7Qfc5SjJknySBLS3N-cMNJ13tVVKn_mAkOBRxbFpApXL7KAHx-3mjX8MIgFifcLQ6PHXGL6Qp5t20WyKeSeuMf6VMgojXUYJOg1UnveBroaAXLLIDTVjjcPwm6X9wwdLyzrrLbKhwsTq0eZlr69NpZOYmwUd7Ww4zdVIxX4Iq6zg7cro4tFkgRUqnaCBTaPG_0h7maYDvOPaLVURxU9Baw3z5nfGcSs25GBm1kqRVMFKRd7aMS1aVuqHq1ZHCh2HR0y3y7ioKIq6UnDKYBpafUhs943vvAv_UCAnRXIbakEob8OEuSwbHIjaYW-mpLxBWkj1kfN-GeVt5irkI_1zJRVPvsn7LlEtDz6dFbHUcmyCdw6SQgKnqyIkKOMT-s_ttGj8a13xvMAxuZbYrrN6eLdKka2F22Lc_tGtDr8MZgDqg9njB4FQJZTf8nsRuuecOegfrkSiTxtJVpm9GugY3-8jM89pcDpdY1vP8jHfVMKaYI2_lFyWRQkIw6KqFB_DEjZJnTe5XOMXhc +yGpzF04SQsZxXyxQyM6xKElxrcKIq2eWp5aRH1XyR12rN0oSkktWn6DoUMbJBmT1IQRLQuFrlK6NwXKqMzGRlStbnlcPVzeCfGJJ-HhyQIu8vRDb9DzXIpIHkuhtlyLvVv4JqgFdStv_TZ1feH4zRxG0WPHYlcwbbptoMlCwwf_mh7joFnmItdJf-LF96exmyXbN4syzi0e-ZkpDqzx3_2BJvqk8aqPGvJKnF_7Q1lZgx75qp965cEa-Qk7yZ9O34HUNfWOEnKtYmmEsdDRBWzDgXeiEOlSIvbpyDT-wiQAAyJldtX5n7Y464E6yaRkF4K1xESinvLOQpXxinwftW2-BN765jfgqUfzFsND7xg65l-6LkTV5xz4XfwU9l8wkog6U1P1OYAkgwUe2MIKfCFo_APBDqz7Foy0hsNvDJN8yA-t3ogNaYtbCMxwP5INhXMf8t8iZp8zbXB5aI9I1Hl3P3TZsotkVLWO1ZzSPB0pPF8eR4oA4BR9y8QBN0HR7KLL9Db4xFymabnQZLB7H7KehJaD3hxgIKi35bJoc1TSscIJODvyam98vcTmk80-CBShwIy5JBDeh9L6OIij21wROrChPDrxp6e9Bl1E8Kb9pDkgHwycHmAc3tNl77aiMY8dWXsyIKVJxUwNg7fN7FsqTptsOuGijresSLbCM +d8qFLaYAQ-TX5Beyf85B0ViTq966wcFaiiupM8QSoa-06CEeLtlDQesMrT89jeV5LoC-EpHbROV-4e0KsFjIdWifGG7O9npd5gRC8rFI9f_mZvyOVNEZjmX19xlbntNFcqqM-F5bY5Yo6Jafeu6mSRxfp5KfZKNAjw== +y32UrDKLLehCJrsW5H771kgpOy91y7_V4gs7uwjo5XvVh6MbB7HP1lwG8lsHzS-p8Mxmc0pe7P5_tQkihlgMiqxb6M5xL-acXxwRlQXL5f6ruM9wBy4hL1It4glV3wfbdiN_Pob-Y1LuVSjrHb0hAHl2htAOR0qef3DUtZQoyd5vTHMZeuHF29Gv6r3AzYtyB6B9ai9FKwVNR0wj08pKyAxT9r2BEODxsgiCk4zr7IlUXnOajpOaq_odrKSiHpE2RpYgANW9s8oooAcAnDHl_rs5rB_62RxSEcXGqrjPNFuCwr3SePjh7XLBHf-iRF7t2rDuHTH2U4m7rsJRLKPVLpg7og== +H28-pYYbQT1WKC_83SROqJcjzOOXAOwSOAwVIYAhAvnXi_Wc0kg75Lghfj6njTxs4K2elpQr7TgU6IbXHFpf_M5pyBlIATpOogBcXmN5YOgmz2z7Qlzpf2AKDGqlJ2_72zXrZyB6GDUlfRmW6t68ufcmMnY2aQptmvhBpjjRmfEuWPHb6HJL1D8DzeGWN5qBy0R-P9me9OAvZ0vZBtN1LbMQXQVPa_iCYHmf-vBpwOF242-XTAOeUJP-sKChINTJqdlCfyqTtT46lEKYv_xtBQEqF9q9hxZI8sOeNu3s1PLkOoDtR9R1rm0ULnf3HaRId6C4-sqXspDp2TDPNYh9hUdtG5B6mhMaeo0QoRHvOGspLFTjXasjj9ztKnbYxWSC6WcsEWe8FdWLKfB_OKco5dq6FzqKOPZhUeax7SBs2NNvs-LjmBgIno3dlivf9cLBYv8dCvGP_yXjUXYStTsb6Ra5KtRKXhV3Ga-ljDdACpg-0RDSHS8k-UYN4FI09CKv7NZnN3-AMW-vY--zAyPyHpanHHlq_R402KmskmL4Wk-71cNB0FZ95cI2gP9Qw9fALq_CpeTumkxHD4cpie79TCtImwKE71K1FAvohI9rxtvO_w== +_a6fRfnC4h9ca_dudoc4h4RiCW3s6J83PIn5CwiWbEbHZk9ABkkVM9pf7hcwwrdzkcY7zF54LzV0vpKM_cZ463EL2oReeVAQxgg9XoQmy1ydekyvwE4iVgucLWeBddhEgOQJpMZ7ehi7NbATmzE9GljMNkeu3zDi5h1fXfO8JERg7OWA1n0YsGJb5PL-KbrLXWhSiCcCgtg5Rv-H788Y1pk0aV2cOdk2ADC6rizg3FmQcTqoB6tVDAj_DM9_hvmh79Uk6nsV4mWS7lf7ESWIjJlb0Mfkh5L3dddvu2Pdk57e006qv0tcu-mwOS0= +aS5ICgf0PPfd6s5SJPj08eBXCgOSwUhjp71QUHPR2RUVZVGjomoUZEHtQXEANyMHhRMq5zxjVI5aSKoyNysBmhJfNkW-aiyEo5t_Ra-YjZUBy3WrsoHsRFJWDkXBJkdP_cPR_zZbg6Uc9u4-4FQCNwUriSF-ksrKscy-wX-3r7-VXNnKSkQqsw== +Dx5tUKiGam8Xg8Q-5jxGyF-_ExC8YfHOKxlfgV5jZnkTc_BS3FjR7N0rFRfGhwwwCY2Z4dT8oyFQtr_LW2ZYgs0KnLGcrBMMd3viqn8vJGlAS-Z80e1lhFQU1mLXUJNWLlZ4fRC-ykZ-_esD3K6GjmfuBOsoUQOaUdHD8Xm6HDRP9W0qU6YLbCWFdRCIDIedAeFDwfviyI10Wk90PX3pvG5CyaGpeAAT38M3FuLq_8sc +BkQpZhAeXJj-Ymwaz4BpGv-mBywZaB0rNaW_baBue8IZN-oys2fbOEmUw03oZTheMiZs0E_4q7n8ko-UB_L8IAvGfyrLx4gMf2VHSexP4K_63X2DAewmDsRugH0BY8BCDBJn0z2GwgLJp0Ie_aho3gDp-7LhMBNo9v76C3wMhunV4Kfprqp6NdHC8cVrMUiayrZ5MHkQFOuZOBx6vJrOCQnkmQI7dGiak6NgWeuk09dezOh-xcCyyyA7lVo7YDC6rjTQto4dGcXQ6uz2x6wjZx4AbPVeYU_OJM52dL8heJ8TVYHTyDRdQqTS-8R5qgBXcYMBJMDnrqN622cXJez3TtVwABW8OYoMxGBdpEvHP3qrWGgGIn2Zljht1k5pkZYpUiPC8gTjI6HjV9QTW-Uotryj53ph0yMZFLSDglQdKDcwdHTgaZpdILZeV6Ofz1CEJl1Qg4XfDq2yfgj-betxcWJknLEcnfkVIfp0tV1IdCu47tzXVJDEmFK-w-aXynIINqlCcEthBkoHuWXEkmXQN4kcSzCE8nMEYIm0DuetbynkATBMh5v2PkYpV-nStnWhvebW6vU_w-hyIMI7L3AxtCSnNgd_e5WySQlSAyLHGjqOu3Brj2avVCRt9Fi8MARwS3ICdJGaXcnRQFgJPGw= +yRiVz9ElgvOOWY0RA8ur-TZpCvt4rRylTFQRMP7PXctgbm7Aafq_nJNj1TIPMUHalu_DuBYVezF8jBFXHTarsXJwoVVzWDMzZJPXuGu-8Wd0V5l1C2iJlwagYaaeHX1aGOahTqm0KQW2p-889cr9RCBWzB9kiSQMhYClIq2ipBMaY5hfF_FXC3eix-WvgootYw-eZ6zYFAQv2UB2Cmd9-iRuZrexNYzsrIhkpeRj5-ULyrFnGj2kNAM0SqHOFqM2w2d9XD3OSpomfjHvtG3OX2u0h1BOlWJzKLrRSNrk97SNviTPAbTYEpjRilOIjEikVSOiqA7elvDUHNI9c4F03Qrml4O3HDm0el9Mie-vmf8N2saSltHpTlEgebCB-lfH1o2HllCdobpP6t9lYyPD1bna2KTuOr8aoFQdi_4bFGh2oIBm_m_P +wshJPn6NEMzEMurNw7e6GS5lhXwEv6NDcIyrZQM-5q8WgLxBDDE2pL1X08RRQ_f8KJngMhCRlg8RSnPE0BAoFnXHJyqQ245QLn0ChdMTeube9BE65c6H5nKqjWTkwC2KmWhL9a0Hz8vtfdld-lNFXM58qRibbVFgOWzbmAGSBnJJhMrx_rnoK2q4OgHxLaoHvCUuAlhLnjyKkrg2GlfQNi_JwMvJkmZzaICxMWSvRdhn3yTLL8FG-riCH2yaqD-HjgBT_DCO5qDCjblsGA== +KgpWTkl_rTT8-i9nM-9ktpC1TIbOKTcAHm6c7qkthknIekGlpiaLF9CPElmx6S0zdLLSJbTvPkL3Zo4Evjj-ZvfpjyDPOQfo-5sayLFCjsRoJ0neXKFilDabTwHKb93GuYpyr_A_b5iJHCOxXqpumQAotAeHZUm5M7FUbgC9PR0vgRU5FR5I9EI_-g799SuLoA1XW3xRz8IqluUvYIO4vjuoZxE1j6t3dmvnGNxKDlgr2QVrJOUmkZGBM5dLHLZLVUcEAX1LQh0PyGTHqJCNcBzQvvOjfNePCqbe9rHnu0AQG6uKYVlxoHTLmRCWuc_Zm5gnlPkhNBthMIA1YGBC0dI-2rzhFSmvxDQKe_pO_aTzh111HV-qKBfsofD-y6iaOG74O6BX4p_dEgFnus3egAmAokUea4SWCT6ymRPgkw== +tSyf5DBTQASxPzCoIFE11zz4YCrDT5C9msp6H8MSa1PVP3-dJJ7gznbZC9uNDm4GM5YGCixvNKv8X36ZpNxdeLsOUsBe5sxPHD1fBJUQFCsm5EDC9A5mn_-Z43szll52TFh3uSnPHr-a-Jv28c6LBQuzBb9TLhIq98hExWhjwo-KzZc7hIVErIw5WfjZCptqRIYAeMdqZJkyjgjF9aTUFKcA66M_eHPpoEKeMjp4ONEtp9BXsjZ9Anu7q9KbdfnyOjee-0nCwWdVUVBjVrghdSZapnqHDxaONuyHlCbC3wQ5l8ApzcJmj4mJZVM4HsCHjZ-05esYgX2EWYtNiF8yyxIwV7iGWKyIX1VUlpa90fskJPuQY6WhUABxeKaVsFvqf2QI0tla__Pq0DEJiE5YY2_H8uH5Kj29iKcOhuJgxgeMKdnVLq-xoDWgD5isVlxpeDaDBGgpRb-UAbNrQaQ0dkSvixMOmNq3E9QOziAj7VKTNOKwSXd-dqziF8RjiZc8m9YPNXeq79JJPLOg5Q_0IYjxvsi5hBciW5VN2RjS7-sA +ksMB7XWQfsLlBx6VRcauBkprDv2k6kUN4rCt4L3MJiKkUjivaB-FOjPXtbCE6X-Mn98AaAbEwGllevQqcuN7GfbWuoTBMXU1zWLZCU_IFs_76dgmvCOKt-8HZL9vR9BjMpmY7a_zEE-0WedxK6QdHk8DJmQazQlnIrrcWA1NswVsVXg08XH_d6V3KybETbc4sQBrfulvlw8fh492KQ7506z68evc7lu3QW8Ia-48yNohjE5D9GBx1vl2gAnBtvvyEJC2pIy6CvnlogDk98dF5CkU0rL8HKAuHSM1LRQ4_0MIxPNLuvBqSsQUGUm34d-e1ZgFn4_0hkQzzvdpyshcIy-adra9ttIVsioo3PT8sJmE2LnEV3JvqP9pPwWvtcAPMb4Q2TdlXuAyr3s3iXNNsRJyusFA8b27rzC_5WYQOpxaAMIw4zSQBzKZYdvUFNOj3lkDiBLuHVMuPY93ubBfVi53HtAVzUHsCBYcF_g3W8shOne29IlRI-et-CzdADXzq752pl5bYi8jYKezV_BpeCxCnVlSvw1ZiL9k-lUyQm2R5T9l240PWPrP6fDl0MFc7MrVp7PWXPs2gelzyjNOalZ3Q6CnD3Q= +rbbBTbq_PKBZaJcY8oAlhcN9-eB5CFmVIzcGkQP1AaToh3mhcmr7DjXhMHdiMjE9V-q18qPvQ0F07vkJmKBucXW0E_nUyZzao4t8Xl4zS80U5lx4jeKd883v4RdtjxVCE9v-zj8z1osbhiSZvdf0kDAxYKeB6TdKDLkL0gGyGZf2tqj-aY4MfzRk4f20fRwkEfOWcFy8077LOrnUDqfcNJJQcBfcDUqGzLI6KZ20erXU_Q1bKEiSwmY-VOF3bU1ED_2Ab8UOOigA5kWzKCHohNgaAng4eo69EoLdP98XgOkEmP6Gh3-G-ok26Ed3j10wwt543FLqJGWR5yA0ZOQc1d2JJDmWZCuW5MsfKJ-87Dpwfu0hYtJy9f9NKDkOVhNbhgu7QCDrQrPg47-vGVlbPnEkt4Mz5YbCM5dyUljAN3nKPG52_pay1P1VOiSSvst3blp3_LNst6VBTWQXcb4Uaehsi3dm_1gVJ_Pnk67R7PcQ5jn1Q0m90JFoh-yZL6Ap5zmZ_fOpIZxIXEcILqhKHd23rQGGQ-l0C7324zEaPnse5lWbam2PHDis0bT-WSLGjFUDJEcpHcfimaDM8Q6N7nqBkTXS9Tn_fZC2irOFTMdbVOor3Q== +6-x49LzwwXKPWPdJbs5L1a-mcEkxu-sTuw_STC-Om9hrMdbQBv7DUJdW2NaAIzi0X0ktA8ysGKYGHYXad17LYTwtPz47nAUSlEYRjUiOKwTX9ekjMdI2QIQeVB6ow7NwPT5ihlM0V7AiOXXVm74c3A4lGu72-Oagf1yXSWOEHUpz4Zn0AlvN2_JqDZBhsWxcC3j3GOp9etTuV9CkKyDtkZ9iz8n1dmyXBq47WzWDKC2JBde7ZfRdgUkpT6io70E_CHRy01ABfcwzYAITb4ojkAbN4y8Zt3FZ6J9RkIfVk2z1HgYq8w== +CEp_ThwgBT1Mh8rEpEvg4s9ReZYW9oX9bnlUiNGQ-UsAysseNP7SZtSRUgXqhBNEcgT-DXT3EyUj_hsgG_r7kYlVULLpl70inHy7jop82rJHzOV4X1Ye9g0b0KiKtpGn-bBoCwTZQDNqyJMudmoBUTAUrdTtJ3bo25jKkhjAnorzMzEqZBHTGmYkoQKz8sxDY1ua7k-AWjFjVpxEI7XOQTSYeWMpkbN7an8cFZ7XUEu9bQBzS66gSX-gOLEO7-DihD97wJQVkFaLLtSbDx7H1XhSmjGhcAJwdcg-3AZBLUZTZ_fOIXOXi3WhxkzdCWv1ynBvIfuyCcthmP2jWt716X_bDnRaCF8iEzg= +VSiETcjwaNOhx1UchjMip37s4990wZd2QeZwOe32ovlvE22xyCMb7nqQeC6JqlsyijoBTntD3H65SQRc8A5cu41CRWaQ7XVcElLDjjbhDnF5htXn3-FDZBvkl3cVI7kFqcDHqIZ1YFRtQjtVtuGm1dyRXd9mkzZ7L7H9IbGiCjUut-L39Sdv7lmUHPlUvUxnT24Wh_p_cp-BK9arUyxStXyAmXgWTMPsaLY0gmG22Gz4-ECfFzDngKf9HdNhbRWqD5NIVoQLn58ZhUbZKekaQe5Rw-Hkf6M4--P82gO-vRrAxsufnVE1VMgA775zB_fk9wo8Vn-fIrhZQ2d12hQL959vEiZPICLa0umW4A-S160jWa3i7dCaf1MD9iUCsiim8NmP5tfNLJSC6teQ8ivyucfuWJ4_QroqZxaQb-UkxHXDkRVl5KKinCyvAJlZW-SKUt4zZWGZZE2xQkxzWgrOeakSj5mxaQR8FHUGu4N3JFF_J40tEqLPJJifJWWMJDttA0MnyoVmUu6Gm4hDbv_HcGdPU5mSq6yQ-U_lZDgpa_NzVkz9Ii6WOrGhVEMpDBul75p6VnQEt-ZT7uU8qu6bzuh0IVai-CacwA== +8PAtQpNwD10LqyIwPxJnPwN6GUnwisTTywZBxBQ5rW2poEjWlB994lzWjTYpieCOZnC-S5YYppEsLZP9VTJPrqfkkRGFFi1t5qaJntuIbkuDjEE2dNdH_HtMpvQkOCweh0jo4MW-NNdF1Fifqy3kXuye-Mf2lACjRquS0Q06g7_4M4vw66yKPtqh3-U42NO5XXFmQ2vvYMdbRfpmPKMQwt5lMNHedx5alC0Okj1hHZ2RU5LNdcRtqQu58Wt0rBfWep0tn0a81NheHurdVqMNGWHwpYcW7_InYbbXpUAyIvcY1i-7zNnWZK310d0_zWFFnhtZ_MoWfTo= +aZd33bIANMCvAASw0cm5vrVg_QMT37U57bFQOdJkySZu3ltVv4TrbG3XITrtXFPtP5CZox_aoC3Pg8D_1X5YcsS2vkyeeAVGIrFSeX20Prj4dkSv4zpHqdpH3wl_5g_FORLUB2Nbo2lwQkPZWzFz5Cc3OVYxgxQIIr8rBV5lltnmx8ApJk3Io3XocxGUHFVMm6ZubapT7Ahg3l5oI_ZQCRlvvZzMwX_HUTKd4Mmkyc0REtzoeOZByHOID4iM62zAUsCYPtpr8PbRcK0oucFi3Ni6YkZiCVgIU5cbT7omUNK4xyay3ur8eLTSu9GCbSdBN3nde6gbTjPVslP-dfvbO4uTGPTlrseVIJWPEBXHc3kXFePCOHtNrd1v-4fpIgC9I9a_G4eavfWmuDd3UoBvNGm8pO-oAYPgZD-L_k9SQejm4TDOAeqWAHTGFmNhNaSE0zmAsJHpTJTtsDmcCvJ2CL-fXPfdzcvMISYJNK4hJSgXRrRJsMAZDs5w1NWvaHxzovSIIbU-CQqdeRPmJabSgMKXxDhYz4ts5KZbDIWELb59B7MRd6QadcWKC3kSPDmllgz9Yb2CTswjFOlT8o9CXT__YxPNnc1ODUXaqzs7hig5k6keh0Ynt4eJZsFbbYAcUqlceQaWTlnbQMGPpvoW8qU0bM85cr1R5hx94_NhwggXjb_UGJp- +80wlc5HYHczVzUNV7p2dzCi1YLz98GQ37PETXjLbwSqSlmWbHpToCzf7uRHg_PGhV2jS3f1devgsRLUaXX3EKpzXPFQskTjiXq21xUQ5eRIYF_8RXURp5ax6NiuyGcJsegkp3OFpMdd05n388kU2aQPgkhv_0lddRtYZ3JmFuT0NN2GcaWZq4O8sXllqr06DVuClEkEY8-TXn4fjplAfRUfIyDdjVY6iBCR7ZJjssR6Tu-jI-AAT3kdBXE94gI4h5el18oA1H1Ve20tI-yfpaXh2m8rTWbVBEFwsujUM547LdkTv0EXG0pM7NweQB7Je2hrUeLKCzJnBQ6geD5FwkXz34rtG7ss93y7pohT0wl7gp4OqdTZw_wxi7_JhzG0kOSr3s-gKF9-Y78C6F1xKjyLSy3av7-RvyhhG6NdBq-w-lsO-MApZm-sF7xxdcreKJBUXf9Ro8QCKdL30KbSNcZ-x1tokdMCMWG7E0xPnIL66godeWcAfihdtOvnnyg1fSQtJBJVD0U1pdZcZVCb6JcLuGqU5s0YoiPxqsNp5M0SkD_3qGKLQWzK34tV_jX-7yeEm63t3-CRNoiVIbqaNWlDJa_8ug0onENEG9toaSzUK2Jh7GrBCybvnrTOzjBnWi0B0WRaYMUY= +QzRaESG1gFRRfgvtBEaRnDpksG1aLTiz1sTs3B8USBH1Apb5eGSKTSVYFPDPNUhCwWGDt4zTI2w7N5INFvO2iPEmQSqem8eUsLY0i2oTgZObcmuyhyI25GnFc6hHI7H7y98t8Gm4Ak1EmC5FnOkEoAgHv8ztDXO3MII3sK_Zuiotghf9pHTxgGof92SguYw0A4fQEjMxnf3NF5IEw74d4hqxS340XAjqcQNgCUjffu7Ih3QW7BMr7Q1WpdQ= +KYGCWcHA9zb7H53ITtAuwD72UMV4S-NZXDKogEW2U91pGsWLvSZtmUZPEafrkiNqzMFYeC48i7WS3qM6QrcVwbALQHedaG64uZgKg3A-9WDzuBe_2py2t8DoNJmlKKKjEV009CQoWm631OnRv69FBlIGyg3tmNXYoJi9oW3Ktq8XJw8S4KqJKplh0pXlFRRETcnzFF5NU9a6y_pjfqm0UnpZOeHbub59-R5BPceW_NP_oPBX3Q5n8P107-vb_ceIaaedanP9aai8InZLGA1fNwGfmyhyce9fTzP3RHRaPA_sHHhb10k-o0y-QkoYUQO_iSBjmf5_25OZYngB-LqC7uTW4texXZclj0K-lJicnTll_2vUJ6Gsoswxrdku3GXfHF-d9nqdMCkcCVhczoPW +SrsnYzXFh35ZQCLOXnXyFAAypXj0OqlrHUbIk24DNeMF_PzlSTFPZaxBQF09tpLYW-H9F0xYZmZ4HWMMhyGDn_v3Xa2PkF0HGCKDQ-Gu0pDAYkIaaaAxU6V1U3S3piAGiEP00cWyPfS-NdNGRfVglwD_fH7iyjuZxB4nmBZjv7OzizopwIADwfDldpPRObJ1PW6xYR2j5EhrsE92e5wgmcOjI7JGqLFqhmgqJWkT6PcWibcNFvnaB6u29Jl_9jPyIIs95-N9K3K4_Ny5ir5WpG80zDNPDAcD0ZiuRcmFLtcb0NeAGIk9QOMqg87tGEMHStto1m_pzFpb0p7vLW8U7AXQpyV8WTE80IObNzV-W2Q_IddfAEEWhanobIIJmy6rIm1xiEb3fB7yc7uyJPxLOxAxDGDbL7w77qOg824C88z3mi9utw== +8woeitkW1lMYNs4rQYbCdrJXyRfjnJgG4U1vVDsn-jgV3zbQpIOSTp7mGkTJM5e1iTIo4JXsJ3DLlVh9-e_ly-tBVqSBKpD3wuoEAbFIyg_IB903Vt2ibxUwvByCwiL57un1kqwYWxXd6_sblp9jyIvDZz7evc0_Xa6ShM4VRHWKqX-8-JMwhsr2XIlHqUBV_eSAFcK9NtUdIEbf5wJIQKqz5j2yiejxNLYXKaDG82QdyNg2ldagK7mmmJuQYfca1Zs= +HSJ980GFCe-VJ73OWlj-j5bpcaGdfathRPlKua1ilQs7LpPOgxdw0Jzv6JrAbRTqA7hmINgTNJ0274ZM4zhwK8ifH5GPCeLC2TmOcSorYLlXosAG59_JXsQLceX3oNPfxIg9-A1Jc_L7UedEFX1KA96a0MjJ2IQRNmW_O76pLnj9SAGDRlJjUiU1S4sxQI9ZjmgQNCRGuJ80SLz6oJrwsRW4sBBWGP9b_XgUnK13AXaAklMFjv214hJlayLD-3QTDu18jCml6V3Z56ou6kFHhygmvMLkfyv5hwsZyfaoMlHaJmEjz-Yz7plqOYEvMBm24OQHbDyUpvJA1-HMxw== +pxpvVHmBrC_g8560mbPJYnhrKEegZezUq0zddJTlTwV66-HHb5Hy5z28ZyDfuCb2Ycg19QM2IJzz19bMkq060GFUAXlzUvrsmMFdYx-FZEyD1kQF7oiL_DxjwUITBH3ERjUEfEIs6iTUbzJv2NnRmW36MbbY-KwSP9d7cVhjRQkfeqcJuVI2xFuadyGMNuNJuhPM9j8mKFJBDrrjT5uUCgrMYK5xqXK_6_jNLQuAKbW52YNVOpe0SLsihU0ZdiAAKA== +ECHwvv7wwUY8yeDJqKsX-b_X4d4cQqd9CtANxtuOstu7L99A_XGkejGaer40PgBflSb4htpy5lFksJhas_PEc8hQ04cwn0aRoWdySDt4QEQyom5Rwf5D4Loel2ENTHf_PFgfnBVUinjPBI3Jh0jgbo7YJzlCfpUiTG2VrW1fokiJzzVZ69IgkDgE3ZFzVMEp69aWuMNxfEDgW3dr1YTqbFRkE_4A2wRAJ6yc7RgRs3-O35vhiSA6cBbOILhfFoVayQRA5NaCVDNnpidp562JJEMGN7u7cC-eJJ1tqIT0QE_xEs-nox4ImKPwtblGT62bLCKOZPYoZ3duzjcwnJ04y4k_-WWeAggQ4pkqyJQiqRREqauI-irTTI7eq34ST-B26oO7gKdVFaitr6h0KLU5ijvrnZHrFZJJNomfNs_FtIpkuAJrvqQecFv4P27ILk-MBipZMKLg4IuDjkwZVvsyL7FV8ALXn2QqAu1SyRHDmUzDF--fnOMdjCz5-pYZh5BS3syY5H4= +BZeEbiWvUhQEZFQwhjz20HRoF2wRxoAIqnJDBd5fzAJf01C_H7xjyr7MR36r3wEhm3rX6SJJKhuw7vZ_1WA7_tBWPEAth-kKLJSFMmmF7nA314ucVJjabRVBUost68asVgIJa93Ulztr0T2orLkIRVMaqCnENKJVMMHxNDOe5XeK1AyQoejanr9DnkR4TgvWgREc-0pHvYIoTrs52cHYenaoetO-kYcxjTbRt2rfCTcJvGYLNiqjPZGFbbRNU8mUzZrd6W1L8GhIHLQj_5pqdMEtBLhyUM64Td8x2pqvRrT6eiYaoxn7N95e5Zk1zXsq_rfSEZMotO0oVK5i3i6M0ZXh7_8Kf6LsJOaFhevHGAMf_8UpWFR8lK_xLAyCWyoUpllYh6YpdFe85k5xnnnlrTpikekT3NulyeouwwYodrVXHhC3p7t-y_xSXqngZKK6YjOv4Ya6uOyIoRzm27BYB3b__rDmRjdjS5tVSS1REPoPVU-hpF70LbkqbDTGBe40V8wUcZLt0ymFFjMihUvsvBmrpwfFjvJdr8nT0FGAfy6Y7MDfiiyYftCtdwJSb3-L3uMsHsQ2UGjT4fvwyCBFTjt8 +id9MnVt7OwflHvdD0PoaL-yPolMJZOk6o0lwjyOgk1a0DoCG5U8nX3yUFh7uH9Vf0pqakDT-zNNGWXOz9OAcuyBWJ7HM4reCm1MdsEyN3a3hze87j1xMOwiLIFl2_MQeE82-Vv5onC4KBGY30aNnHrNP7L-A1dreHrDfnClzidQjlvMlLX062S5JiIpODh7zTM4z-EUUaPMTHJR39atb-ELIasyfcyPxHu5WpVwZhMZvpn-uqJWzybZtshZmwlhnjYSgANuSTOWT7ypNKpMrAwGB1MO2VociKWbZaiwwKFhWs4JsmOGFaDfI7ki92EZE5NqTkBFMehGbWXQ= +O9PkIvTzJSOL45mI3ZF7qR9yFhvX8f3_VQ2m2BWU8AEnyyGh1Dct_X3Vt_8RaLu0_t2st9ewmNQBRNNU8GnrTy67lcJJilRyNeuITIypD9ZF44z4f_cDrn9QoGVrNfj5c-nLuJyEqV2zBIkTEHGKLRYXm13p160-t7Il4eMAZcTmsHZN4vDnRDxgET4W8Gssxev2YZY8_i-xrIuKJx-P5WTe4-IMFYCGuNGda_EHYtE66Ba3zH5gSMyjIQ== +c24--oNg-Im_NdEJ3OrNEruwIu9HfdTrAWqyPQO5Y-WJjYVLk9hT02e49dqtqpv-uL24EHHuwfC9LK66xiiDoGuzr-ZIhgKyEHvPPM0kUH-NVCxopuFZA4KtqSLi3eekNYO5DqMUVAx73LOzIKan7N3J62BmA5WHNt1Gg_jFG2adfs1_eb8_cfvgOoQ42bjhxjl8NeFWRzOLZpM7C2PFcI9uSgtBVuRwiRjBJxtjD8O8dvkse4fiM-hXB9iYHQyAaHHXBH-gpxikpRIs14CEFE_B2d1P_AuoVyntrMXD-KFbBfsBa5lpGmnysRtmIEf5QdJ7DRjjiw3ux76QHN5S0W2iv3tJC00ScZlVw2X6qZiSpfukEfcaAT_x2GJbLnGom2rcsuwKpXXw7Q2GGSAZrtih2DdCT3_2RZkquNLyioeXWu158MRroJLCiQ5IkOPwiYHaoMLUjUpSoEFG1mH3xK-OMUchOd6Tms4NA2FBGqybkMUzZ9V4PYKPPXi-6vFqUBdNJJdFrszGSJAwdy5LOwqtUQVrL_5QKrA7w_msTe2uz5_w_ZFKxmwngpuM3V2lv6L0y3mb9InKHpgpr6upbwbPlHSQ1sGkYpq4GKrjLhtVRuzDtpehHQE2p3V3Q5gEC9uNz0o= +vnYcIAnMVOZrsETmFEM2ZXf9AwRNrZ1aQoDnb8i4veAesJfbCinBnBzR5YYIUGC0pcgLEQovdMoBQ23P0xMoJpo4eYF72mtFcg3iZQbMxFgCo1Ik4hNDjXiS3KALWjCEE6f5idkobyNBmnxTJ7_orc53CGy4MDkjm-3Rm59f1RnwJAv6nFU_c74iOFRTMHsG0hDJnY1q-LU78c7CoN8RbRZpRGmYZiNa9FRwDooUhsxQqBtyK6AweoM76qObbaxK-W8ErA3pCltsuQ== +hurR_6xZTWQktuHJzvPQHzI1L77qSjZireIrM7yTVT-oCthbZey45UmpSyCTTI65oFvzZgW4QVn1fGYbdVqB4RwT0om7f_46n7oi1UcDGWjMbRUuP1Aks1_RYUkVjpNV7izm4vpPrNVKJJ2nWZzbcL0AwaBt7sw2dxIMYCNjyxJ5qI6EnxvI-frl6eY3jnp8p6ICkj1S15MJjIMJhMP-IiN_h6WdqjTBflfd8qYTXpH_JSuatkKjl18mFy1RzI7YbpJOVb6x-pdFuuVMxHu2ohz2EjoXUHcdohXA_bcOIWPHMr8tnZD4vFEoprhYb4ZH5AGmO5avVDr1LIpNTP2gx7B6OXXFa3RJC2zQ9NK5NRMFlU-1danqN7JeccBmH_6yr-jjNmUVGS6RSad3lvMmC0HWsFH8LTm9XuU6RehzO7SLx4XA +Ws286qPJyfLE0L08PTQyyu1J0r-Uu2cTjjRWVtlsH6Mk_SelGJvrZAbjfWaV8hcHopYIqdAZ5dT0tprK1PEG5--7i2579UmNauY7YAttJ-3ilnbzs34sBixXTmkhCZ-sIVT3Br6CyGwa5oSAxqD0OAzt8hjZwQ0-Bo3P8mdz35i2u_ItwlJeUcujGNSA4i7p17kEClWCWIqelaGfX_O1JsRiBfiQIbkN0seuOrQhgOvtlSepTUG5sJ6WcLBuK76oWffGGHzZfuGlmUsByfEqFSywvnbC4G5rLJDsxYuB2-YzguOEsL2TqWPAyprR78EH9pNS3wMmTCcciQ6H8Jw= +39Ntrh2h1__BzoX21kj5qWggCo7MQa2Us9aEMhQN-ngcYe-ose0R1jA7G80qYry2-vevfcvVyBp4d7qfxkwXxyzzWZP7H4e4ggNoY4xdGIcS3p-JbaflPqepnea9FqmUtKbNgf_hj2Tu49_nUmLVD-gIzrtVSNUx6LcaqbesdTujFdheXxbw1S_j0EWkbDLNSsaGumwpmRYEVaA-vToxyfyib4sbVtd5myXF6_yzxTgk9YcOx4QZZgymdbQRwgYxxiHU5zld_PP9O2YzLCkx2Wr9TCS6a1ctfvzEvfFFHXKMVN3q39947KVuX-Z4ezWX4-_5j5V9kJvlx_b0pr3HLAnFJjFXQth9N4PGaDwbirlDOGsMkM7ZXHpN0gZhV7gkZneeG4bC4qK8dZAAmdYYDjRx6M5maatMPYjfO1uC1TyZ8ALbQoWNQ2iqg8K5awHTrsUCfBYI6_UP0ofl1TkcW2Ieu5AoDqxMpB80nBe5A74zcQ1_XW3dXEa3jCVTJB3GX5Kc3FCf2VfPEX__WeaqxWXdzApN6pqqQYsxoan3iW3LlQx_dAMHYkCfE7EzHNoLfs6zX6OOaRl1NrWIYZ5Un0aWD3MDPlEjBdp2Ot9HLogaLlnfOnk= +u327jYnYtvS_TXW6AaWufVrd4BJwlmGt2-z1i1Uy4WiLoPHnH77JJ1jBH4VkoQezyPBhzayhwk_Rx6OC_0o3CllaAOzDUtPeMxUICGDAy-smwyacGnL_3huD1sTg2_wb-10OSn31lbDSkAGdZwyWirfsC6SqR8qntEd3IswEui1YlKmJWYkwZ-A14F6zCuW96v_0WCXkZoN_qAhjuI6nb8GdZ5k3F4MmE8OFFubavAKJO0Ej4MwYsYgqfg3M6nfEF0hGMYZJ1539T7eKsMS2afxzLhSVlSsLhpo92bTry85LiTppVSxZ1VMDXLHstAMJG7K6C9TA9gkEy69Is6bVaaZzfx9Kws0UG3SHSsciyCsWIX71np5wH70m6d31Ap8naKMeof3hH09UFuFbYOltqOil_4KfycfvqaT1f6ZzfTQ1sSoZMd0quY9O6hRzhVP_27v66IKiiMg-mi5-R2IhOtUdOT9ogFTf8KKUlXlJcZyjFCsm8WTKCl3mOLzDxeixvzWY0mXOceErgrYe1fRf24ZsBw== +rDnlz3X7Dn_KYL0mAnyyVAYaajrsNNcbdkBCNhMa9VtJ6CxwUkmrYeY1NAHvCzSJ_uIuoTBY16GH1icv7uh89BCIPGIhfc7u16JDLyBzZ0P2_Xd5DojoyJIvR3Aqmu_YesRH4iQnVV2vqC5PTpVM6IyTxl55nWtns-qym7i8CTHNKfH4ljWs-CnlFM1tAOBEsGM3Q2gIHsE9A-JQYioOe2MDMFNBz-Kiy7eodK1FLiHo2JSj98wki2uCuu3Z1dhoBEi7c0MPxgM_C5T2ucqEHvFlO0rylmjcjhBtfFZEbFLxDHuthxes3ndcBebQnHhRQbhBM1tw24Q9_MtcSVJ1q_0Ij4NuBEQFBl_bqjK8DivtLGj3V5AFRQAqfBqbrk3yXaVJ0SY4CXVH2AziVWmtHmwXWlkQynRvCXifAwKP7rCBIMdMOY7LGrwjpL5y5z6Tvl6tXwkWg2dh_1rp3y1l_agwiNvF3dWyL9_41uJECpRxzi2CZbr4jxvVxJ06czz_esCNROhhJFsZVXznxpnSzgzW-HiUjfwTDK3NnT5vWvpcMYWn4XxAQ8T9xhCLS5pIV_bZCw== +3O4cfet5fdn-4l_Rr4BbXVop-bS8NQO2wLVlBJB0jC9mjudgoRV63jmGX4kNq5J06tvuyUXHpoMhh7I4e7FWp_j9Q47_zc07FtaHi7GbXd668o-qMJSr1oR6633f_emPJRz0daorLsnSAuEDDJN_0S_hiw== +NXKh_iq104ruyhwEncN12oIDCiGRohErIWOVF5hEvIMgQ_XIJQlEcNnb2vigSagQVkPxCrblBd-YRhnNnsg32-Nua2EKkQ-umdoDAQ3z5jEGMLvzh8uhSttLr1uWNvxYYf-IjhmXHJ0Lt9SRjbH03YyL2wN9ye82w8pWGScJz3Zj7HisYCZOwstObypgtVz5ehxuNd5xW31UXd4n2Sa5PDQM4Q5m266Aatfu9kBqseVhK34KDI9uHQLmPe7xLqZocy4yH8NxXKpqoZVf6Dwwx6Ng9kYVOpBrBCA75DzfrOCO4OHgRYDAeoaTkQmzvkxBNojLQwBnT-c0L-foKR564mVcIy0D-odjyvjQyNqmmjjSyuFUU2-sMwDGbenHB2p4pBmYWQ== +3N9H9xFuBM0mqBMOwCC4XQ_speFNvyodqbGoQpNlnaCgfo629TMJLhc0AeT7OZ61ZGkE4ZCLwGHMQuayqU_Q9eDloRM4r8_jiEm8KW6tuHtb9964gg7CjVjJTzw2RPqPtHM-ydRdr8oXxUtyRnzm2B06AOJ2O6TK0FIaB0bXLitPsjjb7GyMgrO7T92oDmsy9pGTVna1SUJwFc5RfTU-MUt1C_nBjOnhK0ZFU9XVM6ow_FZAERxEylXfvk0fD7a5Ni_dMIddcHEcN0-uQA== +sUpqkHtFPzbH5kqwOzZI9raS0hdaa3Duc562K_5QA57Fd-nmpN_EZMiuqesXDsiyDbyWje9Ajs7Q9tmKnKhFIzsjYRaCO6T11BPVutaCLAI06OFG7of1ONEHD-d3HwEVVDOzt5QqH8YT9tENMIVrY4pwZcAKPqvRdl_bQq75wBTlqStdhp6tvPytacdMFMM3c01ScbhC33m3IeO3_ZyzWIYJtSa3Mo-TQFB7iD6jOPa5jrZG9UvntDHCcQwNyrtET3uDxFdEZH9ed9GfQ79Aw8XBlOHjuI0oxVbPp8BD1aasKryRqhNCIm0= +IaNFVhcAK-o4H9KsH7CrBvxkA1d4ObtekSTydOJck-aVLhoxPFD9Yz9eI_XJf3MbO9qi51TSvthd7m6QTWu0QX0_AheXmX5N4Q-LPL3heCnzeJVL-sUh5i9aBSqV4co5i8DyIMdtlu7oCUENnQhE1ngSV671A4-0WfUJ-xnydCozsQ2yHnOnluogfxyydWjBGk25VMFkkmNuF6aTy5Exy5539K67PCJ7NoLvaG1yPAEcdbVJMrE= +TEKVZVAJ6dBxu9RCSloafRX-XHKgrSMcxP4tyQPI1sgSW_RY1Hajkqvg_9kWRlvweDY_z1hRqI2PnpZLbVzaPOVfomqaYY50u5BlZqzyiYMosM-7ipPc14-2SEDhyvoIh_-6Rgc3CDhw0IyslMAR9-vvMsfnqx6NlTG-OYz3hKiO0cJklHLTk9ebmg3Pcu7yGjDIsXI5vY4m2p6px8HoPF2r-L5-pq0ST-wK1ZC6uAbsNaaO6P9p6CzjbUXQYHQpZ_Wdmoj-Qy104PbDo4G5BEA-VVx6WUY-v8Qk23ylUEYgc8z4Rdr-EW94mVsDTGglYsJcdUsMDw-W9ykzUqoqGDUKTS1z4h6QF2lkbxhP5XxTXwRgQ87KfIQHMGqMahSkfPr4DOExrM9_kERvJqfkRQOE7X-rJpzwpYGBV5S_HksmFQ89Y__DGU90ivlnQoh0ElHhLBgJJUeZMR1jXkcfQqpxLTJSWq1ujG71LFXnwyqMNWqmIsr44WJGUnE7vjgO6gguPhxjUjgs3WU1nO2XZB3AtmtmzKsdYMIvPZMwh5oe355GWJmBvBX7r6U-Ht6TRg== +OG9afPTY69JWvwmhkF31HjWd96yLs_HMHANgOfduETiOsGceYUkCbMISmpnhC3SjzHzvVLSWYZsGIkap-Ke8gDK02tki3KHZ41QUF2RgFIpfmwO49yQOoRy4_9vG5-dca2IXi2p4L4yqFI505XWbOJVpENmjiBU10rE2wayeuzwGuZ_z5baI1PaxP6dWJjVTRGxoiUjBUSBqyLcB5SORWU_vZJTBjCfr4EdLd0AQRjyGOrpmfbAFgyzhIEaXqJIV1nHl68rk7dl6l_cS6Zp7fMqXB2r8W13dPaQ8Qsh92Jl1OJ9rJar_tWRxy5O2jZewajQocorgUKCg04zTwx-LKxkTQMMTyUy50SAn7jnFgmhSkFpfQZ5g6dbogtF6one71D4w61OUJbdoVby_emkMgRd1XDWnA7HNqXbfDVPnqSe06OV9kSV3XpYUd4z2QPR8KpuZyBuZBr6Wvsns8R96wtsSgtJ0MSTetiZb7cqLrEh-7dtwFLsTh2MWBTduJKeHYVVDb7XB9mWrgsEXovZUBzzoFkjBfLFpVITki5Ak2o50Azk11Wmu8VdCjFXMHHhR-6KwdW1Fa8mIHlr7 +GTU3Ry3P3eJls_17k8nvDBgJZPvv5PRGnOz6-LDDUfqUXI0-C6E81THci3skNu2kphLe5SmscvKeyKGhHdSAN1p1Zt8_GdEBCdkAhJ8-UQlbrV2TFLWye7IiL03-PLsYTq6zEX9gGRvftlyemdov12mxq1pq6PQM4a34 +D224CzK2lhIgGTFWRf5PHInpM3FyndJagfrqDmSRXx_OFZZntcGovf1nPw2--KH0PGsT3_dHtVOYiLQM6qOeBuPH801RdvaUQsUekAqFdHxQL2NZxr-cfkJGaPUBgqaMzUumg35rQwaYI9163hfjCyhrVwsO9Tvvc7ieMTwPmPRBHqnAsZjyiDfdgSCNRncC6MTrj3KYsPSr8okJhL1Vnd2fn_G1QjnmSu-TlyWF87XvQPiYtrYBBMfyF-keSKpLjWmS78frBl9JA_HKe3D_vU-orlZ8k4an7_md8Ns9lyA6Wk8tD7kXCp_EbiunOSQvz9L4DZRH2u8ofIhZAusmrjPH8Njm3tj1QwS9dnCyqzTkX0phU4-76QIclztjJme8310R8Gt7b5SkKx0IMXLKiS5fWteKX2ih7yslGiNy9x8pKyTU7oKI8YByUUqinLNn-2k5DIOw9R-0Q_DOr_2zSS9jfsGlEFMaFm9puSC99UpHmWUgkBEf8n1j9kBqB-3ztj96yDfJRGQ8bcb22Uq9H7j_L9m0aQu2FB_QfoYZzmvYklq8mbAD4dIN8nhzOmtbKPwi882Bjiu2WB6dkJ1HDXqRPY63_R6pHbq8S59CAnAhCLst48hj +Cwh4-ixaG2D9PMqic2npYX5mIWMcIust5WkfjPPP4Ny-kmVFgUCFVLP9A51vgiGGBxN1DbGfoYF0VChJ3QidHTkl5-rbH_ckl-69HjR1xAR8r2TrkIyiIA_-bP7WQp_i_Kv-TPmvcGfCVEL4FtSihye2KwqzdkWTwhWdhfZbccIYxcZUjXvquyK5oVWB3yUhFyani59w6ShqMN8HS7i9wlFSkdJuLWWb9EYfWQDB16A_OqdlI3b4SsCbfssI9dkNVgaQPvuqDNR9uFSxqlc= +QPF8CLiB8EW64iWRLT9S0yygi4Q-LprnK4aNeZxN56oMPJsbM4nTRjTfSAhVql9wRHSz9pPgxbiZFdtQ6PdaM5ewkNQXKtuf7sRLnxwQY6jVgjJ_x8DIZLXsU0NmTnPPbPySPUbraJSMpcw5p6sBGoqmaYxFebh3xkfxH00whZzK71a8795zw-gxNSs8LZr3f0u8OjSKDUftWd0JGDIdFiVfH5lGfiDDST6h6MfN7eiV7_Svskx6KQUL8QpXZjip +ahFQ3AFxGH_Zr52T6XJuSkc1guCwG0OYgHbM9PNl_wOZYC4CmZQEXli_Lb688jmVgvOsRCV_dIPUgwdvAnN_sWy_tMUpTKQl6xwh25vf90U1hoD-OBTbv6CUB4x6EpETuqFEAuj6-0UzmPlOT-Qha125LVZcbG_Gvsft-QzzDAl_y5oKdL5ADlENad31OTs-C_RGG2vhNSrKRglpD-tsyIGrhlzbQBjF_VP-8PssDxIrbNtZVRGPxe1i5aGzizfEWwF7-hOIGZchG60QoGxky-_gkuR7xCEVYdl8_P66H1RpVVnIXns0-mZByhCSk6xUXWhMNUSb0z85xxPkHclanxG6DooldmvtWP_qdBRkJqJ3y9gkXjFoK-jFP8OGhrlN9Ft18cWEbZx1mSqGO1IEtY7jJ-wuCTBUELuQ4G3M8n56VQJBw6t-88_sZKe2uv4iKpSNDvfiVrYE1G27Ag-iiEBnuwkB4kOHoHLJUaxf56IeyY2AK-NAXJ0mkVgOZpPx6eDycVUc4G5uKmVKLaO4Izf-q7t2KosdwYq1BFGPqzvOaVCeFu_zfiH5EJWo-JHjEN_FEusuJ_kyF-AHP3Yg6xMPo6ghX-Rnpu0_kwIYNwl-a3zu-IwAmR3S5NODs2NbliT3qcU8FDuN8EjWs3GKSyooH0p1osh36gyDJduJPstEaXhhJA== +XdUnbYkvdk10EMPjohpvklkry-W6mcDdrzxRA6LphTV0q6te-W4RQc6iU4Ag9igeLFyQgrY2ZLMDkcm_Lf4cGJuB1EWiWaBSw9o1UV-jveBv1Thy9254nZpvCiZRa7QKM4CIk4hR_1Qs2S-B5_AEXy9CxrgTE06w8mad18v04DakNfoEcjfBDFxCKAcaYhqIHRDVC63AxXBp4uy2KmkH0B7yzw== +Hv8ukPJ5AKfZQCT1bfFtuwg4iD0sV1xg50Lxv7UYxw5ld-VaGWkSfy9nuaNMhprJ0DodvfM_TXXLVT3qUcVhcr_He6fKT8Sph4CHdm-wR6yDS0e74lRvJhQiS6T8YAKxsyW7hn8GzVvu_6QjPgPQPAbyORMzjgOT6zAUrAuWJU2zPesn5F6PItZ_DvUV4eFQ5e0TBw1veHyeAHhVlDL9ftvNrPD_9BWl6vLj6x20EIy31D2lHA3qtyobd0LQXyYEIPnkwFQPMXBoJMG5Q9e40jG_CZpfXwL2gqviQsLNzuVi0Tz-fANBVYAP3SLEmpTrXUwoCq1YuFovLvTQxTzkk-yPq9i8nA3TRZBNL9Hvn9J9snDlejxyVKfFZaJv78UL2QacSA8HSQ2d5vrIzkCyNxYydzVIwwtR_4dq4Ul47FHizBhULcU60MGGzCGv4mU= +o5B67mJ9MbZVfCR46H0i5hiQiwULJP0fxdCEjeG1q-dy9Ra3QfqyopOfAgLVzq-oCTGVpk665NSmCMQqW2K78y3JVTE0OxwK7XyW19htC5fC_W63QmSIyWMyzOc6w601hQaGPLniRJSy31muFPSVEkyvWB69TuYRnj1oboXuXmKNenC4F60yxOId3mqE58P8AZ6p +h34uu0gAimN6ZRmEN4VJRKu15v8Kb_lyRWNYT1eh23KzZKmk1ZUeiwHrd7eEwAp7cuuEyPLeWDdItB6Viic5VXbsRtZ4gIE_soAKCtIpjCZHZV3gh00HTtBZl964B5aM759E8-kuPdLx-0UX2Tn5-2DoX5-dEm6mU988k92Ek1t0OH4tMk1fPa7WH77UbxBJgTldKeyhOGEqKa67QAL-nc9S2U0McRO8L5EEkknZtSZmtb_o3tatw6SeeTXD-bio94lVRjmZ4riDRHp5AgRXoemsFNKoTAHI67jJUqGSuGPArjbScvwmm_7OEOqx76JpYwD7ByJGEchFe6lkD36TIgh1259ZtTb5XDMKZ7RsCnK9jvwq9b0MyS1NQLtPihu42TKti_PBtZfzlE-bh3OcGcjTtBqD7Jrex7mLh6BWkUwHEbBpMc-e0IRUFg== +ZfSpRj5h6WgqB-czKZMmNKLS42TEWznU-Gp72SHe1SuEkr4HC5IAKDx2IyUB_oChnut-tLE8QI899k9isZL4S4jaby5E_FJ_VTD4BhLiJtk24jEl44_Y9qH_sQuHWMqrlXRwZFw63pFmqGr2CTd_pwDtnpxlMkPzHGKXRpZQL1mTuIJAEmXmKhfJr1kwx5WgiXjC_rBVLAwuxlIGvTaShg9OsNiW5ODC7JJubnLCUhpD +BuuiW3zzGvA91aranVxSL1lRa_VWRLW8-30_ycuN25dpw2W5ViaoZpvH20wwiRqqmOZ6gBomxv_4LMfRBOJ1qjZY1VmzJvXrJsM0IBL2edNp9eezZVF4Y_VkC3O2MFTDzOrfBRimx07PrK1j8Cll8TCCG9ZSU3IscV1KGZxlbYv42FVX-WHqDjPunasQ1G5XC7yF9zdaPtnaREiatFxde12Twasw4xxVON8a0k0SFzpy95jGa4KnufX967ES4U8adEyrmSL3-cVmffXW0dFh962ugEjc8lmQO4C04qYhRPKS29t1-Qx5nWgSz1cT6vtSmPoPxBc7VNykXIw9iAZLs41tFU2GOXCyRCzERZBjlyQ4CWV6IpGiImrKcIpHBRsQYmTM9kVFmrnAczIqy2DyB6MQ0JzCR9XYNOh1V7nPntR9-02W_TqycHjlFZwLQvtuuD9lD58g02Q1iZ5vxnzbpv8l6DeDDQsOLOGGV1xxp7cnKNJuxOVpqsMrFTO5mdIDrCiK3h_MPpe0mMN1TVPwsUww6tYGzyO60oA5VpWlv_DPdlmoFN1TdyxeX1T183oWV3Ro78s3 +A-0YAIbigD_gzEbDZ8LH_P7RTAKYstBhfkytbMiuHharE685cYY_6UynYYdO8AlZKA_KR9oxX7I_TJA05or03nQXiiSzEvbJhDS3ORe1hL8_OHQvdS-yDsu1MIYqYzedtdl-5ige5UI1RSi8NIDjD9F61bD8gNjv-5HVXO2tWnXpCAxOKRuc4h54FDeGw6MReXyI29fXWiHX09pF63d7a1vFHn0hLIWWgwoaBBfAGFsBLDMefMp32Uzs_v1w_WE_V7rf4BN-XczLRaIWVPEKPFZD2RjcoHv8Wyq1xEUMU46AKjAFgp8c-eD2m1dZm1c6zfkUkmRX0g47a_7j7NgqwrFnPc46XcYUs3v5g7L4KI9KZaQDMG4_SPOjKvqYpUNE9UKpcoCcrGWH4HL4LokvMK0U2jjc9MVY5Kwnd2_ar30Y4-DfLbWdV2OH9AIxBnToHE5okIBTuUjL8sTSjvI6Gkue-vki1HqaQPBaHQtvL-qjjAhTRVyAFbxme_pWzbgeVynHJKzUwUI= +_d60170ufaShf50yGYRmiEYrf3jiJD3t13C1Rh6BQJhcytNZukonnxGAaovbyPEhN1b0TdUJ-osuOrVICzvPNHLgWmlwZOTPG-00YAm1B3IuYAzt67oYbYPhfu5GMENfYAYgXcF35B718bDANonMJaxB647h1ccf9RKxUqqexyOXq5j7oDBZ57V7Hwru_pfmxj01qqGVKBlniAbCKmN8YXlraG5jlMIUb5G48NGX9JaJFVVhWm-Q-uA7dP0xmoXtk2t8wr45ShAECLGQXxvd-CAxe7B7fxfP5jM12gyYhWTg_BJj4IOrCR0PWT0Wvo5MWVXS2gw78_VCeRrJy9zyNZ_uQCEndxwP3iHTaEF5i2XwPXWl6AAUELiix8UsbuVgQS5uPv-Rwn_FyuAxGAlKszLOl9ha3pSPMnQ3JAEjxmndFVD3YDE0SzzUTxiRIgq-cjFndJk4aFLYMCjPmlpo_rv61CBmEIKUW0d6Wn9KUZnGdCpGqbLb257z1FuwmQvZcoGMM5hBQhRbyWFqaeOQzpwpFSkU6FSWuraNjE-jxVATJA53PUEu5z7j3xyp4nc1pH1UmxF15rqaaau4Q2_aGeV_z51kk8QiH5N65kW2bEuATdkSfcckObmxVZiEmHnNNc34bAZtdLy-zEPPEdGsdM2_VJVGPMVL9rim7yWzX5O1 +sevB6dRZs0dXhYttmL38y_MepxKKCM6oo6VED-uN6j5nlMNNWlvcNXsQ-vWohnZOUjzNofKdDzWWwXhsE2w0EowLS9gr0XFRRq5Qm4munxQkfmFfNZF90DjBRxbKxtb0TdxOpWK3UcpwWU6e4acWWxkgZjkIpOHuNecQ8kWXonjCuMEPbhH9i7Rp +vEAP2l17gQetq7sO8pPaB_SxgqvyGr38LPJRyGXJ38lPtiuvTgXZukG9bVcwXzmih9I3ias8Q8TRspquj_rYgp2Y6WRZb7dNtV6W95BLiRReA6CuSWWodqYCV2cI059gy-TKWSI7xE2SF4_iS44SPzIqNFH-N6nQv0ilKVBp7f8J2kboNzBMUHsIbpVKrRet8AsLZunNttxM_hCRCra_414zKA0KGwam0ejhSJqX-gnUjhsjQD7bf9HDKkvrlmJpr4la7uA8quAhQh4dhZfDlethOKCsHpghaPF145TYHOhmZqerRPbHxOGFabfEp4wpiJM5_Gl3UwSbPyBHv-fzoU528wRyM1oU91qoyk2TTygmh-Cio7fDqjvsHHxSkCrsG21qS0ACtMiSvpUWQ36fBTXf8QyJIeHt0e4CBsRe0B4bDCAsAu0eFMtuAVV9N8G1Ucf7qaYr5SsYsVZ8axAFvprWqMPzjcDllRWqmy3trLhsK19L8V27HQLblM8cpldwbeYfgpPNVSl9xCqjJLxtD2VY_qMeJ4_HzJmYx7KOyyQuejBvgHE= +ja44eJcykHPFgYMtWaYvyg2f9eEm6mALCqWZhihXUpcKz2B_uifhO2lkvVAoFZgnbDy6GQggtzPA2XA5QgnNgeEhuE2Q6cvmE-LIsZYLwz1LOFiD7GirzEdXyrByLvOYbHXgTvEt_zFnT2ELtYELF3VEVrXyhlZkNhs9bgrY8aRdZwQqjU4HURuxakl7zPNTM__RJsc9-U-vs3lnTF8rQ89oj6lkvG03eWF--CfTVjyE3HwXxINADJUBQzX_sZn-yBydN6uML7tmnnH0NhEIcfWMrUm_DxLQgDKW0uiX0x2NeMN3pqQYIWuGOMVxw3iTonv1Ewm8hLf5Z3I9h3pgxmI2knDvAmqTPQG4L4LmwmzO2LtBI1tkEnB6zZypJ7TfJhlxyrGjYfNEFgCCn17BLKCvaVGcTddndoY3SUkPcjRRcDWhVoYXl6YwbQ_F7JgahYWXgKqLk4y4iZpiSIFaXtN4CHEdSTZrVM0zYAZSNFevCsos6oA5azu9rzxvZYD8Uv4WkizrDfiTHdOcZUNCITBZMxPXRz3qWB-WXkTzK6FPzofMjCB0yY4H-afGfioWQzv813LC7Xf5mhZhNOb6EqR9Ow== +mskTw14qQnBpuncGKwWdapkQD72baAtRiaq6Rens199jDHzSHzZp94C9O8Uzc7q-jNub-YofdvqwDXulNyeVyXp-n4h8R2aMTTv0f1ohCVguDwp34uS_sVroWhzPDmKltvYYG0Kc6LxT8V0UEQPDLWdewbPVU5cL30WEYR1DvuloddJEdEjDeB54w_2ncxpaRjATn6ePCRF-hfQMofDso36ujpBZRcTwE1dMLc1DPjBLZF-Q-AQyYesDa-011OqlGKyyhBZ_bp_Y2LqAv2pnFGV1wUvFTiHNqK4bLTkbrLsMEgM4QEIdE8cOKieRrWa3IHvkvazWYOS5KJSsG4wngON6qczymRbk-qZDoxPjRhuGiWTU-pWr-CLeYCvAd6FBuHEVQ-Ud_2Vg6Gm34UFv-iziVv7rh0gatJviuFT-z3ObkT5FCa4-hGd9X-8RLNX0-ZhW0LVPjoO2HuXh9R4wS9QytkgemMRltXPqHPknsB0V-cNK1H0T-9EnbCfAD0PboUU= +msymvy559eplp0A9WYxlhEkUDQKt2oM0PcJr9UrhukvUwaO0ir5LZk9qkQoRJvntmtmMGvbhl3SGRqbkqzIULipLyIhgYZAfUwJMQrvpmzwzMWrbqBrnUVwHTuLX_o4GLfaW6D4Lpg5099eSuNHmMgod_1Y_FRiUmsKdSej7ZlNnrZuh_sDpYF6HbwYGN2g6IqO2JIzC7g7rqNmSyJy8gV3Tq2MZgR9whrnfeUlty7wpHeYQWM3OPF4z9rlR75bmgOn6ZzR-IDPVFbwQpQFO80hI3-N6xziiw9sW5oZwfqB0LLR9XlbEn0x2uTPzPibtk5iZ1uCgu19EK0jjXFE40Uq5bw6K6oaMP4N8m8VTOB6hSZs0EPPres25F9mdph-nFK8aebVFZpGCRYbfwbSnCse0hqKbpNRps3xv1mEQmMll0s5jRw-NUSpeqhbWItKJdmLRkQtOZU8gCEObk1AjmTU1Eg== +JxAky0pnkPK77h0oQWQ1VL7-YnXIJYY3FVGIyKVaC8kET_MbOua-81HnHxwLZkFoOslCel1N7uf1HajTL10WV1Y0Nj3ebLcbTHuvV9ANml16p5-_UIUMvzZIX8e3nFV9jkIxPXhPpKy7iEkrnQhv6aYpkohQGiWesEgQ7P9foZAA5QFsfIoHcOJgUhAqcJOtynmUWhDCYO2EQ0G-CQNwEv7KJwflOwML5ni7_pTeyhb8L1wP05nBnqTJ4sw= +JCZcsEtb1ZhAo9LUVH3hx8y87Hp3NSQ9f2sB8gOj3_RMTjYVcAlKQny-sncXdaT_qxoROgVYwedcudhyzfILwZtKz8L5IZBHNN6YhkodWflIcN90XL34PMOXNhdLDw4g_eas1QCOHicTteenVqH2RuMis5vl7cEXbrOsl2PEjuxRXu-aqP819U6idsgszjE_lRchTl1PJqcs-Tflsi-nKx5PlGJhW-RNacOYSJxn7CVukFXAB3A3GJ7Vf7duNWGuRBYSHTR_AE4aD6d94kzA4NmuERE8zyWDXsrZnLgKcJMtG-UXrrGOHtIDpAjhBSPluCPkHcedJqMM1ZU23VzWMP6HgXXPVjVvY1pSfpEqn8LRkP5FjgKGya0UFPoSkdvcUe0v5JRdHIdKUWXxGSKszewSMqQUWnyd-0smCxaw3inmWVUBhvHHVhb1NLwhyZl9O36eQEXtKx4NozewRl2A9tlTnuvp4GkFcwl3M9BlGN-BvYKvF65rbd9FutCscqft20eamPFDN5NZ6Yqi_YrZuHrrua16a7vWQjhoFnO82-Xp4jZWieEq9NMKwSAPSMbLAI1eLBwD-qKnE78YUkx7qwfUrQMz0b4z8Xs4uxNGU757hBxYehNSMU35Ux5q7kiqdopVeW3XMR9S-Lt1fC5rKwYjogrCo9zPUF4= +_w041f32SN61MwGt9NoLLZYbAXFT3vnIOw1QfdwZIyuI_7I9Z3o3dSkJ-veQ-rn72MC84faPwNDhX3KBq7B1pASiqMWq7uNRyhilEq4pORTjFuMBz10EYsgCv2awbPzcUpgHyMproSpV4WpanvM996EmZzQ4ee2e363ta2-H3rsUT-iIr4JfGUH9yp4K +RDXgEmdfcm255Xb6zU6wEgKFbgUvTbHxbGLqa6SoPMJ5Etd-T-wNTmbG9rVox3nLK6HiuMZlhWyhoXX0xclqvaif8ivW4v4rB5GJcZU_S09dMraM-30S3IDo4yAp2onJreR6QFSa00RDGj_4jTonNgnwehx1yqmhuEPdOYAzIPMFkpHM-oqMfIYcHwb8ANaJktMcQEuYJFqfIa7fbiKiAt--Ae1np6OLX8-x2RL59xmR-xTFs6ZmphIuAbAHT9PvPiZkNRPppniBH3wLDEpSOnJyIZHU5sVuRA== +wYEJqKoBpXkesyRcCswEEQBIvx_rYtklJb0uCtYq-oe8oFU9OQXYMqo7DlOZDjzMlUqZPWUdTrGGHiZ1IKi1jne1H5QgLh92w0-kMAH209LSrac9IPHGeLa-KgAw2EvFs6hj-v4SoHnbSfZHE-p_2toSFBGfmmV8P8nzFJcWZNktHg9NbEc872crlFYePO_ZJhCkMrUmHoLzZmaoSnJwUhABc7v5I4YKiKWuHPWeYLVCf68Q9XiXBKcIkY7of4FFnKZi_68dOXmCyIuEE2ViF_6GFzvmqdlmBKhCmmMhmHCA8Z6VmE3E8obx5e5tA0nylgi6w4DJVfj0d8AqlNnZRHIe9WTOkGPNWVqdCGFKyi5N2OROa4CvPRp9IHsrEzJTbIqiY47NPRxJ60rMTuZH_P5FPW2BGNb96KuxY4r77KcxGGm-ydMa1fytLwtQoUNNIXGxphpZ7qXF2hDbB0qmhnWi7-fHZh1_jXUBH6LnjQTI_Ni6m9iCZchES-gJmUcaSP7QJ0TiEe2JRRMkJQKMNw-EXp_FGIihieCxVpq2ToguDvMulQK8gDXRW1yIpUI0e2vfuFQVqieKP67WzHYd53Btmg1WSMxxs8PVH5wsdJfce8rFV6hmra9NVuuPjKyuHByqOBuLv6NzJsftRyOHHBfRtwfXWnQZT4n6NWvdI_c= +0r_4nxpohIKGVw-fjYKPbJ3IKDgH-hZYL22Ym9nrQupOaNkEDXhsJ0MLXqhrw2esOHVP7usPaA7QVLQMVKUPIkQzkY4ylxRYjp-VOQfdjOn8ZfbnkwuW0tokPMhckOfrU-jebBWKiYSgucmIeSjFG4K59kYoI1CPlyykP5bgUz2YjOP5tOMrMpz2b9P3eE8zm11K3Z3OKCsVTaz6mHg0zkKZcxNMxTvzouCDt12DHyuwzBDsQoIRmobAOS_HngcDxfXh5sgubS2WNEOnfHlFh93dP6oyMiMAjoUUuzAQlqMUKsPJV08ANxkIBwqmPwfMb_-XDCgdLmr-5P4C6g6LsXVXK5uYduhLKlmte_ziE2spgEtXLnGRmYyXlDl1z3EMX1zhIvX-eNAHYiOyAbBppd9G3_K0MN9k7AaahpndnzDoyg_ykNMacrBkiZLofmXd3YzMA4XmyDLlJBaYR-yDv04J6l9f_Y6coj5n1iefeOOiFklIG7TNBddxxxM= +jeMxBT_R5Ny7NbNIZoLryQUDuG9r4cDWGUCYKodzcB9dfkDfc2mi4qzivZLCymkAD-M-IYI2k5gVCBo315C29ROXiL1uLVcjgrDLRNjbtH-IHmXfcH25NxOv9B34xp1yCu3aWtX8XMPEb752351BCEpASRqYPvdeHkAvBxPLl6usmMefBhwxjQ== +B-bCLn0AyxrexFzPu4tD7V_kZPURPTxNUt0NLTj9V7J276iFt272J8H7aAKiNMUuPkeTL9mgLfmmGrUX2Kb3Gceml7JEwxW7OVYv2m_naabGynnlFADdpIkWRUfO4GY_oxA7GNvSpJcgeg_bBOO4pJzLjqihZW5V32CevyDHevHXYGmRiwNxfv2p6YECcXswX2Pb_minLrBkjogQ7H7JbncxRCht-TNq1CiF1lX7YZe9wGT3yUuBdIKJ6Bu9USV9_acVOPVW6PIiBskOft56wMOZh2vi09VqjoQSH-XcRJ3y1VJjVi1yWnztPZQPiB3EAzjKUDbsCDbwMKlxRrr8uKkGUuZ_kvnRXVhnIQ== +Vadk_ZSgriYG3qCtpY2DFzip53hTOsw20oZYlcNxRKeyo71NBpSuyMftw-l3XZ0kiDTF_g2Tf8bMZuHsWKHv0w3O0isQbEqO75CsvowK0wf-NMf5gRUwzfjNDmSBKoBA-npeqrXCFVQbWeUQH_zA1tW3LBiNJ6WaiTUkqf3CwTAu2HhL736lO-6Dbbh8QHbO8Cr0Xo6D26s4Bq4r5ZFsorSffVG01xbiG-mgqOpVLnYo3mCTHpwMwzf4QFoh7qpo7ZXPXg4tLXnd94RrWP81uZu1iS0rKo4Xxbn4l99xDmzPetrez2mWKkokDJcurJd9gG16V-FmEBTnVCJBcMyOr_DQ7LhVQr9bFWnFouPxBi4CCGFw7H-zQMnZiHIiBfSIzglSxuH11E0qNW6kuJB6xqLZQJ-HuYWyQcXy6ZFwV5Tnlk-cDO9Okblr-4A3khnPk7W75OzB0qX5rVeS5gUG4wlQTxLG7azVINuQ-N_2ztWrvWI_j7HWoryoMz6ThHjccsv4F3AAwYs7utklhGko4E5SGViyQkrFbPmNCPvqr1wtBHtUjhE= +7pujXeDplvFZ36D-MM60UZgWfi_WmbWmjzv0txCESyHTIpI1igqDAe07VWZfUORAC7RdhJIRfjD8-n6t4vP7FOV-YLfKnoHU77bvYVUtEsEAC3KlgsFd2VNXRrqpI2L4qBNGhaRQLVSpGTJQL8jc2fY1ARVMQaRFaUPlIE4J82PkwtyUiCb_aistOwWFsedDSkwjqiRWaYf76Phi0liJ-9wYGJTBp-_BFke48NG0uK-Lunwfn95g_JotneIeMwUW6mPTUs8op1PZVrvY0r5BWne1p9m3M4gfX_fJVSPs5-vtKUt9K9_IJur7s6eDIJ2E_lHzUXB8JtnMsPqCwldO7g-FJ1j2HbzOfyQFuROWf0TpyNG4qHeUzm6X18v2PPS8aO4= +4wpFoiowrMZBZrXMOJxbijWUO2AstVInBtBPkCz42lfm-_TB3iZ_EjipIfKSaF-5ZF9pO3KDQ6oiuU0y-3zMPG1_3hMLL8WMVmIbYgD-6wB1e1U7tPkqWBl6nweTmUk1Nb7nXMBbj3adjuzF-XWqgXoSp-vg-H9-sI8dGXPgKtt-CeNVrouP9OxOmVzZblibaCXor3rfHzqARmN3ri_a28-BmmmTd07kI5RyF2rRX14-1pD5XP6z2tIhOE8Q5veJKiF5 +6LBo41Vd-dg-mhdN21H_y-1FZZ3FCIuAVXzW5SavdVhGHVTqf_SYZ2lNNNgG7vAQWKxPVT-7iRUI4HKreB6v_w5tKQ9WwK6aHnnQGxnr4kBB0z5mLxhFM3RFNuJv1D7HxwTr7JwWdiyd1ZtZ5Fzt2XJYKKuZoHL8PrU0a9NWuljB394dg5U3yQ99t8EzvpwoDW_6u-_Mt94PC0IQuuP1WW74W2iek1jAqryx64yzLOOwKBgJPKcY1-bAc5THd24Y_JWvAeEIvvbH_SOajWbKEyT0M3GTMe4mhA1_S1C08tWsIHlxJ4cDfURIe0KCIUPR_efamlMP_e9v7oqm-zL7E2LuE5zMwABfz-FlyKN9p1C6JVC-tv8VUg== +fNRLQsM7fviCeG3SDzMghX5bBn_HYsHoGi9WJ5nQcaEZxSxnEebgycc8mxGIbmp3kd6UFgtRebjeQyLy1-TjtQDvF08zJKv7xvcW-BtmbYIxyU8eRT35n1Y5ErHjgiIKo2JT0p9HkAhVyZJWM5BwDRtZHfQ8XDthj4aXErZWYPNs8bfzDSin_zxRs7Jt2BEfSkl3wIo40S7PHMxp-pVUnFLbwofCQ6sGU7ZctcipulOnoDY-SZhgKuddBIOynXFbwlI0xsADxZoeLJwX6szCKFegGLvn0tr9Mtw6Wze0FppvbqxOYsZoBl2HvaPonMUyHVWStw== +Hxza1FbiBO9TKO5qBcMR4RK-nhdI9sAaDKShWFBXv1xTTIq0fZh1WIektiLjKqtDgicznLo6DXcKw7AWBSjqj56W56sDNJbY7ZrrsOhErS3oSUQFja6I96zqYchMZYQqsq9oa2S5HNS_HnsJk85QKCQvb6Z4O72Gu2AnBIfYcYDlsROlAK20l3Kt0H9k9RbGLfma_1-_jvMPtuLr8Uyto8bDfuDyTvh2cnjuHbJ-2jTWo2CigWj4d0DBuaGMVYRaqhdGkC8lus5qLadgV2bKt7tUMpYnp3U8KkJYXTrv9Tyq +guJnfwo2qKl5xdGFzIr2wr6vXEhfa9HHA5t4R759m4SzwzRDoI8THYGzOrPcsfUrDGAmLOdLBzgJ2TxWmtpzabeeAa77TjMkCzqr7cQqE1rdhsgfFOR4lBd-fLQnN6pRLlQ9a-SV1UpEL4n50w5iO8SyhFhVqoVg4EnaLwFiD1zp71eOrZa9hIEeKRP2FieJfWBpDRizcEqs5e2lfnyROsuJ9_DCu_MbrJlrDMEPjwICsJ-3-PhieD8QvJijj3lZrvT_bQwaH4NHW6p-57AUtdkYumntG8gOmeDVDcDPpGy_kNR_nbSBp5HHmmybXiHbJoiX1RM5elwbmooiNw== +Ir3e5WjCJPT1KEM_ifZjWV7F8JA4ZHc_R0uFp9Tu9nbb0XZwis64A5UFbtWFftTNOVgx-SX9dVZIaE0iXRsZdZrfVCn7_WlfbT311ubZu1QknBRX1uMfxZA03n8uwzrqG5X4Qc2KIji-3COovf82Vwfb-hqhODjsrxYmxX0R84x-tOqje5ZTGV_-3omttDjkYtO4jamhM8CFhg_sKh8CicL0gp1ViOxfrzv8o5y--WCtdJRbWIrFUfjmScpUmqhlocG3T6NXJfGcDij53RPYrWW1fQPZioTa6oz4HaKcqIB0Q2RTGS6TxD6m9q0pjiIzVPcByxluEtwZfbwvpVl8xXo-jBIG8AenhqmGHQ-fSlWKd3-mz1xPukZcmpLj-zQCJDodYc8lu96La0diP4ETyYxYGZoDmyZtNwY-dgdIf2jMha_NLcBQ_t4TXZo8byBjObPhHaSCV64LYRkSuotmtmN_vCStSBaVfjMemPhbb8HjRcDilvvaOmfZ4LCYNzOLlbvctfjnHhrbTipjyaFOYR4jnrDSNUmS9RbmzdPSAGXkcXjLZNCfrnLVqxdTRiulwwNJpAHs2c-2WNTM95l5wTS7f-I-kiIVfAsjlsgt4jqomZFwI9pf9rAOOtBPcUruTy-TEGXCAos= +aGWYGdDrao6CKxS0weFdaBWGIXmJtwDbPTUrp4uz1hvcIZOC__zawj3Hkt-fPI65Mcywlkzjc0jmcSddn1YiFpWQ_AYZmS32vusXLHxHQXsw9DijDETzNEOEMdMFHyxBpcFeQUwJEs0FCB07WxTSg0PCcXbuxMy9Wvz381AGMZwp_dAwT0I6m1RLCwHOs8lwATvf4JwPiVDF3buyrT8nuFLNOlImOrbebarICqkI6Nmj6bl1MY01bK47i8pZCb736RcBsF8rQ-ZhtMs8VilAR-92bnfti8jSl9ZGKvdDreHE34FPVT_6N3aXIapLy9KEUCw4aiMMP7VzM8e-QVDGFKIh4YTyLeohl-fw3x5CNcefy1bF8dPZV5as2u-px1NDCEfGqW9AGgXcoO83woDMKidEVVIuBwGg7QRdZ0V2PMt-wFq9nE142J3oWrhasgD-S8byryIZXCYR-CGnsCV4YcgwLWkmFjBXlQjb9TTcAHNpsxxtfEaHLKFFAdNCtkFQkL5x4lmX5XCjLPBwZgqSCmrqimEGT_cYdbW1SykDms_7BAs96cRfXoZSTAWEimVuf_Voj6DbhNgdodyJ6BaW6bARAJg9MxQCQgWo2AS2dBOUi6Y_9QkkxDV4vcGyJe-A2RW-uw== +MSrkgEuWTbPJ6zP1-FK5eKxAbgS3Q8cm317dhR-zinDEVHmSU8h820EXmJ7emR8fsUXr9e63vfSCQV4Nf83sJCAcDHpfU7F8TuPFw9DY81k8yg9YM6gA1kcMrttCQo5e_h_siWxrbzTTWOsCUUu-1ObyIfecluJ4kP8cKUxCENVrokYLGxF4N2eoN8yr0V9wtGiAATDRoLFpo4M0j_A= +NH4fOI1OR1v59Uj3fSJKfAkui72_2BEir9ibR1bfR6fxey-UJgYwBPBahg-jChKWbC5FsZ3QSQR1cizmm95FuJLmaN5aB7DT9pQ2RvcxEEwg2AT-BQo2WUTHIZ5EkgK5CgCa_A-mB7nOBuHpiTHeN2E8ZYyGHRq_XRNrrbcIDU6CNf0aD2nd3FA9-T-dFZLz6ir8e9gmQi-qwQ-fY6GyWjHvwUid0U01qsmhWLPhk8fysdMBHii5GMc03DQac5DlrmGORJXxasRLonOsS3YRefPJB8wNbbQ452xsX09NC3VItWYDrDe2uO-a1w7LTZLCsq4TTFdgQscl5OnQ19QUQtgvd2Yw-B4rRFpefSmbHeUDYkJ46WaiU6dLIp2GQNDSm7VRruE03ta7b6c8sklx9NHlzYaANOf3Lg== +YnxTNZOAjUwGGlwf8VXw41HMkUjoMeiRIBsHH0hFhYWVElg1tFy5_rJCmNtpaDzfbzWByWIU_CnGa07vqYNJUDXVVjm9-x-q8qTq_Ey83OMMIC_L0YF5D3_WiajloJ7_fQga23KohEi95ipAZrlKADFSz43ilIgyB2wo1kh5POloR1-lMHbZFCVOVwiS1yeo--5jVjCe4Rn1-HzSyNxfp_sqmfwpwI0pje5fDPv0Tok4Fy8dpb_VqlXyroR30xpnUIE1IfaCdQm2BWbM3zKG7QVg5JJgqR8WX-yGKKi3jQj1m8rf +JaaRRRUtQz_XVzQEk_b6wgv1dNzyhYhVfXOBCSy5QEAk1dgqL_5OErh91I1TpHYOFtOGtGDONDfXnBusRcDy7xy8gm_2sVdnoX9hcAc5FyG1kzQir2FBHI_A9y_b-8Jnv_b39c1AvQxG4S-_ou9dxd89KBWyJ_X2qThalW7brfov6QyaymhzIz2E9B3DFIohlRoeNs5Q_RrPEk3XPYgdIDkmmj7Q4mI4dpT_GKHPV7n_imIbn36dSXwSC3KPO7Vw-s6BQGSpBkm6q3gUUiiEjjYZ9yGbRVfgcAK2w5QBQQBjfZLYuM3JTrKEMi3mr8TVKFbuayZ8lc7l9NumX3RElS0q8-BpYpW08NyAtdZhhsUu6maVmN7RuxDbR-Fn550zoZNhh3D0MFZ_6qaxSToiA7p0wIK8eLLuXQml1S990H_YqetkzhLFanf_SN2v9dYjtJt54EVzsmb5DWn-c7Vkfc91lH9mbDfuE8ZuwiuCK8D48zLIsp-2sh5xi7R35uos7wVku5H9qQLUnpZNXKg0s3kQbBgvXij-oNmLv-EsEXFs2WEm58vHzR6hTXDzXw== +34ZmaeDiWg7ZRQlFKK5GaeMUUgC-uLFNRuW_3FOgT84YvWq7jziHnjZhQBDduOcw8P2aeBG-wkaJ2oZ8gtgRc54YagQ3mgDOKDF_rplX8s7RmpuIUIa3iheXBJuy5o95uqixYYMkK1_PhC6Dl5AvX-e2aSZHCPL-mV1HqoLfJHfrb2kA1ce57AUhnpyOXOvn4dG3n7z8kAqUzgr-zODjIzLhuGcLZOpvRXfvB3GGDJc= +ZcakvlkL0Jwp9ToBI5Oa9Y2l5G1frcRFEtnvg5oUt9mLXL9WlPKdfJgHwwhv3Ij4ZT-tSiAMrcxQ1Qsmb5etRpE1uiINJx3Z9qDJJ1UmqfYveyuwEGvNQ-Ih3QK7oJu843lwXDc_w9GeqFd7xaUqTJ1h8t-FFCp8jC425kuLHQ== +6bWQzOtg9wjFWe9xfRpnQYPs6dcol500reqM_UPJq11EzMMXfkF5v88uOtDGw4NluDUdlk2AzswNbJzI-RsJZul1zQn9rYlUMWs8wwb9Y4sdXrx7wdEivGyB3GLnvWVFsbQLNyojRcJSzp5zrRh-20G3x1WN2y1BWpgxiSkBg92-z-6YqfzJnQ== +3Hyo9dYQezKq650s3x_B1JV9-B_XkMhNTdV64ayIArVQSvPxJilW8-kFUL0ErGYh_XR_kdAP1HB132wTi2UC1Cs533iFFXUOWtn9nhvwfnkJPXP7CWFiIsMihii5BwJa6bqwZJOqeddDcHYHuNf_8Bhm1V6KbBNL8NSnsYbsn35OfCWiYualyjinqRgYfNsmAZ3QM5bfWCbp4A2f-Lh8Kl6DxJrneAyUTm0m0l0EezrGuv7obZRRhFNl4yvbQJYH8Ms2byeYTEMKI8qVknz_hCyc7H3_ipCEDqUse-LTiIJh8SSMkMxrDZO5GIgNLq2TsER0qAsGKx4x4qhzWkBQY_BvHgsI-ExUo-y1dpT2I_Ncz1kfOEKuJBWlC0TzGwNhLucQdiZaNDO9M5So3hK7Mv6trGeWSWMX0vI1JA25BDwd87pRINz2SyHfQSni9KqvSK97wxhmcnHL7r7MJ3Au3GgHtgPsQy0eAODgDHGLV6xBYU6zeLJdya1pexnRj3n5OuOEkAu9FKF2vk4aqIxwKaYq4IkPQVFhfOBjb7WWl8U9DB-pkEx3Zr857nq0NDD5o8OwlCFp2JcaYWBLMSsLGDTUm5DMizfyMRue114SY6xwddNdFKpWvMzniIsFSTmlvG9SUTNT +tnS0UWj1P6auU6GNfRyu7tBLlj_thS3vtaabDNHuv0EwgILdg_ifVjh5HTqF2Os3ospzwz3uhOXie0jANaQE-6rUimcOJpfCfjrb-n5gXk7Yg_ykxEJRuV2Y2lUT6LQ0I3jd-nxMxxKlNlmrez3mu1VGM5pbMRPiM3Fu7AjozzveNsXOrG07ZvzeYqW-4rm4joiYU8SepR4cd7Y2Fr08OxWbJK9IvZuw_tCbx_oCe0w9bIyeri1f1EwV2VFa-hvwTDmDFBjn28WVnV1HJGhovfhdoIXtZvD5zVrisZ3gc_rSokJZwijjKSRwX85WtyvdMpZVzXHjed_Tu0KMNM2-SOCthaaKubgS1BGjXlKcwLQhOcllZRhijqh82Ve1VdUZ3npA_4EMniqrzj99DfMA_gJhzTMLJmsKiTYJxASPsxZRtPj-pla-fN_DZirYXEf_Srjr188_fxQV_mLPNROXDOiVra2G8r4r-gFEH5rAsjw4CdnKBx7A572zT1K8Kgm3NS54GMUL4lk3GSjbok1_f0xZ1HPuDFFvtuJUn4CpNCeEaUn7KB4iw6uJd-6JVAm-Jft2mVT7WXT311ypMwggRFxZYSCGysGitYTtFq-v4lZALGPIfC7OP7hIkTKNHkvkIjns4z1y3DkporCjyv6HICOrehW79XI_8CBAgc4VG_1lvVNhkjF8 +ze2ooQZtMOFlz-DZQ_OOHWXxTiMlEbBxSBqxzoico1yQ0KpSRFlIkCKriMKig_TOtoG0OF7_k_OXUf53r9jU9Axl72Xlce_1TKCZ4n2csCHw-4_DbCDBfwM8YOdigJqUy6pVhKrCLShrA00S_dYCPyRBOIVHvJD2994rvgPaiqTTEe-hNTYyWzk2tlRt-A2oht3q456J-0mjbROnpOJHOvXxY42MBT2Nqcf0h5OVxi2NGHlV30e9wp6nAgOonvq4X1yWYUGXnIrQFFYYOUGhWJupjNsWCNPls98krNdP0yn41B4jqlRKyjHp5hBy03rZrJDw_Biq2mlUUZl1b-P61v5gCzyn6Kqo9qPoDgk1PGJ_iXLTkQDGzSwzVsKcSYeTaoZ1OIl23G6FVL6tsE2_htSiw5lcNngCYgm41PEI9EKXm1xIh0A1wv9lws1dbm0IJHry +stiyr_C7OzhUsIkXsAhSFEc6TuQiQIRNYeMfJWCS58CzEeiSr-fl4tmZSAtk13JT_hM1ShFw_KWuArFfNk954sCeanR-_91vFW_YEls5JqqqKzkTgcsn79ZU4TRfEIFeKexzOxOYrRJrGOaC9Fo8WtX-zdPLHIvbQ1XeWWbujGOy7IuR7YiuqMlhjDfGJL8weX8XOmPKNAMXNRfmhZaH6W4R0KfwQSt5WAYs72hXKERz34oPMAKUMFI60AL5zAoGawmSkCHkqBqlgFBg-I5V_AtMKgtU5NMHnizmhF4FocK4nNa1CqjXAL8wAg== diff --git a/test/micro/org/openjdk/bench/java/util/Base64VarLenDecode.java b/test/micro/org/openjdk/bench/java/util/Base64VarLenDecode.java new file mode 100644 index 0000000000000..560f8d2c7c423 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/util/Base64VarLenDecode.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.openjdk.micro.bench.java.util; + +import org.openjdk.jmh.annotations.*; +import java.util.*; + +public class Base64VarLenDecode { + + @State(Scope.Thread) + public static class MyState { + + @Setup(Level.Trial) + public void doSetupTrial() { + ran = new Random(10101); // fixed seed for repeatability + encoder = Base64.getEncoder(); + decoder = Base64.getDecoder(); + System.out.println("Do Trial Setup"); + } + + @Setup(Level.Invocation) + public void doSetupInvocation() { + bin_src_len = 8 + ran.nextInt(20000); + base64_len = ((bin_src_len + 2) / 3) * 4; + unencoded = new byte[bin_src_len]; + encoded = new byte[base64_len]; + decoded = new byte[bin_src_len]; + ran.nextBytes(unencoded); + encoder.encode(unencoded, encoded); + } + + @TearDown(Level.Invocation) + public void doTearDownInvocation() { + // This isn't really a teardown. It's a check for correct functionality. + // Each iteration should produce a correctly decoded buffer that's equal + // to the unencoded data. + if (!Arrays.equals(unencoded, decoded)) { + System.out.println("Original data and decoded data are not equal!"); + for (int j = 0; j < unencoded.length; j++) { + if (unencoded[j] != decoded[j]) { + System.out.format("%06x: %02x %02x\n", j, unencoded[j], decoded[j]); + } + } + System.exit(1); + } + } + + public Random ran; + public Base64.Encoder encoder; + public Base64.Decoder decoder; + public int bin_src_len; + public int base64_len; + public byte[] unencoded; + public byte[] encoded; + public byte[] decoded; + } + + @Benchmark + public void decodeMethod(MyState state) { + state.decoder.decode(state.encoded, state.decoded); + } +} From 2f06893a29fba3b40cc1cf03095b75b217d1bb93 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 12 Nov 2020 01:45:27 +0000 Subject: [PATCH 078/124] 8252526: Remove excessive inclusion of jvmti.h and jvmtiExport.hpp Reviewed-by: ihse, kbarrett --- .../cpu/aarch64/jniFastGetField_aarch64.cpp | 1 + .../cpu/aarch64/methodHandles_aarch64.cpp | 1 + .../cpu/aarch64/templateTable_aarch64.cpp | 1 + src/hotspot/cpu/arm/jniFastGetField_arm.cpp | 3 +- src/hotspot/cpu/arm/methodHandles_arm.cpp | 1 + src/hotspot/cpu/arm/templateTable_arm.cpp | 1 + src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp | 1 + src/hotspot/cpu/ppc/jniFastGetField_ppc.cpp | 3 +- src/hotspot/cpu/ppc/methodHandles_ppc.cpp | 1 + src/hotspot/cpu/ppc/templateTable_ppc_64.cpp | 1 + src/hotspot/cpu/s390/jniFastGetField_s390.cpp | 3 +- src/hotspot/cpu/s390/methodHandles_s390.cpp | 1 + src/hotspot/cpu/s390/templateTable_s390.cpp | 1 + .../cpu/x86/jniFastGetField_x86_32.cpp | 3 +- .../cpu/x86/jniFastGetField_x86_64.cpp | 3 +- src/hotspot/cpu/x86/methodHandles_x86.cpp | 1 + src/hotspot/cpu/x86/templateTable_x86.cpp | 1 + src/hotspot/os/aix/os_aix.cpp | 1 + src/hotspot/os/bsd/os_bsd.cpp | 1 + src/hotspot/os/linux/os_linux.cpp | 1 + src/hotspot/os/windows/os_windows.cpp | 1 + src/hotspot/share/aot/aotLoader.cpp | 1 + src/hotspot/share/c1/c1_Runtime1.cpp | 1 + src/hotspot/share/ci/ciReplay.cpp | 1 + .../share/classfile/defaultMethods.cpp | 1 + src/hotspot/share/classfile/javaClasses.cpp | 29 ++++----- src/hotspot/share/classfile/javaClasses.hpp | 36 +---------- .../share/classfile/javaThreadStatus.hpp | 62 +++++++++++++++++++ .../classfile/systemDictionaryShared.cpp | 1 + src/hotspot/share/code/codeBlob.cpp | 3 +- .../share/compiler/compilationPolicy.cpp | 1 + src/hotspot/share/compiler/compileBroker.cpp | 1 + .../share/compiler/tieredThresholdPolicy.cpp | 1 + .../gc/shenandoah/shenandoahRootProcessor.cpp | 1 + .../share/interpreter/templateInterpreter.cpp | 1 + .../periodic/sampling/jfrThreadSampler.cpp | 5 +- .../share/jvmci/jvmciCodeInstaller.cpp | 1 + src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 1 + src/hotspot/share/jvmci/jvmciEnv.cpp | 1 + src/hotspot/share/jvmci/jvmciRuntime.cpp | 1 + src/hotspot/share/memory/metaspace.cpp | 1 + src/hotspot/share/memory/metaspaceShared.cpp | 1 + src/hotspot/share/oops/constantPool.cpp | 1 + src/hotspot/share/oops/klass.cpp | 1 + src/hotspot/share/opto/macro.cpp | 1 + src/hotspot/share/opto/runtime.cpp | 1 + src/hotspot/share/prims/forte.cpp | 3 +- src/hotspot/share/prims/jni.cpp | 3 +- src/hotspot/share/prims/jvmtiThreadState.hpp | 1 + src/hotspot/share/prims/nativeLookup.cpp | 1 + src/hotspot/share/runtime/deoptimization.cpp | 1 + src/hotspot/share/runtime/javaCalls.cpp | 1 + src/hotspot/share/runtime/objectMonitor.cpp | 1 + src/hotspot/share/runtime/os.hpp | 3 +- .../share/runtime/stubCodeGenerator.cpp | 3 +- src/hotspot/share/runtime/thread.cpp | 7 ++- src/hotspot/share/runtime/thread.hpp | 3 +- .../share/runtime/threadHeapSampler.cpp | 1 + src/hotspot/share/runtime/vframe.cpp | 4 +- src/hotspot/share/runtime/vmStructs.cpp | 21 ++++--- src/hotspot/share/services/management.cpp | 2 +- src/hotspot/share/services/threadService.cpp | 14 ++--- src/hotspot/share/services/threadService.hpp | 27 ++++---- src/hotspot/share/utilities/vmEnums.hpp | 1 + .../sun/jvm/hotspot/oops/OopUtilities.java | 22 +++---- 65 files changed, 198 insertions(+), 107 deletions(-) create mode 100644 src/hotspot/share/classfile/javaThreadStatus.hpp diff --git a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp index f1a331b592ffb..4041ce16b5c84 100644 --- a/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/jniFastGetField_aarch64.cpp @@ -30,6 +30,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #define __ masm-> diff --git a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp index f969ca57e8a10..db9b1507a655f 100644 --- a/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/methodHandles_aarch64.cpp @@ -29,6 +29,7 @@ #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "memory/allocation.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp index 811783fcb7d2a..79e1971edde04 100644 --- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp @@ -35,6 +35,7 @@ #include "oops/method.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/sharedRuntime.hpp" diff --git a/src/hotspot/cpu/arm/jniFastGetField_arm.cpp b/src/hotspot/cpu/arm/jniFastGetField_arm.cpp index debe7f502d9c3..277a73fff6ff9 100644 --- a/src/hotspot/cpu/arm/jniFastGetField_arm.cpp +++ b/src/hotspot/cpu/arm/jniFastGetField_arm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,6 +28,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #define __ masm-> diff --git a/src/hotspot/cpu/arm/methodHandles_arm.cpp b/src/hotspot/cpu/arm/methodHandles_arm.cpp index b9b7e65284a02..26551eeebeceb 100644 --- a/src/hotspot/cpu/arm/methodHandles_arm.cpp +++ b/src/hotspot/cpu/arm/methodHandles_arm.cpp @@ -34,6 +34,7 @@ #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "utilities/preserveException.hpp" diff --git a/src/hotspot/cpu/arm/templateTable_arm.cpp b/src/hotspot/cpu/arm/templateTable_arm.cpp index d0bcfccbb8dde..6ca5d10b23c31 100644 --- a/src/hotspot/cpu/arm/templateTable_arm.cpp +++ b/src/hotspot/cpu/arm/templateTable_arm.cpp @@ -34,6 +34,7 @@ #include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/sharedRuntime.hpp" diff --git a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp index 292accb7852a1..edaca0e462c42 100644 --- a/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/interp_masm_ppc_64.cpp @@ -31,6 +31,7 @@ #include "interp_masm_ppc.hpp" #include "interpreter/interpreterRuntime.hpp" #include "oops/methodData.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" #include "runtime/frame.inline.hpp" #include "runtime/safepointMechanism.hpp" diff --git a/src/hotspot/cpu/ppc/jniFastGetField_ppc.cpp b/src/hotspot/cpu/ppc/jniFastGetField_ppc.cpp index 1652a451d6d34..52b39263d0f9d 100644 --- a/src/hotspot/cpu/ppc/jniFastGetField_ppc.cpp +++ b/src/hotspot/cpu/ppc/jniFastGetField_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,6 +30,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #define __ masm-> diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp index 838908460f9a4..9f0afcae18bc1 100644 --- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp +++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp @@ -31,6 +31,7 @@ #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "utilities/preserveException.hpp" diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp index cc341d8307290..0a6191d2898ed 100644 --- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp +++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp @@ -36,6 +36,7 @@ #include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/safepointMechanism.hpp" diff --git a/src/hotspot/cpu/s390/jniFastGetField_s390.cpp b/src/hotspot/cpu/s390/jniFastGetField_s390.cpp index 1a69d1de5dd4c..c58090b5a2f94 100644 --- a/src/hotspot/cpu/s390/jniFastGetField_s390.cpp +++ b/src/hotspot/cpu/s390/jniFastGetField_s390.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -30,6 +30,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" // TSO ensures that loads are blocking and ordered with respect to diff --git a/src/hotspot/cpu/s390/methodHandles_s390.cpp b/src/hotspot/cpu/s390/methodHandles_s390.cpp index 55270b243b96c..1a2e8fb157606 100644 --- a/src/hotspot/cpu/s390/methodHandles_s390.cpp +++ b/src/hotspot/cpu/s390/methodHandles_s390.cpp @@ -31,6 +31,7 @@ #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "utilities/preserveException.hpp" diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp index 7a4cf869c30fa..daaacf27fb215 100644 --- a/src/hotspot/cpu/s390/templateTable_s390.cpp +++ b/src/hotspot/cpu/s390/templateTable_s390.cpp @@ -34,6 +34,7 @@ #include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/safepointMechanism.hpp" diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp index a5a50261035e7..09bccd13b7659 100644 --- a/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_32.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #define __ masm-> diff --git a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp index 9d6b2659aff25..af94814fbe31d 100644 --- a/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp +++ b/src/hotspot/cpu/x86/jniFastGetField_x86_64.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ #include "memory/resourceArea.hpp" #include "prims/jniFastGetField.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #define __ masm-> diff --git a/src/hotspot/cpu/x86/methodHandles_x86.cpp b/src/hotspot/cpu/x86/methodHandles_x86.cpp index 3d4486867d60a..0209120b80cc5 100644 --- a/src/hotspot/cpu/x86/methodHandles_x86.cpp +++ b/src/hotspot/cpu/x86/methodHandles_x86.cpp @@ -32,6 +32,7 @@ #include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/flags/flagSetting.hpp" #include "runtime/frame.inline.hpp" diff --git a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp index 9f0e5a8694dac..95e0cd89c4440 100644 --- a/src/hotspot/cpu/x86/templateTable_x86.cpp +++ b/src/hotspot/cpu/x86/templateTable_x86.cpp @@ -33,6 +33,7 @@ #include "oops/methodData.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/frame.inline.hpp" #include "runtime/safepointMechanism.hpp" diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index 71cc3056a55ea..e823b62c13df0 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -36,6 +36,7 @@ #include "code/vtableStubs.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/interpreter.hpp" +#include "jvmtifiles/jvmti.h" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "libo4.hpp" diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index d95cb6807d28a..280048cfb0930 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -32,6 +32,7 @@ #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" +#include "jvmtifiles/jvmti.h" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index e7e332c16b580..831b7a2fb5901 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -32,6 +32,7 @@ #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" +#include "jvmtifiles/jvmti.h" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 3b829cddac7a7..70889368283fc 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -37,6 +37,7 @@ #include "compiler/compileBroker.hpp" #include "compiler/disassembler.hpp" #include "interpreter/interpreter.hpp" +#include "jvmtifiles/jvmti.h" #include "logging/log.hpp" #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" diff --git a/src/hotspot/share/aot/aotLoader.cpp b/src/hotspot/share/aot/aotLoader.cpp index d1150589b973b..2d03483a72430 100644 --- a/src/hotspot/share/aot/aotLoader.cpp +++ b/src/hotspot/share/aot/aotLoader.cpp @@ -30,6 +30,7 @@ #include "memory/resourceArea.hpp" #include "oops/compressedOops.hpp" #include "oops/method.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/hotspot/share/c1/c1_Runtime1.cpp b/src/hotspot/share/c1/c1_Runtime1.cpp index 6c776670cac6f..c06c5bca085e6 100644 --- a/src/hotspot/share/c1/c1_Runtime1.cpp +++ b/src/hotspot/share/c1/c1_Runtime1.cpp @@ -55,6 +55,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" #include "runtime/biasedLocking.hpp" #include "runtime/fieldDescriptor.inline.hpp" diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 552c6346741cc..fc66729a3bb77 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -37,6 +37,7 @@ #include "oops/constantPool.hpp" #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/globals_extension.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/hotspot/share/classfile/defaultMethods.cpp b/src/hotspot/share/classfile/defaultMethods.cpp index bc822a7ac9879..d4570ea24e9f8 100644 --- a/src/hotspot/share/classfile/defaultMethods.cpp +++ b/src/hotspot/share/classfile/defaultMethods.cpp @@ -33,6 +33,7 @@ #include "memory/metadataFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/handles.inline.hpp" #include "runtime/signature.hpp" diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 8179e92fc917e..efe7de4221009 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -26,6 +26,7 @@ #include "classfile/altHashing.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/javaThreadStatus.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" @@ -1815,18 +1816,18 @@ jlong java_lang_Thread::stackSize(oop java_thread) { // Write the thread status value to threadStatus field in java.lang.Thread java class. void java_lang_Thread::set_thread_status(oop java_thread, - java_lang_Thread::ThreadStatus status) { - java_thread->int_field_put(_thread_status_offset, status); + JavaThreadStatus status) { + java_thread->int_field_put(_thread_status_offset, static_cast(status)); } // Read thread status value from threadStatus field in java.lang.Thread java class. -java_lang_Thread::ThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { +JavaThreadStatus java_lang_Thread::get_thread_status(oop java_thread) { // Make sure the caller is operating on behalf of the VM or is // running VM code (state == _thread_in_vm). assert(Threads_lock->owned_by_self() || Thread::current()->is_VM_thread() || JavaThread::current()->thread_state() == _thread_in_vm, "Java Thread is not running in vm"); - return (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); + return static_cast(java_thread->int_field(_thread_status_offset)); } @@ -1839,17 +1840,17 @@ oop java_lang_Thread::park_blocker(oop java_thread) { } const char* java_lang_Thread::thread_status_name(oop java_thread) { - ThreadStatus status = (java_lang_Thread::ThreadStatus)java_thread->int_field(_thread_status_offset); + JavaThreadStatus status = static_cast(java_thread->int_field(_thread_status_offset)); switch (status) { - case NEW : return "NEW"; - case RUNNABLE : return "RUNNABLE"; - case SLEEPING : return "TIMED_WAITING (sleeping)"; - case IN_OBJECT_WAIT : return "WAITING (on object monitor)"; - case IN_OBJECT_WAIT_TIMED : return "TIMED_WAITING (on object monitor)"; - case PARKED : return "WAITING (parking)"; - case PARKED_TIMED : return "TIMED_WAITING (parking)"; - case BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)"; - case TERMINATED : return "TERMINATED"; + case JavaThreadStatus::NEW : return "NEW"; + case JavaThreadStatus::RUNNABLE : return "RUNNABLE"; + case JavaThreadStatus::SLEEPING : return "TIMED_WAITING (sleeping)"; + case JavaThreadStatus::IN_OBJECT_WAIT : return "WAITING (on object monitor)"; + case JavaThreadStatus::IN_OBJECT_WAIT_TIMED : return "TIMED_WAITING (on object monitor)"; + case JavaThreadStatus::PARKED : return "WAITING (parking)"; + case JavaThreadStatus::PARKED_TIMED : return "TIMED_WAITING (parking)"; + case JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER : return "BLOCKED (on object monitor)"; + case JavaThreadStatus::TERMINATED : return "TERMINATED"; default : return "UNKNOWN"; }; } diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 2ce053487ae7f..566b5c813a066 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -26,7 +26,6 @@ #define SHARE_CLASSFILE_JAVACLASSES_HPP #include "classfile/systemDictionary.hpp" -#include "jvmtifiles/jvmti.h" #include "oops/oop.hpp" #include "oops/instanceKlass.hpp" #include "oops/symbol.hpp" @@ -421,41 +420,10 @@ class java_lang_Thread : AllStatic { // Blocker object responsible for thread parking static oop park_blocker(oop java_thread); - // Java Thread Status for JVMTI and M&M use. - // This thread status info is saved in threadStatus field of - // java.lang.Thread java class. - enum ThreadStatus { - NEW = 0, - RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running - JVMTI_THREAD_STATE_RUNNABLE, - SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep() - JVMTI_THREAD_STATE_WAITING + - JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + - JVMTI_THREAD_STATE_SLEEPING, - IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait() - JVMTI_THREAD_STATE_WAITING + - JVMTI_THREAD_STATE_WAITING_INDEFINITELY + - JVMTI_THREAD_STATE_IN_OBJECT_WAIT, - IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long) - JVMTI_THREAD_STATE_WAITING + - JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + - JVMTI_THREAD_STATE_IN_OBJECT_WAIT, - PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park() - JVMTI_THREAD_STATE_WAITING + - JVMTI_THREAD_STATE_WAITING_INDEFINITELY + - JVMTI_THREAD_STATE_PARKED, - PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long) - JVMTI_THREAD_STATE_WAITING + - JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + - JVMTI_THREAD_STATE_PARKED, - BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block - JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, - TERMINATED = JVMTI_THREAD_STATE_TERMINATED - }; // Write thread status info to threadStatus field of java.lang.Thread. - static void set_thread_status(oop java_thread_oop, ThreadStatus status); + static void set_thread_status(oop java_thread_oop, JavaThreadStatus status); // Read thread status info from threadStatus field of java.lang.Thread. - static ThreadStatus get_thread_status(oop java_thread_oop); + static JavaThreadStatus get_thread_status(oop java_thread_oop); static const char* thread_status_name(oop java_thread_oop); diff --git a/src/hotspot/share/classfile/javaThreadStatus.hpp b/src/hotspot/share/classfile/javaThreadStatus.hpp new file mode 100644 index 0000000000000..394374209ee2a --- /dev/null +++ b/src/hotspot/share/classfile/javaThreadStatus.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CLASSFILE_JAVATHREADSTATUS_HPP +#define SHARE_CLASSFILE_JAVATHREADSTATUS_HPP + +#include "jvmtifiles/jvmti.h" + +// Java Thread Status for JVMTI and M&M use. +// This thread status info is saved in threadStatus field of +// java.lang.Thread java class. +enum class JavaThreadStatus : int { + NEW = 0, + RUNNABLE = JVMTI_THREAD_STATE_ALIVE + // runnable / running + JVMTI_THREAD_STATE_RUNNABLE, + SLEEPING = JVMTI_THREAD_STATE_ALIVE + // Thread.sleep() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_SLEEPING, + IN_OBJECT_WAIT = JVMTI_THREAD_STATE_ALIVE + // Object.wait() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, + IN_OBJECT_WAIT_TIMED = JVMTI_THREAD_STATE_ALIVE + // Object.wait(long) + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_IN_OBJECT_WAIT, + PARKED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park() + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_INDEFINITELY + + JVMTI_THREAD_STATE_PARKED, + PARKED_TIMED = JVMTI_THREAD_STATE_ALIVE + // LockSupport.park(long) + JVMTI_THREAD_STATE_WAITING + + JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT + + JVMTI_THREAD_STATE_PARKED, + BLOCKED_ON_MONITOR_ENTER = JVMTI_THREAD_STATE_ALIVE + // (re-)entering a synchronization block + JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER, + TERMINATED = JVMTI_THREAD_STATE_TERMINATED +}; + +#endif // SHARE_CLASSFILE_JAVATHREADSTATUS_HPP diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index 3723680192745..3d2b836b60157 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -57,6 +57,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" #include "runtime/javaCalls.hpp" diff --git a/src/hotspot/share/code/codeBlob.cpp b/src/hotspot/share/code/codeBlob.cpp index 42a4fc0f76991..b4f95bf96da06 100644 --- a/src/hotspot/share/code/codeBlob.cpp +++ b/src/hotspot/share/code/codeBlob.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "prims/forte.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/mutexLocker.hpp" diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index 869ecdd4f7944..030357f3d5902 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -34,6 +34,7 @@ #include "oops/methodData.hpp" #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/nativeLookup.hpp" #include "runtime/frame.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/compiler/compileBroker.cpp b/src/hotspot/share/compiler/compileBroker.cpp index 336a0c4f95a25..f29497a0d4288 100644 --- a/src/hotspot/share/compiler/compileBroker.cpp +++ b/src/hotspot/share/compiler/compileBroker.cpp @@ -46,6 +46,7 @@ #include "oops/methodData.hpp" #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/nativeLookup.hpp" #include "prims/whitebox.hpp" #include "runtime/arguments.hpp" diff --git a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp index 3af2dbb1d4fa7..b4a45479de66e 100644 --- a/src/hotspot/share/compiler/tieredThresholdPolicy.cpp +++ b/src/hotspot/share/compiler/tieredThresholdPolicy.cpp @@ -27,6 +27,7 @@ #include "compiler/compilerOracle.hpp" #include "compiler/tieredThresholdPolicy.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/frame.inline.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp index dd49bfea8bcbd..5cd7d236885f6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp @@ -34,6 +34,7 @@ #include "gc/shenandoah/shenandoahStringDedup.hpp" #include "memory/iterator.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/thread.hpp" ShenandoahWeakSerialRoot::ShenandoahWeakSerialRoot(ShenandoahWeakSerialRoot::WeakOopsDo weak_oops_do, diff --git a/src/hotspot/share/interpreter/templateInterpreter.cpp b/src/hotspot/share/interpreter/templateInterpreter.cpp index b65e1d57a8be0..732c12532f7f2 100644 --- a/src/hotspot/share/interpreter/templateInterpreter.cpp +++ b/src/hotspot/share/interpreter/templateInterpreter.cpp @@ -31,6 +31,7 @@ #include "interpreter/templateTable.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/safepoint.hpp" #include "runtime/timerTrace.hpp" #include "utilities/copy.hpp" diff --git a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp index bd235c710de98..e28bec341771d 100644 --- a/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp +++ b/src/hotspot/share/jfr/periodic/sampling/jfrThreadSampler.cpp @@ -32,6 +32,7 @@ #include "jfr/support/jfrThreadId.hpp" #include "jfr/support/jfrThreadLocal.hpp" #include "jfr/utilities/jfrTime.hpp" +#include "jfrfiles/jfrEventClasses.hpp" #include "logging/log.hpp" #include "runtime/frame.inline.hpp" #include "runtime/os.hpp" @@ -192,7 +193,7 @@ void OSThreadSampler::protected_task(const os::SuspendedThreadTaskContext& conte ev->set_starttime(_suspend_time); ev->set_endtime(_suspend_time); // fake to not take an end time ev->set_sampledThread(JFR_THREAD_ID(jth)); - ev->set_state(java_lang_Thread::get_thread_status(_thread_oop)); + ev->set_state(static_cast(java_lang_Thread::get_thread_status(_thread_oop))); } } } @@ -222,7 +223,7 @@ static void write_native_event(JfrThreadSampleClosure& closure, JavaThread* jt, EventNativeMethodSample *ev = closure.next_event_native(); ev->set_starttime(JfrTicks::now()); ev->set_sampledThread(JFR_THREAD_ID(jt)); - ev->set_state(java_lang_Thread::get_thread_status(thread_oop)); + ev->set_state(static_cast(java_lang_Thread::get_thread_status(thread_oop))); } void JfrNativeSamplerCallback::call() { diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index 55fdf1748f656..520ffd7e69366 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -30,6 +30,7 @@ #include "jvmci/jvmciRuntime.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/jniHandles.inline.hpp" diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 5e3e6de80dd15..be021285601fb 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -43,6 +43,7 @@ #include "oops/constantPool.inline.hpp" #include "oops/method.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "prims/nativeLookup.hpp" #include "runtime/atomic.hpp" diff --git a/src/hotspot/share/jvmci/jvmciEnv.cpp b/src/hotspot/share/jvmci/jvmciEnv.cpp index b87314f91560b..fe4675fb38273 100644 --- a/src/hotspot/share/jvmci/jvmciEnv.cpp +++ b/src/hotspot/share/jvmci/jvmciEnv.cpp @@ -32,6 +32,7 @@ #include "memory/universe.hpp" #include "oops/objArrayKlass.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/deoptimization.hpp" #include "runtime/jniHandles.inline.hpp" #include "runtime/javaCalls.hpp" diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index ed9ad397aad0c..d4cd5762f2ad4 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -38,6 +38,7 @@ #include "oops/objArrayKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/methodHandles.hpp" #include "runtime/atomic.hpp" #include "runtime/biasedLocking.hpp" diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp index 81881c76ef3c5..6d1ada365b56a 100644 --- a/src/hotspot/share/memory/metaspace.cpp +++ b/src/hotspot/share/memory/metaspace.cpp @@ -45,6 +45,7 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/atomic.hpp" #include "runtime/globals_extension.hpp" diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index f5000089c2b5b..aeab5c2f2a1c0 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -57,6 +57,7 @@ #include "oops/objArrayOop.hpp" #include "oops/oop.inline.hpp" #include "oops/oopHandle.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/os.hpp" #include "runtime/safepointVerifiers.hpp" diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 922584e6c2d05..ced79943e9af3 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -50,6 +50,7 @@ #include "oops/objArrayOop.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/typeArrayOop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 44468459338be..cd7d461d3268e 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -45,6 +45,7 @@ #include "oops/klass.inline.hpp" #include "oops/oop.inline.hpp" #include "oops/oopHandle.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/arguments.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index b51fafcb440ef..7df9eb5ba1cf6 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -49,6 +49,7 @@ #include "opto/subnode.hpp" #include "opto/subtypenode.hpp" #include "opto/type.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/sharedRuntime.hpp" #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index b123d99dc8ce9..0bf1e17527176 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -61,6 +61,7 @@ #include "opto/output.hpp" #include "opto/runtime.hpp" #include "opto/subnode.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" diff --git a/src/hotspot/share/prims/forte.cpp b/src/hotspot/share/prims/forte.cpp index 140b024cffae3..ea0321e72c198 100644 --- a/src/hotspot/share/prims/forte.cpp +++ b/src/hotspot/share/prims/forte.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ #include "memory/universe.hpp" #include "oops/oop.inline.hpp" #include "prims/forte.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/frame.inline.hpp" #include "runtime/javaCalls.hpp" #include "runtime/thread.inline.hpp" diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index bea5f6d54721e..e251b48f2e139 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -32,6 +32,7 @@ #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/javaThreadStatus.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/modules.hpp" #include "classfile/symbolTable.hpp" @@ -4014,7 +4015,7 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae // Set java thread status. java_lang_Thread::set_thread_status(thread->threadObj(), - java_lang_Thread::RUNNABLE); + JavaThreadStatus::RUNNABLE); // Notify the debugger if (JvmtiExport::should_post_thread_life()) { diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp index 7aec805579175..32d19fa6d20a5 100644 --- a/src/hotspot/share/prims/jvmtiThreadState.hpp +++ b/src/hotspot/share/prims/jvmtiThreadState.hpp @@ -28,6 +28,7 @@ #include "jvmtifiles/jvmti.h" #include "memory/allocation.hpp" #include "prims/jvmtiEventController.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/thread.hpp" #include "utilities/growableArray.hpp" diff --git a/src/hotspot/share/prims/nativeLookup.cpp b/src/hotspot/share/prims/nativeLookup.cpp index 558abdb9e7be4..1523556ad4d36 100644 --- a/src/hotspot/share/prims/nativeLookup.cpp +++ b/src/hotspot/share/prims/nativeLookup.cpp @@ -36,6 +36,7 @@ #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "prims/jvm_misc.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/nativeLookup.hpp" #include "prims/unsafe.hpp" #include "runtime/arguments.hpp" diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp index 6c89b3c773f3f..c7b5367456fe1 100644 --- a/src/hotspot/share/runtime/deoptimization.cpp +++ b/src/hotspot/share/runtime/deoptimization.cpp @@ -49,6 +49,7 @@ #include "oops/typeArrayOop.inline.hpp" #include "oops/verifyOopClosure.hpp" #include "prims/jvmtiDeferredUpdates.hpp" +#include "prims/jvmtiExport.hpp" #include "prims/jvmtiThreadState.hpp" #include "prims/vectorSupport.hpp" #include "prims/methodHandles.hpp" diff --git a/src/hotspot/share/runtime/javaCalls.cpp b/src/hotspot/share/runtime/javaCalls.cpp index d3a1c8bfb5076..c150cf78f601a 100644 --- a/src/hotspot/share/runtime/javaCalls.cpp +++ b/src/hotspot/share/runtime/javaCalls.cpp @@ -37,6 +37,7 @@ #include "oops/method.inline.hpp" #include "oops/oop.inline.hpp" #include "prims/jniCheck.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" #include "runtime/javaCalls.hpp" diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp index 8cbe4dd93325e..3786c4d896ede 100644 --- a/src/hotspot/share/runtime/objectMonitor.cpp +++ b/src/hotspot/share/runtime/objectMonitor.cpp @@ -35,6 +35,7 @@ #include "oops/markWord.hpp" #include "oops/oop.inline.hpp" #include "prims/jvmtiDeferredUpdates.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index d91b9f561ca86..1809a489361e8 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -26,7 +26,6 @@ #define SHARE_RUNTIME_OS_HPP #include "jvm.h" -#include "jvmtifiles/jvmti.h" #include "metaprogramming/integralConstant.hpp" #include "utilities/exceptions.hpp" #include "utilities/ostream.hpp" @@ -52,6 +51,8 @@ class methodHandle; class OSThread; class Mutex; +struct jvmtiTimerInfo; + template class GrowableArray; // %%%%% Moved ThreadState, START_FN, OSThread to new osThread.hpp. -- Rose diff --git a/src/hotspot/share/runtime/stubCodeGenerator.cpp b/src/hotspot/share/runtime/stubCodeGenerator.cpp index 73b8f7b3279ff..b99d6e61fb3d4 100644 --- a/src/hotspot/share/runtime/stubCodeGenerator.cpp +++ b/src/hotspot/share/runtime/stubCodeGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ #include "compiler/disassembler.hpp" #include "oops/oop.inline.hpp" #include "prims/forte.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/stubCodeGenerator.hpp" diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index 615a11514117d..bace144baa13d 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -27,6 +27,7 @@ #include "aot/aotLoader.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/javaThreadStatus.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" @@ -499,7 +500,7 @@ void Thread::start(Thread* thread) { // exact thread state at that time. It could be in MONITOR_WAIT or // in SLEEPING or some other state. java_lang_Thread::set_thread_status(thread->as_Java_thread()->threadObj(), - java_lang_Thread::RUNNABLE); + JavaThreadStatus::RUNNABLE); } os::start_thread(thread); } @@ -887,7 +888,7 @@ static void create_initial_thread(Handle thread_group, JavaThread* thread, // Set thread status to running since main thread has // been started and running. java_lang_Thread::set_thread_status(thread_oop(), - java_lang_Thread::RUNNABLE); + JavaThreadStatus::RUNNABLE); } char java_version[64] = ""; @@ -1818,7 +1819,7 @@ static void ensure_join(JavaThread* thread) { // Ignore pending exception (ThreadDeath), since we are exiting anyway thread->clear_pending_exception(); // Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED. - java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); + java_lang_Thread::set_thread_status(threadObj(), JavaThreadStatus::TERMINATED); // Clear the native thread instance - this makes isAlive return false and allows the join() // to complete once we've done the notify_all below java_lang_Thread::set_thread(threadObj(), NULL); diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp index ee77c409d936f..81e6ee26fcd81 100644 --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -30,7 +30,6 @@ #include "gc/shared/threadLocalAllocBuffer.hpp" #include "memory/allocation.hpp" #include "oops/oop.hpp" -#include "prims/jvmtiExport.hpp" #include "runtime/frame.hpp" #include "runtime/globals.hpp" #include "runtime/handshake.hpp" @@ -63,7 +62,9 @@ class ThreadsList; class ThreadsSMRSupport; class JvmtiRawMonitor; +class JvmtiSampledObjectAllocEventCollector; class JvmtiThreadState; +class JvmtiVMObjectAllocEventCollector; class ThreadStatistics; class ConcurrentLocksDump; class ParkEvent; diff --git a/src/hotspot/share/runtime/threadHeapSampler.cpp b/src/hotspot/share/runtime/threadHeapSampler.cpp index 21f57c9f05537..b65f3084982df 100644 --- a/src/hotspot/share/runtime/threadHeapSampler.cpp +++ b/src/hotspot/share/runtime/threadHeapSampler.cpp @@ -26,6 +26,7 @@ #include "precompiled.hpp" #include "logging/log.hpp" #include "logging/logTag.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/atomic.hpp" #include "runtime/handles.inline.hpp" #include "runtime/sharedRuntime.hpp" diff --git a/src/hotspot/share/runtime/vframe.cpp b/src/hotspot/share/runtime/vframe.cpp index 773069913efdd..c60d4da245ede 100644 --- a/src/hotspot/share/runtime/vframe.cpp +++ b/src/hotspot/share/runtime/vframe.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/javaClasses.inline.hpp" +#include "classfile/javaThreadStatus.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "code/codeCache.hpp" @@ -36,6 +37,7 @@ #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" #include "oops/oop.inline.hpp" +#include "prims/jvmtiExport.hpp" #include "runtime/frame.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/objectMonitor.hpp" @@ -190,7 +192,7 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) { if (sv->type() == T_OBJECT) { Handle o = locs->at(0)->get_obj(); if (java_lang_Thread::get_thread_status(thread()->threadObj()) == - java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) { + JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER) { wait_state = "waiting to re-lock in wait()"; } print_locked_object_class_name(st, o, wait_state); diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp index 444c68c54b392..b2e0fffe2a71a 100644 --- a/src/hotspot/share/runtime/vmStructs.cpp +++ b/src/hotspot/share/runtime/vmStructs.cpp @@ -32,6 +32,7 @@ #include "classfile/classLoaderDataGraph.hpp" #include "classfile/dictionary.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/javaThreadStatus.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" @@ -2331,18 +2332,18 @@ typedef HashtableEntry KlassHashtableEntry; declare_constant(ConstantPoolCacheEntry::tos_state_shift) \ \ /***************************************/ \ - /* java_lang_Thread::ThreadStatus enum */ \ + /* JavaThreadStatus enum */ \ /***************************************/ \ \ - declare_constant(java_lang_Thread::NEW) \ - declare_constant(java_lang_Thread::RUNNABLE) \ - declare_constant(java_lang_Thread::SLEEPING) \ - declare_constant(java_lang_Thread::IN_OBJECT_WAIT) \ - declare_constant(java_lang_Thread::IN_OBJECT_WAIT_TIMED) \ - declare_constant(java_lang_Thread::PARKED) \ - declare_constant(java_lang_Thread::PARKED_TIMED) \ - declare_constant(java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) \ - declare_constant(java_lang_Thread::TERMINATED) \ + declare_constant(JavaThreadStatus::NEW) \ + declare_constant(JavaThreadStatus::RUNNABLE) \ + declare_constant(JavaThreadStatus::SLEEPING) \ + declare_constant(JavaThreadStatus::IN_OBJECT_WAIT) \ + declare_constant(JavaThreadStatus::IN_OBJECT_WAIT_TIMED) \ + declare_constant(JavaThreadStatus::PARKED) \ + declare_constant(JavaThreadStatus::PARKED_TIMED) \ + declare_constant(JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER) \ + declare_constant(JavaThreadStatus::TERMINATED) \ \ /******************************/ \ /* Debug info */ \ diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp index 65a8481ad53c0..7b22e7b4e50d3 100644 --- a/src/hotspot/share/services/management.cpp +++ b/src/hotspot/share/services/management.cpp @@ -303,7 +303,7 @@ static void initialize_ThreadInfo_constructor_arguments(JavaCallArguments* args, waited_time = max_julong; } - int thread_status = snapshot->thread_status(); + int thread_status = static_cast(snapshot->thread_status()); assert((thread_status & JMM_THREAD_STATE_FLAG_MASK) == 0, "Flags already set in thread_status in Thread object"); if (snapshot->is_ext_suspended()) { thread_status |= JMM_THREAD_STATE_FLAG_SUSPENDED; diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp index e31e935ad84b1..8f1ef6f05554b 100644 --- a/src/hotspot/share/services/threadService.cpp +++ b/src/hotspot/share/services/threadService.cpp @@ -882,17 +882,17 @@ void ThreadSnapshot::initialize(ThreadsList * t_list, JavaThread* thread) { oop blocker_object = NULL; oop blocker_object_owner = NULL; - if (_thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER || - _thread_status == java_lang_Thread::IN_OBJECT_WAIT || - _thread_status == java_lang_Thread::IN_OBJECT_WAIT_TIMED) { + if (_thread_status == JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER || + _thread_status == JavaThreadStatus::IN_OBJECT_WAIT || + _thread_status == JavaThreadStatus::IN_OBJECT_WAIT_TIMED) { if (obj() == NULL) { // monitor no longer exists; thread is not blocked - _thread_status = java_lang_Thread::RUNNABLE; + _thread_status = JavaThreadStatus::RUNNABLE; } else { blocker_object = obj(); JavaThread* owner = ObjectSynchronizer::get_lock_owner(t_list, obj); - if ((owner == NULL && _thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER) + if ((owner == NULL && _thread_status == JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER) || (owner != NULL && owner->is_attaching_via_jni())) { // ownership information of the monitor is not available // (may no longer be owned or releasing to some other thread) @@ -900,7 +900,7 @@ void ThreadSnapshot::initialize(ThreadsList * t_list, JavaThread* thread) { // And when the owner thread is in attaching state, the java thread // is not completely initialized. For example thread name and id // and may not be set, so hide the attaching thread. - _thread_status = java_lang_Thread::RUNNABLE; + _thread_status = JavaThreadStatus::RUNNABLE; blocker_object = NULL; } else if (owner != NULL) { blocker_object_owner = owner->threadObj(); @@ -909,7 +909,7 @@ void ThreadSnapshot::initialize(ThreadsList * t_list, JavaThread* thread) { } // Support for JSR-166 locks - if (_thread_status == java_lang_Thread::PARKED || _thread_status == java_lang_Thread::PARKED_TIMED) { + if (_thread_status == JavaThreadStatus::PARKED || _thread_status == JavaThreadStatus::PARKED_TIMED) { blocker_object = thread->current_park_blocker(); if (blocker_object != NULL && blocker_object->is_a(SystemDictionary::java_util_concurrent_locks_AbstractOwnableSynchronizer_klass())) { blocker_object_owner = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(blocker_object); diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp index d79da0294a7b4..470bead25a603 100644 --- a/src/hotspot/share/services/threadService.hpp +++ b/src/hotspot/share/services/threadService.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" +#include "classfile/javaThreadStatus.hpp" #include "runtime/handles.hpp" #include "runtime/init.hpp" #include "runtime/jniHandles.hpp" @@ -194,7 +195,7 @@ class ThreadSnapshot : public CHeapObj { // protected by a ThreadsListSetter (ThreadDumpResult). JavaThread* _thread; OopHandle _threadObj; - java_lang_Thread::ThreadStatus _thread_status; + JavaThreadStatus _thread_status; bool _is_ext_suspended; bool _is_in_native; @@ -223,7 +224,7 @@ class ThreadSnapshot : public CHeapObj { public: ~ThreadSnapshot(); - java_lang_Thread::ThreadStatus thread_status() { return _thread_status; } + JavaThreadStatus thread_status() { return _thread_status; } oop threadObj() const; @@ -418,7 +419,7 @@ class ThreadsListEnumerator : public StackObj { // abstract utility class to set new thread states, and restore previous after the block exits class JavaThreadStatusChanger : public StackObj { private: - java_lang_Thread::ThreadStatus _old_state; + JavaThreadStatus _old_state; JavaThread* _java_thread; bool _is_alive; @@ -432,23 +433,23 @@ class JavaThreadStatusChanger : public StackObj { public: static void set_thread_status(JavaThread* java_thread, - java_lang_Thread::ThreadStatus state) { + JavaThreadStatus state) { java_lang_Thread::set_thread_status(java_thread->threadObj(), state); } - void set_thread_status(java_lang_Thread::ThreadStatus state) { + void set_thread_status(JavaThreadStatus state) { if (is_alive()) { set_thread_status(_java_thread, state); } } JavaThreadStatusChanger(JavaThread* java_thread, - java_lang_Thread::ThreadStatus state) : _old_state(java_lang_Thread::NEW) { + JavaThreadStatus state) : _old_state(JavaThreadStatus::NEW) { save_old_state(java_thread); set_thread_status(state); } - JavaThreadStatusChanger(JavaThread* java_thread) : _old_state(java_lang_Thread::NEW) { + JavaThreadStatusChanger(JavaThread* java_thread) : _old_state(JavaThreadStatus::NEW) { save_old_state(java_thread); } @@ -474,7 +475,7 @@ class JavaThreadInObjectWaitState : public JavaThreadStatusChanger { public: JavaThreadInObjectWaitState(JavaThread *java_thread, bool timed) : JavaThreadStatusChanger(java_thread, - timed ? java_lang_Thread::IN_OBJECT_WAIT_TIMED : java_lang_Thread::IN_OBJECT_WAIT) { + timed ? JavaThreadStatus::IN_OBJECT_WAIT_TIMED : JavaThreadStatus::IN_OBJECT_WAIT) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); @@ -503,7 +504,7 @@ class JavaThreadParkedState : public JavaThreadStatusChanger { public: JavaThreadParkedState(JavaThread *java_thread, bool timed) : JavaThreadStatusChanger(java_thread, - timed ? java_lang_Thread::PARKED_TIMED : java_lang_Thread::PARKED) { + timed ? JavaThreadStatus::PARKED_TIMED : JavaThreadStatus::PARKED) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); @@ -530,7 +531,7 @@ class JavaThreadBlockedOnMonitorEnterState : public JavaThreadStatusChanger { bool _active; static bool contended_enter_begin(JavaThread *java_thread) { - set_thread_status(java_thread, java_lang_Thread::BLOCKED_ON_MONITOR_ENTER); + set_thread_status(java_thread, JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER); ThreadStatistics* stat = java_thread->get_thread_stat(); stat->contended_enter(); bool active = ThreadService::is_thread_monitoring_contention(); @@ -556,7 +557,7 @@ class JavaThreadBlockedOnMonitorEnterState : public JavaThreadStatusChanger { if (active) { java_thread->get_thread_stat()->contended_enter_end(); } - set_thread_status(java_thread, java_lang_Thread::RUNNABLE); + set_thread_status(java_thread, JavaThreadStatus::RUNNABLE); } JavaThreadBlockedOnMonitorEnterState(JavaThread *java_thread, ObjectMonitor *obj_m) : @@ -587,7 +588,7 @@ class JavaThreadSleepState : public JavaThreadStatusChanger { bool _active; public: JavaThreadSleepState(JavaThread *java_thread) : - JavaThreadStatusChanger(java_thread, java_lang_Thread::SLEEPING) { + JavaThreadStatusChanger(java_thread, JavaThreadStatus::SLEEPING) { if (is_alive()) { _stat = java_thread->get_thread_stat(); _active = ThreadService::is_thread_monitoring_contention(); diff --git a/src/hotspot/share/utilities/vmEnums.hpp b/src/hotspot/share/utilities/vmEnums.hpp index bc044e675e8e2..136cd1e190975 100644 --- a/src/hotspot/share/utilities/vmEnums.hpp +++ b/src/hotspot/share/utilities/vmEnums.hpp @@ -29,6 +29,7 @@ // you don't use their members directly. This way you don't need to include the // complex header files that have the full definitions of these enums. +enum class JavaThreadStatus : int; enum class JVMFlagOrigin : int; enum JVMFlagsEnum : int; enum class vmSymbolID : int; diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java index 749e55290abd6..7aecb63b490eb 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/OopUtilities.java @@ -64,7 +64,7 @@ public class OopUtilities { private static IntField threadPriorityField; private static BooleanField threadDaemonField; - // possible values of java_lang_Thread::ThreadStatus + // possible values of JavaThreadStatus public static int THREAD_STATUS_NEW; public static int THREAD_STATUS_RUNNABLE; @@ -234,16 +234,16 @@ private static void initThreadFields() { threadPriorityField = (IntField) k.findField("priority", "I"); threadDaemonField = (BooleanField) k.findField("daemon", "Z"); TypeDataBase db = VM.getVM().getTypeDataBase(); - THREAD_STATUS_NEW = db.lookupIntConstant("java_lang_Thread::NEW").intValue(); - - THREAD_STATUS_RUNNABLE = db.lookupIntConstant("java_lang_Thread::RUNNABLE").intValue(); - THREAD_STATUS_SLEEPING = db.lookupIntConstant("java_lang_Thread::SLEEPING").intValue(); - THREAD_STATUS_IN_OBJECT_WAIT = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT").intValue(); - THREAD_STATUS_IN_OBJECT_WAIT_TIMED = db.lookupIntConstant("java_lang_Thread::IN_OBJECT_WAIT_TIMED").intValue(); - THREAD_STATUS_PARKED = db.lookupIntConstant("java_lang_Thread::PARKED").intValue(); - THREAD_STATUS_PARKED_TIMED = db.lookupIntConstant("java_lang_Thread::PARKED_TIMED").intValue(); - THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = db.lookupIntConstant("java_lang_Thread::BLOCKED_ON_MONITOR_ENTER").intValue(); - THREAD_STATUS_TERMINATED = db.lookupIntConstant("java_lang_Thread::TERMINATED").intValue(); + THREAD_STATUS_NEW = db.lookupIntConstant("JavaThreadStatus::NEW").intValue(); + + THREAD_STATUS_RUNNABLE = db.lookupIntConstant("JavaThreadStatus::RUNNABLE").intValue(); + THREAD_STATUS_SLEEPING = db.lookupIntConstant("JavaThreadStatus::SLEEPING").intValue(); + THREAD_STATUS_IN_OBJECT_WAIT = db.lookupIntConstant("JavaThreadStatus::IN_OBJECT_WAIT").intValue(); + THREAD_STATUS_IN_OBJECT_WAIT_TIMED = db.lookupIntConstant("JavaThreadStatus::IN_OBJECT_WAIT_TIMED").intValue(); + THREAD_STATUS_PARKED = db.lookupIntConstant("JavaThreadStatus::PARKED").intValue(); + THREAD_STATUS_PARKED_TIMED = db.lookupIntConstant("JavaThreadStatus::PARKED_TIMED").intValue(); + THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER = db.lookupIntConstant("JavaThreadStatus::BLOCKED_ON_MONITOR_ENTER").intValue(); + THREAD_STATUS_TERMINATED = db.lookupIntConstant("JavaThreadStatus::TERMINATED").intValue(); if (Assert.ASSERTS_ENABLED) { // it is okay to miss threadStatusField, because this was From 14e25e2059328f68deb051c76653077c6b70a94c Mon Sep 17 00:00:00 2001 From: Lin Zang Date: Thu, 12 Nov 2020 02:12:15 +0000 Subject: [PATCH 079/124] 8255982: Extend BasicJMapTest to test with different GC Heap Reviewed-by: shade, iignatyev --- test/jdk/TEST.ROOT | 6 ++- test/jdk/sun/tools/jmap/BasicJMapTest.java | 60 ++++++++++++++++++++-- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index ab63cc78ef6b2..f24f1fc73b6af 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -46,8 +46,12 @@ requires.extraPropDefns.vmOpts = -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI requires.properties= \ sun.arch.data.model \ java.runtime.name \ - vm.gc.Z \ + vm.gc.G1 \ + vm.gc.Serial \ + vm.gc.Parallel \ vm.gc.Shenandoah \ + vm.gc.Epsilon \ + vm.gc.Z \ vm.graal.enabled \ vm.compiler1.enabled \ vm.compiler2.enabled \ diff --git a/test/jdk/sun/tools/jmap/BasicJMapTest.java b/test/jdk/sun/tools/jmap/BasicJMapTest.java index 078a6aefff7ac..00e8a4548ce07 100644 --- a/test/jdk/sun/tools/jmap/BasicJMapTest.java +++ b/test/jdk/sun/tools/jmap/BasicJMapTest.java @@ -34,16 +34,70 @@ import jdk.test.lib.process.ProcessTools; /* - * @test - * @summary Unit test for jmap utility + * @test id=Serial + * @requires vm.gc.Serial + * @summary Unit test for jmap utility (Serial GC) * @key intermittent * @library /test/lib * @build jdk.test.lib.hprof.* * @build jdk.test.lib.hprof.model.* * @build jdk.test.lib.hprof.parser.* * @build jdk.test.lib.hprof.util.* - * @run main/timeout=240 BasicJMapTest + * @run main/othervm/timeout=240 -XX:+UseSerialGC BasicJMapTest */ + +/* + * @test id=Parallel + * @requires vm.gc.Parallel + * @summary Unit test for jmap utility (Parallel GC) + * @key intermittent + * @library /test/lib + * @build jdk.test.lib.hprof.* + * @build jdk.test.lib.hprof.model.* + * @build jdk.test.lib.hprof.parser.* + * @build jdk.test.lib.hprof.util.* + * @run main/othervm/timeout=240 -XX:+UseParallelGC BasicJMapTest + */ + +/* + * @test id=G1 + * @requires vm.gc.G1 + * @summary Unit test for jmap utility (G1 GC) + * @key intermittent + * @library /test/lib + * @build jdk.test.lib.hprof.* + * @build jdk.test.lib.hprof.model.* + * @build jdk.test.lib.hprof.parser.* + * @build jdk.test.lib.hprof.util.* + * @run main/othervm/timeout=240 -XX:+UseG1GC BasicJMapTest + */ + +/* + * @test id=Shenandoah + * @requires vm.gc.Shenandoah + * @summary Unit test for jmap utility (Shenandoah GC) + * @key intermittent + * @library /test/lib + * @build jdk.test.lib.hprof.* + * @build jdk.test.lib.hprof.model.* + * @build jdk.test.lib.hprof.parser.* + * @build jdk.test.lib.hprof.util.* + * @run main/othervm/timeout=240 -XX:+UseShenandoahGC BasicJMapTest + */ + +/* + * @test id=Z + * @requires vm.gc.Z + * @summary Unit test for jmap utility (Z GC) + * @key intermittent + * @library /test/lib + * @build jdk.test.lib.hprof.* + * @build jdk.test.lib.hprof.model.* + * @build jdk.test.lib.hprof.parser.* + * @build jdk.test.lib.hprof.util.* + * @run main/othervm/timeout=240 -XX:+UseZGC BasicJMapTest + */ + public class BasicJMapTest { private static ProcessBuilder processBuilder = new ProcessBuilder(); From da48003abd2e88ad0d435c01a7c17073e1a61569 Mon Sep 17 00:00:00 2001 From: Hao Sun Date: Thu, 12 Nov 2020 02:30:39 +0000 Subject: [PATCH 080/124] 8255975: Fix AArch64 OpenJDK build failure with gcc-5 Reviewed-by: dholmes --- .../vm_version_linux_aarch64.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp index 199a096d7f97d..37bcbb1b4b75d 100644 --- a/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp +++ b/src/hotspot/os_cpu/linux_aarch64/vm_version_linux_aarch64.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2014, 2019, Red Hat Inc. All rights reserved. + * Copyright (c) 2006, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,19 +97,19 @@ void VM_Version::get_os_cpu_info() { uint64_t auxv = getauxval(AT_HWCAP); uint64_t auxv2 = getauxval(AT_HWCAP2); - static_assert(CPU_FP == HWCAP_FP); - static_assert(CPU_ASIMD == HWCAP_ASIMD); - static_assert(CPU_EVTSTRM == HWCAP_EVTSTRM); - static_assert(CPU_AES == HWCAP_AES); - static_assert(CPU_PMULL == HWCAP_PMULL); - static_assert(CPU_SHA1 == HWCAP_SHA1); - static_assert(CPU_SHA2 == HWCAP_SHA2); - static_assert(CPU_CRC32 == HWCAP_CRC32); - static_assert(CPU_LSE == HWCAP_ATOMICS); - static_assert(CPU_DCPOP == HWCAP_DCPOP); - static_assert(CPU_SHA3 == HWCAP_SHA3); - static_assert(CPU_SHA512 == HWCAP_SHA512); - static_assert(CPU_SVE == HWCAP_SVE); + static_assert(CPU_FP == HWCAP_FP, "Flag CPU_FP must follow Linux HWCAP"); + static_assert(CPU_ASIMD == HWCAP_ASIMD, "Flag CPU_ASIMD must follow Linux HWCAP"); + static_assert(CPU_EVTSTRM == HWCAP_EVTSTRM, "Flag CPU_EVTSTRM must follow Linux HWCAP"); + static_assert(CPU_AES == HWCAP_AES, "Flag CPU_AES must follow Linux HWCAP"); + static_assert(CPU_PMULL == HWCAP_PMULL, "Flag CPU_PMULL must follow Linux HWCAP"); + static_assert(CPU_SHA1 == HWCAP_SHA1, "Flag CPU_SHA1 must follow Linux HWCAP"); + static_assert(CPU_SHA2 == HWCAP_SHA2, "Flag CPU_SHA2 must follow Linux HWCAP"); + static_assert(CPU_CRC32 == HWCAP_CRC32, "Flag CPU_CRC32 must follow Linux HWCAP"); + static_assert(CPU_LSE == HWCAP_ATOMICS, "Flag CPU_LSE must follow Linux HWCAP"); + static_assert(CPU_DCPOP == HWCAP_DCPOP, "Flag CPU_DCPOP must follow Linux HWCAP"); + static_assert(CPU_SHA3 == HWCAP_SHA3, "Flag CPU_SHA3 must follow Linux HWCAP"); + static_assert(CPU_SHA512 == HWCAP_SHA512, "Flag CPU_SHA512 must follow Linux HWCAP"); + static_assert(CPU_SVE == HWCAP_SVE, "Flag CPU_SVE must follow Linux HWCAP"); _features = auxv & ( HWCAP_FP | HWCAP_ASIMD | From ec08b3f28d133c2cc8a9cb8396c5bfa36333ff98 Mon Sep 17 00:00:00 2001 From: Matthias Baesken Date: Thu, 12 Nov 2020 06:29:13 +0000 Subject: [PATCH 081/124] 8256188: Adjust output of make/autoconf/configure Reviewed-by: clanger, ihse --- make/autoconf/configure | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/make/autoconf/configure b/make/autoconf/configure index f635c19b644f5..7e0ece129f4df 100644 --- a/make/autoconf/configure +++ b/make/autoconf/configure @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ export LC_ALL=C if test "x$CUSTOM_CONFIG_DIR" != x; then custom_hook=$CUSTOM_CONFIG_DIR/custom-hook.m4 if test ! -e $custom_hook; then - echo "CUSTOM_CONFIG_DIR not pointing to a proper custom config dir." + echo "CUSTOM_CONFIG_DIR ($CUSTOM_CONFIG_DIR) not pointing to a proper custom config dir." echo "Error: Cannot continue" 1>&2 exit 1 fi @@ -83,6 +83,7 @@ autoconf_missing_help() { BREW="`type -p brew 2> /dev/null`" ZYPPER="`type -p zypper 2> /dev/null`" CYGWIN="`type -p cygpath 2> /dev/null`" + UNAMEOUT="`uname 2> /dev/null`" if test "x$ZYPPER" != x; then PKGHANDLER_COMMAND="sudo zypper install autoconf" @@ -92,6 +93,8 @@ autoconf_missing_help() { PKGHANDLER_COMMAND="sudo yum install autoconf" elif test "x$BREW" != x; then PKGHANDLER_COMMAND="brew install autoconf" + elif test "x$UNAMEOUT" == xAIX; then + echo "You might be able to fix this by installing autoconf from the 'AIX Toolbox for Linux Applications' (or compile it from the sources)." elif test "x$CYGWIN" != x; then PKGHANDLER_COMMAND="( cd && cmd /c setup -q -P autoconf )" fi @@ -114,7 +117,7 @@ generate_configure_script() { AUTOCONF="`type -p autoconf 2> /dev/null`" if test "x$AUTOCONF" = x; then echo - echo "Autoconf is not found on the PATH, and AUTOCONF is not set." + echo "Autoconf is not found on the PATH ($PATH), and AUTOCONF is not set." echo "You need autoconf to be able to generate a runnable configure script." autoconf_missing_help echo "Error: Cannot find autoconf" 1>&2 From 4df8abc200568f09ac714437397dbc7f521015cf Mon Sep 17 00:00:00 2001 From: Harold Seigel Date: Thu, 12 Nov 2020 13:23:57 +0000 Subject: [PATCH 082/124] 8255787: Tag container tests that use cGroups with cgroups keyword Reviewed-by: sspitsyn --- test/hotspot/jtreg/TEST.ROOT | 3 ++- .../jtreg/containers/cgroup/CgroupSubsystemFactory.java | 1 + test/hotspot/jtreg/containers/cgroup/PlainRead.java | 1 + test/hotspot/jtreg/containers/docker/TestCPUAwareness.java | 1 + test/hotspot/jtreg/containers/docker/TestCPUSets.java | 1 + test/hotspot/jtreg/containers/docker/TestJFREvents.java | 1 + test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java | 1 + test/jdk/TEST.ROOT | 3 ++- test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java | 3 ++- .../platform/cgroup/TestCgroupSubsystemController.java | 1 + .../internal/platform/cgroup/TestCgroupSubsystemFactory.java | 1 + .../jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java | 1 + .../jdk/internal/platform/docker/TestDockerMemoryMetrics.java | 3 ++- .../jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java | 1 + test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java | 3 ++- 15 files changed, 20 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index 0ef3c2d2dc4ea..dc6b0f4c435ce 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -31,7 +31,8 @@ # headful: test can be run only on headful host # intermittent: flaky test, known to fail intermittently # randomness: test uses randomness, test cases differ from run to run -keys=stress headful intermittent randomness +# cgroups: test uses cgroups +keys=stress headful intermittent randomness cgroups groups=TEST.groups TEST.quick-groups diff --git a/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java b/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java index ae38b865bf8e8..1e891b99b5aa7 100644 --- a/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java +++ b/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java @@ -23,6 +23,7 @@ /* * @test CgroupSubsystemFactory + * @key cgroups * @requires os.family == "linux" * @library /testlibrary /test/lib * @build sun.hotspot.WhiteBox diff --git a/test/hotspot/jtreg/containers/cgroup/PlainRead.java b/test/hotspot/jtreg/containers/cgroup/PlainRead.java index 74cf1cbff04b7..04c898b1935e0 100644 --- a/test/hotspot/jtreg/containers/cgroup/PlainRead.java +++ b/test/hotspot/jtreg/containers/cgroup/PlainRead.java @@ -23,6 +23,7 @@ /* * @test PlainRead + * @key cgroups * @requires os.family == "linux" * @library /testlibrary /test/lib * @build sun.hotspot.WhiteBox diff --git a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java index da35f26a8720a..c29181e8b3697 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java @@ -24,6 +24,7 @@ /* * @test + * @key cgroups * @summary Test JVM's CPU resource awareness when running inside docker container * @requires docker.support * @library /test/lib diff --git a/test/hotspot/jtreg/containers/docker/TestCPUSets.java b/test/hotspot/jtreg/containers/docker/TestCPUSets.java index 04c0b2e193b97..8d94a22cf0e53 100644 --- a/test/hotspot/jtreg/containers/docker/TestCPUSets.java +++ b/test/hotspot/jtreg/containers/docker/TestCPUSets.java @@ -24,6 +24,7 @@ /* * @test + * @key cgroups * @summary Test JVM's awareness of cpu sets (cpus and mems) * @requires docker.support * @requires (os.arch != "s390x") diff --git a/test/hotspot/jtreg/containers/docker/TestJFREvents.java b/test/hotspot/jtreg/containers/docker/TestJFREvents.java index 6b27ee03baf46..8bb2fb8a9d5d6 100644 --- a/test/hotspot/jtreg/containers/docker/TestJFREvents.java +++ b/test/hotspot/jtreg/containers/docker/TestJFREvents.java @@ -24,6 +24,7 @@ /* * @test + * @key cgroups * @summary Ensure that certain JFR events return correct results for resource values * when run inside Docker container, such as available CPU and memory. * Also make sure that PIDs are based on value provided by container, diff --git a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java index e261eebd66192..fb303cd05dff2 100644 --- a/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java +++ b/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java @@ -24,6 +24,7 @@ /* * @test + * @key cgroups * @summary Test JVM's memory resource awareness when running inside docker container * @requires docker.support * @library /test/lib diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT index f24f1fc73b6af..a922828c3ef1d 100644 --- a/test/jdk/TEST.ROOT +++ b/test/jdk/TEST.ROOT @@ -13,8 +13,9 @@ # run. Tests that are not headful are "headless". # A test flagged with key "printer" requires a printer to succeed, else # throws a PrinterException or the like. +# A test flagged with cgroups uses cgroups. -keys=2d dnd headful i18n intermittent printer randomness jfr +keys=2d dnd headful i18n intermittent printer randomness jfr cgroups # Tests that must run in othervm mode othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/swing javax/print \ diff --git a/test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java b/test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java index bbbc2af9204ab..d1f2c3932fb0e 100644 --- a/test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java +++ b/test/jdk/jdk/internal/platform/cgroup/TestCgroupMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @key cgroups * @requires os.family == "linux" * @modules java.base/jdk.internal.platform * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemController.java b/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemController.java index 2c7f06a5cc791..2db1d6cb922a5 100644 --- a/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemController.java +++ b/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemController.java @@ -43,6 +43,7 @@ /* * @test + * @key cgroups * @requires os.family == "linux" * @modules java.base/jdk.internal.platform * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemFactory.java b/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemFactory.java index 62b3d70ca5840..fed1ea560bdd8 100644 --- a/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemFactory.java +++ b/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemFactory.java @@ -42,6 +42,7 @@ /* * @test + * @key cgroups * @requires os.family == "linux" * @modules java.base/jdk.internal.platform * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java index a4a0284e3a5ea..54d495684b07d 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java @@ -32,6 +32,7 @@ /* * @test + * @key cgroups * @summary Test JDK Metrics class when running inside docker container * @requires docker.support * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java index 7e767d6fbadec..1001570da919d 100644 --- a/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ /* * @test + * @key cgroups * @summary Test JDK Metrics class when running inside docker container * @requires docker.support * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java index d152377ab899c..180da101cdf6b 100644 --- a/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java +++ b/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java @@ -23,6 +23,7 @@ /* * @test + * @key cgroups * @bug 8242480 * @requires docker.support * @library /test/lib diff --git a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java index 46df8826648ad..a54299f794d2c 100644 --- a/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java +++ b/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,7 @@ /* * @test + * @key cgroups * @summary Test JDK Metrics class when running inside docker container * @requires docker.support * @library /test/lib From bd8693a0849845d01a208555a99a7ab53dcf2a41 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Thu, 12 Nov 2020 14:05:50 +0000 Subject: [PATCH 083/124] 8256181: Remove Allocation of old generation on alternate memory devices functionality Reviewed-by: ayang, iignatyev, iklam --- src/hotspot/os/linux/os_linux.cpp | 2 +- src/hotspot/share/gc/g1/g1Arguments.cpp | 75 +-- src/hotspot/share/gc/g1/g1Arguments.hpp | 9 - src/hotspot/share/gc/g1/g1CollectedHeap.cpp | 147 +++-- src/hotspot/share/gc/g1/g1CollectedHeap.hpp | 24 +- .../share/gc/g1/g1CollectedHeap.inline.hpp | 14 +- src/hotspot/share/gc/g1/g1HeapVerifier.cpp | 8 +- .../share/gc/g1/g1HeterogeneousHeapPolicy.cpp | 58 -- .../share/gc/g1/g1HeterogeneousHeapPolicy.hpp | 47 -- .../g1/g1HeterogeneousHeapYoungGenSizer.cpp | 51 -- .../g1/g1HeterogeneousHeapYoungGenSizer.hpp | 51 -- .../share/gc/g1/g1PageBasedVirtualSpace.cpp | 6 - .../share/gc/g1/g1PageBasedVirtualSpace.hpp | 2 - src/hotspot/share/gc/g1/g1Policy.cpp | 24 +- src/hotspot/share/gc/g1/g1Policy.hpp | 15 +- .../share/gc/g1/g1RegionToSpaceMapper.cpp | 146 ----- .../share/gc/g1/g1RegionToSpaceMapper.hpp | 30 - src/hotspot/share/gc/g1/g1YoungGenSizer.cpp | 9 - src/hotspot/share/gc/g1/g1YoungGenSizer.hpp | 11 +- src/hotspot/share/gc/g1/g1_globals.hpp | 11 +- src/hotspot/share/gc/g1/heapRegionManager.cpp | 10 +- src/hotspot/share/gc/g1/heapRegionManager.hpp | 42 +- src/hotspot/share/gc/g1/heapRegionSet.cpp | 15 - src/hotspot/share/gc/g1/heapRegionSet.hpp | 2 - src/hotspot/share/gc/g1/heapRegionType.hpp | 2 +- .../gc/g1/heterogeneousHeapRegionManager.cpp | 533 ------------------ .../gc/g1/heterogeneousHeapRegionManager.hpp | 146 ----- src/hotspot/share/gc/g1/vmStructs_g1.hpp | 2 +- .../share/gc/parallel/parallelArguments.cpp | 61 -- .../share/gc/parallel/parallelArguments.hpp | 3 - .../gc/parallel/parallelScavengeHeap.cpp | 6 +- .../gc/parallel/psFileBackedVirtualspace.cpp | 83 --- .../gc/parallel/psFileBackedVirtualspace.hpp | 44 -- src/hotspot/share/gc/parallel/psOldGen.cpp | 10 +- src/hotspot/share/gc/shared/gcArguments.cpp | 25 - src/hotspot/share/gc/shared/gcArguments.hpp | 2 - src/hotspot/share/prims/whitebox.cpp | 116 ---- src/hotspot/share/runtime/arguments.cpp | 3 - src/hotspot/share/runtime/globals.hpp | 6 - .../jvm/hotspot/gc/g1/G1CollectedHeap.java | 8 +- test/hotspot/jtreg/TEST.ROOT | 1 - test/hotspot/jtreg/TEST.groups | 7 +- .../jtreg/gc/nvdimm/TestAllocateOldGenAt.java | 67 --- .../gc/nvdimm/TestAllocateOldGenAtError.java | 95 ---- .../nvdimm/TestAllocateOldGenAtMultiple.java | 73 --- .../nvdimm/TestHumongousObjectsOnNvdimm.java | 109 ---- .../gc/nvdimm/TestOldObjectsOnNvdimm.java | 109 ---- .../gc/nvdimm/TestYoungObjectsOnDram.java | 113 ---- test/jtreg-ext/requires/VMProps.java | 6 - test/lib/sun/hotspot/WhiteBox.java | 4 - 50 files changed, 132 insertions(+), 2311 deletions(-) delete mode 100644 src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.cpp delete mode 100644 src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.hpp delete mode 100644 src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.cpp delete mode 100644 src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp delete mode 100644 src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp delete mode 100644 src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp delete mode 100644 src/hotspot/share/gc/parallel/psFileBackedVirtualspace.cpp delete mode 100644 src/hotspot/share/gc/parallel/psFileBackedVirtualspace.hpp delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAt.java delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtError.java delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtMultiple.java delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestHumongousObjectsOnNvdimm.java delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestOldObjectsOnNvdimm.java delete mode 100644 test/hotspot/jtreg/gc/nvdimm/TestYoungObjectsOnDram.java diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 831b7a2fb5901..48254fe981441 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -4624,7 +4624,7 @@ jint os::init_2(void) { // initialize thread priority policy prio_init(); - if (!FLAG_IS_DEFAULT(AllocateHeapAt) || !FLAG_IS_DEFAULT(AllocateOldGenAt)) { + if (!FLAG_IS_DEFAULT(AllocateHeapAt)) { set_coredump_filter(DAX_SHARED_BIT); } diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp index 7ce19abcbe787..0d369451313a4 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.cpp +++ b/src/hotspot/share/gc/g1/g1Arguments.cpp @@ -36,9 +36,6 @@ #include "runtime/globals_extension.hpp" #include "runtime/java.hpp" -static const double MaxRamFractionForYoung = 0.8; -size_t G1Arguments::MaxMemoryForYoung; - static size_t calculate_heap_alignment(size_t space_alignment) { size_t card_table_alignment = CardTableRS::ct_max_alignment_constraint(); size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size(); @@ -194,85 +191,15 @@ void G1Arguments::initialize() { initialize_verification_types(); } -static size_t calculate_reasonable_max_memory_for_young(FormatBuffer<100> &calc_str, double max_ram_fraction_for_young) { - julong phys_mem; - // If MaxRam is specified, we use that as maximum physical memory available. - if (FLAG_IS_DEFAULT(MaxRAM)) { - phys_mem = os::physical_memory(); - calc_str.append("Physical_Memory"); - } else { - phys_mem = (julong)MaxRAM; - calc_str.append("MaxRAM"); - } - - julong reasonable_max = phys_mem; - - // If either MaxRAMFraction or MaxRAMPercentage is specified, we use them to calculate - // reasonable max size of young generation. - if (!FLAG_IS_DEFAULT(MaxRAMFraction)) { - reasonable_max = (julong)(phys_mem / MaxRAMFraction); - calc_str.append(" / MaxRAMFraction"); - } else if (!FLAG_IS_DEFAULT(MaxRAMPercentage)) { - reasonable_max = (julong)((phys_mem * MaxRAMPercentage) / 100); - calc_str.append(" * MaxRAMPercentage / 100"); - } else { - // We use our own fraction to calculate max size of young generation. - reasonable_max = phys_mem * max_ram_fraction_for_young; - calc_str.append(" * %0.2f", max_ram_fraction_for_young); - } - - return (size_t)reasonable_max; -} - void G1Arguments::initialize_heap_flags_and_sizes() { - if (AllocateOldGenAt != NULL) { - initialize_heterogeneous(); - } - GCArguments::initialize_heap_flags_and_sizes(); } -void G1Arguments::initialize_heterogeneous() { - FormatBuffer<100> calc_str(""); - - MaxMemoryForYoung = calculate_reasonable_max_memory_for_young(calc_str, MaxRamFractionForYoung); - - if (MaxNewSize > MaxMemoryForYoung) { - if (FLAG_IS_CMDLINE(MaxNewSize)) { - log_warning(gc, ergo)("Setting MaxNewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s))", - MaxMemoryForYoung, calc_str.buffer()); - } else { - log_info(gc, ergo)("Setting MaxNewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s)). " - "Dram usage can be lowered by setting MaxNewSize to a lower value", MaxMemoryForYoung, calc_str.buffer()); - } - MaxNewSize = MaxMemoryForYoung; - } - if (NewSize > MaxMemoryForYoung) { - if (FLAG_IS_CMDLINE(NewSize)) { - log_warning(gc, ergo)("Setting NewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s))", - MaxMemoryForYoung, calc_str.buffer()); - } - NewSize = MaxMemoryForYoung; - } - -} - CollectedHeap* G1Arguments::create_heap() { return new G1CollectedHeap(); } -bool G1Arguments::is_heterogeneous_heap() { - return AllocateOldGenAt != NULL; -} - -size_t G1Arguments::reasonable_max_memory_for_young() { - return MaxMemoryForYoung; -} - size_t G1Arguments::heap_reserved_size_bytes() { - return (is_heterogeneous_heap() ? 2 : 1) * MaxHeapSize; -} - -size_t G1Arguments::heap_max_size_bytes() { return MaxHeapSize; } + diff --git a/src/hotspot/share/gc/g1/g1Arguments.hpp b/src/hotspot/share/gc/g1/g1Arguments.hpp index f6796cd27de6a..3a33b6f7ddfd6 100644 --- a/src/hotspot/share/gc/g1/g1Arguments.hpp +++ b/src/hotspot/share/gc/g1/g1Arguments.hpp @@ -33,9 +33,6 @@ class CollectedHeap; class G1Arguments : public GCArguments { friend class G1HeapVerifierTest; -private: - static size_t MaxMemoryForYoung; - static void initialize_mark_stack_size(); static void initialize_verification_types(); static void parse_verification_type(const char* type); @@ -43,18 +40,12 @@ class G1Arguments : public GCArguments { virtual void initialize_alignments(); virtual void initialize_heap_flags_and_sizes(); - void initialize_heterogeneous(); - virtual void initialize(); virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); public: - // Heterogeneous heap support - static bool is_heterogeneous_heap(); - static size_t reasonable_max_memory_for_young(); static size_t heap_reserved_size_bytes(); - static size_t heap_max_size_bytes(); }; #endif // SHARE_GC_G1_G1ARGUMENTS_HPP diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp index 70b534b6ff10b..8084d9cdca937 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp @@ -184,7 +184,7 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, "the only time we use this to allocate a humongous region is " "when we are allocating a single humongous region"); - HeapRegion* res = _hrm->allocate_free_region(type, node_index); + HeapRegion* res = _hrm.allocate_free_region(type, node_index); if (res == NULL && do_expand && _expand_heap_after_alloc_failure) { // Currently, only attempts to allocate GC alloc regions set @@ -204,7 +204,7 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, // always expand the heap by an amount aligned to the heap // region size, the free list should in theory not be empty. // In either case allocate_free_region() will check for NULL. - res = _hrm->allocate_free_region(type, node_index); + res = _hrm.allocate_free_region(type, node_index); } else { _expand_heap_after_alloc_failure = false; } @@ -346,12 +346,12 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) { uint obj_regions = (uint) humongous_obj_size_in_regions(word_size); // Policy: First try to allocate a humongous object in the free list. - HeapRegion* humongous_start = _hrm->allocate_humongous(obj_regions); + HeapRegion* humongous_start = _hrm.allocate_humongous(obj_regions); if (humongous_start == NULL) { // Policy: We could not find enough regions for the humongous object in the // free list. Look through the heap to find a mix of free and uncommitted regions. // If so, expand the heap and allocate the humongous object. - humongous_start = _hrm->expand_and_allocate_humongous(obj_regions); + humongous_start = _hrm.expand_and_allocate_humongous(obj_regions); if (humongous_start != NULL) { // We managed to find a region by expanding the heap. log_debug(gc, ergo, heap)("Heap expansion (humongous allocation request). Allocation request: " SIZE_FORMAT "B", @@ -545,7 +545,7 @@ void G1CollectedHeap::end_archive_alloc_range(GrowableArray* ranges, bool G1CollectedHeap::check_archive_addresses(MemRegion* ranges, size_t count) { assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm->reserved(); + MemRegion reserved = _hrm.reserved(); for (size_t i = 0; i < count; i++) { if (!reserved.contains(ranges[i].start()) || !reserved.contains(ranges[i].last())) { return false; @@ -562,7 +562,7 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, assert(count != 0, "No MemRegions provided"); MutexLocker x(Heap_lock); - MemRegion reserved = _hrm->reserved(); + MemRegion reserved = _hrm.reserved(); HeapWord* prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; @@ -592,7 +592,7 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, // range ended, and adjust the start address so we don't try to allocate // the same region again. If the current range is entirely within that // region, skip it, just adjusting the recorded top. - HeapRegion* start_region = _hrm->addr_to_region(start_address); + HeapRegion* start_region = _hrm.addr_to_region(start_address); if ((prev_last_region != NULL) && (start_region == prev_last_region)) { start_address = start_region->end(); if (start_address > last_address) { @@ -602,12 +602,12 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, } start_region->set_top(start_address); curr_range = MemRegion(start_address, last_address + 1); - start_region = _hrm->addr_to_region(start_address); + start_region = _hrm.addr_to_region(start_address); } // Perform the actual region allocation, exiting if it fails. // Then note how much new space we have allocated. - if (!_hrm->allocate_containing_regions(curr_range, &commits, workers())) { + if (!_hrm.allocate_containing_regions(curr_range, &commits, workers())) { return false; } increase_used(word_size * HeapWordSize); @@ -619,8 +619,8 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, // Mark each G1 region touched by the range as archive, add it to // the old set, and set top. - HeapRegion* curr_region = _hrm->addr_to_region(start_address); - HeapRegion* last_region = _hrm->addr_to_region(last_address); + HeapRegion* curr_region = _hrm.addr_to_region(start_address); + HeapRegion* last_region = _hrm.addr_to_region(last_address); prev_last_region = last_region; while (curr_region != NULL) { @@ -637,7 +637,7 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, HeapRegion* next_region; if (curr_region != last_region) { top = curr_region->end(); - next_region = _hrm->next_region_in_heap(curr_region); + next_region = _hrm.next_region_in_heap(curr_region); } else { top = last_address + 1; next_region = NULL; @@ -653,7 +653,7 @@ void G1CollectedHeap::fill_archive_regions(MemRegion* ranges, size_t count) { assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm->reserved(); + MemRegion reserved = _hrm.reserved(); HeapWord *prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; @@ -673,8 +673,8 @@ void G1CollectedHeap::fill_archive_regions(MemRegion* ranges, size_t count) { "Ranges not in ascending order: " PTR_FORMAT " <= " PTR_FORMAT , p2i(start_address), p2i(prev_last_addr)); - HeapRegion* start_region = _hrm->addr_to_region(start_address); - HeapRegion* last_region = _hrm->addr_to_region(last_address); + HeapRegion* start_region = _hrm.addr_to_region(start_address); + HeapRegion* last_region = _hrm.addr_to_region(last_address); HeapWord* bottom_address = start_region->bottom(); // Check for a range beginning in the same region in which the @@ -690,7 +690,7 @@ void G1CollectedHeap::fill_archive_regions(MemRegion* ranges, size_t count) { guarantee(curr_region->is_archive(), "Expected archive region at index %u", curr_region->hrm_index()); if (curr_region != last_region) { - curr_region = _hrm->next_region_in_heap(curr_region); + curr_region = _hrm.next_region_in_heap(curr_region); } else { curr_region = NULL; } @@ -739,7 +739,7 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(ranges != NULL, "MemRegion array NULL"); assert(count != 0, "No MemRegions provided"); - MemRegion reserved = _hrm->reserved(); + MemRegion reserved = _hrm.reserved(); HeapWord* prev_last_addr = NULL; HeapRegion* prev_last_region = NULL; size_t size_used = 0; @@ -761,8 +761,8 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { size_used += ranges[i].byte_size(); prev_last_addr = last_address; - HeapRegion* start_region = _hrm->addr_to_region(start_address); - HeapRegion* last_region = _hrm->addr_to_region(last_address); + HeapRegion* start_region = _hrm.addr_to_region(start_address); + HeapRegion* last_region = _hrm.addr_to_region(last_address); // Check for ranges that start in the same G1 region in which the previous // range ended, and adjust the start address so we don't try to free @@ -773,7 +773,7 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { if (start_address > last_address) { continue; } - start_region = _hrm->addr_to_region(start_address); + start_region = _hrm.addr_to_region(start_address); } prev_last_region = last_region; @@ -788,11 +788,11 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) { curr_region->set_free(); curr_region->set_top(curr_region->bottom()); if (curr_region != last_region) { - curr_region = _hrm->next_region_in_heap(curr_region); + curr_region = _hrm.next_region_in_heap(curr_region); } else { curr_region = NULL; } - _hrm->shrink_at(curr_index, 1); + _hrm.shrink_at(curr_index, 1); uncommitted_regions++; } } @@ -1005,8 +1005,7 @@ void G1CollectedHeap::prepare_heap_for_full_collection() { // after this full GC. abandon_collection_set(collection_set()); - hrm()->remove_all_free_regions(); - hrm()->prepare_for_full_collection_start(); + _hrm.remove_all_free_regions(); } void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) { @@ -1018,8 +1017,6 @@ void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) { } void G1CollectedHeap::prepare_heap_for_mutators() { - hrm()->prepare_for_full_collection_end(); - // Delete metaspaces for unloaded class loaders and clean up loader_data graph ClassLoaderDataGraph::purge(/*at_safepoint*/true); DEBUG_ONLY(MetaspaceUtils::verify();) @@ -1058,7 +1055,7 @@ void G1CollectedHeap::abort_refinement() { } void G1CollectedHeap::verify_after_full_collection() { - _hrm->verify_optional(); + _hrm.verify_optional(); _verifier->verify_region_sets_optional(); _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull); @@ -1234,7 +1231,7 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) { if (expand(expand_bytes, _workers)) { - _hrm->verify_optional(); + _hrm.verify_optional(); _verifier->verify_region_sets_optional(); return attempt_allocation_at_safepoint(word_size, false /* expect_null_mutator_alloc_region */); @@ -1259,7 +1256,7 @@ bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, do uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes); assert(regions_to_expand > 0, "Must expand by at least one region"); - uint expanded_by = _hrm->expand_by(regions_to_expand, pretouch_workers); + uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers); if (expand_time_ms != NULL) { *expand_time_ms = (os::elapsedTime() - expand_heap_start_time_sec) * MILLIUNITS; } @@ -1274,7 +1271,7 @@ bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, do // The expansion of the virtual storage space was unsuccessful. // Let's see if it was because we ran out of swap. if (G1ExitOnExpansionFailure && - _hrm->available() >= regions_to_expand) { + _hrm.available() >= regions_to_expand) { // We had head room... vm_exit_out_of_memory(aligned_expand_bytes, OOM_MMAP_ERROR, "G1 heap expansion"); } @@ -1283,10 +1280,10 @@ bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, do } bool G1CollectedHeap::expand_single_region(uint node_index) { - uint expanded_by = _hrm->expand_on_preferred_node(node_index); + uint expanded_by = _hrm.expand_on_preferred_node(node_index); if (expanded_by == 0) { - assert(is_maximal_no_gc(), "Should be no regions left, available: %u", _hrm->available()); + assert(is_maximal_no_gc(), "Should be no regions left, available: %u", _hrm.available()); log_debug(gc, ergo, heap)("Did not expand the heap (heap already fully expanded)"); return false; } @@ -1302,7 +1299,7 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) { HeapRegion::GrainBytes); uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes); - uint num_regions_removed = _hrm->shrink_by(num_regions_to_remove); + uint num_regions_removed = _hrm.shrink_by(num_regions_to_remove); size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes; log_debug(gc, ergo, heap)("Shrink the heap. requested shrinking amount: " SIZE_FORMAT "B aligned shrinking amount: " SIZE_FORMAT "B attempted shrinking amount: " SIZE_FORMAT "B", @@ -1325,11 +1322,11 @@ void G1CollectedHeap::shrink(size_t shrink_bytes) { // Instead of tearing down / rebuilding the free lists here, we // could instead use the remove_all_pending() method on free_list to // remove only the ones that we need to remove. - hrm()->remove_all_free_regions(); + _hrm.remove_all_free_regions(); shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); - _hrm->verify_optional(); + _hrm.verify_optional(); _verifier->verify_region_sets_optional(); } @@ -1408,7 +1405,7 @@ G1CollectedHeap::G1CollectedHeap() : _bot(NULL), _listener(), _numa(G1NUMA::create()), - _hrm(NULL), + _hrm(), _allocator(NULL), _verifier(NULL), _summary_bytes_used(0), @@ -1428,7 +1425,7 @@ G1CollectedHeap::G1CollectedHeap() : _survivor(), _gc_timer_stw(new (ResourceObj::C_HEAP, mtGC) STWGCTimer()), _gc_tracer_stw(new (ResourceObj::C_HEAP, mtGC) G1NewTracer()), - _policy(G1Policy::create_policy(_gc_timer_stw)), + _policy(new G1Policy(_gc_timer_stw)), _heap_sizing_policy(NULL), _collection_set(this, _policy), _hot_card_cache(NULL), @@ -1603,12 +1600,12 @@ jint G1CollectedHeap::initialize() { // Create space mappers. size_t page_size = actual_reserved_page_size(heap_rs); G1RegionToSpaceMapper* heap_storage = - G1RegionToSpaceMapper::create_heap_mapper(heap_rs, - heap_rs.size(), - page_size, - HeapRegion::GrainBytes, - 1, - mtJavaHeap); + G1RegionToSpaceMapper::create_mapper(heap_rs, + heap_rs.size(), + page_size, + HeapRegion::GrainBytes, + 1, + mtJavaHeap); if(heap_storage == NULL) { vm_shutdown_during_initialization("Could not initialize G1 heap"); return JNI_ERR; @@ -1644,9 +1641,7 @@ jint G1CollectedHeap::initialize() { G1RegionToSpaceMapper* next_bitmap_storage = create_aux_memory_mapper("Next Bitmap", bitmap_size, G1CMBitMap::heap_map_factor()); - _hrm = HeapRegionManager::create_manager(this); - - _hrm->initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); + _hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); _card_table->initialize(cardtable_storage); // Do later initialization work for concurrent refinement. @@ -1723,7 +1718,7 @@ jint G1CollectedHeap::initialize() { // Here we allocate the dummy HeapRegion that is required by the // G1AllocRegion class. - HeapRegion* dummy_region = _hrm->get_dummy_region(); + HeapRegion* dummy_region = _hrm.get_dummy_region(); // We'll re-use the same region whether the alloc region will // require BOT updates or not and, if it doesn't, then a non-young @@ -1842,11 +1837,11 @@ SoftRefPolicy* G1CollectedHeap::soft_ref_policy() { } size_t G1CollectedHeap::capacity() const { - return _hrm->length() * HeapRegion::GrainBytes; + return _hrm.length() * HeapRegion::GrainBytes; } size_t G1CollectedHeap::unused_committed_regions_in_bytes() const { - return _hrm->total_free_bytes(); + return _hrm.total_free_bytes(); } void G1CollectedHeap::iterate_hcc_closure(G1CardTableEntryClosure* cl, uint worker_id) { @@ -1902,9 +1897,7 @@ bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { } bool G1CollectedHeap::should_upgrade_to_full_gc(GCCause::Cause cause) { - if (policy()->force_upgrade_to_full()) { - return true; - } else if (should_do_concurrent_full_gc(_gc_cause)) { + if (should_do_concurrent_full_gc(_gc_cause)) { return false; } else if (has_regions_left_for_allocation()) { return false; @@ -2238,7 +2231,7 @@ bool G1CollectedHeap::try_collect(GCCause::Cause cause) { } bool G1CollectedHeap::is_in(const void* p) const { - return is_in_reserved(p) && _hrm->is_available(addr_to_region((HeapWord*)p)); + return is_in_reserved(p) && _hrm.is_available(addr_to_region((HeapWord*)p)); } // Iteration functions. @@ -2291,18 +2284,18 @@ void G1CollectedHeap::keep_alive(oop obj) { } void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { - _hrm->iterate(cl); + _hrm.iterate(cl); } void G1CollectedHeap::heap_region_par_iterate_from_worker_offset(HeapRegionClosure* cl, HeapRegionClaimer *hrclaimer, uint worker_id) const { - _hrm->par_iterate(cl, hrclaimer, hrclaimer->offset_for_worker(worker_id)); + _hrm.par_iterate(cl, hrclaimer, hrclaimer->offset_for_worker(worker_id)); } void G1CollectedHeap::heap_region_par_iterate_from_start(HeapRegionClosure* cl, HeapRegionClaimer *hrclaimer) const { - _hrm->par_iterate(cl, hrclaimer, 0); + _hrm.par_iterate(cl, hrclaimer, 0); } void G1CollectedHeap::collection_set_iterate_all(HeapRegionClosure* cl) { @@ -2369,10 +2362,6 @@ bool G1CollectedHeap::supports_concurrent_gc_breakpoints() const { return true; } -bool G1CollectedHeap::is_heterogeneous_heap() const { - return G1Arguments::is_heterogeneous_heap(); -} - bool G1CollectedHeap::is_archived_object(oop object) const { return object != NULL && heap_region_containing(object)->is_archive(); } @@ -2421,13 +2410,11 @@ void G1CollectedHeap::print_heap_regions() const { void G1CollectedHeap::print_on(outputStream* st) const { size_t heap_used = Heap_lock->owned_by_self() ? used() : used_unlocked(); st->print(" %-20s", "garbage-first heap"); - if (_hrm != NULL) { - st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", - capacity()/K, heap_used/K); - st->print(" [" PTR_FORMAT ", " PTR_FORMAT ")", - p2i(_hrm->reserved().start()), - p2i(_hrm->reserved().end())); - } + st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", + capacity()/K, heap_used/K); + st->print(" [" PTR_FORMAT ", " PTR_FORMAT ")", + p2i(_hrm.reserved().start()), + p2i(_hrm.reserved().end())); st->cr(); st->print(" region size " SIZE_FORMAT "K, ", HeapRegion::GrainBytes / K); uint young_regions = young_regions_count(); @@ -2442,7 +2429,7 @@ void G1CollectedHeap::print_on(outputStream* st) const { st->print(" remaining free region(s) on each NUMA node: "); const int* node_ids = _numa->node_ids(); for (uint node_index = 0; node_index < num_nodes; node_index++) { - uint num_free_regions = (_hrm != NULL ? _hrm->num_free_regions(node_index) : 0); + uint num_free_regions = _hrm.num_free_regions(node_index); st->print("%d=%u ", node_ids[node_index], num_free_regions); } st->cr(); @@ -2451,10 +2438,6 @@ void G1CollectedHeap::print_on(outputStream* st) const { } void G1CollectedHeap::print_regions_on(outputStream* st) const { - if (_hrm == NULL) { - return; - } - st->print_cr("Heap Regions: E=young(eden), S=young(survivor), O=old, " "HS=humongous(starts), HC=humongous(continues), " "CS=collection set, F=free, " @@ -2468,10 +2451,8 @@ void G1CollectedHeap::print_extended_on(outputStream* st) const { print_on(st); // Print the per-region information. - if (_hrm != NULL) { - st->cr(); - print_regions_on(st); - } + st->cr(); + print_regions_on(st); } void G1CollectedHeap::print_on_error(outputStream* st) const { @@ -3047,7 +3028,7 @@ void G1CollectedHeap::do_collection_pause_at_safepoint_helper(double target_paus policy()->print_phases(); heap_transition.print(); - _hrm->verify_optional(); + _hrm.verify_optional(); _verifier->verify_region_sets_optional(); TASKQUEUE_STATS_ONLY(print_taskqueue_stats()); @@ -4009,7 +3990,7 @@ void G1CollectedHeap::record_obj_copy_mem_stats() { void G1CollectedHeap::free_region(HeapRegion* hr, FreeRegionList* free_list) { assert(!hr->is_free(), "the region should not be free"); assert(!hr->is_empty(), "the region should not be empty"); - assert(_hrm->is_available(hr->hrm_index()), "region should be committed"); + assert(_hrm.is_available(hr->hrm_index()), "region should be committed"); if (G1VerifyBitmaps) { MemRegion mr(hr->bottom(), hr->end()); @@ -4054,7 +4035,7 @@ void G1CollectedHeap::prepend_to_freelist(FreeRegionList* list) { assert(list != NULL, "list can't be null"); if (!list->is_empty()) { MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); - _hrm->insert_list_into_free_list(list); + _hrm.insert_list_into_free_list(list); } } @@ -4335,7 +4316,7 @@ void G1CollectedHeap::free_collection_set(G1CollectionSet* collection_set, G1Eva phase_times()->record_total_free_cset_time_ms((free_cset_end_time - free_cset_start_time).seconds() * 1000.0); // Now rebuild the free region list. - hrm()->rebuild_free_list(workers()); + _hrm.rebuild_free_list(workers()); phase_times()->record_total_rebuild_freelist_time_ms((Ticks::now() - free_cset_end_time).seconds() * 1000.0); collection_set->clear(); @@ -4638,7 +4619,7 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) { _survivor.clear(); } - RebuildRegionSetsClosure cl(free_list_only, &_old_set, _hrm); + RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrm); heap_region_iterate(&cl); if (!free_list_only) { @@ -4755,14 +4736,14 @@ void G1CollectedHeap::retire_gc_alloc_region(HeapRegion* alloc_region, HeapRegion* G1CollectedHeap::alloc_highest_free_region() { bool expanded = false; - uint index = _hrm->find_highest_free(&expanded); + uint index = _hrm.find_highest_free(&expanded); if (index != G1_NO_HRM_INDEX) { if (expanded) { log_debug(gc, ergo, heap)("Attempt heap expansion (requested address range outside heap bounds). region size: " SIZE_FORMAT "B", HeapRegion::GrainWords * HeapWordSize); } - return _hrm->allocate_free_regions_starting_at(index, 1); + return _hrm.allocate_free_regions_starting_at(index, 1); } return NULL; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp index 3048f9ceae104..080cbc665dff8 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp @@ -47,7 +47,6 @@ #include "gc/g1/g1YCTypes.hpp" #include "gc/g1/heapRegionManager.hpp" #include "gc/g1/heapRegionSet.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" #include "gc/shared/barrierSet.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcHeapSummary.hpp" @@ -194,7 +193,7 @@ class G1CollectedHeap : public CollectedHeap { G1NUMA* _numa; // The sequence of all heap regions in the heap. - HeapRegionManager* _hrm; + HeapRegionManager _hrm; // Manages all allocations with regions except humongous object allocations. G1Allocator* _allocator; @@ -1016,8 +1015,6 @@ class G1CollectedHeap : public CollectedHeap { inline G1GCPhaseTimes* phase_times() const; - HeapRegionManager* hrm() const { return _hrm; } - const G1CollectionSet* collection_set() const { return &_collection_set; } G1CollectionSet* collection_set() { return &_collection_set; } @@ -1063,7 +1060,7 @@ class G1CollectedHeap : public CollectedHeap { // But G1CollectedHeap doesn't yet support this. virtual bool is_maximal_no_gc() const { - return _hrm->available() == 0; + return _hrm.available() == 0; } // Returns whether there are any regions left in the heap for allocation. @@ -1072,23 +1069,23 @@ class G1CollectedHeap : public CollectedHeap { } // The current number of regions in the heap. - uint num_regions() const { return _hrm->length(); } + uint num_regions() const { return _hrm.length(); } // The max number of regions reserved for the heap. Except for static array // sizing purposes you probably want to use max_regions(). - uint max_reserved_regions() const { return _hrm->reserved_length(); } + uint max_reserved_regions() const { return _hrm.reserved_length(); } // Max number of regions that can be committed. - uint max_regions() const { return _hrm->max_length(); } + uint max_regions() const { return _hrm.max_length(); } // The number of regions that are completely free. - uint num_free_regions() const { return _hrm->num_free_regions(); } + uint num_free_regions() const { return _hrm.num_free_regions(); } // The number of regions that can be allocated into. - uint num_free_or_available_regions() const { return num_free_regions() + _hrm->available(); } + uint num_free_or_available_regions() const { return num_free_regions() + _hrm.available(); } MemoryUsage get_auxiliary_data_memory_usage() const { - return _hrm->get_auxiliary_data_memory_usage(); + return _hrm.get_auxiliary_data_memory_usage(); } // The number of regions that are not completely free. @@ -1096,7 +1093,7 @@ class G1CollectedHeap : public CollectedHeap { #ifdef ASSERT bool is_on_master_free_list(HeapRegion* hr) { - return _hrm->is_free(hr); + return _hrm.is_free(hr); } #endif // ASSERT @@ -1151,7 +1148,7 @@ class G1CollectedHeap : public CollectedHeap { inline G1HeapRegionAttr region_attr(uint idx) const; MemRegion reserved() const { - return _hrm->reserved(); + return _hrm.reserved(); } bool is_in_reserved(const void* addr) const { @@ -1414,7 +1411,6 @@ class G1CollectedHeap : public CollectedHeap { // WhiteBox testing support. virtual bool supports_concurrent_gc_breakpoints() const; - bool is_heterogeneous_heap() const; virtual WorkGang* safepoint_workers() { return _workers; } diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp index 47b394d36c187..797da6903bf01 100644 --- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp +++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp @@ -64,13 +64,13 @@ size_t G1CollectedHeap::desired_plab_sz(G1HeapRegionAttr dest) { // Inline functions for G1CollectedHeap // Return the region with the given index. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm->at(index); } +inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); } // Return the region with the given index, or NULL if unmapped. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm->at_or_null(index); } +inline HeapRegion* G1CollectedHeap::region_at_or_null(uint index) const { return _hrm.at_or_null(index); } inline HeapRegion* G1CollectedHeap::next_region_in_humongous(HeapRegion* hr) const { - return _hrm->next_region_in_humongous(hr); + return _hrm.next_region_in_humongous(hr); } inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { @@ -81,7 +81,7 @@ inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { } inline HeapWord* G1CollectedHeap::bottom_addr_for_region(uint index) const { - return _hrm->reserved().start() + index * HeapRegion::GrainWords; + return _hrm.reserved().start() + index * HeapRegion::GrainWords; } template @@ -90,7 +90,7 @@ inline HeapRegion* G1CollectedHeap::heap_region_containing(const T addr) const { assert(is_in_reserved((const void*) addr), "Address " PTR_FORMAT " is outside of the heap ranging from [" PTR_FORMAT " to " PTR_FORMAT ")", p2i((void*)addr), p2i(reserved().start()), p2i(reserved().end())); - return _hrm->addr_to_region((HeapWord*)(void*) addr); + return _hrm.addr_to_region((HeapWord*)(void*) addr); } template @@ -290,12 +290,12 @@ inline bool G1CollectedHeap::is_obj_dead_full(const oop obj) const { } inline void G1CollectedHeap::set_humongous_reclaim_candidate(uint region, bool value) { - assert(_hrm->at(region)->is_starts_humongous(), "Must start a humongous object"); + assert(_hrm.at(region)->is_starts_humongous(), "Must start a humongous object"); _humongous_reclaim_candidates.set_candidate(region, value); } inline bool G1CollectedHeap::is_humongous_reclaim_candidate(uint region) { - assert(_hrm->at(region)->is_starts_humongous(), "Must start a humongous object"); + assert(_hrm.at(region)->is_starts_humongous(), "Must start a humongous object"); return _humongous_reclaim_candidates.is_candidate(region); } diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp index d96bd5c211cea..ec97cb463d3b2 100644 --- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp +++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp @@ -597,14 +597,14 @@ void G1HeapVerifier::verify_region_sets() { assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); // First, check the explicit lists. - _g1h->_hrm->verify(); + _g1h->_hrm.verify(); // Finally, make sure that the region accounting in the lists is // consistent with what we see in the heap. - VerifyRegionListsClosure cl(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, _g1h->_hrm); + VerifyRegionListsClosure cl(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, &_g1h->_hrm); _g1h->heap_region_iterate(&cl); - cl.verify_counts(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, _g1h->_hrm); + cl.verify_counts(&_g1h->_old_set, &_g1h->_archive_set, &_g1h->_humongous_set, &_g1h->_hrm); } void G1HeapVerifier::prepare_for_verify() { @@ -845,7 +845,7 @@ class G1CheckRegionAttrTableClosure : public HeapRegionClosure { bool G1HeapVerifier::check_region_attr_table() { G1CheckRegionAttrTableClosure cl; - _g1h->_hrm->iterate(&cl); + _g1h->_hrm.iterate(&cl); return !cl.failures(); } #endif // PRODUCT diff --git a/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.cpp b/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.cpp deleted file mode 100644 index 92aa996ef5c6b..0000000000000 --- a/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc/g1/g1CollectedHeap.hpp" -#include "gc/g1/g1HeterogeneousHeapPolicy.hpp" -#include "gc/g1/g1Policy.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" - -G1HeterogeneousHeapPolicy::G1HeterogeneousHeapPolicy(STWGCTimer* gc_timer) : - G1Policy(gc_timer), _manager(NULL) {} - -// We call the super class init(), after which we provision young_list_target_length() regions in dram. -void G1HeterogeneousHeapPolicy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) { - G1Policy::init(g1h, collection_set); - _manager = HeterogeneousHeapRegionManager::manager(); - _manager->adjust_dram_regions((uint)young_list_target_length(), G1CollectedHeap::heap()->workers()); -} - -// After a collection pause, young list target length is updated. So we need to make sure we have enough regions in dram for young gen. -void G1HeterogeneousHeapPolicy::record_collection_pause_end(double pause_time_ms, bool concurrent_operation_is_full_mark) { - G1Policy::record_collection_pause_end(pause_time_ms, concurrent_operation_is_full_mark); - _manager->adjust_dram_regions((uint)young_list_target_length(), G1CollectedHeap::heap()->workers()); -} - -// After a full collection, young list target length is updated. So we need to make sure we have enough regions in dram for young gen. -void G1HeterogeneousHeapPolicy::record_full_collection_end() { - G1Policy::record_full_collection_end(); - _manager->adjust_dram_regions((uint)young_list_target_length(), G1CollectedHeap::heap()->workers()); -} - -bool G1HeterogeneousHeapPolicy::force_upgrade_to_full() { - if (_manager->has_borrowed_regions()) { - return true; - } - return false; -} diff --git a/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.hpp b/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.hpp deleted file mode 100644 index 6051fc532b819..0000000000000 --- a/src/hotspot/share/gc/g1/g1HeterogeneousHeapPolicy.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_G1HETEROGENEOUSHEAPPOLICY_HPP -#define SHARE_GC_G1_G1HETEROGENEOUSHEAPPOLICY_HPP - -#include "gc/g1/g1Policy.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" - -class G1HeterogeneousHeapPolicy : public G1Policy { - // Stash a pointer to the hrm. - HeterogeneousHeapRegionManager* _manager; - -public: - G1HeterogeneousHeapPolicy(STWGCTimer* gc_timer); - - // initialize policy - virtual void init(G1CollectedHeap* g1h, G1CollectionSet* collection_set); - // Record end of an evacuation pause. - virtual void record_collection_pause_end(double pause_time_ms, bool concurrent_operation_is_full_mark); - // Record the end of full collection. - virtual void record_full_collection_end(); - - virtual bool force_upgrade_to_full(); -}; -#endif // SHARE_GC_G1_G1HETEROGENEOUSHEAPPOLICY_HPP diff --git a/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.cpp b/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.cpp deleted file mode 100644 index c1d261ce78034..0000000000000 --- a/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc/g1/g1Arguments.hpp" -#include "gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp" -#include "gc/g1/heapRegion.hpp" - -G1HeterogeneousHeapYoungGenSizer::G1HeterogeneousHeapYoungGenSizer() : G1YoungGenSizer() { - // will be used later when min and max young size is calculated. - _max_young_length = (uint)(G1Arguments::reasonable_max_memory_for_young() / HeapRegion::GrainBytes); -} - -// Since heap is sized potentially to larger value accounting for dram + nvdimm, we need to limit -// max young gen size to the available dram. -// Call parent class method first and then adjust sizes based on available dram -void G1HeterogeneousHeapYoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) { - G1YoungGenSizer::adjust_max_new_size(number_of_heap_regions); - adjust_lengths_based_on_dram_memory(); -} - -void G1HeterogeneousHeapYoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) { - G1YoungGenSizer::heap_size_changed(new_number_of_heap_regions); - adjust_lengths_based_on_dram_memory(); -} - -void G1HeterogeneousHeapYoungGenSizer::adjust_lengths_based_on_dram_memory() { - _min_desired_young_length = MIN2(_min_desired_young_length, _max_young_length); - _max_desired_young_length = MIN2(_max_desired_young_length, _max_young_length); -} diff --git a/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp b/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp deleted file mode 100644 index 6f676a425ca5b..0000000000000 --- a/src/hotspot/share/gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_G1HETEROGENEOUSHEAPYOUNGGENSIZER_HPP -#define SHARE_GC_G1_G1HETEROGENEOUSHEAPYOUNGGENSIZER_HPP - -#include "gc/g1/g1YoungGenSizer.hpp" - -// This class prevents the size of young generation of G1 heap to exceed dram -// memory available. If set on command line, MaxRAM and MaxRAMFraction/MaxRAMPercentage -// are used to determine the maximum size that young generation can grow. -// Else we set the maximum size to 80% of dram available in the system. - -class G1HeterogeneousHeapYoungGenSizer : public G1YoungGenSizer { -private: - // maximum no of regions that young generation can grow to. Calculated in constructor. - uint _max_young_length; - void adjust_lengths_based_on_dram_memory(); - -public: - G1HeterogeneousHeapYoungGenSizer(); - - // Calculate the maximum length of the young gen given the number of regions - // depending on the sizing algorithm. - virtual void adjust_max_new_size(uint number_of_heap_regions); - - virtual void heap_size_changed(uint new_number_of_heap_regions); -}; - -#endif // SHARE_GC_G1_G1HETEROGENEOUSHEAPYOUNGGENSIZER_HPP diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp index 58eea806fbb69..4f0adbe2334b9 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp @@ -101,12 +101,6 @@ size_t G1PageBasedVirtualSpace::uncommitted_size() const { return reserved_size() - committed_size(); } -void G1PageBasedVirtualSpace::commit_and_set_special() { - commit_internal(addr_to_page_index(_low_boundary), addr_to_page_index(_high_boundary)); - _special = true; - _dirty.initialize(reserved_size()/_page_size); -} - size_t G1PageBasedVirtualSpace::addr_to_page_index(char* addr) const { return (addr - _low_boundary) / _page_size; } diff --git a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp index cdcea4f28fe82..0bfb8c4dd775e 100644 --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.hpp @@ -134,8 +134,6 @@ class G1PageBasedVirtualSpace { // Memory left to use/expand in this virtual space. size_t uncommitted_size() const; - void commit_and_set_special(); - bool contains(const void* p) const; MemRegion reserved() { diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp index 1354296cb9bb2..0fb2e40e36d4f 100644 --- a/src/hotspot/share/gc/g1/g1Policy.cpp +++ b/src/hotspot/share/gc/g1/g1Policy.cpp @@ -33,7 +33,6 @@ #include "gc/g1/g1ConcurrentRefine.hpp" #include "gc/g1/g1ConcurrentRefineStats.hpp" #include "gc/g1/g1CollectionSetChooser.hpp" -#include "gc/g1/g1HeterogeneousHeapPolicy.hpp" #include "gc/g1/g1HotCardCache.hpp" #include "gc/g1/g1IHOPControl.hpp" #include "gc/g1/g1GCPhaseTimes.hpp" @@ -68,7 +67,7 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : _survivor_surv_rate_group(new G1SurvRateGroup()), _reserve_factor((double) G1ReservePercent / 100.0), _reserve_regions(0), - _young_gen_sizer(G1YoungGenSizer::create_gen_sizer()), + _young_gen_sizer(), _free_regions_at_end_of_collection(0), _rs_length(0), _rs_length_prediction(0), @@ -88,15 +87,6 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) : G1Policy::~G1Policy() { delete _ihop_control; - delete _young_gen_sizer; -} - -G1Policy* G1Policy::create_policy(STWGCTimer* gc_timer_stw) { - if (G1Arguments::is_heterogeneous_heap()) { - return new G1HeterogeneousHeapPolicy(gc_timer_stw); - } else { - return new G1Policy(gc_timer_stw); - } } G1CollectorState* G1Policy::collector_state() const { return _g1h->collector_state(); } @@ -108,9 +98,9 @@ void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) { assert(Heap_lock->owned_by_self(), "Locking discipline."); if (!use_adaptive_young_list_length()) { - _young_list_fixed_length = _young_gen_sizer->min_desired_young_length(); + _young_list_fixed_length = _young_gen_sizer.min_desired_young_length(); } - _young_gen_sizer->adjust_max_new_size(_g1h->max_regions()); + _young_gen_sizer.adjust_max_new_size(_g1h->max_regions()); _free_regions_at_end_of_collection = _g1h->num_free_regions(); @@ -184,7 +174,7 @@ void G1Policy::record_new_heap_size(uint new_number_of_regions) { // smaller than 1.0) we'll get 1. _reserve_regions = (uint) ceil(reserve_regions_d); - _young_gen_sizer->heap_size_changed(new_number_of_regions); + _young_gen_sizer.heap_size_changed(new_number_of_regions); _ihop_control->update_target_occupancy(new_number_of_regions * HeapRegion::GrainBytes); } @@ -203,14 +193,14 @@ uint G1Policy::calculate_young_list_desired_min_length(uint base_min_length) con } desired_min_length += base_min_length; // make sure we don't go below any user-defined minimum bound - return MAX2(_young_gen_sizer->min_desired_young_length(), desired_min_length); + return MAX2(_young_gen_sizer.min_desired_young_length(), desired_min_length); } uint G1Policy::calculate_young_list_desired_max_length() const { // Here, we might want to also take into account any additional // constraints (i.e., user-defined minimum bound). Currently, we // effectively don't set this bound. - return _young_gen_sizer->max_desired_young_length(); + return _young_gen_sizer.max_desired_young_length(); } uint G1Policy::update_young_list_max_and_target_length() { @@ -976,7 +966,7 @@ bool G1Policy::can_expand_young_list() const { } bool G1Policy::use_adaptive_young_list_length() const { - return _young_gen_sizer->use_adaptive_young_list_length(); + return _young_gen_sizer.use_adaptive_young_list_length(); } size_t G1Policy::desired_survivor_size(uint max_regions) const { diff --git a/src/hotspot/share/gc/g1/g1Policy.hpp b/src/hotspot/share/gc/g1/g1Policy.hpp index 6062213e3f82f..d3d30805c5d8b 100644 --- a/src/hotspot/share/gc/g1/g1Policy.hpp +++ b/src/hotspot/share/gc/g1/g1Policy.hpp @@ -49,7 +49,6 @@ class G1CollectionSetChooser; class G1IHOPControl; class G1Analytics; class G1SurvivorRegions; -class G1YoungGenSizer; class GCPolicyCounters; class STWGCTimer; @@ -95,7 +94,7 @@ class G1Policy: public CHeapObj { // for the first time during initialization. uint _reserve_regions; - G1YoungGenSizer* _young_gen_sizer; + G1YoungGenSizer _young_gen_sizer; uint _free_regions_at_end_of_collection; @@ -302,8 +301,6 @@ class G1Policy: public CHeapObj { virtual ~G1Policy(); - static G1Policy* create_policy(STWGCTimer* gc_timer_stw); - G1CollectorState* collector_state() const; G1GCPhaseTimes* phase_times() const; @@ -316,7 +313,7 @@ class G1Policy: public CHeapObj { // This should be called after the heap is resized. void record_new_heap_size(uint new_number_of_regions); - virtual void init(G1CollectedHeap* g1h, G1CollectionSet* collection_set); + void init(G1CollectedHeap* g1h, G1CollectionSet* collection_set); void note_gc_start(); @@ -328,11 +325,11 @@ class G1Policy: public CHeapObj { // Record the start and end of an evacuation pause. void record_collection_pause_start(double start_time_sec); - virtual void record_collection_pause_end(double pause_time_ms, bool concurrent_operation_is_full_mark); + void record_collection_pause_end(double pause_time_ms, bool concurrent_operation_is_full_mark); // Record the start and end of a full collection. void record_full_collection_start(); - virtual void record_full_collection_end(); + void record_full_collection_end(); // Must currently be called while the world is stopped. void record_concurrent_mark_init_end(); @@ -449,10 +446,6 @@ class G1Policy: public CHeapObj { void update_max_gc_locker_expansion(); void update_survivors_policy(); - - virtual bool force_upgrade_to_full() { - return false; - } }; #endif // SHARE_GC_G1_G1POLICY_HPP diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp index 69e0778a9045f..94d44e8ab4ac3 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.cpp @@ -26,15 +26,11 @@ #include "gc/g1/g1BiasedArray.hpp" #include "gc/g1/g1NUMA.hpp" #include "gc/g1/g1RegionToSpaceMapper.hpp" -#include "logging/log.hpp" #include "memory/allocation.inline.hpp" #include "memory/virtualspace.hpp" -#include "runtime/java.hpp" -#include "runtime/os.inline.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" -#include "utilities/formatBuffer.hpp" #include "utilities/powerOfTwo.hpp" G1RegionToSpaceMapper::G1RegionToSpaceMapper(ReservedSpace rs, @@ -212,144 +208,6 @@ void G1RegionToSpaceMapper::fire_on_commit(uint start_idx, size_t num_regions, b } } -static bool map_nvdimm_space(ReservedSpace rs) { - assert(AllocateOldGenAt != NULL, ""); - int _backing_fd = os::create_file_for_heap(AllocateOldGenAt); - if (_backing_fd == -1) { - log_error(gc, init)("Could not create file for Old generation at location %s", AllocateOldGenAt); - return false; - } - // commit this memory in nv-dimm - char* ret = os::attempt_map_memory_to_file_at(rs.base(), rs.size(), _backing_fd); - - if (ret != rs.base()) { - if (ret != NULL) { - os::unmap_memory(rs.base(), rs.size()); - } - log_error(gc, init)("Error in mapping Old Gen to given AllocateOldGenAt = %s", AllocateOldGenAt); - os::close(_backing_fd); - return false; - } - - os::close(_backing_fd); - return true; -} - -G1RegionToHeteroSpaceMapper::G1RegionToHeteroSpaceMapper(ReservedSpace rs, - size_t actual_size, - size_t page_size, - size_t alloc_granularity, - size_t commit_factor, - MEMFLAGS type) : - G1RegionToSpaceMapper(rs, actual_size, page_size, alloc_granularity, commit_factor, type), - _rs(rs), - _dram_mapper(NULL), - _num_committed_dram(0), - _num_committed_nvdimm(0), - _start_index_of_dram(0), - _page_size(page_size), - _commit_factor(commit_factor), - _type(type) { - assert(actual_size == 2 * MaxHeapSize, "For 2-way heterogenuous heap, reserved space is two times MaxHeapSize"); -} - -bool G1RegionToHeteroSpaceMapper::initialize() { - // Since we need to re-map the reserved space - 'Xmx' to nv-dimm and 'Xmx' to dram, we need to release the reserved memory first. - // Because on some OSes (e.g. Windows) you cannot do a file mapping on memory reserved with regular mapping. - os::release_memory(_rs.base(), _rs.size()); - // First half of size Xmx is for nv-dimm. - ReservedSpace rs_nvdimm = _rs.first_part(MaxHeapSize); - assert(rs_nvdimm.base() == _rs.base(), "We should get the same base address"); - - // Second half of reserved memory is mapped to dram. - ReservedSpace rs_dram = _rs.last_part(MaxHeapSize); - - assert(rs_dram.size() == rs_nvdimm.size() && rs_nvdimm.size() == MaxHeapSize, "They all should be same"); - - // Reserve dram memory - char* base = os::attempt_reserve_memory_at(rs_dram.base(), rs_dram.size()); - if (base != rs_dram.base()) { - if (base != NULL) { - os::release_memory(base, rs_dram.size()); - } - log_error(gc, init)("Error in re-mapping memory on dram during G1 heterogenous memory initialization"); - return false; - } - - // We reserve and commit this entire space to NV-DIMM. - if (!map_nvdimm_space(rs_nvdimm)) { - log_error(gc, init)("Error in re-mapping memory to nv-dimm during G1 heterogenous memory initialization"); - return false; - } - - if (_region_granularity >= (_page_size * _commit_factor)) { - _dram_mapper = new G1RegionsLargerThanCommitSizeMapper(rs_dram, rs_dram.size(), _page_size, _region_granularity, _commit_factor, _type); - } else { - _dram_mapper = new G1RegionsSmallerThanCommitSizeMapper(rs_dram, rs_dram.size(), _page_size, _region_granularity, _commit_factor, _type); - } - - _start_index_of_dram = (uint)(rs_nvdimm.size() / _region_granularity); - return true; -} - -void G1RegionToHeteroSpaceMapper::commit_regions(uint start_idx, size_t num_regions, WorkGang* pretouch_gang) { - uint end_idx = (start_idx + (uint)num_regions - 1); - - uint num_dram = end_idx >= _start_index_of_dram ? MIN2((end_idx - _start_index_of_dram + 1), (uint)num_regions) : 0; - uint num_nvdimm = (uint)num_regions - num_dram; - - if (num_nvdimm > 0) { - // We do not need to commit nv-dimm regions, since they are committed in the beginning. - _num_committed_nvdimm += num_nvdimm; - } - if (num_dram > 0) { - _dram_mapper->commit_regions(start_idx > _start_index_of_dram ? (start_idx - _start_index_of_dram) : 0, num_dram, pretouch_gang); - _num_committed_dram += num_dram; - } -} - -void G1RegionToHeteroSpaceMapper::uncommit_regions(uint start_idx, size_t num_regions) { - uint end_idx = (start_idx + (uint)num_regions - 1); - uint num_dram = end_idx >= _start_index_of_dram ? MIN2((end_idx - _start_index_of_dram + 1), (uint)num_regions) : 0; - uint num_nvdimm = (uint)num_regions - num_dram; - - if (num_nvdimm > 0) { - // We do not uncommit memory for nv-dimm regions. - _num_committed_nvdimm -= num_nvdimm; - } - - if (num_dram > 0) { - _dram_mapper->uncommit_regions(start_idx > _start_index_of_dram ? (start_idx - _start_index_of_dram) : 0, num_dram); - _num_committed_dram -= num_dram; - } -} - -uint G1RegionToHeteroSpaceMapper::num_committed_dram() const { - return _num_committed_dram; -} - -uint G1RegionToHeteroSpaceMapper::num_committed_nvdimm() const { - return _num_committed_nvdimm; -} - -G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_heap_mapper(ReservedSpace rs, - size_t actual_size, - size_t page_size, - size_t region_granularity, - size_t commit_factor, - MEMFLAGS type) { - if (AllocateOldGenAt != NULL) { - G1RegionToHeteroSpaceMapper* mapper = new G1RegionToHeteroSpaceMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); - if (!mapper->initialize()) { - delete mapper; - return NULL; - } - return (G1RegionToSpaceMapper*)mapper; - } else { - return create_mapper(rs, actual_size, page_size, region_granularity, commit_factor, type); - } -} - G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_mapper(ReservedSpace rs, size_t actual_size, size_t page_size, @@ -362,7 +220,3 @@ G1RegionToSpaceMapper* G1RegionToSpaceMapper::create_mapper(ReservedSpace rs, return new G1RegionsSmallerThanCommitSizeMapper(rs, actual_size, page_size, region_granularity, commit_factor, type); } } - -void G1RegionToSpaceMapper::commit_and_set_special() { - _storage.commit_and_set_special(); -} diff --git a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp index 7a76d4eea9385..931f4beb00681 100644 --- a/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp +++ b/src/hotspot/share/gc/g1/g1RegionToSpaceMapper.hpp @@ -68,7 +68,6 @@ class G1RegionToSpaceMapper : public CHeapObj { virtual ~G1RegionToSpaceMapper() {} - void commit_and_set_special(); virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL) = 0; virtual void uncommit_regions(uint start_idx, size_t num_regions = 1) = 0; @@ -86,35 +85,6 @@ class G1RegionToSpaceMapper : public CHeapObj { size_t region_granularity, size_t byte_translation_factor, MEMFLAGS type); - - static G1RegionToSpaceMapper* create_heap_mapper(ReservedSpace rs, - size_t actual_size, - size_t page_size, - size_t region_granularity, - size_t byte_translation_factor, - MEMFLAGS type); }; -// G1RegionToSpaceMapper implementation where -// part of space is mapped to dram and part to nv-dimm -class G1RegionToHeteroSpaceMapper : public G1RegionToSpaceMapper { -private: - ReservedSpace _rs; - G1RegionToSpaceMapper* _dram_mapper; - uint _num_committed_dram; - uint _num_committed_nvdimm; - uint _start_index_of_dram; - size_t _page_size; - size_t _commit_factor; - MEMFLAGS _type; - -public: - G1RegionToHeteroSpaceMapper(ReservedSpace rs, size_t used_size, size_t page_size, size_t region_granularity, size_t commit_factor, MEMFLAGS type); - bool initialize(); - uint num_committed_dram() const; - uint num_committed_nvdimm() const; - - virtual void commit_regions(uint start_idx, size_t num_regions = 1, WorkGang* pretouch_workers = NULL); - virtual void uncommit_regions(uint start_idx, size_t num_regions = 1); -}; #endif // SHARE_GC_G1_G1REGIONTOSPACEMAPPER_HPP diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp index 7dea6238b6d8b..13bbb22252cb9 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.cpp @@ -24,7 +24,6 @@ #include "precompiled.hpp" #include "gc/g1/g1Arguments.hpp" -#include "gc/g1/g1HeterogeneousHeapYoungGenSizer.hpp" #include "gc/g1/g1YoungGenSizer.hpp" #include "gc/g1/heapRegion.hpp" #include "logging/log.hpp" @@ -130,11 +129,3 @@ void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) { recalculate_min_max_young_length(new_number_of_heap_regions, &_min_desired_young_length, &_max_desired_young_length); } - -G1YoungGenSizer* G1YoungGenSizer::create_gen_sizer() { - if (G1Arguments::is_heterogeneous_heap()) { - return new G1HeterogeneousHeapYoungGenSizer(); - } else { - return new G1YoungGenSizer(); - } -} diff --git a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp index 9c574642568f6..9a4ecb4462459 100644 --- a/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp +++ b/src/hotspot/share/gc/g1/g1YoungGenSizer.hpp @@ -63,7 +63,7 @@ // // NewSize and MaxNewSize override NewRatio. So, NewRatio is ignored if it is // combined with either NewSize or MaxNewSize. (A warning message is printed.) -class G1YoungGenSizer : public CHeapObj { +class G1YoungGenSizer { private: enum SizerKind { SizerDefaults, @@ -78,6 +78,9 @@ class G1YoungGenSizer : public CHeapObj { // true otherwise. bool _use_adaptive_sizing; + uint _min_desired_young_length; + uint _max_desired_young_length; + uint calculate_default_min_length(uint new_number_of_heap_regions); uint calculate_default_max_length(uint new_number_of_heap_regions); @@ -85,10 +88,6 @@ class G1YoungGenSizer : public CHeapObj { // given the number of heap regions depending on the kind of sizing algorithm. void recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length); -protected: - uint _min_desired_young_length; - uint _max_desired_young_length; - public: G1YoungGenSizer(); // Calculate the maximum length of the young gen given the number of regions @@ -106,8 +105,6 @@ class G1YoungGenSizer : public CHeapObj { bool use_adaptive_young_list_length() const { return _use_adaptive_sizing; } - - static G1YoungGenSizer* create_gen_sizer(); }; #endif // SHARE_GC_G1_G1YOUNGGENSIZER_HPP diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index 8cc6e0c814e0e..070144ec1a217 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -302,16 +302,7 @@ "of getloadavg() at which G1 triggers a periodic GC. A load " \ "above this value cancels a given periodic GC. A value of zero " \ "disables this check.") \ - range(0.0, (double)max_uintx) \ - \ - product(uintx, G1YoungExpansionBufferPercent, 10, EXPERIMENTAL, \ - "When heterogenous heap is enabled by AllocateOldGenAt " \ - "option, after every GC, young gen is re-sized which " \ - "involves system calls to commit/uncommit memory. To " \ - "reduce these calls, we keep a buffer of extra regions to " \ - "absorb small changes in young gen length. This flag takes " \ - "the buffer size as an percentage of young gen length") \ - range(0, 100) + range(0.0, (double)max_uintx) // end of GC_G1_FLAGS diff --git a/src/hotspot/share/gc/g1/heapRegionManager.cpp b/src/hotspot/share/gc/g1/heapRegionManager.cpp index f527eb8c7b2e1..e8ed1b905069e 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.cpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.cpp @@ -30,7 +30,6 @@ #include "gc/g1/heapRegion.hpp" #include "gc/g1/heapRegionManager.inline.hpp" #include "gc/g1/heapRegionSet.inline.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" #include "jfr/jfrEvents.hpp" #include "logging/logStream.hpp" #include "memory/allocation.hpp" @@ -77,13 +76,6 @@ HeapRegionManager::HeapRegionManager() : _free_list("Free list", new MasterFreeRegionListChecker()) { } -HeapRegionManager* HeapRegionManager::create_manager(G1CollectedHeap* heap) { - if (G1Arguments::is_heterogeneous_heap()) { - return new HeterogeneousHeapRegionManager((uint)(G1Arguments::heap_max_size_bytes() / HeapRegion::GrainBytes) /*heap size as num of regions*/); - } - return new HeapRegionManager(); -} - void HeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage, G1RegionToSpaceMapper* prev_bitmap, G1RegionToSpaceMapper* next_bitmap, @@ -669,7 +661,7 @@ void HeapRegionManager::verify_optional() { #endif // PRODUCT HeapRegionClaimer::HeapRegionClaimer(uint n_workers) : - _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm->_allocated_heapregions_length), _claims(NULL) { + _n_workers(n_workers), _n_regions(G1CollectedHeap::heap()->_hrm._allocated_heapregions_length), _claims(NULL) { assert(n_workers > 0, "Need at least one worker."); uint* new_claims = NEW_C_HEAP_ARRAY(uint, _n_regions, mtGC); memset(new_claims, Unclaimed, sizeof(*_claims) * _n_regions); diff --git a/src/hotspot/share/gc/g1/heapRegionManager.hpp b/src/hotspot/share/gc/g1/heapRegionManager.hpp index c629b3ba562d5..be7bd39434cf9 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp @@ -135,7 +135,6 @@ class HeapRegionManager: public CHeapObj { // Checks the G1MemoryNodeManager to see if this region is on the preferred node. bool is_on_preferred_index(uint region_index, uint preferred_node_index); -protected: G1HeapRegionTable _regions; G1RegionToSpaceMapper* _heap_mapper; G1RegionToSpaceMapper* _prev_bitmap_mapper; @@ -148,8 +147,8 @@ class HeapRegionManager: public CHeapObj { HeapRegion* new_heap_region(uint hrm_index); // Humongous allocation helpers - virtual HeapRegion* allocate_humongous_from_free_list(uint num_regions); - virtual HeapRegion* allocate_humongous_allow_expand(uint num_regions); + HeapRegion* allocate_humongous_from_free_list(uint num_regions); + HeapRegion* allocate_humongous_allow_expand(uint num_regions); // Expand helper for cases when the regions to expand are well defined. void expand_exact(uint start, uint num_regions, WorkGang* pretouch_workers); @@ -162,25 +161,18 @@ class HeapRegionManager: public CHeapObj { // Empty constructor, we'll initialize it with the initialize() method. HeapRegionManager(); - static HeapRegionManager* create_manager(G1CollectedHeap* heap); - - virtual void initialize(G1RegionToSpaceMapper* heap_storage, - G1RegionToSpaceMapper* prev_bitmap, - G1RegionToSpaceMapper* next_bitmap, - G1RegionToSpaceMapper* bot, - G1RegionToSpaceMapper* cardtable, - G1RegionToSpaceMapper* card_counts); - - // Prepare heap regions before and after full collection. - // Nothing to be done in this class. - virtual void prepare_for_full_collection_start() {} - virtual void prepare_for_full_collection_end() {} + void initialize(G1RegionToSpaceMapper* heap_storage, + G1RegionToSpaceMapper* prev_bitmap, + G1RegionToSpaceMapper* next_bitmap, + G1RegionToSpaceMapper* bot, + G1RegionToSpaceMapper* cardtable, + G1RegionToSpaceMapper* card_counts); // Return the "dummy" region used for G1AllocRegion. This is currently a hardwired // new HeapRegion that owns HeapRegion at index 0. Since at the moment we commit // the heap from the lowest address, this region (and its associated data // structures) are available and we do not need to check further. - virtual HeapRegion* get_dummy_region() { return new_heap_region(0); } + HeapRegion* get_dummy_region() { return new_heap_region(0); } // Return the HeapRegion at the given index. Assume that the index // is valid. @@ -213,7 +205,7 @@ class HeapRegionManager: public CHeapObj { } // Allocate a free region with specific node index. If fails allocate with next node index. - virtual HeapRegion* allocate_free_region(HeapRegionType type, uint requested_node_index); + HeapRegion* allocate_free_region(HeapRegionType type, uint requested_node_index); // Allocate a humongous object from the free list HeapRegion* allocate_humongous(uint num_regions); @@ -251,7 +243,7 @@ class HeapRegionManager: public CHeapObj { uint reserved_length() const { return (uint)_regions.length(); } // Return maximum number of regions that heap can expand to. - virtual uint max_length() const { return reserved_length(); } + uint max_length() const { return reserved_length(); } MemoryUsage get_auxiliary_data_memory_usage() const; @@ -261,22 +253,22 @@ class HeapRegionManager: public CHeapObj { // HeapRegions, or re-use existing ones. Returns the number of regions the // sequence was expanded by. If a HeapRegion allocation fails, the resulting // number of regions might be smaller than what's desired. - virtual uint expand_by(uint num_regions, WorkGang* pretouch_workers); + uint expand_by(uint num_regions, WorkGang* pretouch_workers); // Makes sure that the regions from start to start+num_regions-1 are available // for allocation. Returns the number of regions that were committed to achieve // this. - virtual uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); + uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); // Try to expand on the given node index, returning the index of the new region. - virtual uint expand_on_preferred_node(uint node_index); + uint expand_on_preferred_node(uint node_index); HeapRegion* next_region_in_heap(const HeapRegion* r) const; // Find the highest free or uncommitted region in the reserved heap, // and if uncommitted, commit it. If none are available, return G1_NO_HRM_INDEX. // Set the 'expanded' boolean true if a new region was committed. - virtual uint find_highest_free(bool* expanded); + uint find_highest_free(bool* expanded); // Allocate the regions that contain the address range specified, committing the // regions if necessary. Return false if any of the regions is already committed @@ -291,13 +283,13 @@ class HeapRegionManager: public CHeapObj { // Uncommit up to num_regions_to_remove regions that are completely free. // Return the actual number of uncommitted regions. - virtual uint shrink_by(uint num_regions_to_remove); + uint shrink_by(uint num_regions_to_remove); // Uncommit a number of regions starting at the specified index, which must be available, // empty, and free. void shrink_at(uint index, size_t num_regions); - virtual void verify(); + void verify(); // Do some sanity checking. void verify_optional() PRODUCT_RETURN; diff --git a/src/hotspot/share/gc/g1/heapRegionSet.cpp b/src/hotspot/share/gc/g1/heapRegionSet.cpp index f6b546951d843..546c734b42dd5 100644 --- a/src/hotspot/share/gc/g1/heapRegionSet.cpp +++ b/src/hotspot/share/gc/g1/heapRegionSet.cpp @@ -287,21 +287,6 @@ void FreeRegionList::remove_starting_at(HeapRegion* first, uint num_regions) { verify_optional(); } -uint FreeRegionList::num_of_regions_in_range(uint start, uint end) const { - HeapRegion* cur = _head; - uint num = 0; - while (cur != NULL) { - uint index = cur->hrm_index(); - if (index > end) { - break; - } else if (index >= start) { - num++; - } - cur = cur->next(); - } - return num; -} - void FreeRegionList::verify() { // See comment in HeapRegionSetBase::verify() about MT safety and // verification. diff --git a/src/hotspot/share/gc/g1/heapRegionSet.hpp b/src/hotspot/share/gc/g1/heapRegionSet.hpp index fff8bdc9a2b90..440c924c1dde9 100644 --- a/src/hotspot/share/gc/g1/heapRegionSet.hpp +++ b/src/hotspot/share/gc/g1/heapRegionSet.hpp @@ -234,8 +234,6 @@ class FreeRegionList : public HeapRegionSetBase { virtual void verify(); - uint num_of_regions_in_range(uint start, uint end) const; - using HeapRegionSetBase::length; uint length(uint node_index) const; }; diff --git a/src/hotspot/share/gc/g1/heapRegionType.hpp b/src/hotspot/share/gc/g1/heapRegionType.hpp index 404ba2bfaa7ec..9f0f58f340bd3 100644 --- a/src/hotspot/share/gc/g1/heapRegionType.hpp +++ b/src/hotspot/share/gc/g1/heapRegionType.hpp @@ -117,7 +117,7 @@ friend class VMStructs; _tag = tag; } - // Private constructor used static constants + // Private constructor used for static constants HeapRegionType(Tag t) : _tag(t) { hrt_assert_is_valid(_tag); } public: diff --git a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp b/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp deleted file mode 100644 index 85a0af541f0f8..0000000000000 --- a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc/g1/g1CollectedHeap.inline.hpp" -#include "gc/g1/g1ConcurrentRefine.hpp" -#include "gc/g1/heapRegion.hpp" -#include "gc/g1/heapRegionManager.inline.hpp" -#include "gc/g1/heapRegionSet.inline.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" -#include "memory/allocation.hpp" - - -HeterogeneousHeapRegionManager* HeterogeneousHeapRegionManager::manager() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - assert(g1h != NULL, "Uninitialized access to HeterogeneousHeapRegionManager::manager()"); - - HeapRegionManager* hrm = g1h->hrm(); - assert(hrm != NULL, "Uninitialized access to HeterogeneousHeapRegionManager::manager()"); - return (HeterogeneousHeapRegionManager*)hrm; -} - -void HeterogeneousHeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage, - G1RegionToSpaceMapper* prev_bitmap, - G1RegionToSpaceMapper* next_bitmap, - G1RegionToSpaceMapper* bot, - G1RegionToSpaceMapper* cardtable, - G1RegionToSpaceMapper* card_counts) { - HeapRegionManager::initialize(heap_storage, prev_bitmap, next_bitmap, bot, cardtable, card_counts); - - // We commit bitmap for all regions during initialization and mark the bitmap space as special. - // This allows regions to be un-committed while concurrent-marking threads are accessing the bitmap concurrently. - _prev_bitmap_mapper->commit_and_set_special(); - _next_bitmap_mapper->commit_and_set_special(); -} - -// expand_by() is called to grow the heap. We grow into nvdimm now. -// Dram regions are committed later as needed during mutator region allocation or -// when young list target length is determined after gc cycle. -uint HeterogeneousHeapRegionManager::expand_by(uint num_regions, WorkGang* pretouch_workers) { - uint num_regions_possible = total_regions_committed() >= max_length() ? 0 : max_length() - total_regions_committed(); - uint num_expanded = expand_nvdimm(MIN2(num_regions, num_regions_possible), pretouch_workers); - return num_expanded; -} - -// Expands heap starting from 'start' index. The question is should we expand from one memory (e.g. nvdimm) to another (e.g. dram). -// Looking at the code, expand_at() is called for humongous allocation where 'start' is in nv-dimm. -// So we only allocate regions in the same kind of memory as 'start'. -uint HeterogeneousHeapRegionManager::expand_at(uint start, uint num_regions, WorkGang* pretouch_workers) { - if (num_regions == 0) { - return 0; - } - uint target_num_regions = MIN2(num_regions, max_length() - total_regions_committed()); - uint end = is_in_nvdimm(start) ? end_index_of_nvdimm() : end_index_of_dram(); - - uint num_expanded = expand_in_range(start, end, target_num_regions, pretouch_workers); - assert(total_regions_committed() <= max_length(), "must be"); - return num_expanded; -} - -// This function ensures that there are 'expected_num_regions' committed regions in dram. -// If new regions are committed, it un-commits that many regions from nv-dimm. -// If there are already more regions committed in dram, extra regions are un-committed. -void HeterogeneousHeapRegionManager::adjust_dram_regions(uint expected_num_regions, WorkGang* pretouch_workers) { - - // Release back the extra regions allocated in evacuation failure scenario. - if(_no_borrowed_regions > 0) { - _no_borrowed_regions -= shrink_dram(_no_borrowed_regions); - _no_borrowed_regions -= shrink_nvdimm(_no_borrowed_regions); - } - - if(expected_num_regions > free_list_dram_length()) { - // If we are going to expand DRAM, we expand a little more so that we can absorb small variations in Young gen sizing. - uint targeted_dram_regions = expected_num_regions * (1 + (double)G1YoungExpansionBufferPercent / 100); - uint to_be_made_available = targeted_dram_regions - free_list_dram_length(); - -#ifdef ASSERT - uint total_committed_before = total_regions_committed(); -#endif - uint can_be_made_available = shrink_nvdimm(to_be_made_available); - uint ret = expand_dram(can_be_made_available, pretouch_workers); -#ifdef ASSERT - assert(ret == can_be_made_available, "should be equal"); - assert(total_committed_before == total_regions_committed(), "invariant not met"); -#endif - } else { - uint to_be_released = free_list_dram_length() - expected_num_regions; - // if number of extra DRAM regions is small, do not shrink. - if (to_be_released < expected_num_regions * G1YoungExpansionBufferPercent / 100) { - return; - } - -#ifdef ASSERT - uint total_committed_before = total_regions_committed(); -#endif - uint ret = shrink_dram(to_be_released); - assert(ret == to_be_released, "Should be able to shrink by given amount"); - ret = expand_nvdimm(to_be_released, pretouch_workers); -#ifdef ASSERT - assert(ret == to_be_released, "Should be able to expand by given amount"); - assert(total_committed_before == total_regions_committed(), "invariant not met"); -#endif - } -} - -uint HeterogeneousHeapRegionManager::total_regions_committed() const { - return num_committed_dram() + num_committed_nvdimm(); -} - -uint HeterogeneousHeapRegionManager::num_committed_dram() const { - // This class does not keep count of committed regions in dram and nv-dimm. - // G1RegionToHeteroSpaceMapper keeps this information. - return static_cast(_heap_mapper)->num_committed_dram(); -} - -uint HeterogeneousHeapRegionManager::num_committed_nvdimm() const { - // See comment for num_committed_dram() - return static_cast(_heap_mapper)->num_committed_nvdimm(); -} - -// Return maximum number of regions that heap can expand to. -uint HeterogeneousHeapRegionManager::max_length() const { - return _max_regions; -} - -uint HeterogeneousHeapRegionManager::find_unavailable_in_range(uint start_idx, uint end_idx, uint* res_idx) const { - guarantee(res_idx != NULL, "checking"); - guarantee(start_idx <= (reserved_length() + 1), "checking"); - - uint num_regions = 0; - - uint cur = start_idx; - while (cur <= end_idx && is_available(cur)) { - cur++; - } - if (cur == end_idx + 1) { - return num_regions; - } - *res_idx = cur; - while (cur <= end_idx && !is_available(cur)) { - cur++; - } - num_regions = cur - *res_idx; - -#ifdef ASSERT - for (uint i = *res_idx; i < (*res_idx + num_regions); i++) { - assert(!is_available(i), "just checking"); - } - assert(cur == end_idx + 1 || num_regions == 0 || is_available(cur), - "The region at the current position %u must be available or at the end", cur); -#endif - return num_regions; -} - -uint HeterogeneousHeapRegionManager::expand_dram(uint num_regions, WorkGang* pretouch_workers) { - return expand_in_range(start_index_of_dram(), end_index_of_dram(), num_regions, pretouch_workers); -} - -uint HeterogeneousHeapRegionManager::expand_nvdimm(uint num_regions, WorkGang* pretouch_workers) { - return expand_in_range(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, pretouch_workers); -} - -// Follows same logic as expand_at() form HeapRegionManager. -uint HeterogeneousHeapRegionManager::expand_in_range(uint start, uint end, uint num_regions, WorkGang* pretouch_gang) { - - uint so_far = 0; - uint chunk_start = 0; - uint num_last_found = 0; - while (so_far < num_regions && - (num_last_found = find_unavailable_in_range(start, end, &chunk_start)) > 0) { - uint to_commit = MIN2(num_regions - so_far, num_last_found); - make_regions_available(chunk_start, to_commit, pretouch_gang); - so_far += to_commit; - start = chunk_start + to_commit + 1; - } - - return so_far; -} - -// Shrink in the range of indexes which are reserved for dram. -uint HeterogeneousHeapRegionManager::shrink_dram(uint num_regions, bool update_free_list) { - return shrink_in_range(start_index_of_dram(), end_index_of_dram(), num_regions, update_free_list); -} - -// Shrink in the range of indexes which are reserved for nv-dimm. -uint HeterogeneousHeapRegionManager::shrink_nvdimm(uint num_regions, bool update_free_list) { - return shrink_in_range(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, update_free_list); -} - -// Find empty regions in given range, un-commit them and return the count. -uint HeterogeneousHeapRegionManager::shrink_in_range(uint start, uint end, uint num_regions, bool update_free_list) { - - if (num_regions == 0) { - return 0; - } - uint so_far = 0; - uint idx_last_found = 0; - uint num_last_found; - while (so_far < num_regions && - (num_last_found = find_empty_in_range_reverse(start, end, &idx_last_found)) > 0) { - uint to_uncommit = MIN2(num_regions - so_far, num_last_found); - if(update_free_list) { - _free_list.remove_starting_at(at(idx_last_found + num_last_found - to_uncommit), to_uncommit); - } - uncommit_regions(idx_last_found + num_last_found - to_uncommit, to_uncommit); - so_far += to_uncommit; - end = idx_last_found; - } - return so_far; -} - -uint HeterogeneousHeapRegionManager::find_empty_in_range_reverse(uint start_idx, uint end_idx, uint* res_idx) { - guarantee(res_idx != NULL, "checking"); - guarantee(start_idx < reserved_length(), "checking"); - guarantee(end_idx < reserved_length(), "checking"); - if(start_idx > end_idx) { - return 0; - } - - uint num_regions_found = 0; - - jlong cur = end_idx; - while (cur >= start_idx && !(is_available(cur) && at(cur)->is_empty())) { - cur--; - } - if (cur == start_idx - 1) { - return num_regions_found; - } - jlong old_cur = cur; - // cur indexes the first empty region - while (cur >= start_idx && is_available(cur) && at(cur)->is_empty()) { - cur--; - } - *res_idx = cur + 1; - num_regions_found = old_cur - cur; - -#ifdef ASSERT - for (uint i = *res_idx; i < (*res_idx + num_regions_found); i++) { - assert(at(i)->is_empty(), "just checking"); - } -#endif - return num_regions_found; -} - -HeapRegion* HeterogeneousHeapRegionManager::allocate_free_region(HeapRegionType type, uint node_index) { - - // We want to prevent mutators from proceeding when we have borrowed regions from the last collection. This - // will force a full collection to remedy the situation. - // Free region requests from GC threads can proceed. - if(type.is_eden() || type.is_humongous()) { - if(has_borrowed_regions()) { - return NULL; - } - } - - // old and humongous regions are allocated from nv-dimm; eden and survivor regions are allocated from dram - // assumption: dram regions take higher indexes - bool from_nvdimm = (type.is_old() || type.is_humongous()) ? true : false; - bool from_head = from_nvdimm; - HeapRegion* hr = _free_list.remove_region(from_head); - - if (hr != NULL && ( (from_nvdimm && !is_in_nvdimm(hr->hrm_index())) || (!from_nvdimm && !is_in_dram(hr->hrm_index())) ) ) { - _free_list.add_ordered(hr); - hr = NULL; - } - -#ifdef ASSERT - uint total_committed_before = total_regions_committed(); -#endif - - if (hr == NULL) { - if (!from_nvdimm) { - uint ret = shrink_nvdimm(1); - if (ret == 1) { - ret = expand_dram(1, NULL); - assert(ret == 1, "We should be able to commit one region"); - hr = _free_list.remove_region(from_head); - } - } - else { /*is_old*/ - uint ret = shrink_dram(1); - if (ret == 1) { - ret = expand_nvdimm(1, NULL); - assert(ret == 1, "We should be able to commit one region"); - hr = _free_list.remove_region(from_head); - } - } - } -#ifdef ASSERT - assert(total_committed_before == total_regions_committed(), "invariant not met"); -#endif - - // When an old region is requested (which happens during collection pause) and we can't find any empty region - // in the set of available regions (which is an evacuation failure scenario), we borrow (or pre-allocate) an unavailable region - // from nv-dimm. This region is used to evacuate surviving objects from eden, survivor or old. - if(hr == NULL && type.is_old()) { - hr = borrow_old_region_for_gc(); - } - - if (hr != NULL) { - assert(hr->next() == NULL, "Single region should not have next"); - assert(is_available(hr->hrm_index()), "Must be committed"); - } - return hr; -} - -HeapRegion* HeterogeneousHeapRegionManager::allocate_humongous_from_free_list(uint num_regions) { - if (has_borrowed_regions()) { - return NULL; - } - uint candidate = find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, true); - if (candidate == G1_NO_HRM_INDEX) { - return NULL; - } - return allocate_free_regions_starting_at(candidate, num_regions); -} - -HeapRegion* HeterogeneousHeapRegionManager::allocate_humongous_allow_expand(uint num_regions) { - if (has_borrowed_regions()) { - return NULL; - } - uint candidate = find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, false); - if (candidate == G1_NO_HRM_INDEX) { - return NULL; - } - - expand_exact(candidate, num_regions, NULL); - return allocate_free_regions_starting_at(candidate, num_regions); -} - -uint HeterogeneousHeapRegionManager::find_contiguous(size_t start, size_t end, size_t num, bool empty_only) { - uint found = 0; - size_t length_found = 0; - uint cur = (uint)start; - uint length_unavailable = 0; - - while (length_found < num && cur <= end) { - HeapRegion* hr = _regions.get_by_index(cur); - if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { - // This region is a potential candidate for allocation into. - if (!is_available(cur)) { - if(shrink_dram(1) == 1) { - uint ret = expand_in_range(cur, cur, 1, NULL); - assert(ret == 1, "We should be able to expand at this index"); - } else { - length_unavailable++; - } - } - length_found++; - } - else { - // This region is not a candidate. The next region is the next possible one. - found = cur + 1; - length_found = 0; - } - cur++; - } - - if (length_found == num) { - for (uint i = found; i < (found + num); i++) { - HeapRegion* hr = _regions.get_by_index(i); - // sanity check - guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), - "Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT - " that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr)); - } - if (!empty_only && length_unavailable > (max_length() - total_regions_committed())) { - // if 'length_unavailable' number of regions will be made available, we will exceed max regions. - return G1_NO_HRM_INDEX; - } - return found; - } - else { - return G1_NO_HRM_INDEX; - } -} - -uint HeterogeneousHeapRegionManager::find_highest_free(bool* expanded) { - // Loop downwards from the highest dram region index, looking for an - // entry which is either free or not yet committed. If not yet - // committed, expand_at that index. - uint curr = end_index_of_dram(); - while (true) { - HeapRegion *hr = _regions.get_by_index(curr); - if (hr == NULL && !(total_regions_committed() < _max_regions)) { - uint res = shrink_nvdimm(1); - if (res == 1) { - res = expand_in_range(curr, curr, 1, NULL); - assert(res == 1, "We should be able to expand since shrink was successful"); - *expanded = true; - return curr; - } - } - else { - if (hr->is_free()) { - *expanded = false; - return curr; - } - } - if (curr == start_index_of_dram()) { - return G1_NO_HRM_INDEX; - } - curr--; - } -} - -// We need to override this since region 0 which serves are dummy region in base class may not be available here. -// This is a corner condition when either number of regions is small. When adaptive sizing is used, initial heap size -// could be just one region. This region is commited in dram to be used for young generation, leaving region 0 (which is in nvdimm) -// unavailable. -HeapRegion* HeterogeneousHeapRegionManager::get_dummy_region() { - uint curr = 0; - - while (curr < _regions.length()) { - if (is_available(curr)) { - return new_heap_region(curr); - } - curr++; - } - assert(false, "We should always find a region available for dummy region"); - return NULL; -} - -// First shrink in dram, then in nv-dimm. -uint HeterogeneousHeapRegionManager::shrink_by(uint num_regions) { - // This call is made at end of full collection. Before making this call the region sets are tore down (tear_down_region_sets()). - // So shrink() calls below do not need to remove uncomitted regions from free list. - uint ret = shrink_dram(num_regions, false /* update_free_list */); - ret += shrink_nvdimm(num_regions - ret, false /* update_free_list */); - return ret; -} - -void HeterogeneousHeapRegionManager::verify() { - HeapRegionManager::verify(); -} - -uint HeterogeneousHeapRegionManager::free_list_dram_length() const { - return _free_list.num_of_regions_in_range(start_index_of_dram(), end_index_of_dram()); -} - -uint HeterogeneousHeapRegionManager::free_list_nvdimm_length() const { - return _free_list.num_of_regions_in_range(start_index_of_nvdimm(), end_index_of_nvdimm()); -} - -bool HeterogeneousHeapRegionManager::is_in_nvdimm(uint index) const { - return index >= start_index_of_nvdimm() && index <= end_index_of_nvdimm(); -} - -bool HeterogeneousHeapRegionManager::is_in_dram(uint index) const { - return index >= start_index_of_dram() && index <= end_index_of_dram(); -} - -// We have to make sure full collection copies all surviving objects to NV-DIMM. -// We might not have enough regions in nvdimm_set, so we need to make more regions on NV-DIMM available for full collection. -// Note: by doing this we are breaking the in-variant that total number of committed regions is equal to current heap size. -// After full collection ends, we will re-establish this in-variant by freeing DRAM regions. -void HeterogeneousHeapRegionManager::prepare_for_full_collection_start() { - _total_commited_before_full_gc = total_regions_committed() - _no_borrowed_regions; - _no_borrowed_regions = 0; - expand_nvdimm(num_committed_dram(), NULL); - remove_all_free_regions(); -} - -// We need to bring back the total committed regions to before full collection start. -// Unless we are close to OOM, all regular (not pinned) regions in DRAM should be free. -// We shrink all free regions in DRAM and if needed from NV-DIMM (when there are pinned DRAM regions) -// If we can't bring back committed regions count to _total_commited_before_full_gc, we keep the extra count in _no_borrowed_regions. -// When this GC finishes, new regions won't be allocated since has_borrowed_regions() is true. VM will be forced to re-try GC -// with clear soft references followed by OOM error in worst case. -void HeterogeneousHeapRegionManager::prepare_for_full_collection_end() { - uint shrink_size = total_regions_committed() - _total_commited_before_full_gc; - uint so_far = 0; - uint idx_last_found = 0; - uint num_last_found; - uint end = (uint)_regions.length() - 1; - while (so_far < shrink_size && - (num_last_found = find_empty_in_range_reverse(0, end, &idx_last_found)) > 0) { - uint to_uncommit = MIN2(shrink_size - so_far, num_last_found); - uncommit_regions(idx_last_found + num_last_found - to_uncommit, to_uncommit); - so_far += to_uncommit; - end = idx_last_found; - } - // See comment above the function. - _no_borrowed_regions = shrink_size - so_far; -} - -uint HeterogeneousHeapRegionManager::start_index_of_dram() const { return _max_regions;} - -uint HeterogeneousHeapRegionManager::end_index_of_dram() const { return 2*_max_regions - 1; } - -uint HeterogeneousHeapRegionManager::start_index_of_nvdimm() const { return 0; } - -uint HeterogeneousHeapRegionManager::end_index_of_nvdimm() const { return _max_regions - 1; } - -// This function is called when there are no free nv-dimm regions. -// It borrows a region from the set of unavailable regions in nv-dimm for GC purpose. -HeapRegion* HeterogeneousHeapRegionManager::borrow_old_region_for_gc() { - assert(free_list_nvdimm_length() == 0, "this function should be called only when there are no nv-dimm regions in free list"); - - uint ret = expand_nvdimm(1, NULL); - if(ret != 1) { - return NULL; - } - HeapRegion* hr = _free_list.remove_region(true /*from_head*/); - assert(is_in_nvdimm(hr->hrm_index()), "allocated region should be in nv-dimm"); - _no_borrowed_regions++; - return hr; -} - -bool HeterogeneousHeapRegionManager::has_borrowed_regions() const { - return _no_borrowed_regions > 0; -} diff --git a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp b/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp deleted file mode 100644 index ad4a446b6d280..0000000000000 --- a/src/hotspot/share/gc/g1/heterogeneousHeapRegionManager.hpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_G1_HETEROGENEOUSHEAPREGIONMANAGER_HPP -#define SHARE_GC_G1_HETEROGENEOUSHEAPREGIONMANAGER_HPP - -#include "gc/g1/heapRegionManager.hpp" - -// This class manages heap regions on heterogenous memory comprising of dram and nv-dimm. -// Regions in dram (dram_set) are used for young objects and archive regions (CDS). -// Regions in nv-dimm (nvdimm_set) are used for old objects and humongous objects. -// At any point there are some regions committed on dram and some on nv-dimm with the following guarantees: -// 1. The total number of regions committed in dram and nv-dimm equals the current size of heap. -// 2. Consequently, total number of regions committed is less than or equal to Xmx. -// 3. To maintain the guarantee stated by 1., whenever one set grows (new regions committed), the other set shrinks (regions un-committed). -// 3a. If more dram regions are needed (young generation expansion), corresponding number of regions in nv-dimm are un-committed. -// 3b. When old generation or humongous set grows, and new regions need to be committed to nv-dimm, corresponding number of regions -// are un-committed in dram. -class HeterogeneousHeapRegionManager : public HeapRegionManager { - const uint _max_regions; - uint _max_dram_regions; - uint _max_nvdimm_regions; - uint _start_index_of_nvdimm; - uint _total_commited_before_full_gc; - uint _no_borrowed_regions; - - uint total_regions_committed() const; - uint num_committed_dram() const; - uint num_committed_nvdimm() const; - - // Similar to find_unavailable_from_idx() function from base class, difference is this function searches in range [start, end]. - uint find_unavailable_in_range(uint start_idx, uint end_idx, uint* res_idx) const; - - // Expand into dram. Maintains the invariant that total number of committed regions is less than current heap size. - uint expand_dram(uint num_regions, WorkGang* pretouch_workers); - - // Expand into nv-dimm. - uint expand_nvdimm(uint num_regions, WorkGang* pretouch_workers); - - // Expand by finding unavailable regions in [start, end] range. - uint expand_in_range(uint start, uint end, uint num_regions, WorkGang* pretouch_workers); - - // Shrink dram set of regions. - uint shrink_dram(uint num_regions, bool update_free_list = true); - - // Shrink nv-dimm set of regions. - uint shrink_nvdimm(uint num_regions, bool update_free_list = true); - - // Shrink regions from [start, end] range. - uint shrink_in_range(uint start, uint end, uint num_regions, bool update_free_list = true); - - // Similar to find_empty_from_idx_reverse() in base class. Only here it searches in a range. - uint find_empty_in_range_reverse(uint start_idx, uint end_idx, uint* res_idx); - - // Similar to find_contiguous() in base class, with [start, end] range - uint find_contiguous(size_t start, size_t end, size_t num, bool empty_only); - - // This function is called when there are no free nv-dimm regions. - // It borrows a region from the set of unavailable regions in nv-dimm for GC purpose. - HeapRegion* borrow_old_region_for_gc(); - - uint free_list_dram_length() const; - uint free_list_nvdimm_length() const; - - // is region with given index in nv-dimm? - bool is_in_nvdimm(uint index) const; - bool is_in_dram(uint index) const; - -public: - - // Empty constructor, we'll initialize it with the initialize() method. - HeterogeneousHeapRegionManager(uint num_regions) : _max_regions(num_regions), _max_dram_regions(0), - _max_nvdimm_regions(0), _start_index_of_nvdimm(0), - _total_commited_before_full_gc(0), _no_borrowed_regions(0) - {} - - static HeterogeneousHeapRegionManager* manager(); - - virtual void initialize(G1RegionToSpaceMapper* heap_storage, - G1RegionToSpaceMapper* prev_bitmap, - G1RegionToSpaceMapper* next_bitmap, - G1RegionToSpaceMapper* bot, - G1RegionToSpaceMapper* cardtable, - G1RegionToSpaceMapper* card_counts); - - uint start_index_of_nvdimm() const; - uint start_index_of_dram() const; - uint end_index_of_nvdimm() const; - uint end_index_of_dram() const; - - // Override. - HeapRegion* get_dummy_region(); - - // Adjust dram_set to provision 'expected_num_regions' regions. - void adjust_dram_regions(uint expected_num_regions, WorkGang* pretouch_workers); - - // Prepare heap regions before and after full collection. - void prepare_for_full_collection_start(); - void prepare_for_full_collection_end(); - - virtual HeapRegion* allocate_free_region(HeapRegionType type, uint node_index); - virtual HeapRegion* allocate_humongous_from_free_list(uint num_regions); - virtual HeapRegion* allocate_humongous_allow_expand(uint num_regions); - - // Return maximum number of regions that heap can expand to. - uint max_length() const; - - // Override. Expand in nv-dimm. - uint expand_by(uint num_regions, WorkGang* pretouch_workers); - - // Override. - uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); - - // Overrides base class implementation to find highest free region in dram. - uint find_highest_free(bool* expanded); - - // Override. This fuction is called to shrink the heap, we shrink in dram first then in nv-dimm. - uint shrink_by(uint num_regions_to_remove); - - bool has_borrowed_regions() const; - - void verify(); -}; - -#endif // SHARE_GC_G1_HETEROGENEOUSHEAPREGIONMANAGER_HPP diff --git a/src/hotspot/share/gc/g1/vmStructs_g1.hpp b/src/hotspot/share/gc/g1/vmStructs_g1.hpp index d2d46028b9a2e..27fba80f589da 100644 --- a/src/hotspot/share/gc/g1/vmStructs_g1.hpp +++ b/src/hotspot/share/gc/g1/vmStructs_g1.hpp @@ -55,7 +55,7 @@ nonstatic_field(HeapRegionManager, _regions, G1HeapRegionTable) \ \ volatile_nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ - nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager*) \ + nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager) \ nonstatic_field(G1CollectedHeap, _g1mm, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, HeapRegionSetBase) \ nonstatic_field(G1CollectedHeap, _archive_set, HeapRegionSetBase) \ diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp index 7b2ae1126a13b..185d2266796bd 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.cpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp @@ -37,8 +37,6 @@ #include "utilities/defaultStream.hpp" #include "utilities/powerOfTwo.hpp" -static const double MaxRamFractionForYoung = 0.8; - size_t ParallelArguments::conservative_max_heap_alignment() { return compute_heap_alignment(); } @@ -116,10 +114,6 @@ void ParallelArguments::initialize_heap_flags_and_sizes_one_pass() { } void ParallelArguments::initialize_heap_flags_and_sizes() { - if (is_heterogeneous_heap()) { - initialize_heterogeneous(); - } - initialize_heap_flags_and_sizes_one_pass(); const size_t min_pages = 4; // 1 for eden + 1 for each survivor + 1 for old @@ -136,61 +130,6 @@ void ParallelArguments::initialize_heap_flags_and_sizes() { } } -// Check the available dram memory to limit NewSize and MaxNewSize before -// calling base class initialize_flags(). -void ParallelArguments::initialize_heterogeneous() { - FormatBuffer<100> calc_str(""); - - julong phys_mem; - // If MaxRam is specified, we use that as maximum physical memory available. - if (FLAG_IS_DEFAULT(MaxRAM)) { - phys_mem = os::physical_memory(); - calc_str.append("Physical_Memory"); - } else { - phys_mem = (julong)MaxRAM; - calc_str.append("MaxRAM"); - } - - julong reasonable_max = phys_mem; - - // If either MaxRAMFraction or MaxRAMPercentage is specified, we use them to calculate - // reasonable max size of young generation. - if (!FLAG_IS_DEFAULT(MaxRAMFraction)) { - reasonable_max = (julong)(phys_mem / MaxRAMFraction); - calc_str.append(" / MaxRAMFraction"); - } else if (!FLAG_IS_DEFAULT(MaxRAMPercentage)) { - reasonable_max = (julong)((phys_mem * MaxRAMPercentage) / 100); - calc_str.append(" * MaxRAMPercentage / 100"); - } else { - // We use our own fraction to calculate max size of young generation. - reasonable_max = phys_mem * MaxRamFractionForYoung; - calc_str.append(" * %0.2f", MaxRamFractionForYoung); - } - reasonable_max = align_up(reasonable_max, GenAlignment); - - if (MaxNewSize > reasonable_max) { - if (FLAG_IS_CMDLINE(MaxNewSize)) { - log_warning(gc, ergo)("Setting MaxNewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s))", - (size_t)reasonable_max, calc_str.buffer()); - } else { - log_info(gc, ergo)("Setting MaxNewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s)). " - "Dram usage can be lowered by setting MaxNewSize to a lower value", (size_t)reasonable_max, calc_str.buffer()); - } - MaxNewSize = reasonable_max; - } - if (NewSize > reasonable_max) { - if (FLAG_IS_CMDLINE(NewSize)) { - log_warning(gc, ergo)("Setting NewSize to " SIZE_FORMAT " based on dram available (calculation = align(%s))", - (size_t)reasonable_max, calc_str.buffer()); - } - NewSize = reasonable_max; - } -} - -bool ParallelArguments::is_heterogeneous_heap() { - return AllocateOldGenAt != NULL; -} - size_t ParallelArguments::heap_reserved_size_bytes() { return MaxHeapSize; } diff --git a/src/hotspot/share/gc/parallel/parallelArguments.hpp b/src/hotspot/share/gc/parallel/parallelArguments.hpp index 6072ab9e398d1..d23c42b6586ae 100644 --- a/src/hotspot/share/gc/parallel/parallelArguments.hpp +++ b/src/hotspot/share/gc/parallel/parallelArguments.hpp @@ -36,15 +36,12 @@ class ParallelArguments : public GenArguments { virtual void initialize_heap_flags_and_sizes(); void initialize_heap_flags_and_sizes_one_pass(); - void initialize_heterogeneous(); virtual void initialize(); virtual size_t conservative_max_heap_alignment(); virtual CollectedHeap* create_heap(); public: - // Heterogeneous heap support - static bool is_heterogeneous_heap(); static size_t heap_reserved_size_bytes(); static size_t heap_max_size_bytes(); }; diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp index 4f3f41e5ccf58..a4c85b9b98ae4 100644 --- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp +++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp @@ -83,8 +83,7 @@ jint ParallelScavengeHeap::initialize() { assert(MinNewSize <= NewSize && NewSize <= MaxNewSize, "Parameter check"); // Layout the reserved space for the generations. - // If OldGen is allocated on nv-dimm, we need to split the reservation (this is required for windows). - ReservedSpace old_rs = heap_rs.first_part(MaxOldSize, ParallelArguments::is_heterogeneous_heap() /* split */); + ReservedSpace old_rs = heap_rs.first_part(MaxOldSize); ReservedSpace young_rs = heap_rs.last_part(MaxOldSize); assert(young_rs.size() == MaxNewSize, "Didn't reserve all of the heap"); @@ -123,8 +122,7 @@ jint ParallelScavengeHeap::initialize() { GCTimeRatio ); - assert(ParallelArguments::is_heterogeneous_heap() || - (old_gen()->virtual_space()->high_boundary() == + assert((old_gen()->virtual_space()->high_boundary() == young_gen()->virtual_space()->low_boundary()), "Boundaries must meet"); // initialize the policy counters - 2 collectors, 2 generations diff --git a/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.cpp b/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.cpp deleted file mode 100644 index 455049d9a92e6..0000000000000 --- a/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#include "precompiled.hpp" -#include "gc/parallel/psFileBackedVirtualspace.hpp" -#include "memory/virtualspace.hpp" -#include "runtime/os.inline.hpp" - -PSFileBackedVirtualSpace::PSFileBackedVirtualSpace(ReservedSpace rs, size_t alignment, const char* path) : PSVirtualSpace(rs, alignment), - _file_path(path), _fd(-1), _mapping_succeeded(false) { - assert(!rs.special(), "ReservedSpace passed to PSFileBackedVirtualSpace cannot be special"); -} - -bool PSFileBackedVirtualSpace::initialize() { - _fd = os::create_file_for_heap(_file_path); - if (_fd == -1) { - return false; - } - // We map the reserved space to a file at initialization. - char* ret = os::replace_existing_mapping_with_file_mapping(reserved_low_addr(), reserved_size(), _fd); - if (ret != reserved_low_addr()) { - os::close(_fd); - return false; - } - // _mapping_succeeded is false if we return before this point. - // expand calls later check value of this flag and return error if it is false. - _mapping_succeeded = true; - _special = true; - os::close(_fd); - return true; -} - -bool PSFileBackedVirtualSpace::expand_by(size_t bytes) { - assert(special(), "Since entire space is committed at initialization, _special should always be true for PSFileBackedVirtualSpace"); - - // if mapping did not succeed during intialization return false - if (!_mapping_succeeded) { - return false; - } - return PSVirtualSpace::expand_by(bytes); - -} - -bool PSFileBackedVirtualSpace::shrink_by(size_t bytes) { - assert(special(), "Since entire space is committed at initialization, _special should always be true for PSFileBackedVirtualSpace"); - return PSVirtualSpace::shrink_by(bytes); -} - -size_t PSFileBackedVirtualSpace::expand_into(PSVirtualSpace* space, size_t bytes) { - // not supported. Since doing this will change page mapping which will lead to large TLB penalties. - assert(false, "expand_into() should not be called for PSFileBackedVirtualSpace"); - return 0; -} - -void PSFileBackedVirtualSpace::release() { - os::close(_fd); - _fd = -1; - _file_path = NULL; - - PSVirtualSpace::release(); -} - diff --git a/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.hpp b/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.hpp deleted file mode 100644 index 7f689647d0b4f..0000000000000 --- a/src/hotspot/share/gc/parallel/psFileBackedVirtualspace.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - * - */ - -#ifndef SHARE_GC_PARALLEL_PSFILEBACKEDVIRTUALSPACE_HPP -#define SHARE_GC_PARALLEL_PSFILEBACKEDVIRTUALSPACE_HPP - -#include "gc/parallel/psVirtualspace.hpp" - -class PSFileBackedVirtualSpace : public PSVirtualSpace { -private: - const char* _file_path; - int _fd; - bool _mapping_succeeded; -public: - PSFileBackedVirtualSpace(ReservedSpace rs, size_t alignment, const char* file_path); - - bool initialize(); - bool expand_by(size_t bytes); - bool shrink_by(size_t bytes); - size_t expand_into(PSVirtualSpace* space, size_t bytes); - void release(); -}; -#endif // SHARE_GC_PARALLEL_PSFILEBACKEDVIRTUALSPACE_HPP diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp index 8506079d5f012..51500f8c3075f 100644 --- a/src/hotspot/share/gc/parallel/psOldGen.cpp +++ b/src/hotspot/share/gc/parallel/psOldGen.cpp @@ -28,7 +28,6 @@ #include "gc/parallel/parallelScavengeHeap.hpp" #include "gc/parallel/psAdaptiveSizePolicy.hpp" #include "gc/parallel/psCardTable.hpp" -#include "gc/parallel/psFileBackedVirtualspace.hpp" #include "gc/parallel/psOldGen.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/gcLocker.hpp" @@ -62,14 +61,7 @@ void PSOldGen::initialize_virtual_space(ReservedSpace rs, size_t initial_size, size_t alignment) { - if(ParallelArguments::is_heterogeneous_heap()) { - _virtual_space = new PSFileBackedVirtualSpace(rs, alignment, AllocateOldGenAt); - if (!(static_cast (_virtual_space))->initialize()) { - vm_exit_during_initialization("Could not map space for PSOldGen at given AllocateOldGenAt path"); - } - } else { - _virtual_space = new PSVirtualSpace(rs, alignment); - } + _virtual_space = new PSVirtualSpace(rs, alignment); if (!_virtual_space->expand_by(initial_size)) { vm_exit_during_initialization("Could not reserve enough space for " "object heap"); diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp index 5114e01bb2000..17da38e0e3cad 100644 --- a/src/hotspot/share/gc/shared/gcArguments.cpp +++ b/src/hotspot/share/gc/shared/gcArguments.cpp @@ -30,7 +30,6 @@ #include "runtime/arguments.hpp" #include "runtime/globals.hpp" #include "runtime/globals_extension.hpp" -#include "utilities/defaultStream.hpp" #include "utilities/macros.hpp" size_t HeapAlignment = 0; @@ -59,15 +58,6 @@ void GCArguments::initialize() { // If class unloading is disabled, also disable concurrent class unloading. FLAG_SET_CMDLINE(ClassUnloadingWithConcurrentMark, false); } - - if (!FLAG_IS_DEFAULT(AllocateOldGenAt)) { - // CompressedOops not supported when AllocateOldGenAt is set. - LP64_ONLY(FLAG_SET_DEFAULT(UseCompressedOops, false)); - LP64_ONLY(FLAG_SET_DEFAULT(UseCompressedClassPointers, false)); - // When AllocateOldGenAt is set, we cannot use largepages for entire heap memory. - // Only young gen which is allocated in dram can use large pages, but we currently don't support that. - FLAG_SET_DEFAULT(UseLargePages, false); - } } void GCArguments::initialize_heap_sizes() { @@ -94,21 +84,6 @@ size_t GCArguments::compute_heap_alignment() { return alignment; } -bool GCArguments::check_args_consistency() { - bool status = true; - if (!FLAG_IS_DEFAULT(AllocateHeapAt) && !FLAG_IS_DEFAULT(AllocateOldGenAt)) { - jio_fprintf(defaultStream::error_stream(), - "AllocateHeapAt and AllocateOldGenAt cannot be used together.\n"); - status = false; - } - if (!FLAG_IS_DEFAULT(AllocateOldGenAt) && (UseSerialGC || UseEpsilonGC || UseZGC)) { - jio_fprintf(defaultStream::error_stream(), - "AllocateOldGenAt is not supported for selected GC.\n"); - status = false; - } - return status; -} - #ifdef ASSERT void GCArguments::assert_flags() { assert(InitialHeapSize <= MaxHeapSize, "Ergonomics decided on incompatible initial and maximum heap sizes"); diff --git a/src/hotspot/share/gc/shared/gcArguments.hpp b/src/hotspot/share/gc/shared/gcArguments.hpp index 1bcb3164f1293..0de6140dd3686 100644 --- a/src/hotspot/share/gc/shared/gcArguments.hpp +++ b/src/hotspot/share/gc/shared/gcArguments.hpp @@ -56,8 +56,6 @@ class GCArguments { void initialize_heap_sizes(); static size_t compute_heap_alignment(); - - static bool check_args_consistency(); }; #endif // SHARE_GC_SHARED_GCARGUMENTS_HPP diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp index 64734266ed61f..7f3c8730b2f41 100644 --- a/src/hotspot/share/prims/whitebox.cpp +++ b/src/hotspot/share/prims/whitebox.cpp @@ -97,7 +97,6 @@ #include "gc/g1/g1ConcurrentMark.hpp" #include "gc/g1/g1ConcurrentMarkThread.inline.hpp" #include "gc/g1/heapRegionRemSet.hpp" -#include "gc/g1/heterogeneousHeapRegionManager.hpp" #endif // INCLUDE_G1GC #if INCLUDE_PARALLELGC #include "gc/parallel/parallelScavengeHeap.inline.hpp" @@ -507,115 +506,6 @@ WB_END #endif // INCLUDE_G1GC -#if INCLUDE_G1GC || INCLUDE_PARALLELGC -WB_ENTRY(jlong, WB_DramReservedStart(JNIEnv* env, jobject o)) -#if INCLUDE_G1GC - if (UseG1GC) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - HeapWord* base = g1h->reserved().start(); - if (g1h->is_heterogeneous_heap()) { - uint start_region = HeterogeneousHeapRegionManager::manager()->start_index_of_dram(); - return (jlong)(base + start_region * HeapRegion::GrainBytes); - } else { - return (jlong)base; - } - } -#endif // INCLUDE_G1GC -#if INCLUDE_PARALLELGC - if (UseParallelGC) { - ParallelScavengeHeap* ps_heap = ParallelScavengeHeap::heap(); - if (AllocateOldGenAt != NULL) { - MemRegion reserved = ps_heap->young_gen()->reserved(); - return (jlong)reserved.start(); - } else { - return (jlong)ps_heap->base(); - } - } -#endif // INCLUDE_PARALLELGC - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_DramReservedStart: enabled only for G1 and Parallel GC"); -WB_END - -WB_ENTRY(jlong, WB_DramReservedEnd(JNIEnv* env, jobject o)) -#if INCLUDE_G1GC - if (UseG1GC) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - HeapWord* base = g1h->reserved().start(); - if (g1h->is_heterogeneous_heap()) { - uint end_region = HeterogeneousHeapRegionManager::manager()->end_index_of_dram(); - return (jlong)(base + (end_region + 1) * HeapRegion::GrainBytes - 1); - } else { - return (jlong)base + G1Arguments::heap_max_size_bytes(); - } - } -#endif // INCLUDE_G1GC -#if INCLUDE_PARALLELGC - if (UseParallelGC) { - ParallelScavengeHeap* ps_heap = ParallelScavengeHeap::heap(); - if (AllocateOldGenAt != NULL) { - MemRegion reserved = ps_heap->young_gen()->reserved(); - return (jlong)reserved.end(); - } else { - return (jlong)ps_heap->reserved_region().end(); - } - } -#endif // INCLUDE_PARALLELGC - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_DramReservedEnd: enabled only for G1 and Parallel GC"); -WB_END - -WB_ENTRY(jlong, WB_NvdimmReservedStart(JNIEnv* env, jobject o)) -#if INCLUDE_G1GC - if (UseG1GC) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_heterogeneous_heap()) { - uint start_region = HeterogeneousHeapRegionManager::manager()->start_index_of_nvdimm(); - return (jlong)(g1h->reserved().start() + start_region * HeapRegion::GrainBytes); - } else { - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedStart: Old gen is not allocated on NV-DIMM using AllocateOldGenAt flag"); - } - } -#endif // INCLUDE_G1GC -#if INCLUDE_PARALLELGC - if (UseParallelGC) { - ParallelScavengeHeap* ps_heap = ParallelScavengeHeap::heap(); - if (AllocateOldGenAt != NULL) { - MemRegion reserved = ps_heap->old_gen()->reserved(); - return (jlong)reserved.start(); - } else { - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedStart: Old gen is not allocated on NV-DIMM using AllocateOldGenAt flag"); - } - } -#endif // INCLUDE_PARALLELGC - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedStart: enabled only for G1 and Parallel GC"); -WB_END - -WB_ENTRY(jlong, WB_NvdimmReservedEnd(JNIEnv* env, jobject o)) -#if INCLUDE_G1GC - if (UseG1GC) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_heterogeneous_heap()) { - uint end_region = HeterogeneousHeapRegionManager::manager()->start_index_of_nvdimm(); - return (jlong)(g1h->reserved().start() + (end_region + 1) * HeapRegion::GrainBytes - 1); - } else { - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedEnd: Old gen is not allocated on NV-DIMM using AllocateOldGenAt flag"); - } - } -#endif // INCLUDE_G1GC -#if INCLUDE_PARALLELGC - if (UseParallelGC) { - ParallelScavengeHeap* ps_heap = ParallelScavengeHeap::heap(); - if (AllocateOldGenAt != NULL) { - MemRegion reserved = ps_heap->old_gen()->reserved(); - return (jlong)reserved.end(); - } else { - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedEnd: Old gen is not allocated on NV-DIMM using AllocateOldGenAt flag"); - } - } -#endif // INCLUDE_PARALLELGC - THROW_MSG_0(vmSymbols::java_lang_UnsupportedOperationException(), "WB_NvdimmReservedEnd: enabled only for G1 and Parallel GC"); -WB_END - -#endif // INCLUDE_G1GC || INCLUDE_PARALLELGC - #if INCLUDE_PARALLELGC WB_ENTRY(jlong, WB_PSVirtualSpaceAlignment(JNIEnv* env, jobject o)) @@ -2418,12 +2308,6 @@ static JNINativeMethod methods[] = { {CC"g1MemoryNodeIds", CC"()[I", (void*)&WB_G1MemoryNodeIds }, {CC"g1GetMixedGCInfo", CC"(I)[J", (void*)&WB_G1GetMixedGCInfo }, #endif // INCLUDE_G1GC -#if INCLUDE_G1GC || INCLUDE_PARALLELGC - {CC"dramReservedStart", CC"()J", (void*)&WB_DramReservedStart }, - {CC"dramReservedEnd", CC"()J", (void*)&WB_DramReservedEnd }, - {CC"nvdimmReservedStart", CC"()J", (void*)&WB_NvdimmReservedStart }, - {CC"nvdimmReservedEnd", CC"()J", (void*)&WB_NvdimmReservedEnd }, -#endif // INCLUDE_G1GC || INCLUDE_PARALLELGC #if INCLUDE_PARALLELGC {CC"psVirtualSpaceAlignment",CC"()J", (void*)&WB_PSVirtualSpaceAlignment}, {CC"psHeapGenerationAlignment",CC"()J", (void*)&WB_PSHeapGenerationAlignment}, diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 9a828e5aa1889..549370cb8fa0c 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -2161,8 +2161,6 @@ bool Arguments::check_vm_args_consistency() { } #endif - status = status && GCArguments::check_args_consistency(); - return status; } @@ -4080,7 +4078,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { #if defined(AIX) UNSUPPORTED_OPTION_NULL(AllocateHeapAt); - UNSUPPORTED_OPTION_NULL(AllocateOldGenAt); #endif #ifndef PRODUCT diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 83d80da53cca4..b0310915ae6f6 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -2470,12 +2470,6 @@ const intx ObjectAlignmentInBytes = 8; "Path to the directoy where a temporary file will be created " \ "to use as the backing store for Java Heap.") \ \ - product(ccstr, AllocateOldGenAt, NULL, EXPERIMENTAL, \ - "Path to the directoy where a temporary file will be " \ - "created to use as the backing store for old generation." \ - "File of size Xmx is pre-allocated for performance reason, so" \ - "we need that much space available") \ - \ develop(int, VerifyMetaspaceInterval, DEBUG_ONLY(500) NOT_DEBUG(0), \ "Run periodic metaspace verifications (0 - none, " \ "1 - always, >1 every nth interval)") \ diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java index 42e50b5237379..73a089e759ce8 100644 --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/g1/G1CollectedHeap.java @@ -48,7 +48,7 @@ public class G1CollectedHeap extends CollectedHeap { // HeapRegionManager _hrm; - static private AddressField hrmField; + static private long hrmFieldOffset; // MemRegion _g1_reserved; static private long g1ReservedFieldOffset; // size_t _summary_bytes_used; @@ -73,7 +73,7 @@ public void update(Observable o, Object data) { static private synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("G1CollectedHeap"); - hrmField = type.getAddressField("_hrm"); + hrmFieldOffset = type.getField("_hrm").getOffset(); summaryBytesUsedField = type.getCIntegerField("_summary_bytes_used"); g1mmField = type.getAddressField("_g1mm"); oldSetFieldOffset = type.getField("_old_set").getOffset(); @@ -94,9 +94,9 @@ public long n_regions() { } public HeapRegionManager hrm() { - Address hrmAddr = hrmField.getValue(addr); + Address hrmAddr = addr.addOffsetTo(hrmFieldOffset); return (HeapRegionManager) VMObjectFactory.newObject(HeapRegionManager.class, - hrmAddr); + hrmAddr); } public G1MonitoringSupport g1mm() { diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT index dc6b0f4c435ce..feeecfd086a01 100644 --- a/test/hotspot/jtreg/TEST.ROOT +++ b/test/hotspot/jtreg/TEST.ROOT @@ -76,7 +76,6 @@ requires.properties= \ vm.compiler2.enabled \ vm.musl \ docker.support \ - test.vm.gc.nvdimm \ jdk.containerized # Minimum jtreg version diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups index 3a53477ed887d..7efdfc779492b 100644 --- a/test/hotspot/jtreg/TEST.groups +++ b/test/hotspot/jtreg/TEST.groups @@ -42,8 +42,7 @@ hotspot_compiler_all_gcs = \ -compiler/graalunit hotspot_gc = \ - gc \ - -gc/nvdimm + gc # By design this group should include ALL tests under runtime sub-directory hotspot_runtime = \ @@ -66,7 +65,6 @@ hotspot_misc = \ -:hotspot_gc \ -:hotspot_runtime \ -:hotspot_serviceability \ - -gc/nvdimm \ -:hotspot_containers hotspot_native_sanity = \ @@ -203,8 +201,7 @@ tier1_gc_2 = \ -gc/logging/TestUnifiedLoggingSwitchStress.java \ -gc/stress \ -gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java \ - -gc/shenandoah \ - -gc/nvdimm + -gc/shenandoah gc_epsilon = \ gc/epsilon/ \ diff --git a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAt.java b/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAt.java deleted file mode 100644 index ef4b7b4b848cc..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAt.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* @test TestAllocateOldGenAt.java - * @summary Test to check allocation of Java Heap with AllocateOldGenAt option - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @library /test/lib - * @modules java.base/jdk.internal.misc - * @run driver gc.nvdimm.TestAllocateOldGenAt - */ - -import jdk.test.lib.JDKToolFinder; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import java.util.ArrayList; -import java.util.Collections; - -public class TestAllocateOldGenAt { - private static String[] commonFlags; - - public static void main(String args[]) throws Exception { - String test_dir = System.getProperty("test.dir", "."); - commonFlags = new String[] { - "-XX:+UnlockExperimentalVMOptions", - "-XX:AllocateOldGenAt=" + test_dir, - "-Xmx32m", - "-Xms32m", - "-version"}; - - runTest("-XX:+UseG1GC"); - runTest("-XX:+UseParallelGC"); - } - - private static void runTest(String... extraFlags) throws Exception { - ArrayList flags = new ArrayList<>(); - Collections.addAll(flags, commonFlags); - Collections.addAll(flags, extraFlags); - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - output.shouldHaveExitValue(0); - - } -} diff --git a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtError.java b/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtError.java deleted file mode 100644 index c9e4fc1df862d..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtError.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* @test TestAllocateOldGenAtError.java - * @summary Test to check correct handling of non-existent directory passed to AllocateOldGenAt option - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @library /test/lib - * @modules java.base/jdk.internal.misc - * @run driver gc.nvdimm.TestAllocateOldGenAtError - */ - -import java.io.File; -import jdk.test.lib.JDKToolFinder; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.UUID; - -public class TestAllocateOldGenAtError { - private static String[] commonFlags; - - public static void main(String args[]) throws Exception { - String test_dir = System.getProperty("test.dir", "."); - - File f = null; - do { - f = new File(test_dir, UUID.randomUUID().toString()); - } while(f.exists()); - - commonFlags = new String[] { - "-XX:+UnlockExperimentalVMOptions", - "-XX:AllocateOldGenAt=" + f.getName(), - "-Xlog:gc+heap=info", - "-Xmx32m", - "-Xms32m", - "-version"}; - - testG1(); - testParallelOld(); - } - - private static void testG1() throws Exception { - System.out.println("Testing G1 GC"); - - OutputAnalyzer output = runTest("-XX:+UseG1GC"); - - output.shouldContain("Could not initialize G1 heap"); - output.shouldContain("Error occurred during initialization of VM"); - output.shouldNotHaveExitValue(0); - - } - - private static void testParallelOld() throws Exception { - System.out.println("Testing Parallel GC"); - - OutputAnalyzer output = runTest("-XX:+UseParallelGC"); - - output.shouldContain("Error occurred during initialization of VM"); - output.shouldNotHaveExitValue(0); - } - - private static OutputAnalyzer runTest(String... extraFlags) throws Exception { - ArrayList flags = new ArrayList<>(); - Collections.addAll(flags, commonFlags); - Collections.addAll(flags, extraFlags); - - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - return output; - } -} diff --git a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtMultiple.java b/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtMultiple.java deleted file mode 100644 index a972a42f71cfb..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestAllocateOldGenAtMultiple.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* @test TestAllocateOldGenAtMultiple.java - * @summary Test to check allocation of Java Heap with AllocateOldGenAt option. Has multiple sub-tests to cover different code paths. - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @library /test/lib - * @modules java.base/jdk.internal.misc - * @requires vm.bits == "64" - * @run driver gc.nvdimm.TestAllocateOldGenAtMultiple - */ - -import jdk.test.lib.JDKToolFinder; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import java.util.ArrayList; -import java.util.Collections; - -public class TestAllocateOldGenAtMultiple { - public static void main(String args[]) throws Exception { - ArrayList flags = new ArrayList<>(); - String test_dir = System.getProperty("test.dir", "."); - - // Extra flags for each of the sub-tests - String[][] extraFlagsList = { - {"-Xmx32m", "-Xms32m", "-XX:+UseCompressedOops"}, // 1. With compressedoops enabled. - {"-Xmx32m", "-Xms32m", "-XX:-UseCompressedOops"}, // 2. With compressedoops disabled. - {"-Xmx32m", "-Xms32m", "-XX:HeapBaseMinAddress=3g"}, // 3. With user specified HeapBaseMinAddress. - {"-Xmx4g", "-Xms4g"}, // 4. With larger heap size (UnscaledNarrowOop not possible). - {"-Xmx4g", "-Xms4g", "-XX:+UseLargePages"}, // 5. Set UseLargePages. - {"-Xmx4g", "-Xms4g", "-XX:+UseNUMA"} // 6. Set UseNUMA. - }; - - for (String[] extraFlags : extraFlagsList) { - flags.clear(); - // Add extra flags specific to the sub-test. - Collections.addAll(flags, extraFlags); - // Add common flags - Collections.addAll(flags, new String[] {"-XX:+UnlockExperimentalVMOptions", - "-XX:AllocateOldGenAt=" + test_dir, - "-version"}); - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - - System.out.println("Output:\n" + output.getOutput()); - - output.shouldHaveExitValue(0); - } - } -} diff --git a/test/hotspot/jtreg/gc/nvdimm/TestHumongousObjectsOnNvdimm.java b/test/hotspot/jtreg/gc/nvdimm/TestHumongousObjectsOnNvdimm.java deleted file mode 100644 index 39a997fb9dc77..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestHumongousObjectsOnNvdimm.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* - * @test TestHumongousObjectsOnNvdimm - * @summary Check that humongous objects reside in nv-dimm - * @library /test/lib / - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @build sun.hotspot.WhiteBox - * @run driver ClassFileInstaller sun.hotspot.WhiteBox - * @run driver gc.nvdimm.TestHumongousObjectsOnNvdimm - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Asserts; -import sun.hotspot.WhiteBox; - -import java.util.ArrayList; -import java.util.Collections; -import gc.testlibrary.Helpers; - -/** - * Test spawns HumongousObjectTest in a separate VM and expects that it - * completes without a RuntimeException. - */ -public class TestHumongousObjectsOnNvdimm { - - private static String[] commonFlags; - - public static void main(String args[]) throws Exception { - commonFlags = new String[] { - "-Xbootclasspath/a:.", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:AllocateOldGenAt=" + System.getProperty("test.dir", "."), - "-Xms10M", "-Xmx10M", - "-XX:G1HeapRegionSize=1m" - }; - - // Test with G1 GC - runTest("-XX:+UseG1GC"); - } - - private static void runTest(String... extraFlags) throws Exception { - ArrayList flags = new ArrayList<>(); - Collections.addAll(flags, commonFlags); - Collections.addAll(flags, extraFlags); - flags.add(HumongousObjectTest.class.getName()); - - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - output.shouldHaveExitValue(0); - } -} - -/** - * This class tests that a humongous object resides in NVDIMM. - */ -class HumongousObjectTest { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); - - private static void validateObject(Object o) { - Asserts.assertTrue(WB.isObjectInOldGen(o), - "Object is supposed to be in OldGen"); - - long obj_addr = WB.getObjectAddress(o); - long nvdimm_heap_start = WB.nvdimmReservedStart(); - long nvdimm_heap_end = WB.nvdimmReservedEnd(); - - Asserts.assertTrue(WB.g1BelongsToHumongousRegion(obj_addr), "Object address should be in Humongous set"); - Asserts.assertTrue(obj_addr >= nvdimm_heap_start && obj_addr < nvdimm_heap_end, - "Humongous object does not reside in NVDIMM"); - } - - public static void main(String args[]) throws Exception { - // allocate an humongous object - int byteArrayMemoryOverhead = Helpers.detectByteArrayAllocationOverhead(); - int MinByteArrayHumongousSize = (WB.g1RegionSize() / 2) - byteArrayMemoryOverhead + 1; - byte[] obj = new byte[MinByteArrayHumongousSize]; - - validateObject(obj); - } -} diff --git a/test/hotspot/jtreg/gc/nvdimm/TestOldObjectsOnNvdimm.java b/test/hotspot/jtreg/gc/nvdimm/TestOldObjectsOnNvdimm.java deleted file mode 100644 index c419333153767..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestOldObjectsOnNvdimm.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* - * @test TestOldObjectsOnNvdimm - * @summary Check that objects in old generation reside in dram. - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @library /test/lib - * @build sun.hotspot.WhiteBox - * @run driver ClassFileInstaller sun.hotspot.WhiteBox - * @run driver gc.nvdimm.TestOldObjectsOnNvdimm - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Asserts; -import sun.hotspot.WhiteBox; - -import java.util.ArrayList; -import java.util.Collections; - -/* - * Test spawns OldObjectTest in a separate VM and expects that it - * completes without a RuntimeException. - */ -public class TestOldObjectsOnNvdimm { - - public static final int ALLOCATION_SIZE = 100; - private static String[] commonFlags; - - public static void main(String args[]) throws Exception { - commonFlags = new String[] { - "-Xbootclasspath/a:.", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:AllocateOldGenAt=" + System.getProperty("test.dir", "."), - "-Xms10M", "-Xmx10M", - "-XX:MaxTenuringThreshold=1" // Promote objects to Old Gen - }; - runTest("-XX:+UseG1GC"); - runTest("-XX:+UseParallelGC"); - } - - private static void runTest(String... extraFlags) throws Exception { - ArrayList flags = new ArrayList<>(); - Collections.addAll(flags, commonFlags); - Collections.addAll(flags, extraFlags); - flags.add(OldObjectTest.class.getName()); - - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - System.out.println(output.getStdout()); - output.shouldHaveExitValue(0); - } -} - -/* - * This class tests that object is in Old generation after tenuring and resides in NVDIMM. - * The necessary condition for this test is running in VM with the following flags: - * -XX:AllocateOldGenAt=, -XX:MaxTenuringThreshold=1 - */ -class OldObjectTest { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); - - private static void validateOldObject(Object o) { - Asserts.assertTrue(WB.isObjectInOldGen(o), - "Object is supposed to be in OldGen"); - - long oldObj_addr = WB.getObjectAddress(o); - long nvdimm_heap_start = WB.nvdimmReservedStart(); - long nvdimm_heap_end = WB.nvdimmReservedEnd(); - - Asserts.assertTrue(oldObj_addr >= nvdimm_heap_start && oldObj_addr <= nvdimm_heap_end, - "Old object does not reside in NVDIMM"); - } - - public static void main(String args[]) throws Exception { - // allocate an object and perform Young GCs to promote it to Old - byte[] oldObj = new byte[TestOldObjectsOnNvdimm.ALLOCATION_SIZE]; - WB.youngGC(); - WB.youngGC(); - validateOldObject(oldObj); - } -} diff --git a/test/hotspot/jtreg/gc/nvdimm/TestYoungObjectsOnDram.java b/test/hotspot/jtreg/gc/nvdimm/TestYoungObjectsOnDram.java deleted file mode 100644 index 6a1d9e6526b92..0000000000000 --- a/test/hotspot/jtreg/gc/nvdimm/TestYoungObjectsOnDram.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package gc.nvdimm; - -/* - * @test TestYoungObjectsOnDram - * @summary Check that objects in young generation reside in dram. - * @requires vm.gc=="null" & os.family != "aix" - * @requires test.vm.gc.nvdimm - * @library /test/lib - * @build sun.hotspot.WhiteBox - * @run driver ClassFileInstaller sun.hotspot.WhiteBox - * @run driver gc.nvdimm.TestYoungObjectsOnDram - */ - -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.Asserts; -import sun.hotspot.WhiteBox; - -import java.util.ArrayList; -import java.util.Collections; - -/** - * Test spawns YoungObjectTest in a separate VM and expects that it - * completes without a RuntimeException. - */ -public class TestYoungObjectsOnDram { - - public static final int ALLOCATION_SIZE = 100; - private static String[] commonFlags; - - public static void main(String args[]) throws Exception { - commonFlags = new String[] { - "-Xbootclasspath/a:.", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+WhiteBoxAPI", - "-XX:AllocateOldGenAt=" + System.getProperty("test.dir", "."), - "-XX:SurvivorRatio=1", // Survivor-to-eden ratio is 1:1 - "-Xms10M", "-Xmx10M", - "-XX:InitialTenuringThreshold=15" // avoid promotion of objects to Old Gen - }; - runTest("-XX:+UseG1GC"); - runTest("-XX:+UseParallelGC"); - } - - private static void runTest(String... extraFlags) throws Exception { - ArrayList flags = new ArrayList<>(); - Collections.addAll(flags, commonFlags); - Collections.addAll(flags, extraFlags); - flags.add(YoungObjectTest.class.getName()); - - ProcessBuilder pb = ProcessTools.createTestJvm(flags); - - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - System.out.println(output.getStdout()); - output.shouldHaveExitValue(0); - } -} - -/** - * This class tests that newly created object is in Young generation and resides in DRAM. - * The necessary condition for this test is running in VM with the following flags: - * -XX:AllocateOldGenAt=, -XX:InitialTenuringThreshold=15, -XX:SurvivorRatio=1 - */ -class YoungObjectTest { - private static final WhiteBox WB = WhiteBox.getWhiteBox(); - - private static void validateYoungObject(Object o) { - Asserts.assertTrue(!WB.isObjectInOldGen(o), - "Object is supposed to be in YoungGen"); - - long youngObj_addr = WB.getObjectAddress(o); - long dram_heap_start = WB.dramReservedStart(); - long dram_heap_end = WB.dramReservedEnd(); - - Asserts.assertTrue(youngObj_addr >= dram_heap_start && youngObj_addr <= dram_heap_end, - "Young object does not reside in DRAM"); - } - - public static void main(String args[]) throws Exception { - // allocate an object - byte[] youngObj = new byte[TestYoungObjectsOnDram.ALLOCATION_SIZE]; - validateYoungObject(youngObj); - - // Start a Young GC and check that object is still in DRAM. - // We have used -XX:InitialTenuringThreshold=15 to invoke this test - WB.youngGC(); - validateYoungObject(youngObj); - } -} diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java index 7dae9191b0145..68a6f1e3f88ca 100644 --- a/test/jtreg-ext/requires/VMProps.java +++ b/test/jtreg-ext/requires/VMProps.java @@ -117,7 +117,6 @@ public Map call() { map.put("docker.support", this::dockerSupport); map.put("vm.musl", this::isMusl); map.put("release.implementor", this::implementor); - map.put("test.vm.gc.nvdimm", this::isNvdimmTestEnabled); map.put("jdk.containerized", this::jdkContainerized); vmGC(map); // vm.gc.X = true/false vmOptFinalFlags(map); @@ -541,11 +540,6 @@ private String implementor() { } } - private String isNvdimmTestEnabled() { - String isEnabled = System.getenv("TEST_VM_GC_NVDIMM"); - return "" + "true".equalsIgnoreCase(isEnabled); - } - private String jdkContainerized() { String isEnabled = System.getenv("TEST_JDK_CONTAINERIZED"); return "" + "true".equalsIgnoreCase(isEnabled); diff --git a/test/lib/sun/hotspot/WhiteBox.java b/test/lib/sun/hotspot/WhiteBox.java index 5cba6ffa80682..f72a34b4b45c3 100644 --- a/test/lib/sun/hotspot/WhiteBox.java +++ b/test/lib/sun/hotspot/WhiteBox.java @@ -189,10 +189,6 @@ public boolean g1BelongsToFreeRegion(long adr) { public native long g1NumMaxRegions(); public native long g1NumFreeRegions(); public native int g1RegionSize(); - public native long dramReservedStart(); - public native long dramReservedEnd(); - public native long nvdimmReservedStart(); - public native long nvdimmReservedEnd(); public native MemoryUsage g1AuxiliaryMemoryUsage(); private native Object[] parseCommandLine0(String commandline, char delim, DiagnosticCommand[] args); public Object[] parseCommandLine(String commandline, char delim, DiagnosticCommand[] args) { From 70c7b1d93c942b48446695d41bd604de0cc68d86 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 12 Nov 2020 14:15:40 +0000 Subject: [PATCH 084/124] 8250607: C2: Filter type in PhiNode::Value() for induction variables of trip-counted integer loops Reviewed-by: chagedorn, thartmann --- src/hotspot/share/opto/cfgnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 486d0b2bec852..1aa220c5947b0 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -1105,9 +1105,9 @@ const Type* PhiNode::Value(PhaseGVN* phase) const { if (bt != BoolTest::ne) { if (stride_t->_hi < 0) { // Down-counter loop swap(lo, hi); - return TypeInt::make(MIN2(lo->_lo, hi->_lo) , hi->_hi, 3); + return TypeInt::make(MIN2(lo->_lo, hi->_lo) , hi->_hi, 3)->filter_speculative(_type); } else if (stride_t->_lo >= 0) { - return TypeInt::make(lo->_lo, MAX2(lo->_hi, hi->_hi), 3); + return TypeInt::make(lo->_lo, MAX2(lo->_hi, hi->_hi), 3)->filter_speculative(_type); } } } From f7685a4639f5f69a3f974a56e52ab32d2d0ced0b Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 12 Nov 2020 14:20:48 +0000 Subject: [PATCH 085/124] 8256203: Simplify RegMask::Empty Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/regmask.cpp | 7 +------ src/hotspot/share/opto/regmask.hpp | 4 +++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/regmask.cpp b/src/hotspot/share/opto/regmask.cpp index f024387c98524..70f2b12c7bb23 100644 --- a/src/hotspot/share/opto/regmask.cpp +++ b/src/hotspot/share/opto/regmask.cpp @@ -49,12 +49,7 @@ void OptoReg::dump(int r, outputStream *st) { //============================================================================= -const RegMask RegMask::Empty( -# define BODY(I) 0, - FORALL_BODY -# undef BODY - 0 -); +const RegMask RegMask::Empty; //============================================================================= bool RegMask::is_vector(uint ireg) { diff --git a/src/hotspot/share/opto/regmask.hpp b/src/hotspot/share/opto/regmask.hpp index bed1c416e1aaa..6f4fe652530e7 100644 --- a/src/hotspot/share/opto/regmask.hpp +++ b/src/hotspot/share/opto/regmask.hpp @@ -136,7 +136,9 @@ class RegMask { } // Construct an empty mask - RegMask() : _RM_UP(), _lwm(_RM_SIZE - 1), _hwm(0) {} + RegMask() : _RM_UP(), _lwm(_RM_SIZE - 1), _hwm(0) { + assert(valid_watermarks(), "post-condition"); + } // Construct a mask with a single bit RegMask(OptoReg::Name reg) : RegMask() { From 19bade02804b8fdb9fa5bbd091c6a59a08d70cb2 Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Thu, 12 Nov 2020 14:21:24 +0000 Subject: [PATCH 086/124] 8256238: Remove Matcher::pass_original_key_for_aes Reviewed-by: thartmann, chagedorn --- src/hotspot/cpu/aarch64/aarch64.ad | 5 -- src/hotspot/cpu/arm/arm.ad | 5 -- src/hotspot/cpu/ppc/ppc.ad | 5 -- src/hotspot/cpu/s390/s390.ad | 5 -- src/hotspot/cpu/x86/x86.ad | 6 -- src/hotspot/share/opto/library_call.cpp | 81 ++++++------------------- src/hotspot/share/opto/library_call.hpp | 1 - src/hotspot/share/opto/matcher.hpp | 3 - src/hotspot/share/opto/runtime.cpp | 24 -------- 9 files changed, 17 insertions(+), 118 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index ff82cd08cc168..a9df812c634bc 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2516,11 +2516,6 @@ const uint Matcher::vector_ideal_reg(int len) { return 0; } -// AES support not yet implemented -const bool Matcher::pass_original_key_for_aes() { - return false; -} - // aarch64 supports misaligned vectors store/load. const bool Matcher::misaligned_vectors_ok() { return true; diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index b7c6ec48896a7..7ea64d1d1ac9e 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1041,11 +1041,6 @@ const bool Matcher::misaligned_vectors_ok() { return false; } -// ARM doesn't support AES intrinsics -const bool Matcher::pass_original_key_for_aes() { - return false; -} - const bool Matcher::convL2FSupported(void) { return false; } diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index b8f4f26995f8a..27dad6cff948c 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -2200,11 +2200,6 @@ const bool Matcher::misaligned_vectors_ok() { return false; } -// PPC AES support not yet implemented -const bool Matcher::pass_original_key_for_aes() { - return false; -} - // RETURNS: whether this branch offset is short enough that a short // branch can be used. // diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index de1565194ed18..98194c73047c1 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -1588,11 +1588,6 @@ const bool Matcher::misaligned_vectors_ok() { return true; } -// Not yet ported to z/Architecture. -const bool Matcher::pass_original_key_for_aes() { - return false; -} - // RETURNS: whether this branch offset is short enough that a short // branch can be used. // diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad index 7551dfaa0fc5f..7895073be03ab 100644 --- a/src/hotspot/cpu/x86/x86.ad +++ b/src/hotspot/cpu/x86/x86.ad @@ -1899,12 +1899,6 @@ const bool Matcher::misaligned_vectors_ok() { return true; } -// x86 AES instructions are compatible with SunJCE expanded -// keys, hence we do not need to pass the original key to stubs -const bool Matcher::pass_original_key_for_aes() { - return false; -} - const bool Matcher::convi2l_type_required = true; diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index a9acf6733187f..43028fdb56f56 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -5614,22 +5614,10 @@ bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); if (k_start == NULL) return false; - if (Matcher::pass_original_key_for_aes()) { - // on SPARC we need to pass the original key since key expansion needs to happen in intrinsics due to - // compatibility issues between Java key expansion and SPARC crypto instructions - Node* original_k_start = get_original_key_start_from_aescrypt_object(aescrypt_object); - if (original_k_start == NULL) return false; - - // Call the stub. - make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, original_k_start); - } else { - // Call the stub. - make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start); - } + // Call the stub. + make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start); return true; } @@ -5712,25 +5700,11 @@ bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { if (objRvec == NULL) return false; Node* r_start = array_element_address(objRvec, intcon(0), T_BYTE); - Node* cbcCrypt; - if (Matcher::pass_original_key_for_aes()) { - // on SPARC we need to pass the original key since key expansion needs to happen in intrinsics due to - // compatibility issues between Java key expansion and SPARC crypto instructions - Node* original_k_start = get_original_key_start_from_aescrypt_object(aescrypt_object); - if (original_k_start == NULL) return false; - - // Call the stub, passing src_start, dest_start, k_start, r_start, src_len and original_k_start - cbcCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::cipherBlockChaining_aescrypt_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, r_start, len, original_k_start); - } else { - // Call the stub, passing src_start, dest_start, k_start, r_start and src_len - cbcCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::cipherBlockChaining_aescrypt_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, r_start, len); - } + // Call the stub, passing src_start, dest_start, k_start, r_start and src_len + Node* cbcCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::cipherBlockChaining_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, r_start, len); // return cipher length (int) Node* retvalue = _gvn.transform(new ProjNode(cbcCrypt, TypeFunc::Parms)); @@ -5809,16 +5783,11 @@ bool LibraryCallKit::inline_electronicCodeBook_AESCrypt(vmIntrinsics::ID id) { Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); if (k_start == NULL) return false; - Node* ecbCrypt; - if (Matcher::pass_original_key_for_aes()) { - // no SPARC version for AES/ECB intrinsics now. - return false; - } // Call the stub, passing src_start, dest_start, k_start, r_start and src_len - ecbCrypt = make_runtime_call(RC_LEAF | RC_NO_FP, - OptoRuntime::electronicCodeBook_aescrypt_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, len); + Node* ecbCrypt = make_runtime_call(RC_LEAF | RC_NO_FP, + OptoRuntime::electronicCodeBook_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, len); // return cipher length (int) Node* retvalue = _gvn.transform(new ProjNode(ecbCrypt, TypeFunc::Parms)); @@ -5893,16 +5862,11 @@ bool LibraryCallKit::inline_counterMode_AESCrypt(vmIntrinsics::ID id) { Node* saved_encCounter_start = array_element_address(saved_encCounter, intcon(0), T_BYTE); Node* used = field_address_from_object(counterMode_object, "used", "I", /*is_exact*/ false); - Node* ctrCrypt; - if (Matcher::pass_original_key_for_aes()) { - // no SPARC version for AES/CTR intrinsics now. - return false; - } // Call the stub, passing src_start, dest_start, k_start, r_start and src_len - ctrCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, - OptoRuntime::counterMode_aescrypt_Type(), - stubAddr, stubName, TypePtr::BOTTOM, - src_start, dest_start, k_start, cnt_start, len, saved_encCounter_start, used); + Node* ctrCrypt = make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::counterMode_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, cnt_start, len, saved_encCounter_start, used); // return cipher length (int) Node* retvalue = _gvn.transform(new ProjNode(ctrCrypt, TypeFunc::Parms)); @@ -5934,17 +5898,6 @@ Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) return k_start; } -//------------------------------get_original_key_start_from_aescrypt_object----------------------- -Node * LibraryCallKit::get_original_key_start_from_aescrypt_object(Node *aescrypt_object) { - Node* objAESCryptKey = load_field_from_object(aescrypt_object, "lastKey", "[B", /*is_exact*/ false); - assert (objAESCryptKey != NULL, "wrong version of com.sun.crypto.provider.AESCrypt"); - if (objAESCryptKey == NULL) return (Node *) NULL; - - // now have the array, need to get the start address of the lastKey array - Node* original_k_start = array_element_address(objAESCryptKey, intcon(0), T_BYTE); - return original_k_start; -} - //----------------------------inline_cipherBlockChaining_AESCrypt_predicate---------------------------- // Return node representing slow path of predicate check. // the pseudo code we want to emulate with this predicate is: diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 476e1098fbfd3..cfff1d1052b17 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -273,7 +273,6 @@ class LibraryCallKit : public GraphKit { Node* inline_electronicCodeBook_AESCrypt_predicate(bool decrypting); Node* inline_counterMode_AESCrypt_predicate(); Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); - Node* get_original_key_start_from_aescrypt_object(Node* aescrypt_object); bool inline_ghash_processBlocks(); bool inline_base64_encodeBlock(); bool inline_base64_decodeBlock(); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 5409dc09bb153..285f33d8e9303 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -351,9 +351,6 @@ class Matcher : public PhaseTransform { // CPU supports misaligned vectors store/load. static const bool misaligned_vectors_ok(); - // Should original key array reference be passed to AES stubs - static const bool pass_original_key_for_aes(); - // Used to determine a "low complexity" 64-bit constant. (Zero is simple.) // The standard of comparison is one (StoreL ConL) vs. two (StoreI ConI). // Depends on the details of 64-bit constant generation on the CPU. diff --git a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp index 0bf1e17527176..b5943bde690d9 100644 --- a/src/hotspot/share/opto/runtime.cpp +++ b/src/hotspot/share/opto/runtime.cpp @@ -786,18 +786,12 @@ const TypeFunc* OptoRuntime::array_fill_Type() { const TypeFunc* OptoRuntime::aescrypt_block_Type() { // create input type (domain) int num_args = 3; - if (Matcher::pass_original_key_for_aes()) { - num_args = 4; - } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; fields[argp++] = TypePtr::NOTNULL; // src fields[argp++] = TypePtr::NOTNULL; // dest fields[argp++] = TypePtr::NOTNULL; // k array - if (Matcher::pass_original_key_for_aes()) { - fields[argp++] = TypePtr::NOTNULL; // original k array - } assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); @@ -879,9 +873,6 @@ const TypeFunc* OptoRuntime::updateBytesAdler32_Type() { const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { // create input type (domain) int num_args = 5; - if (Matcher::pass_original_key_for_aes()) { - num_args = 6; - } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; @@ -890,9 +881,6 @@ const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { fields[argp++] = TypePtr::NOTNULL; // k array fields[argp++] = TypePtr::NOTNULL; // r array fields[argp++] = TypeInt::INT; // src len - if (Matcher::pass_original_key_for_aes()) { - fields[argp++] = TypePtr::NOTNULL; // original k array - } assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); @@ -907,9 +895,6 @@ const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { const TypeFunc* OptoRuntime::electronicCodeBook_aescrypt_Type() { // create input type (domain) int num_args = 4; - if (Matcher::pass_original_key_for_aes()) { - num_args = 5; - } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; @@ -917,9 +902,6 @@ const TypeFunc* OptoRuntime::electronicCodeBook_aescrypt_Type() { fields[argp++] = TypePtr::NOTNULL; // dest fields[argp++] = TypePtr::NOTNULL; // k array fields[argp++] = TypeInt::INT; // src len - if (Matcher::pass_original_key_for_aes()) { - fields[argp++] = TypePtr::NOTNULL; // original k array - } assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields); @@ -934,9 +916,6 @@ const TypeFunc* OptoRuntime::electronicCodeBook_aescrypt_Type() { const TypeFunc* OptoRuntime::counterMode_aescrypt_Type() { // create input type (domain) int num_args = 7; - if (Matcher::pass_original_key_for_aes()) { - num_args = 8; - } int argcnt = num_args; const Type** fields = TypeTuple::fields(argcnt); int argp = TypeFunc::Parms; @@ -947,9 +926,6 @@ const TypeFunc* OptoRuntime::counterMode_aescrypt_Type() { fields[argp++] = TypeInt::INT; // src len fields[argp++] = TypePtr::NOTNULL; // saved_encCounter fields[argp++] = TypePtr::NOTNULL; // saved used addr - if (Matcher::pass_original_key_for_aes()) { - fields[argp++] = TypePtr::NOTNULL; // original k array - } assert(argp == TypeFunc::Parms + argcnt, "correct decoding"); const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + argcnt, fields); // returning cipher len (int) From 943acd22e58f92cca0789f3b3a1e8d696bedbe9f Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Thu, 12 Nov 2020 15:45:26 +0000 Subject: [PATCH 087/124] 8256276: Temporarily disable gtest special_flags Reviewed-by: tschatzl, dcubed --- test/hotspot/gtest/runtime/test_special_flags.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/gtest/runtime/test_special_flags.cpp b/test/hotspot/gtest/runtime/test_special_flags.cpp index eaff1ff18dc58..14e803f4cf5b0 100644 --- a/test/hotspot/gtest/runtime/test_special_flags.cpp +++ b/test/hotspot/gtest/runtime/test_special_flags.cpp @@ -30,7 +30,7 @@ // The test will only fail late in the release cycle as a reminder, // in case it has been forgotten. #ifdef ASSERT -TEST_VM(special_flags, verify_special_flags) { +TEST_VM(DISABLED_special_flags, verify_special_flags) { ASSERT_TRUE(Arguments::verify_special_jvm_flags(true)) << "Special flag verification failed"; } #endif From c6ab0fdb158ff18015e8f9f5e9c8e66a223ea3d7 Mon Sep 17 00:00:00 2001 From: Calvin Cheung Date: Thu, 12 Nov 2020 16:14:29 +0000 Subject: [PATCH 088/124] 8255990: Bitmap region of dynamic CDS archive is not unmapped Reviewed-by: iklam, minqi --- src/hotspot/share/memory/metaspaceShared.cpp | 1 + .../dynamicArchive/DynamicArchiveRelocationTest.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index aeab5c2f2a1c0..18e4cd6e50af2 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -1748,6 +1748,7 @@ void MetaspaceShared::initialize_shared_spaces() { SymbolTable::serialize_shared_table_header(&rc, false); SystemDictionaryShared::serialize_dictionary_headers(&rc, false); dynamic_mapinfo->close(); + dynamic_mapinfo->unmap_region(MetaspaceShared::bm); } if (PrintSharedArchiveAndExit) { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java index 8c5e70b63d108..3c6daa61269d7 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java @@ -91,6 +91,8 @@ static void doTest() throws Exception { String topArchiveName = getNewArchiveName("top"); String runtimeMsg = "Try to map archive(s) at an alternative address"; + String unmapPrefix = ".*Unmapping region #3 at base 0x.*"; + String unmapPattern = unmapPrefix + "(Bitmap)"; String unlockArg = "-XX:+UnlockDiagnosticVMOptions"; // (1) Dump base archive (static) @@ -121,7 +123,12 @@ static void doTest() throws Exception { "-cp", appJar, mainClass) .assertNormalExit(output -> { if (run_reloc) { - output.shouldContain(runtimeMsg); + output.shouldContain(runtimeMsg) + // Check that there are two of the following lines in + // the output. One for static archive and one for + // dynamic archive: + // Unmapping region #3 at base 0x (Bitmap) + .shouldMatchByLine(unmapPrefix, "Hello World", unmapPattern); } }); } From 3e70aac5cc0833b73a7fed4e4e0404c2284d90d0 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 12 Nov 2020 16:37:23 +0000 Subject: [PATCH 089/124] 8254162: Implementation of Foreign-Memory Access API (Third Incubator) Reviewed-by: erikj, psandoz, alanb --- make/modules/java.base/Gensrc.gmk | 1 + .../gensrc/GensrcScopedMemoryAccess.gmk | 151 ++ .../share/classfile/classFileParser.cpp | 8 + src/hotspot/share/classfile/vmSymbols.hpp | 1 + src/hotspot/share/oops/method.hpp | 11 +- src/hotspot/share/prims/nativeLookup.cpp | 2 + .../share/prims/scopedMemoryAccess.cpp | 186 +++ .../share/prims/scopedMemoryAccess.hpp | 35 + .../invoke/MemoryAccessVarHandleBase.java | 17 +- .../MemoryAccessVarHandleGenerator.java | 554 ------- .../java/lang/invoke/MethodHandleImpl.java | 57 +- .../classes/java/lang/invoke/VarHandles.java | 36 +- .../X-VarHandleByteArrayView.java.template | 96 +- .../X-VarHandleMemoryAccess.java.template | 372 ++--- .../share/classes/java/nio/Bits.java | 6 +- .../share/classes/java/nio/Buffer.java | 31 +- .../classes/java/nio/BufferMismatch.java | 17 +- .../nio/ByteBufferAs-X-Buffer.java.template | 14 +- .../nio/Direct-X-Buffer-bin.java.template | 6 +- .../java/nio/Direct-X-Buffer.java.template | 57 +- .../java/nio/Heap-X-Buffer.java.template | 89 +- .../classes/java/nio/MappedByteBuffer.java | 9 +- .../classes/java/nio/X-Buffer.java.template | 17 +- .../internal/access/JavaLangInvokeAccess.java | 40 +- .../jdk/internal/access/JavaNioAccess.java | 15 + .../access/foreign/MemorySegmentProxy.java | 70 +- .../X-ScopedMemoryAccess-bin.java.template | 698 +++++++++ .../misc/X-ScopedMemoryAccess.java.template | 327 ++++ .../jdk/internal/util/ArraysSupport.java | 31 - src/java.base/share/classes/module-info.java | 3 +- .../jdk/incubator/foreign/Addressable.java | 46 + .../foreign/MappedMemorySegment.java | 132 -- .../foreign/MappedMemorySegments.java | 160 ++ .../jdk/incubator/foreign/MemoryAccess.java | 1336 +++++++++++++++++ .../jdk/incubator/foreign/MemoryAddress.java | 137 +- .../jdk/incubator/foreign/MemoryHandles.java | 168 +-- .../jdk/incubator/foreign/MemoryLayout.java | 24 +- .../jdk/incubator/foreign/MemoryLayouts.java | 27 +- .../jdk/incubator/foreign/MemorySegment.java | 529 +++++-- .../jdk/incubator/foreign/package-info.java | 39 +- .../foreign/AbstractMemorySegmentImpl.java | 281 ++-- .../foreign/HeapMemorySegmentImpl.java | 2 +- .../jdk/internal/foreign/LayoutPath.java | 57 +- .../foreign/MappedMemorySegmentImpl.java | 37 +- .../internal/foreign/MemoryAddressImpl.java | 85 +- .../jdk/internal/foreign/MemoryScope.java | 415 +++-- .../foreign/NativeMemorySegmentImpl.java | 32 +- .../classes/jdk/internal/foreign/Utils.java | 20 +- test/jdk/ProblemList.txt | 1 - .../jdk/java/foreign/TestAdaptVarHandles.java | 142 +- test/jdk/java/foreign/TestAddressHandle.java | 51 +- test/jdk/java/foreign/TestArrays.java | 129 +- test/jdk/java/foreign/TestByteBuffer.java | 372 +++-- test/jdk/java/foreign/TestCleaner.java | 185 +++ test/jdk/java/foreign/TestHandshake.java | 260 ++++ test/jdk/java/foreign/TestLayouts.java | 43 +- test/jdk/java/foreign/TestMemoryAccess.java | 167 +-- .../jdk/java/foreign/TestMemoryAlignment.java | 25 +- test/jdk/java/foreign/TestMemoryCopy.java | 9 +- .../foreign/TestMemoryHandleAsUnsigned.java | 53 +- test/jdk/java/foreign/TestMismatch.java | 24 +- test/jdk/java/foreign/TestNative.java | 86 +- .../foreign/TestNoForeignUnsafeOverride.java | 4 +- test/jdk/java/foreign/TestRebase.java | 23 +- test/jdk/java/foreign/TestSegments.java | 128 +- test/jdk/java/foreign/TestSharedAccess.java | 128 +- test/jdk/java/foreign/TestSlices.java | 10 +- test/jdk/java/foreign/TestSpliterator.java | 34 +- .../foreign/TestVarHandleCombinators.java | 96 +- .../invoke/VarHandles/VarHandleTestExact.java | 21 +- .../util/stream/SegmentTestDataProvider.java | 25 +- .../java/util/stream/SpliteratorTest.java | 2 +- .../incubator/foreign/LoopOverConstant.java | 8 +- .../jdk/incubator/foreign/LoopOverNew.java | 12 +- .../foreign/LoopOverNonConstant.java | 21 +- .../foreign/LoopOverNonConstantHeap.java | 19 +- .../foreign/LoopOverNonConstantMapped.java | 24 +- .../foreign/LoopOverNonConstantShared.java | 166 ++ .../jdk/incubator/foreign/ParallelSum.java | 47 +- .../foreign/TestAdaptVarHandles.java | 6 +- .../jdk/incubator/foreign/VarHandleExact.java | 8 +- .../foreign/points/support/PanamaPoint.java | 8 +- 82 files changed, 6011 insertions(+), 2810 deletions(-) create mode 100644 make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk create mode 100644 src/hotspot/share/prims/scopedMemoryAccess.cpp create mode 100644 src/hotspot/share/prims/scopedMemoryAccess.hpp delete mode 100644 src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java create mode 100644 src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess-bin.java.template create mode 100644 src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template create mode 100644 src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java delete mode 100644 src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegment.java create mode 100644 src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegments.java create mode 100644 src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java create mode 100644 test/jdk/java/foreign/TestCleaner.java create mode 100644 test/jdk/java/foreign/TestHandshake.java create mode 100644 test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java diff --git a/make/modules/java.base/Gensrc.gmk b/make/modules/java.base/Gensrc.gmk index 3d473df7a1c2c..f1eb5ff7e8000 100644 --- a/make/modules/java.base/Gensrc.gmk +++ b/make/modules/java.base/Gensrc.gmk @@ -35,6 +35,7 @@ include gensrc/GensrcExceptions.gmk include gensrc/GensrcVarHandles.gmk include gensrc/GensrcModuleLoaderMap.gmk include gensrc/GensrcEmojiData.gmk +include gensrc/GensrcScopedMemoryAccess.gmk # GensrcLocaleData.gmk does not set TARGETS, so we must choose which targets # to include. diff --git a/make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk b/make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk new file mode 100644 index 0000000000000..3afd76473feb1 --- /dev/null +++ b/make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk @@ -0,0 +1,151 @@ +# +# Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +SCOPED_MEMORY_ACCESS_GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/jdk/internal/misc +SCOPED_MEMORY_ACCESS_SRC_DIR := $(TOPDIR)/src/java.base/share/classes/jdk/internal/misc +SCOPED_MEMORY_ACCESS_TEMPLATE := $(SCOPED_MEMORY_ACCESS_SRC_DIR)/X-ScopedMemoryAccess.java.template +SCOPED_MEMORY_ACCESS_BIN_TEMPLATE := $(SCOPED_MEMORY_ACCESS_SRC_DIR)/X-ScopedMemoryAccess-bin.java.template +SCOPED_MEMORY_ACCESS_DEST := $(SCOPED_MEMORY_ACCESS_GENSRC_DIR)/ScopedMemoryAccess.java + +################################################################################ +# Setup a rule for generating the ScopedMemoryAccess java class +# Param 1 - Variable declaration prefix +# Param 2 - Type with first letter capitalized +define GenerateScopedOp + + $1_Type := $2 + + ifeq ($$($1_Type), Byte) + $1_type := byte + $1_BoxType := $$($1_Type) + + $1_rawType := $$($1_type) + $1_RawType := $$($1_Type) + $1_RawBoxType := $$($1_BoxType) + + $1_ARGS += -Kbyte + endif + + ifeq ($$($1_Type), Short) + $1_type := short + $1_BoxType := $$($1_Type) + + $1_rawType := $$($1_type) + $1_RawType := $$($1_Type) + $1_RawBoxType := $$($1_BoxType) + $1_ARGS += -KUnaligned + endif + + ifeq ($$($1_Type), Char) + $1_type := char + $1_BoxType := Character + + $1_rawType := $$($1_type) + $1_RawType := $$($1_Type) + $1_RawBoxType := $$($1_BoxType) + $1_ARGS += -KUnaligned + endif + + ifeq ($$($1_Type), Int) + $1_type := int + $1_BoxType := Integer + + $1_rawType := $$($1_type) + $1_RawType := $$($1_Type) + $1_RawBoxType := $$($1_BoxType) + + $1_ARGS += -KCAS + $1_ARGS += -KAtomicAdd + $1_ARGS += -KBitwise + $1_ARGS += -KUnaligned + endif + + ifeq ($$($1_Type), Long) + $1_type := long + $1_BoxType := $$($1_Type) + + $1_rawType := $$($1_type) + $1_RawType := $$($1_Type) + $1_RawBoxType := $$($1_BoxType) + + $1_ARGS += -KCAS + $1_ARGS += -KAtomicAdd + $1_ARGS += -KBitwise + $1_ARGS += -KUnaligned + endif + + ifeq ($$($1_Type), Float) + $1_type := float + $1_BoxType := $$($1_Type) + + $1_rawType := int + $1_RawType := Int + $1_RawBoxType := Integer + + $1_ARGS += -KCAS + $1_ARGS += -KfloatingPoint + endif + + ifeq ($$($1_Type), Double) + $1_type := double + $1_BoxType := $$($1_Type) + + $1_rawType := long + $1_RawType := Long + $1_RawBoxType := Long + + $1_ARGS += -KCAS + $1_ARGS += -KfloatingPoint + endif + + ifneq ($$(findstring $$($1_Type), Byte Short Char Int Long Float Double), ) + $1_ARGS += -KAtomicAdd + endif + + ifneq ($$(findstring $$($1_Type), Boolean Byte Short Char Int Long), ) + $1_ARGS += -KBitwise + endif + + ifneq ($$(findstring $$($1_Type), Byte Short Char), ) + $1_ARGS += -KShorterThanInt + endif +endef + +################################################################################ +# Setup a rule for generating the ScopedMemoryAccess java class + +SCOPE_MEMORY_ACCESS_TYPES := Byte Short Char Int Long Float Double +$(foreach t, $(SCOPE_MEMORY_ACCESS_TYPES), \ + $(eval $(call GenerateScopedOp,BIN_$t,$t))) + +$(SCOPED_MEMORY_ACCESS_DEST): $(BUILD_TOOLS_JDK) $(SCOPED_MEMORY_ACCESS_TEMPLATE) $(SCOPED_MEMORY_ACCESS_BIN_TEMPLATE) + $(call MakeDir, $(SCOPED_MEMORY_ACCESS_GENSRC_DIR)) + $(CP) $(SCOPED_MEMORY_ACCESS_TEMPLATE) $(SCOPED_MEMORY_ACCESS_DEST) + $(foreach t, $(SCOPE_MEMORY_ACCESS_TYPES), \ + $(TOOL_SPP) -nel -K$(BIN_$t_type) -Dtype=$(BIN_$t_type) -DType=$(BIN_$t_Type) $(BIN_$t_ARGS) \ + -i$(SCOPED_MEMORY_ACCESS_BIN_TEMPLATE) -o$(SCOPED_MEMORY_ACCESS_DEST) ;) + $(PRINTF) "}\n" >> $(SCOPED_MEMORY_ACCESS_DEST) + +TARGETS += $(SCOPED_MEMORY_ACCESS_DEST) diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 52767594516b6..c1e8564eea3e3 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1086,6 +1086,7 @@ class AnnotationCollector : public ResourceObj{ _method_InjectedProfile, _method_LambdaForm_Compiled, _method_Hidden, + _method_Scoped, _method_IntrinsicCandidate, _jdk_internal_vm_annotation_Contended, _field_Stable, @@ -2117,6 +2118,11 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, if (!privileged) break; // only allow in privileged code return _method_Hidden; } + case VM_SYMBOL_ENUM_NAME(jdk_internal_misc_Scoped_signature): { + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_Scoped; + } case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_IntrinsicCandidate_signature): { if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code @@ -2174,6 +2180,8 @@ void MethodAnnotationCollector::apply_to(const methodHandle& m) { m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm); if (has_annotation(_method_Hidden)) m->set_hidden(true); + if (has_annotation(_method_Scoped)) + m->set_scoped(true); if (has_annotation(_method_IntrinsicCandidate) && !m->is_synthetic()) m->set_intrinsic_candidate(true); if (has_annotation(_jdk_internal_vm_annotation_ReservedStackAccess)) diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index df2737f743fe6..d7f81ff966d53 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -302,6 +302,7 @@ template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \ template(jdk_internal_vm_annotation_ForceInline_signature, "Ljdk/internal/vm/annotation/ForceInline;") \ template(jdk_internal_vm_annotation_Hidden_signature, "Ljdk/internal/vm/annotation/Hidden;") \ + template(jdk_internal_misc_Scoped_signature, "Ljdk/internal/misc/ScopedMemoryAccess$Scoped;") \ template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \ template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp index a549e7253b31d..4ac77138c691a 100644 --- a/src/hotspot/share/oops/method.hpp +++ b/src/hotspot/share/oops/method.hpp @@ -91,7 +91,8 @@ class Method : public Metadata { _has_injected_profile = 1 << 4, _running_emcp = 1 << 5, _intrinsic_candidate = 1 << 6, - _reserved_stack_access = 1 << 7 + _reserved_stack_access = 1 << 7, + _scoped = 1 << 8 }; mutable u2 _flags; @@ -901,6 +902,14 @@ class Method : public Metadata { _flags = x ? (_flags | _hidden) : (_flags & ~_hidden); } + bool is_scoped() const { + return (_flags & _scoped) != 0; + } + + void set_scoped(bool x) { + _flags = x ? (_flags | _scoped) : (_flags & ~_scoped); + } + bool intrinsic_candidate() { return (_flags & _intrinsic_candidate) != 0; } diff --git a/src/hotspot/share/prims/nativeLookup.cpp b/src/hotspot/share/prims/nativeLookup.cpp index 1523556ad4d36..9fd0cd75f2784 100644 --- a/src/hotspot/share/prims/nativeLookup.cpp +++ b/src/hotspot/share/prims/nativeLookup.cpp @@ -39,6 +39,7 @@ #include "prims/jvmtiExport.hpp" #include "prims/nativeLookup.hpp" #include "prims/unsafe.hpp" +#include "prims/scopedMemoryAccess.hpp" #include "runtime/arguments.hpp" #include "runtime/handles.inline.hpp" #include "runtime/interfaceSupport.inline.hpp" @@ -240,6 +241,7 @@ static JNINativeMethod lookup_special_native_methods[] = { #if INCLUDE_JFR { CC"Java_jdk_jfr_internal_JVM_registerNatives", NULL, FN_PTR(jfr_register_natives) }, #endif + { CC"Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives", NULL, FN_PTR(JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods) }, }; static address lookup_special_native(const char* jni_name) { diff --git a/src/hotspot/share/prims/scopedMemoryAccess.cpp b/src/hotspot/share/prims/scopedMemoryAccess.cpp new file mode 100644 index 0000000000000..3f03a25fc679e --- /dev/null +++ b/src/hotspot/share/prims/scopedMemoryAccess.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jni.h" +#include "jvm.h" +#include "classfile/vmSymbols.hpp" +#include "oops/access.inline.hpp" +#include "oops/oop.inline.hpp" +#include "runtime/jniHandles.inline.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/vframe.inline.hpp" +#include "runtime/deoptimization.hpp" +#include "prims/stackwalk.hpp" + + +class CloseScopedMemoryFindOopClosure : public OopClosure { + oop _deopt; + bool _found; + +public: + CloseScopedMemoryFindOopClosure(jobject deopt) : + _deopt(JNIHandles::resolve(deopt)), + _found(false) {} + + template + void do_oop_work(T* p) { + if (_found) { + return; + } + if (RawAccess<>::oop_load(p) == _deopt) { + _found = true; + } + } + + virtual void do_oop(oop* p) { + do_oop_work(p); + } + + virtual void do_oop(narrowOop* p) { + do_oop_work(p); + } + + bool found() { + return _found; + } +}; + +class CloseScopedMemoryClosure : public HandshakeClosure { + jobject _deopt; + jobject _exception; + +public: + jboolean _found; + + CloseScopedMemoryClosure(jobject deopt, jobject exception) + : HandshakeClosure("CloseScopedMemory") + , _deopt(deopt) + , _exception(exception) + , _found(false) {} + + void do_thread(Thread* thread) { + + JavaThread* jt = (JavaThread*)thread; + + if (!jt->has_last_Java_frame()) { + return; + } + + frame last_frame = jt->last_frame(); + RegisterMap register_map(jt, true); + + if (last_frame.is_safepoint_blob_frame()) { + last_frame = last_frame.sender(®ister_map); + } + + ResourceMark rm; + if (_deopt != NULL && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) { + CloseScopedMemoryFindOopClosure cl(_deopt); + CompiledMethod* cm = last_frame.cb()->as_compiled_method(); + + /* FIXME: this doesn't work if reachability fences are violated by C2 + last_frame.oops_do(&cl, NULL, ®ister_map); + if (cl.found()) { + //Found the deopt oop in a compiled method; deoptimize. + Deoptimization::deoptimize(jt, last_frame); + } + so... we unconditionally deoptimize, for now: */ + Deoptimization::deoptimize(jt, last_frame); + } + + const int max_critical_stack_depth = 10; + int depth = 0; + for (vframeStream stream(jt); !stream.at_end(); stream.next()) { + Method* m = stream.method(); + if (m->is_scoped()) { + StackValueCollection* locals = stream.asJavaVFrame()->locals(); + for (int i = 0; i < locals->size(); i++) { + StackValue* var = locals->at(i); + if (var->type() == T_OBJECT) { + if (var->get_obj() == JNIHandles::resolve(_deopt)) { + assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth); + _found = true; + return; + } + } + } + break; + } + depth++; +#ifndef ASSERT + if (depth >= max_critical_stack_depth) { + break; + } +#endif + } + } +}; + +/* + * This function issues a global handshake operation with all + * Java threads. This is useful for implementing asymmetric + * dekker synchronization schemes, where expensive synchronization + * in performance sensitive common paths, may be shifted to + * a less common slow path instead. + * Top frames containg obj will be deoptimized. + */ +JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception)) + CloseScopedMemoryClosure cl(deopt, exception); + Handshake::execute(&cl); + return !cl._found; +JVM_END + +/// JVM_RegisterUnsafeMethods + +#define PKG "Ljdk/internal/misc/" + +#define MEMACCESS "ScopedMemoryAccess" +#define SCOPE PKG MEMACCESS "$Scope;" +#define SCOPED_ERR PKG MEMACCESS "$Scope$ScopedAccessError;" + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) + +static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = { + {CC "closeScope0", CC "(" SCOPE SCOPED_ERR ")Z", FN_PTR(ScopedMemoryAccess_closeScope)}, +}; + +#undef CC +#undef FN_PTR + +#undef PKG +#undef MEMACCESS +#undef SCOPE +#undef SCOPED_EXC + +// This function is exported, used by NativeLookup. + +JVM_ENTRY(void, JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass)) + ThreadToNativeFromVM ttnfv(thread); + + int ok = env->RegisterNatives(scopedMemoryAccessClass, jdk_internal_misc_ScopedMemoryAccess_methods, sizeof(jdk_internal_misc_ScopedMemoryAccess_methods)/sizeof(JNINativeMethod)); + guarantee(ok == 0, "register jdk.internal.misc.ScopedMemoryAccess natives"); +JVM_END diff --git a/src/hotspot/share/prims/scopedMemoryAccess.hpp b/src/hotspot/share/prims/scopedMemoryAccess.hpp new file mode 100644 index 0000000000000..050e729f84481 --- /dev/null +++ b/src/hotspot/share/prims/scopedMemoryAccess.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + + +#ifndef SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP +#define SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP + +#include "jni.h" + +extern "C" { + void JNICALL JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jobject rec, jobject scope, jthrowable exception); +} + +#endif // SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP diff --git a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java index 0176cbf6d474d..59098e9fa74dc 100644 --- a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java +++ b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleBase.java @@ -36,28 +36,21 @@ abstract class MemoryAccessVarHandleBase extends VarHandle { /** access size (in bytes, computed from var handle carrier type) **/ final long length; - /** access offset (in bytes); must be compatible with {@code alignmentMask} **/ - final long offset; - /** alignment constraint (in bytes, expressed as a bit mask) **/ final long alignmentMask; - MemoryAccessVarHandleBase(VarForm form, boolean be, long length, long offset, long alignmentMask, boolean exact) { + /** if true, only the base part of the address will be checked for alignment **/ + final boolean skipAlignmentMaskCheck; + + MemoryAccessVarHandleBase(VarForm form, boolean skipAlignmentMaskCheck, boolean be, long length, long alignmentMask, boolean exact) { super(form, exact); + this.skipAlignmentMaskCheck = skipAlignmentMaskCheck; this.be = be; this.length = length; - this.offset = offset; this.alignmentMask = alignmentMask; } static IllegalStateException newIllegalStateExceptionForMisalignedAccess(long address) { return new IllegalStateException("Misaligned access at address: " + address); } - - /** - * Strides used for multi-dimensional access; each stride must be compatible with {@code alignmentMask}. - */ - abstract long[] strides(); - - abstract Class carrier(); } diff --git a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java b/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java deleted file mode 100644 index addd09f82b722..0000000000000 --- a/src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.invoke; - -import jdk.internal.access.foreign.MemoryAddressProxy; -import jdk.internal.org.objectweb.asm.ClassReader; -import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.ConstantDynamic; -import jdk.internal.org.objectweb.asm.Handle; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; -import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; -import jdk.internal.vm.annotation.ForceInline; -import sun.security.action.GetBooleanAction; -import sun.security.action.GetPropertyAction; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.Arrays; -import java.util.HashMap; - -import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; -import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; -import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH; -import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; -import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD; -import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0; -import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1; -import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2; -import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3; -import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE; -import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.NEW; -import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; -import static jdk.internal.org.objectweb.asm.Opcodes.DUP; -import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH; -import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG; -import static jdk.internal.org.objectweb.asm.Opcodes.V14; - -class MemoryAccessVarHandleGenerator { - private static final String DEBUG_DUMP_CLASSES_DIR_PROPERTY = "jdk.internal.foreign.ClassGenerator.DEBUG_DUMP_CLASSES_DIR"; - - private static final boolean DEBUG = - GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.ClassGenerator.DEBUG"); - - private static final Class BASE_CLASS = MemoryAccessVarHandleBase.class; - - private static final HashMap, Class> helperClassCache; - - private final static MethodType OFFSET_OP_TYPE; - - private final static MethodHandle ADD_OFFSETS_HANDLE; - private final static MethodHandle MUL_OFFSETS_HANDLE; - - private final static MethodType CONSTR_TYPE = MethodType.methodType(void.class, VarForm.class, - boolean.class, long.class, long.class, long.class, boolean.class, long[].class); - // MemoryAccessVarHandleBase - private final static MethodType SUPER_CONTR_TYPE = MethodType.methodType(void.class, VarForm.class, - boolean.class, long.class, long.class, long.class, boolean.class); - - static { - helperClassCache = new HashMap<>(); - helperClassCache.put(byte.class, MemoryAccessVarHandleByteHelper.class); - helperClassCache.put(short.class, MemoryAccessVarHandleShortHelper.class); - helperClassCache.put(char.class, MemoryAccessVarHandleCharHelper.class); - helperClassCache.put(int.class, MemoryAccessVarHandleIntHelper.class); - helperClassCache.put(long.class, MemoryAccessVarHandleLongHelper.class); - helperClassCache.put(float.class, MemoryAccessVarHandleFloatHelper.class); - helperClassCache.put(double.class, MemoryAccessVarHandleDoubleHelper.class); - - OFFSET_OP_TYPE = MethodType.methodType(long.class, long.class, long.class, MemoryAddressProxy.class); - - try { - ADD_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "addOffsets", OFFSET_OP_TYPE); - MUL_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "multiplyOffsets", OFFSET_OP_TYPE); - } catch (Throwable ex) { - throw new ExceptionInInitializerError(ex); - } - } - - private static final File DEBUG_DUMP_CLASSES_DIR; - - static { - String path = GetPropertyAction.privilegedGetProperty(DEBUG_DUMP_CLASSES_DIR_PROPERTY); - if (path == null) { - DEBUG_DUMP_CLASSES_DIR = null; - } else { - DEBUG_DUMP_CLASSES_DIR = new File(path); - } - } - - private final String implClassName; - private final int dimensions; - private final Class carrier; - private final Class helperClass; - private final VarForm form; - private final Object[] classData; - - MemoryAccessVarHandleGenerator(Class carrier, int dims) { - this.dimensions = dims; - this.carrier = carrier; - Class[] components = new Class[dimensions]; - Arrays.fill(components, long.class); - this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components); - this.helperClass = helperClassCache.get(carrier); - this.implClassName = internalName(helperClass) + dimensions; - // live constants - Class[] intermediate = new Class[dimensions]; - Arrays.fill(intermediate, long.class); - this.classData = new Object[] { carrier, intermediate, ADD_OFFSETS_HANDLE, MUL_OFFSETS_HANDLE }; - } - - /* - * Generate a VarHandle memory access factory. - * The factory has type (ZJJ[J)VarHandle. - */ - MethodHandle generateHandleFactory() { - byte[] classBytes = generateClassBytes(); - if (DEBUG_DUMP_CLASSES_DIR != null) { - debugWriteClassToFile(classBytes); - } - try { - MethodHandles.Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(classBytes, classData); - Class implCls = lookup.lookupClass(); - Class[] components = new Class[dimensions]; - Arrays.fill(components, long.class); - - VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components); - - MethodHandle constr = lookup.findConstructor(implCls, CONSTR_TYPE); - constr = MethodHandles.insertArguments(constr, 0, form); - return constr; - } catch (Throwable ex) { - debugPrintClass(classBytes); - throw new AssertionError(ex); - } - } - - /* - * Generate a specialized VarHandle class for given carrier - * and access coordinates. - */ - byte[] generateClassBytes() { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); - - if (DEBUG) { - System.out.println("Generating header implementation class"); - } - - cw.visit(V14, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null); - - //add dimension fields - for (int i = 0; i < dimensions; i++) { - cw.visitField(ACC_PRIVATE | ACC_FINAL, "dim" + i, "J", null, null); - } - - addStaticInitializer(cw); - - addConstructor(cw); - - addAccessModeTypeMethod(cw); - - addStridesAccessor(cw); - - addCarrierAccessor(cw); - - addAsExact(cw); - addAsGeneric(cw); - - for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) { - addAccessModeMethodIfNeeded(mode, cw); - } - - cw.visitEnd(); - return cw.toByteArray(); - } - - void addStaticInitializer(ClassWriter cw) { - // carrier and intermediate - cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "carrier", Class.class.descriptorString(), null, null); - cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "intermediate", Class[].class.descriptorString(), null, null); - cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "addHandle", MethodHandle.class.descriptorString(), null, null); - cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "mulHandle", MethodHandle.class.descriptorString(), null, null); - - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); - mv.visitCode(); - // extract class data in static final fields - MethodType mtype = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class); - Handle bsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData", - mtype.descriptorString(), false); - ConstantDynamic dynamic = new ConstantDynamic("classData", Object[].class.descriptorString(), bsm); - mv.visitLdcInsn(dynamic); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object[].class)); - mv.visitVarInsn(ASTORE, 0); - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class)); - mv.visitFieldInsn(PUTSTATIC, implClassName, "carrier", Class.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_1); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class)); - mv.visitFieldInsn(PUTSTATIC, implClassName, "intermediate", Class[].class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_2); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); - mv.visitFieldInsn(PUTSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_3); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); - mv.visitFieldInsn(PUTSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString()); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - void addConstructor(ClassWriter cw) { - MethodVisitor mv = cw.visitMethod(0, "", CONSTR_TYPE.toMethodDescriptorString(), null, null); - mv.visitCode(); - //super call - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); // vform - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class)); - mv.visitVarInsn(ILOAD, 2); // be - mv.visitVarInsn(LLOAD, 3); // length - mv.visitVarInsn(LLOAD, 5); // offset - mv.visitVarInsn(LLOAD, 7); // alignmentMask - mv.visitVarInsn(ILOAD, 9); // exact - mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "", - SUPER_CONTR_TYPE.toMethodDescriptorString(), false); - //init dimensions - for (int i = 0 ; i < dimensions ; i++) { - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 10); - mv.visitLdcInsn(i); - mv.visitInsn(LALOAD); - mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J"); - } - mv.visitInsn(RETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - void addAccessModeTypeMethod(ClassWriter cw) { - MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessType.class); - MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null); - mv.visitCode(); - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(Type.getType(MemoryAddressProxy.class)); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class)); - mv.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString()); - mv.visitFieldInsn(GETSTATIC, implClassName, "intermediate", Class[].class.descriptorString()); - - mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class), - "accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false); - - mv.visitInsn(ARETURN); - - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, ClassWriter cw) { - String methName = mode.methodName(); - MethodType methType = form.getMethodType(mode.at.ordinal()) - .insertParameterTypes(0, VarHandle.class); - - try { - MethodType helperType = methType.insertParameterTypes(2, long.class); - if (dimensions > 0) { - helperType = helperType.dropParameterTypes(3, 3 + dimensions); - } - //try to resolve... - String helperMethodName = methName + "0"; - MethodHandles.Lookup.IMPL_LOOKUP - .findStatic(helperClass, - helperMethodName, - helperType); - - - MethodVisitor mv = cw.visitMethod(ACC_STATIC, methName, methType.toMethodDescriptorString(), null, null); - mv.visitAnnotation(Type.getDescriptor(ForceInline.class), true); - mv.visitCode(); - - mv.visitVarInsn(ALOAD, 0); // handle impl - mv.visitVarInsn(ALOAD, 1); // receiver - - // offset calculation - int slot = 2; - mv.visitVarInsn(ALOAD, 0); // load recv - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(BASE_CLASS)); - mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J"); - for (int i = 0 ; i < dimensions ; i++) { - // load ADD MH - mv.visitFieldInsn(GETSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString()); - - //fixup stack so that ADD MH ends up bottom - mv.visitInsn(Opcodes.DUP_X2); - mv.visitInsn(Opcodes.POP); - - // load MUL MH - mv.visitFieldInsn(GETSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString()); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); - - mv.visitVarInsn(ALOAD, 0); // load recv - mv.visitTypeInsn(CHECKCAST, implClassName); - mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J"); - mv.visitVarInsn(LLOAD, slot); - - mv.visitVarInsn(ALOAD, 1); // receiver - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class)); - - //MUL - mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", - OFFSET_OP_TYPE.toMethodDescriptorString(), false); - - mv.visitVarInsn(ALOAD, 1); // receiver - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class)); - - //ADD - mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", - OFFSET_OP_TYPE.toMethodDescriptorString(), false); - slot += 2; - } - - for (int i = 2 + dimensions; i < methType.parameterCount() ; i++) { - Class param = methType.parameterType(i); - mv.visitVarInsn(loadInsn(param), slot); // load index - slot += getSlotsForType(param); - } - - //call helper - mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(helperClass), helperMethodName, - helperType.toMethodDescriptorString(), false); - - mv.visitInsn(returnInsn(helperType.returnType())); - - mv.visitMaxs(0, 0); - mv.visitEnd(); - } catch (ReflectiveOperationException ex) { - //not found, skip - } - } - - void addStridesAccessor(ClassWriter cw) { - MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null); - mv.visitCode(); - iConstInsn(mv, dimensions); - mv.visitIntInsn(NEWARRAY, T_LONG); - - for (int i = 0 ; i < dimensions ; i++) { - mv.visitInsn(DUP); - iConstInsn(mv, i); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J"); - mv.visitInsn(LASTORE); - } - - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - void addCarrierAccessor(ClassWriter cw) { - MethodVisitor mv = cw.visitMethod(ACC_FINAL, "carrier", "()Ljava/lang/Class;", null, null); - mv.visitCode(); - mv.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString()); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - private void addAsExact(ClassWriter cw) { - addAsExactOrAsGeneric(cw, "asExact", true); - } - - private void addAsGeneric(ClassWriter cw) { - addAsExactOrAsGeneric(cw, "asGeneric", false); - } - - private void addAsExactOrAsGeneric(ClassWriter cw, String name, boolean exact) { - MethodVisitor mv = cw.visitMethod(ACC_FINAL, name, "()Ljava/lang/invoke/VarHandle;", null, null); - mv.visitCode(); - mv.visitTypeInsn(NEW, implClassName); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, internalName(VarHandle.class), "vform", VarForm.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "be", boolean.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "length", long.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "offset", long.class.descriptorString()); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "alignmentMask", long.class.descriptorString()); - mv.visitIntInsn(BIPUSH, exact ? 1 : 0); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, implClassName, "strides", "()[J", false); - mv.visitMethodInsn(INVOKESPECIAL, implClassName, "", CONSTR_TYPE.descriptorString(), false); - mv.visitInsn(ARETURN); - mv.visitMaxs(0, 0); - mv.visitEnd(); - } - - // shared code generation helpers - - private static int getSlotsForType(Class c) { - if (c == long.class || c == double.class) { - return 2; - } - return 1; - } - - private static String internalName(Class cls) { - return cls.getName().replace('.', '/'); - } - - /** - * Emits an actual return instruction conforming to the given return type. - */ - private int returnInsn(Class type) { - return switch (LambdaForm.BasicType.basicType(type)) { - case I_TYPE -> Opcodes.IRETURN; - case J_TYPE -> Opcodes.LRETURN; - case F_TYPE -> Opcodes.FRETURN; - case D_TYPE -> Opcodes.DRETURN; - case L_TYPE -> Opcodes.ARETURN; - case V_TYPE -> RETURN; - }; - } - - private int loadInsn(Class type) { - return switch (LambdaForm.BasicType.basicType(type)) { - case I_TYPE -> Opcodes.ILOAD; - case J_TYPE -> LLOAD; - case F_TYPE -> Opcodes.FLOAD; - case D_TYPE -> Opcodes.DLOAD; - case L_TYPE -> Opcodes.ALOAD; - case V_TYPE -> throw new IllegalStateException("Cannot load void"); - }; - } - - private static void iConstInsn(MethodVisitor mv, int i) { - switch (i) { - case -1, 0, 1, 2, 3, 4, 5: - mv.visitInsn(ICONST_0 + i); - break; - default: - if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, i); - } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, i); - } else { - mv.visitLdcInsn(i); - } - } - } - - // debug helpers - - private static String debugPrintClass(byte[] classFile) { - ClassReader cr = new ClassReader(classFile); - StringWriter sw = new StringWriter(); - cr.accept(new TraceClassVisitor(new PrintWriter(sw)), 0); - return sw.toString(); - } - - private void debugWriteClassToFile(byte[] classFile) { - File file = new File(DEBUG_DUMP_CLASSES_DIR, implClassName + ".class"); - - if (DEBUG) { - System.err.println("Dumping class " + implClassName + " to " + file); - } - - try { - debugWriteDataToFile(classFile, file); - } catch (Exception e) { - throw new RuntimeException("Failed to write class " + implClassName + " to file " + file); - } - } - - private void debugWriteDataToFile(byte[] data, File file) { - if (file.exists()) { - file.delete(); - } - if (file.exists()) { - throw new RuntimeException("Failed to remove pre-existing file " + file); - } - - File parent = file.getParentFile(); - if (!parent.exists()) { - parent.mkdirs(); - } - if (!parent.exists()) { - throw new RuntimeException("Failed to create " + parent); - } - if (!parent.isDirectory()) { - throw new RuntimeException(parent + " is not a directory"); - } - - try (FileOutputStream fos = new FileOutputStream(file)) { - fos.write(data); - } catch (IOException e) { - throw new RuntimeException("Failed to write class " + implClassName + " to file " + file); - } - } -} diff --git a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java index d282e58cf9620..ea5d80ad1e801 100644 --- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java +++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java @@ -1769,40 +1769,9 @@ public Map generateHolderClasses(Stream traces) { } @Override - public VarHandle memoryAccessVarHandle(Class carrier, long alignmentMask, - ByteOrder order, long offset, long[] strides) { - return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides); - } - - @Override - public Class memoryAddressCarrier(VarHandle handle) { - return checkMemoryAccessHandle(handle).carrier(); - } - - @Override - public long memoryAddressAlignmentMask(VarHandle handle) { - return checkMemoryAccessHandle(handle).alignmentMask; - } - - @Override - public ByteOrder memoryAddressByteOrder(VarHandle handle) { - return checkMemoryAccessHandle(handle).be ? - ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN; - } - - @Override - public long memoryAddressOffset(VarHandle handle) { - return checkMemoryAccessHandle(handle).offset; - } - - @Override - public long[] memoryAddressStrides(VarHandle handle) { - return checkMemoryAccessHandle(handle).strides(); - } - - @Override - public boolean isMemoryAccessVarHandle(VarHandle handle) { - return asMemoryAccessVarHandle(handle) != null; + public VarHandle memoryAccessVarHandle(Class carrier, boolean skipAlignmentMaskCheck, long alignmentMask, + ByteOrder order) { + return VarHandles.makeMemoryAddressViewHandle(carrier, skipAlignmentMaskCheck, alignmentMask, order); } @Override @@ -1834,26 +1803,6 @@ public VarHandle collectCoordinates(VarHandle target, int pos, MethodHandle filt public VarHandle insertCoordinates(VarHandle target, int pos, Object... values) { return VarHandles.insertCoordinates(target, pos, values); } - - private MemoryAccessVarHandleBase asMemoryAccessVarHandle(VarHandle handle) { - if (handle instanceof MemoryAccessVarHandleBase) { - return (MemoryAccessVarHandleBase)handle; - } else if (handle.target() instanceof MemoryAccessVarHandleBase) { - // skip first adaptation, since we have to step over MemoryAddressProxy - // see JDK-8237349 - return (MemoryAccessVarHandleBase)handle.target(); - } else { - return null; - } - } - - private MemoryAccessVarHandleBase checkMemoryAccessHandle(VarHandle handle) { - MemoryAccessVarHandleBase base = asMemoryAccessVarHandle(handle); - if (base == null) { - throw new IllegalArgumentException("Not a memory access varhandle: " + handle); - } - return base; - } }); } diff --git a/src/java.base/share/classes/java/lang/invoke/VarHandles.java b/src/java.base/share/classes/java/lang/invoke/VarHandles.java index b06fedac185e4..53fb642886722 100644 --- a/src/java.base/share/classes/java/lang/invoke/VarHandles.java +++ b/src/java.base/share/classes/java/lang/invoke/VarHandles.java @@ -315,30 +315,36 @@ else if (viewComponentType == float.class) { * to a single fixed offset to compute an effective offset from the given MemoryAddress for the access. * * @param carrier the Java carrier type. + * @param skipAlignmentMaskCheck if true, only the base part of the address will be checked for alignment. * @param alignmentMask alignment requirement to be checked upon access. In bytes. Expressed as a mask. * @param byteOrder the byte order. - * @param offset a constant offset for the access. - * @param strides the scale factors with which to multiply given access coordinates. * @return the created VarHandle. */ - static VarHandle makeMemoryAddressViewHandle(Class carrier, long alignmentMask, - ByteOrder byteOrder, long offset, long[] strides) { + static VarHandle makeMemoryAddressViewHandle(Class carrier, boolean skipAlignmentMaskCheck, long alignmentMask, + ByteOrder byteOrder) { if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) { throw new IllegalArgumentException("Invalid carrier: " + carrier.getName()); } long size = Wrapper.forPrimitiveType(carrier).bitWidth() / 8; boolean be = byteOrder == ByteOrder.BIG_ENDIAN; - - Map carrierFactory = ADDRESS_FACTORIES.get(carrier); - MethodHandle fac = carrierFactory.computeIfAbsent(strides.length, - dims -> new MemoryAccessVarHandleGenerator(carrier, dims) - .generateHandleFactory()); - - try { - boolean exact = false; - return maybeAdapt((VarHandle)fac.invoke(be, size, offset, alignmentMask, exact, strides)); - } catch (Throwable ex) { - throw new IllegalStateException(ex); + boolean exact = false; + + if (carrier == byte.class) { + return maybeAdapt(new MemoryAccessVarHandleByteHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == char.class) { + return maybeAdapt(new MemoryAccessVarHandleCharHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == short.class) { + return maybeAdapt(new MemoryAccessVarHandleShortHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == int.class) { + return maybeAdapt(new MemoryAccessVarHandleIntHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == float.class) { + return maybeAdapt(new MemoryAccessVarHandleFloatHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == long.class) { + return maybeAdapt(new MemoryAccessVarHandleLongHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else if (carrier == double.class) { + return maybeAdapt(new MemoryAccessVarHandleDoubleHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact)); + } else { + throw new IllegalStateException("Cannot get here"); } } diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template index cdbe6df68c4cd..7b6ec9ee6071c 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleByteArrayView.java.template @@ -27,6 +27,8 @@ package java.lang.invoke; import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.MemorySegmentProxy; +import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.misc.ScopedMemoryAccess.Scope; import jdk.internal.misc.Unsafe; import jdk.internal.util.Preconditions; import jdk.internal.vm.annotation.ForceInline; @@ -41,9 +43,11 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE; final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { - static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); + static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess(); static final int ALIGN = $BoxType$.BYTES - 1; + + static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); #if[floatingPoint] @ForceInline @@ -601,13 +605,17 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { @ForceInline static int index(ByteBuffer bb, int index) { - MemorySegmentProxy segmentProxy = nioAccess.bufferSegment(bb); - if (segmentProxy != null) { - segmentProxy.checkValidState(); - } + MemorySegmentProxy segmentProxy = NIO_ACCESS.bufferSegment(bb); return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null); } + @ForceInline + static Scope scope(ByteBuffer bb) { + MemorySegmentProxy segmentProxy = NIO_ACCESS.bufferSegment(bb); + return segmentProxy != null ? + segmentProxy.scope() : null; + } + @ForceInline static int indexRO(ByteBuffer bb, int index) { if (UNSAFE.getBoolean(bb, BYTE_BUFFER_IS_READ_ONLY)) @@ -628,13 +636,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); #if[floatingPoint] - $rawType$ rawValue = UNSAFE.get$RawType$Unaligned( + $rawType$ rawValue = SCOPED_MEMORY_ACCESS.get$RawType$Unaligned(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), ((long) index(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS), handle.be); return $Type$.$rawType$BitsTo$Type$(rawValue); #else[floatingPoint] - return UNSAFE.get$Type$Unaligned( + return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), ((long) index(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS), handle.be); @@ -646,13 +654,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); #if[floatingPoint] - UNSAFE.put$RawType$Unaligned( + SCOPED_MEMORY_ACCESS.put$RawType$Unaligned(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), ((long) indexRO(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS), $Type$.$type$ToRaw$RawType$Bits(value), handle.be); #else[floatingPoint] - UNSAFE.put$Type$Unaligned( + SCOPED_MEMORY_ACCESS.put$Type$Unaligned(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), ((long) indexRO(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS), value, @@ -665,7 +673,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.get$RawType$Volatile( + SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, index(bb, index)))); } @@ -674,7 +682,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static void setVolatile(VarHandle ob, Object obb, int index, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - UNSAFE.put$RawType$Volatile( + SCOPED_MEMORY_ACCESS.put$RawType$Volatile(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value)); @@ -685,7 +693,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.get$RawType$Acquire( + SCOPED_MEMORY_ACCESS.get$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, index(bb, index)))); } @@ -694,7 +702,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static void setRelease(VarHandle ob, Object obb, int index, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - UNSAFE.put$RawType$Release( + SCOPED_MEMORY_ACCESS.put$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value)); @@ -705,7 +713,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.get$RawType$Opaque( + SCOPED_MEMORY_ACCESS.get$RawType$Opaque(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, index(bb, index)))); } @@ -714,7 +722,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static void setOpaque(VarHandle ob, Object obb, int index, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - UNSAFE.put$RawType$Opaque( + SCOPED_MEMORY_ACCESS.put$RawType$Opaque(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value)); @@ -726,12 +734,12 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); #if[Object] - return UNSAFE.compareAndSetReference( + return SCOPED_MEMORY_ACCESS.compareAndSetReference(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); #else[Object] - return UNSAFE.compareAndSet$RawType$( + return SCOPED_MEMORY_ACCESS.compareAndSet$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); @@ -743,7 +751,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value))); @@ -754,7 +762,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$Acquire( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value))); @@ -765,7 +773,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$Release( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value))); @@ -775,7 +783,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static boolean weakCompareAndSetPlain(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - return UNSAFE.weakCompareAndSet$RawType$Plain( + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Plain(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); @@ -785,7 +793,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static boolean weakCompareAndSet(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - return UNSAFE.weakCompareAndSet$RawType$( + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); @@ -795,7 +803,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static boolean weakCompareAndSetAcquire(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - return UNSAFE.weakCompareAndSet$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); @@ -805,7 +813,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { static boolean weakCompareAndSetRelease(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); - return UNSAFE.weakCompareAndSet$RawType$Release( + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, expected), convEndian(handle.be, value)); @@ -817,13 +825,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); #if[Object] return convEndian(handle.be, - UNSAFE.getAndSetReference( + SCOPED_MEMORY_ACCESS.getAndSetReference(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value))); #else[Object] return convEndian(handle.be, - UNSAFE.getAndSet$RawType$( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value))); @@ -835,7 +843,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.getAndSet$RawType$Acquire( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value))); @@ -846,7 +854,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); return convEndian(handle.be, - UNSAFE.getAndSet$RawType$Release( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), convEndian(handle.be, value))); @@ -859,7 +867,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), delta); @@ -873,7 +881,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), delta); @@ -887,7 +895,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), delta); @@ -902,7 +910,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB); long offset = address(bb, indexRO(bb, index)); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta))); @@ -916,7 +924,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -930,7 +938,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -944,7 +952,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -959,7 +967,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB); long offset = address(bb, indexRO(bb, index)); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value))); @@ -971,7 +979,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -985,7 +993,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -999,7 +1007,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -1014,7 +1022,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB); long offset = address(bb, indexRO(bb, index)); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value))); @@ -1027,7 +1035,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -1041,7 +1049,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -1055,7 +1063,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { ByteBufferHandle handle = (ByteBufferHandle)ob; ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(scope(bb), UNSAFE.getReference(bb, BYTE_BUFFER_HB), address(bb, indexRO(bb, index)), value); @@ -1070,7 +1078,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase { Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB); long offset = address(bb, indexRO(bb, index)); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value))); diff --git a/src/java.base/share/classes/java/lang/invoke/X-VarHandleMemoryAccess.java.template b/src/java.base/share/classes/java/lang/invoke/X-VarHandleMemoryAccess.java.template index 49b18c124f9c4..c5a8ee7f724b6 100644 --- a/src/java.base/share/classes/java/lang/invoke/X-VarHandleMemoryAccess.java.template +++ b/src/java.base/share/classes/java/lang/invoke/X-VarHandleMemoryAccess.java.template @@ -24,21 +24,51 @@ */ package java.lang.invoke; -import jdk.internal.access.foreign.MemoryAddressProxy; +import jdk.internal.access.foreign.MemorySegmentProxy; +import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.vm.annotation.ForceInline; +import java.lang.ref.Reference; + import java.util.Objects; import static java.lang.invoke.MethodHandleStatics.UNSAFE; #warn -final class MemoryAccessVarHandle$Type$Helper { +final class MemoryAccessVarHandle$Type$Helper extends MemoryAccessVarHandleBase { static final boolean BE = UNSAFE.isBigEndian(); + + static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); static final int VM_ALIGN = $BoxType$.BYTES - 1; + static final VarForm FORM = new VarForm(MemoryAccessVarHandle$Type$Helper.class, MemorySegmentProxy.class, $type$.class, long.class); + + MemoryAccessVarHandle$Type$Helper(boolean skipAlignmentMaskCheck, boolean be, long length, long alignmentMask, boolean exact) { + super(FORM, skipAlignmentMaskCheck, be, length, alignmentMask, exact); + } + + @Override + final MethodType accessModeTypeUncached(VarHandle.AccessType accessType) { + return accessType.accessModeType(MemorySegmentProxy.class, $type$.class, long.class); + } + + @Override + public MemoryAccessVarHandle$Type$Helper withInvokeExactBehavior() { + return hasInvokeExactBehavior() ? + this : + new MemoryAccessVarHandle$Type$Helper(skipAlignmentMaskCheck, be, length, alignmentMask, true); + } + + @Override + public MemoryAccessVarHandle$Type$Helper withInvokeBehavior() { + return !hasInvokeExactBehavior() ? + this : + new MemoryAccessVarHandle$Type$Helper(skipAlignmentMaskCheck, be, length, alignmentMask, true); + } + #if[floatingPoint] @ForceInline static $rawType$ convEndian(boolean big, $type$ v) { @@ -66,15 +96,15 @@ final class MemoryAccessVarHandle$Type$Helper { #end[floatingPoint] @ForceInline - static MemoryAddressProxy checkAddress(Object obb, long offset, long length, boolean ro) { - MemoryAddressProxy oo = (MemoryAddressProxy)Objects.requireNonNull(obb); + static MemorySegmentProxy checkAddress(Object obb, long offset, long length, boolean ro) { + MemorySegmentProxy oo = (MemorySegmentProxy)Objects.requireNonNull(obb); oo.checkAccess(offset, length, ro); return oo; } - + @ForceInline - static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) { - long address = offsetNoVMAlignCheck(bb, offset, alignmentMask); + static long offset(boolean skipAlignmentMaskCheck, MemorySegmentProxy bb, long offset, long alignmentMask) { + long address = offsetNoVMAlignCheck(skipAlignmentMaskCheck, bb, offset, alignmentMask); if ((address & VM_ALIGN) != 0) { throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address); } @@ -82,60 +112,66 @@ final class MemoryAccessVarHandle$Type$Helper { } @ForceInline - static long offsetNoVMAlignCheck(MemoryAddressProxy bb, long offset, long alignmentMask) { + static long offsetNoVMAlignCheck(boolean skipAlignmentMaskCheck, MemorySegmentProxy bb, long offset, long alignmentMask) { long base = bb.unsafeGetOffset(); long address = base + offset; - //note: the offset portion has already been aligned-checked, by construction - if ((base & alignmentMask) != 0) { - throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address); + if (skipAlignmentMaskCheck) { + //note: the offset portion has already been aligned-checked, by construction + if ((base & alignmentMask) != 0) { + throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address); + } + } else { + if ((address & alignmentMask) != 0) { + throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address); + } } return address; } - + @ForceInline - static $type$ get0(VarHandle ob, Object obb, long base) { + static $type$ get(VarHandle ob, Object obb, long base) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true); #if[floatingPoint] - $rawType$ rawValue = UNSAFE.get$RawType$Unaligned( + $rawType$ rawValue = SCOPED_MEMORY_ACCESS.get$RawType$Unaligned(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), handle.be); return $Type$.$rawType$BitsTo$Type$(rawValue); #else[floatingPoint] #if[byte] - return UNSAFE.get$Type$( + return SCOPED_MEMORY_ACCESS.get$Type$(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask)); + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask)); #else[byte] - return UNSAFE.get$Type$Unaligned( + return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), handle.be); #end[byte] #end[floatingPoint] } @ForceInline - static void set0(VarHandle ob, Object obb, long base, $type$ value) { + static void set(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); #if[floatingPoint] - UNSAFE.put$RawType$Unaligned( + SCOPED_MEMORY_ACCESS.put$RawType$Unaligned(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), $Type$.$type$ToRaw$RawType$Bits(value), handle.be); #else[floatingPoint] #if[byte] - UNSAFE.put$Type$( + SCOPED_MEMORY_ACCESS.put$Type$(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); #else[byte] - UNSAFE.put$Type$Unaligned( + SCOPED_MEMORY_ACCESS.put$Type$Unaligned(bb.scope(), bb.unsafeGetBase(), - offsetNoVMAlignCheck(bb, base, handle.alignmentMask), + offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value, handle.be); #end[byte] @@ -143,234 +179,234 @@ final class MemoryAccessVarHandle$Type$Helper { } @ForceInline - static $type$ getVolatile0(VarHandle ob, Object obb, long base) { + static $type$ getVolatile(VarHandle ob, Object obb, long base) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true); return convEndian(handle.be, - UNSAFE.get$RawType$Volatile( + SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask))); } @ForceInline - static void setVolatile0(VarHandle ob, Object obb, long base, $type$ value) { + static void setVolatile(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - UNSAFE.put$RawType$Volatile( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + SCOPED_MEMORY_ACCESS.put$RawType$Volatile(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value)); } @ForceInline - static $type$ getAcquire0(VarHandle ob, Object obb, long base) { + static $type$ getAcquire(VarHandle ob, Object obb, long base) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true); return convEndian(handle.be, - UNSAFE.get$RawType$Acquire( + SCOPED_MEMORY_ACCESS.get$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask))); } @ForceInline - static void setRelease0(VarHandle ob, Object obb, long base, $type$ value) { + static void setRelease(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - UNSAFE.put$RawType$Release( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + SCOPED_MEMORY_ACCESS.put$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value)); } @ForceInline - static $type$ getOpaque0(VarHandle ob, Object obb, long base) { + static $type$ getOpaque(VarHandle ob, Object obb, long base) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true); return convEndian(handle.be, - UNSAFE.get$RawType$Opaque( + SCOPED_MEMORY_ACCESS.get$RawType$Opaque(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask))); + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask))); } @ForceInline - static void setOpaque0(VarHandle ob, Object obb, long base, $type$ value) { + static void setOpaque(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - UNSAFE.put$RawType$Opaque( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + SCOPED_MEMORY_ACCESS.put$RawType$Opaque(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value)); } #if[CAS] @ForceInline - static boolean compareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static boolean compareAndSet(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - return UNSAFE.compareAndSet$RawType$( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + return SCOPED_MEMORY_ACCESS.compareAndSet$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @ForceInline - static $type$ compareAndExchange0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static $type$ compareAndExchange(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @ForceInline - static $type$ compareAndExchangeAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static $type$ compareAndExchangeAcquire(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$Acquire( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @ForceInline - static $type$ compareAndExchangeRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static $type$ compareAndExchangeRelease(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.compareAndExchange$RawType$Release( + SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value))); } @ForceInline - static boolean weakCompareAndSetPlain0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static boolean weakCompareAndSetPlain(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - return UNSAFE.weakCompareAndSet$RawType$Plain( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Plain(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @ForceInline - static boolean weakCompareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static boolean weakCompareAndSet(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - return UNSAFE.weakCompareAndSet$RawType$( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @ForceInline - static boolean weakCompareAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static boolean weakCompareAndSetAcquire(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - return UNSAFE.weakCompareAndSet$RawType$Acquire( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @ForceInline - static boolean weakCompareAndSetRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { + static boolean weakCompareAndSetRelease(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); - return UNSAFE.weakCompareAndSet$RawType$Release( + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); + return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, expected), convEndian(handle.be, value)); } @ForceInline - static $type$ getAndSet0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndSet(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.getAndSet$RawType$( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value))); } @ForceInline - static $type$ getAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndSetAcquire(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.getAndSet$RawType$Acquire( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value))); } @ForceInline - static $type$ getAndSetRelease0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndSetRelease(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); return convEndian(handle.be, - UNSAFE.getAndSet$RawType$Release( + SCOPED_MEMORY_ACCESS.getAndSet$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), convEndian(handle.be, value))); } #end[CAS] #if[AtomicAdd] @ForceInline - static $type$ getAndAdd0(VarHandle ob, Object obb, long base, $type$ delta) { + static $type$ getAndAdd(VarHandle ob, Object obb, long base, $type$ delta) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } } @ForceInline - static $type$ getAndAddAcquire0(VarHandle ob, Object obb, long base, $type$ delta) { + static $type$ getAndAddAcquire(VarHandle ob, Object obb, long base, $type$ delta) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } } @ForceInline - static $type$ getAndAddRelease0(VarHandle ob, Object obb, long base, $type$ delta) { + static $type$ getAndAddRelease(VarHandle ob, Object obb, long base, $type$ delta) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndAdd$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } else { - return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta); + return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta); } } @ForceInline - static $type$ getAndAddConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ delta) { + static $type$ getAndAddConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ delta) { $type$ nativeExpectedValue, expectedValue; Object base = bb.unsafeGetBase(); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); - } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, + } while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta))); return expectedValue; } @@ -378,164 +414,164 @@ final class MemoryAccessVarHandle$Type$Helper { #if[Bitwise] @ForceInline - static $type$ getAndBitwiseOr0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseOr(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseOrRelease0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseOrRelease(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseOrAcquire0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseOr$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseOrConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) { + static $type$ getAndBitwiseOrConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) { $type$ nativeExpectedValue, expectedValue; Object base = bb.unsafeGetBase(); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); - } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, + } while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value))); return expectedValue; } @ForceInline - static $type$ getAndBitwiseAnd0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseAndRelease0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseAndRelease(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseAndAcquire0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseAnd$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseAndConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) { + static $type$ getAndBitwiseAndConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) { $type$ nativeExpectedValue, expectedValue; Object base = bb.unsafeGetBase(); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); - } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, + } while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value))); return expectedValue; } @ForceInline - static $type$ getAndBitwiseXor0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseXorRelease0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseXorRelease(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$Release( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseXorAcquire0(VarHandle ob, Object obb, long base, $type$ value) { + static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object obb, long base, $type$ value) { MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob; - MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false); + MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false); if (handle.be == BE) { - return UNSAFE.getAndBitwiseXor$RawType$Acquire( + return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(bb.scope(), bb.unsafeGetBase(), - offset(bb, base, handle.alignmentMask), + offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } else { - return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value); + return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value); } } @ForceInline - static $type$ getAndBitwiseXorConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) { + static $type$ getAndBitwiseXorConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) { $type$ nativeExpectedValue, expectedValue; Object base = bb.unsafeGetBase(); do { - nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset); + nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset); expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue); - } while (!UNSAFE.weakCompareAndSet$RawType$(base, offset, + } while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset, nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value))); return expectedValue; } diff --git a/src/java.base/share/classes/java/nio/Bits.java b/src/java.base/share/classes/java/nio/Bits.java index 7e8b833dbea9c..262ccb4e8ac53 100644 --- a/src/java.base/share/classes/java/nio/Bits.java +++ b/src/java.base/share/classes/java/nio/Bits.java @@ -106,7 +106,7 @@ static boolean unaligned() { // These methods should be called whenever direct memory is allocated or // freed. They allow the user to control the amount of direct memory // which a process may access. All sizes are specified in bytes. - static void reserveMemory(long size, int cap) { + static void reserveMemory(long size, long cap) { if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) { MAX_MEMORY = VM.maxDirectMemory(); @@ -185,7 +185,7 @@ static void reserveMemory(long size, int cap) { } } - private static boolean tryReserveMemory(long size, int cap) { + private static boolean tryReserveMemory(long size, long cap) { // -XX:MaxDirectMemorySize limits the total capacity rather than the // actual memory usage, which will differ when buffers are page @@ -203,7 +203,7 @@ private static boolean tryReserveMemory(long size, int cap) { } - static void unreserveMemory(long size, int cap) { + static void unreserveMemory(long size, long cap) { long cnt = COUNT.decrementAndGet(); long reservedMem = RESERVED_MEMORY.addAndGet(-size); long totalCap = TOTAL_CAPACITY.addAndGet(-cap); diff --git a/src/java.base/share/classes/java/nio/Buffer.java b/src/java.base/share/classes/java/nio/Buffer.java index b47b3d2b68a31..8e286584b1fd6 100644 --- a/src/java.base/share/classes/java/nio/Buffer.java +++ b/src/java.base/share/classes/java/nio/Buffer.java @@ -29,6 +29,7 @@ import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.access.foreign.UnmapperProxy; +import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.misc.Unsafe; import jdk.internal.misc.VM.BufferPool; import jdk.internal.vm.annotation.ForceInline; @@ -193,6 +194,8 @@ public abstract class Buffer { // Cached unsafe-access object static final Unsafe UNSAFE = Unsafe.getUnsafe(); + static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + /** * The characteristics of Spliterators that traverse and split elements * maintained in Buffers. @@ -754,9 +757,18 @@ final void discardMark() { // package-private } @ForceInline - final void checkSegment() { + final ScopedMemoryAccess.Scope scope() { if (segment != null) { - segment.checkValidState(); + return segment.scope(); + } else { + return null; + } + } + + final void checkScope() { + ScopedMemoryAccess.Scope scope = scope(); + if (scope != null) { + scope.checkValidState(); } } @@ -827,6 +839,21 @@ public void unload(long address, boolean isSync, long size) { public boolean isLoaded(long address, boolean isSync, long size) { return MappedMemoryUtils.isLoaded(address, isSync, size); } + + @Override + public void reserveMemory(long size, long cap) { + Bits.reserveMemory(size, cap); + } + + @Override + public void unreserveMemory(long size, long cap) { + Bits.unreserveMemory(size, cap); + } + + @Override + public int pageSize() { + return Bits.pageSize(); + } }); } diff --git a/src/java.base/share/classes/java/nio/BufferMismatch.java b/src/java.base/share/classes/java/nio/BufferMismatch.java index 4a69eb6d99c79..8a6069d751b73 100644 --- a/src/java.base/share/classes/java/nio/BufferMismatch.java +++ b/src/java.base/share/classes/java/nio/BufferMismatch.java @@ -24,6 +24,7 @@ */ package java.nio; +import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.util.ArraysSupport; /** @@ -31,12 +32,14 @@ */ final class BufferMismatch { + final static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + static int mismatch(ByteBuffer a, int aOff, ByteBuffer b, int bOff, int length) { int i = 0; if (length > 7) { if (a.get(aOff) != b.get(bOff)) return 0; - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + aOff, b.base(), b.address + bOff, length, @@ -60,7 +63,7 @@ static int mismatch(CharBuffer a, int aOff, CharBuffer b, int bOff, int length) && a.charRegionOrder() != null && b.charRegionOrder() != null) { if (a.get(aOff) != b.get(bOff)) return 0; - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE), length, @@ -80,7 +83,7 @@ static int mismatch(ShortBuffer a, int aOff, ShortBuffer b, int bOff, int length if (length > 3 && a.order() == b.order()) { if (a.get(aOff) != b.get(bOff)) return 0; - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE), length, @@ -100,7 +103,7 @@ static int mismatch(IntBuffer a, int aOff, IntBuffer b, int bOff, int length) { if (length > 1 && a.order() == b.order()) { if (a.get(aOff) != b.get(bOff)) return 0; - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE), length, @@ -119,7 +122,7 @@ static int mismatch(FloatBuffer a, int aOff, FloatBuffer b, int bOff, int length int i = 0; if (length > 1 && a.order() == b.order()) { if (Float.floatToRawIntBits(a.get(aOff)) == Float.floatToRawIntBits(b.get(bOff))) { - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE), length, @@ -158,7 +161,7 @@ static int mismatch(LongBuffer a, int aOff, LongBuffer b, int bOff, int length) if (length > 0 && a.order() == b.order()) { if (a.get(aOff) != b.get(bOff)) return 0; - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE), length, @@ -176,7 +179,7 @@ static int mismatch(DoubleBuffer a, int aOff, DoubleBuffer b, int bOff, int leng int i = 0; if (length > 0 && a.order() == b.order()) { if (Double.doubleToRawLongBits(a.get(aOff)) == Double.doubleToRawLongBits(b.get(bOff))) { - i = ArraysSupport.vectorizedMismatch( + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(), a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE), b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE), length, diff --git a/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template b/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template index 048a48a68141e..12788a2b9985f 100644 --- a/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template @@ -130,22 +130,20 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private } public $type$ get() { - checkSegment(); - $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()), + $memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), bb.hb, byteOffset(nextGetIndex()), {#if[boB]?true:false}); return $fromBits$(x); } public $type$ get(int i) { - checkSegment(); - $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), + $memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), bb.hb, byteOffset(checkIndex(i)), {#if[boB]?true:false}); return $fromBits$(x); } #if[streamableType] $type$ getUnchecked(int i) { - $memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(i), + $memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(null, bb.hb, byteOffset(i), {#if[boB]?true:false}); return $fromBits$(x); } @@ -155,9 +153,8 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private public $Type$Buffer put($type$ x) { #if[rw] - checkSegment(); $memtype$ y = $toBits$(x); - UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y, + SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), bb.hb, byteOffset(nextPutIndex()), y, {#if[boB]?true:false}); return this; #else[rw] @@ -167,9 +164,8 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private public $Type$Buffer put(int i, $type$ x) { #if[rw] - checkSegment(); $memtype$ y = $toBits$(x); - UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y, + SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), bb.hb, byteOffset(checkIndex(i)), y, {#if[boB]?true:false}); return this; #else[rw] diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template index d6e0f58e0c3ab..4a1eb380f03b2 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer-bin.java.template @@ -33,8 +33,7 @@ class XXX { private $type$ get$Type$(long a) { try { - checkSegment(); - $memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian); + $memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), null, a, bigEndian); return $fromBits$(x); } finally { Reference.reachabilityFence(this); @@ -62,9 +61,8 @@ class XXX { private ByteBuffer put$Type$(long a, $type$ x) { #if[rw] try { - checkSegment(); $memtype$ y = $toBits$(x); - UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian); + SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), null, a, y, bigEndian); } finally { Reference.reachabilityFence(this); } diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index 07c7d3c7d26f2..81a40c8bd404f 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -31,6 +31,7 @@ import java.io.FileDescriptor; import java.lang.ref.Reference; import java.util.Objects; import jdk.internal.access.foreign.MemorySegmentProxy; +import jdk.internal.misc.ScopedMemoryAccess.Scope; import jdk.internal.misc.VM; import jdk.internal.ref.Cleaner; import sun.nio.ch.DirectBuffer; @@ -264,6 +265,17 @@ class Direct$Type$Buffer$RW$$BO$ #if[rw] public long address() { + Scope scope = scope(); + if (scope != null) { + if (scope.ownerThread() == null) { + throw new UnsupportedOperationException("ByteBuffer derived from shared segments not supported"); + } + try { + scope.checkValidState(); + } catch (Scope.ScopedAccessError e) { + throw new IllegalStateException("This segment is already closed"); + } + } return address; } @@ -273,8 +285,7 @@ class Direct$Type$Buffer$RW$$BO$ public $type$ get() { try { - checkSegment(); - return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex())))); + return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(scope(), null, ix(nextGetIndex())))); } finally { Reference.reachabilityFence(this); } @@ -282,8 +293,7 @@ class Direct$Type$Buffer$RW$$BO$ public $type$ get(int i) { try { - checkSegment(); - return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i))))); + return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(scope(), null, ix(checkIndex(i))))); } finally { Reference.reachabilityFence(this); } @@ -292,7 +302,7 @@ class Direct$Type$Buffer$RW$$BO$ #if[streamableType] $type$ getUnchecked(int i) { try { - return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(i)))); + return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(null, null, ix(i)))); } finally { Reference.reachabilityFence(this); } @@ -301,7 +311,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer get($type$[] dst, int offset, int length) { #if[rw] - checkSegment(); if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { Objects.checkFromIndexSize(offset, length, dst.length); int pos = position(); @@ -315,7 +324,7 @@ class Direct$Type$Buffer$RW$$BO$ try { #if[!byte] if (order() != ByteOrder.nativeOrder()) - UNSAFE.copySwapMemory(null, + SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, null, ix(pos), dst, dstOffset, @@ -323,7 +332,7 @@ class Direct$Type$Buffer$RW$$BO$ (long)1 << $LG_BYTES_PER_VALUE$); else #end[!byte] - UNSAFE.copyMemory(null, + SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null, ix(pos), dst, dstOffset, @@ -343,7 +352,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer get(int index, $type$[] dst, int offset, int length) { #if[rw] - checkSegment(); if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) { Objects.checkFromIndexSize(index, length, limit()); Objects.checkFromIndexSize(offset, length, dst.length); @@ -352,7 +360,7 @@ class Direct$Type$Buffer$RW$$BO$ try { #if[!byte] if (order() != ByteOrder.nativeOrder()) - UNSAFE.copySwapMemory(null, + SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, null, ix(index), dst, dstOffset, @@ -360,7 +368,7 @@ class Direct$Type$Buffer$RW$$BO$ (long)1 << $LG_BYTES_PER_VALUE$); else #end[!byte] - UNSAFE.copyMemory(null, + SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null, ix(index), dst, dstOffset, @@ -381,8 +389,7 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put($type$ x) { #if[rw] try { - checkSegment(); - UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x))); + SCOPED_MEMORY_ACCESS.put$Swaptype$(scope(), null, ix(nextPutIndex()), $swap$($toBits$(x))); } finally { Reference.reachabilityFence(this); } @@ -395,8 +402,7 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put(int i, $type$ x) { #if[rw] try { - checkSegment(); - UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x))); + SCOPED_MEMORY_ACCESS.put$Swaptype$(scope(), null, ix(checkIndex(i)), $swap$($toBits$(x))); } finally { Reference.reachabilityFence(this); } @@ -408,7 +414,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put($Type$Buffer src) { #if[rw] - checkSegment(); super.put(src); return this; #else[rw] @@ -418,7 +423,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { #if[rw] - checkSegment(); super.put(index, src, offset, length); return this; #else[rw] @@ -428,7 +432,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put($type$[] src, int offset, int length) { #if[rw] - checkSegment(); if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) { Objects.checkFromIndexSize(offset, length, src.length); int pos = position(); @@ -442,7 +445,7 @@ class Direct$Type$Buffer$RW$$BO$ try { #if[!byte] if (order() != ByteOrder.nativeOrder()) - UNSAFE.copySwapMemory(src, + SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, src, srcOffset, null, ix(pos), @@ -450,7 +453,7 @@ class Direct$Type$Buffer$RW$$BO$ (long)1 << $LG_BYTES_PER_VALUE$); else #end[!byte] - UNSAFE.copyMemory(src, + SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, src, srcOffset, null, ix(pos), @@ -470,7 +473,6 @@ class Direct$Type$Buffer$RW$$BO$ public $Type$Buffer put(int index, $type$[] src, int offset, int length) { #if[rw] - checkSegment(); if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) { Objects.checkFromIndexSize(index, length, limit()); Objects.checkFromIndexSize(offset, length, src.length); @@ -480,7 +482,7 @@ class Direct$Type$Buffer$RW$$BO$ try { #if[!byte] if (order() != ByteOrder.nativeOrder()) - UNSAFE.copySwapMemory(src, + SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, src, srcOffset, null, ix(index), @@ -488,11 +490,9 @@ class Direct$Type$Buffer$RW$$BO$ (long)1 << $LG_BYTES_PER_VALUE$); else #end[!byte] - UNSAFE.copyMemory(src, - srcOffset, - null, - ix(index), - (long)length << $LG_BYTES_PER_VALUE$); + SCOPED_MEMORY_ACCESS.copyMemory( + scope(), null, src, + srcOffset, null, ix(index), (long)length << $LG_BYTES_PER_VALUE$); } finally { Reference.reachabilityFence(this); } @@ -512,7 +512,8 @@ class Direct$Type$Buffer$RW$$BO$ assert (pos <= lim); int rem = (pos <= lim ? lim - pos : 0); try { - UNSAFE.copyMemory(ix(pos), ix(0), (long)rem << $LG_BYTES_PER_VALUE$); + SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null, + ix(pos), null, ix(0), (long)rem << $LG_BYTES_PER_VALUE$); } finally { Reference.reachabilityFence(this); } diff --git a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index e4a89efc6d8f2..4646db35ca548 100644 --- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -162,12 +162,10 @@ class Heap$Type$Buffer$RW$ #end[byte] public $type$ get() { - checkSegment(); return hb[ix(nextGetIndex())]; } public $type$ get(int i) { - checkSegment(); return hb[ix(checkIndex(i))]; } @@ -178,7 +176,7 @@ class Heap$Type$Buffer$RW$ #end[streamableType] public $Type$Buffer get($type$[] dst, int offset, int length) { - checkSegment(); + checkScope(); Objects.checkFromIndexSize(offset, length, dst.length); int pos = position(); if (length > limit() - pos) @@ -189,7 +187,7 @@ class Heap$Type$Buffer$RW$ } public $Type$Buffer get(int index, $type$[] dst, int offset, int length) { - checkSegment(); + checkScope(); Objects.checkFromIndexSize(index, length, limit()); Objects.checkFromIndexSize(offset, length, dst.length); System.arraycopy(hb, ix(index), dst, offset, length); @@ -208,7 +206,6 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put($type$ x) { #if[rw] - checkSegment(); hb[ix(nextPutIndex())] = x; return this; #else[rw] @@ -218,7 +215,6 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put(int i, $type$ x) { #if[rw] - checkSegment(); hb[ix(checkIndex(i))] = x; return this; #else[rw] @@ -228,7 +224,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put($type$[] src, int offset, int length) { #if[rw] - checkSegment(); + checkScope(); Objects.checkFromIndexSize(offset, length, src.length); int pos = position(); if (length > limit() - pos) @@ -243,7 +239,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put($Type$Buffer src) { #if[rw] - checkSegment(); + checkScope(); super.put(src); return this; #else[rw] @@ -253,7 +249,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { #if[rw] - checkSegment(); + checkScope(); super.put(index, src, offset, length); return this; #else[rw] @@ -263,7 +259,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer put(int index, $type$[] src, int offset, int length) { #if[rw] - checkSegment(); + checkScope(); Objects.checkFromIndexSize(index, length, limit()); Objects.checkFromIndexSize(offset, length, src.length); System.arraycopy(src, offset, hb, ix(index), length); @@ -276,7 +272,7 @@ class Heap$Type$Buffer$RW$ #if[char] public $Type$Buffer put(String src, int start, int end) { - checkSegment(); + checkScope(); int length = end - start; Objects.checkFromIndexSize(start, length, src.length()); if (isReadOnly()) @@ -328,20 +324,18 @@ class Heap$Type$Buffer$RW$ #if[rw] public char getChar() { - checkSegment(); - return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian); + return SCOPED_MEMORY_ACCESS.getCharUnaligned(scope(), hb, byteOffset(nextGetIndex(2)), bigEndian); } public char getChar(int i) { - return UNSAFE.getCharUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian); + return SCOPED_MEMORY_ACCESS.getCharUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), bigEndian); } #end[rw] public $Type$Buffer putChar(char x) { #if[rw] - checkSegment(); - UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putCharUnaligned(scope(), hb, byteOffset(nextPutIndex(2)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -350,8 +344,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putChar(int i, char x) { #if[rw] - checkSegment(); - UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putCharUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -383,21 +376,18 @@ class Heap$Type$Buffer$RW$ #if[rw] public short getShort() { - checkSegment(); - return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian); + return SCOPED_MEMORY_ACCESS.getShortUnaligned(scope(), hb, byteOffset(nextGetIndex(2)), bigEndian); } public short getShort(int i) { - checkSegment(); - return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian); + return SCOPED_MEMORY_ACCESS.getShortUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), bigEndian); } #end[rw] public $Type$Buffer putShort(short x) { #if[rw] - checkSegment(); - UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putShortUnaligned(scope(), hb, byteOffset(nextPutIndex(2)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -406,8 +396,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putShort(int i, short x) { #if[rw] - checkSegment(); - UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putShortUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -439,21 +428,18 @@ class Heap$Type$Buffer$RW$ #if[rw] public int getInt() { - checkSegment(); - return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian); + return SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(nextGetIndex(4)), bigEndian); } public int getInt(int i) { - checkSegment(); - return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian); + return SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), bigEndian); } #end[rw] public $Type$Buffer putInt(int x) { #if[rw] - checkSegment(); - UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(nextPutIndex(4)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -462,8 +448,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putInt(int i, int x) { #if[rw] - checkSegment(); - UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -495,21 +480,18 @@ class Heap$Type$Buffer$RW$ #if[rw] public long getLong() { - checkSegment(); - return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian); + return SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(nextGetIndex(8)), bigEndian); } public long getLong(int i) { - checkSegment(); - return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian); + return SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), bigEndian); } #end[rw] public $Type$Buffer putLong(long x) { #if[rw] - checkSegment(); - UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(nextPutIndex(8)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -518,8 +500,7 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putLong(int i, long x) { #if[rw] - checkSegment(); - UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian); + SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), x, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -551,14 +532,12 @@ class Heap$Type$Buffer$RW$ #if[rw] public float getFloat() { - checkSegment(); - int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian); + int x = SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(nextGetIndex(4)), bigEndian); return Float.intBitsToFloat(x); } public float getFloat(int i) { - checkSegment(); - int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian); + int x = SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), bigEndian); return Float.intBitsToFloat(x); } @@ -566,9 +545,8 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putFloat(float x) { #if[rw] - checkSegment(); int y = Float.floatToRawIntBits(x); - UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian); + SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(nextPutIndex(4)), y, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -577,9 +555,8 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putFloat(int i, float x) { #if[rw] - checkSegment(); int y = Float.floatToRawIntBits(x); - UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian); + SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), y, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -611,14 +588,12 @@ class Heap$Type$Buffer$RW$ #if[rw] public double getDouble() { - checkSegment(); - long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian); + long x = SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(nextGetIndex(8)), bigEndian); return Double.longBitsToDouble(x); } public double getDouble(int i) { - checkSegment(); - long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian); + long x = SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), bigEndian); return Double.longBitsToDouble(x); } @@ -626,9 +601,8 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putDouble(double x) { #if[rw] - checkSegment(); long y = Double.doubleToRawLongBits(x); - UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian); + SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(nextPutIndex(8)), y, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); @@ -637,9 +611,8 @@ class Heap$Type$Buffer$RW$ public $Type$Buffer putDouble(int i, double x) { #if[rw] - checkSegment(); long y = Double.doubleToRawLongBits(x); - UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian); + SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), y, bigEndian); return this; #else[rw] throw new ReadOnlyBufferException(); diff --git a/src/java.base/share/classes/java/nio/MappedByteBuffer.java b/src/java.base/share/classes/java/nio/MappedByteBuffer.java index d5461b639019b..c8a0509218a8a 100644 --- a/src/java.base/share/classes/java/nio/MappedByteBuffer.java +++ b/src/java.base/share/classes/java/nio/MappedByteBuffer.java @@ -31,6 +31,7 @@ import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.access.foreign.UnmapperProxy; +import jdk.internal.misc.ScopedMemoryAccess; /** @@ -87,6 +88,8 @@ public abstract class MappedByteBuffer // determines the behavior of force operations. private final boolean isSync; + static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + // This should only be invoked by the DirectByteBuffer constructors // MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private @@ -173,7 +176,7 @@ public final boolean isLoaded() { if (fd == null) { return true; } - return MappedMemoryUtils.isLoaded(address, isSync, capacity()); + return SCOPED_MEMORY_ACCESS.isLoaded(scope(), address, isSync, capacity()); } /** @@ -191,7 +194,7 @@ public final MappedByteBuffer load() { return this; } try { - MappedMemoryUtils.load(address, isSync, capacity()); + SCOPED_MEMORY_ACCESS.load(scope(), address, isSync, capacity()); } finally { Reference.reachabilityFence(this); } @@ -280,7 +283,7 @@ public final MappedByteBuffer force(int index, int length) { if ((address != 0) && (limit != 0)) { // check inputs Objects.checkFromIndexSize(index, length, limit); - MappedMemoryUtils.force(fd, address, isSync, index, length); + SCOPED_MEMORY_ACCESS.force(scope(), fd, address, isSync, index, length); } return this; } diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index 8a5be7ceaf6ac..afa8e9f2e9fe7 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -1045,11 +1045,9 @@ public abstract class $Type$Buffer if (this.order() == src.order()) { #end[!byte] try { - UNSAFE.copyMemory(srcBase, - srcAddr, - base, - addr, - len); + SCOPED_MEMORY_ACCESS.copyMemory( + scope(), src.scope(), srcBase, + srcAddr, base, addr, len); } finally { Reference.reachabilityFence(src); Reference.reachabilityFence(this); @@ -1057,12 +1055,9 @@ public abstract class $Type$Buffer #if[!byte] } else { try { - UNSAFE.copySwapMemory(srcBase, - srcAddr, - base, - addr, - len, - (long)1 << $LG_BYTES_PER_VALUE$); + SCOPED_MEMORY_ACCESS.copySwapMemory( + scope(), src.scope(), srcBase, + srcAddr, base, addr, len, (long)1 << $LG_BYTES_PER_VALUE$); } finally { Reference.reachabilityFence(src); Reference.reachabilityFence(this); diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java index f47bf5efcbea5..20090014264c9 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java @@ -81,44 +81,8 @@ public interface JavaLangInvokeAccess { * Used by {@code jdk.internal.foreign.LayoutPath} and * {@code jdk.incubator.foreign.MemoryHandles}. */ - VarHandle memoryAccessVarHandle(Class carrier, long alignmentMask, - ByteOrder order, long offset, long[] strides); - - /** - * Is {@code handle} a memory access varhandle? - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - boolean isMemoryAccessVarHandle(VarHandle handle); - - /** - * Returns the carrier associated with a memory access var handle. - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - Class memoryAddressCarrier(VarHandle handle); - - /** - * Returns the alignment mask associated with a memory access var handle. - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - long memoryAddressAlignmentMask(VarHandle handle); - - /** - * Returns the byte order associated with a memory access var handle. - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - ByteOrder memoryAddressByteOrder(VarHandle handle); - - /** - * Returns the offset associated with a memory access var handle. - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - long memoryAddressOffset(VarHandle handle); - - /** - * Returns the strides associated with a memory access var handle. - * Used by {@code jdk.incubator.foreign.MemoryHandles}. - */ - long[] memoryAddressStrides(VarHandle handle); + VarHandle memoryAccessVarHandle(Class carrier, boolean skipAlignmentMaskCheck, long alignmentMask, + ByteOrder order); /** * Var handle carrier combinator. diff --git a/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java index 3baf67cad7739..aedac5960f8f3 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaNioAccess.java @@ -104,4 +104,19 @@ public interface JavaNioAccess { * Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views. */ boolean isLoaded(long address, boolean isSync, long size); + + /** + * Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}. + */ + void reserveMemory(long size, long cap); + + /** + * Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}. + */ + void unreserveMemory(long size, long cap); + + /** + * Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}. + */ + int pageSize(); } diff --git a/src/java.base/share/classes/jdk/internal/access/foreign/MemorySegmentProxy.java b/src/java.base/share/classes/jdk/internal/access/foreign/MemorySegmentProxy.java index 6c6ec80987a26..e45ad08f8fecc 100644 --- a/src/java.base/share/classes/jdk/internal/access/foreign/MemorySegmentProxy.java +++ b/src/java.base/share/classes/jdk/internal/access/foreign/MemorySegmentProxy.java @@ -26,10 +26,78 @@ package jdk.internal.access.foreign; +import jdk.internal.misc.ScopedMemoryAccess; + /** * This proxy interface is required to allow instances of the {@code MemorySegment} interface (which is defined inside * an incubating module) to be accessed from the memory access var handles. */ public interface MemorySegmentProxy { - void checkValidState(); + /** + * Check that memory access is within spatial bounds and that access is compatible with segment access modes. + * @throws UnsupportedOperationException if underlying segment has incompatible access modes (e.g. attempting to write + * a read-only segment). + * @throws IndexOutOfBoundsException if access is out-of-bounds. + */ + void checkAccess(long offset, long length, boolean readOnly); + long unsafeGetOffset(); + Object unsafeGetBase(); + boolean isSmall(); + ScopedMemoryAccess.Scope scope(); + + /* Helper functions for offset computations. These are required so that we can avoid issuing long opcodes + * (e.g. LMUL, LADD) when we're operating on 'small' segments (segments whose length can be expressed with an int). + * C2 BCE code is very sensitive to the kind of opcode being emitted, and this workaround allows us to rescue + * BCE when working with small segments. This workaround should be dropped when JDK-8223051 is resolved. + */ + + static long addOffsets(long op1, long op2, MemorySegmentProxy segmentProxy) { + if (segmentProxy.isSmall()) { + // force ints for BCE + if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE + || op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) { + throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + int i1 = (int)op1; + int i2 = (int)op2; + try { + return Math.addExact(i1, i2); + } catch (ArithmeticException ex) { + throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + } else { + try { + return Math.addExact(op1, op2); + } catch (ArithmeticException ex) { + throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE); + } + } + } + + static long multiplyOffsets(long op1, long op2, MemorySegmentProxy segmentProxy) { + if (segmentProxy.isSmall()) { + if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE + || op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) { + throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + // force ints for BCE + int i1 = (int)op1; + int i2 = (int)op2; + try { + return Math.multiplyExact(i1, i2); + } catch (ArithmeticException ex) { + throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE); + } + } else { + try { + return Math.multiplyExact(op1, op2); + } catch (ArithmeticException ex) { + throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE); + } + } + } + + private static IndexOutOfBoundsException overflowException(long min, long max) { + return new IndexOutOfBoundsException(String.format("Overflow occurred during offset computation ; offset exceeded range { %d .. %d }", min, max)); + } } diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess-bin.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess-bin.java.template new file mode 100644 index 0000000000000..0f1a978724457 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess-bin.java.template @@ -0,0 +1,698 @@ + @ForceInline + public $type$ get$Type$(Scope scope, Object base, long offset) { + try { + return get$Type$Internal(scope, base, offset); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ get$Type$Internal(Scope scope, Object base, long offset) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.get$Type$(base, offset); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void put$Type$(Scope scope, Object base, long offset, $type$ value) { + try { + put$Type$Internal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void put$Type$Internal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.put$Type$(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + +#if[Unaligned] + @ForceInline + public $type$ get$Type$Unaligned(Scope scope, Object base, long offset, boolean be) { + try { + return get$Type$UnalignedInternal(scope, base, offset, be); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ get$Type$UnalignedInternal(Scope scope, Object base, long offset, boolean be) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.get$Type$Unaligned(base, offset, be); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void put$Type$Unaligned(Scope scope, Object base, long offset, $type$ value, boolean be) { + try { + put$Type$UnalignedInternal(scope, base, offset, value, be); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void put$Type$UnalignedInternal(Scope scope, Object base, long offset, $type$ value, boolean be) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.put$Type$Unaligned(base, offset, value, be); + } finally { + Reference.reachabilityFence(scope); + } + } +#end[Unaligned] + + @ForceInline + public $type$ get$Type$Volatile(Scope scope, Object base, long offset) { + try { + return get$Type$VolatileInternal(scope, base, offset); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ get$Type$VolatileInternal(Scope scope, Object base, long offset) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.get$Type$Volatile(base, offset); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void put$Type$Volatile(Scope scope, Object base, long offset, $type$ value) { + try { + put$Type$VolatileInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void put$Type$VolatileInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.put$Type$Volatile(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ get$Type$Acquire(Scope scope, Object base, long offset) { + try { + return get$Type$AcquireInternal(scope, base, offset); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ get$Type$AcquireInternal(Scope scope, Object base, long offset) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.get$Type$Acquire(base, offset); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void put$Type$Release(Scope scope, Object base, long offset, $type$ value) { + try { + put$Type$ReleaseInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void put$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.put$Type$Release(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ get$Type$Opaque(Scope scope, Object base, long offset) { + try { + return get$Type$OpaqueInternal(scope, base, offset); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ get$Type$OpaqueInternal(Scope scope, Object base, long offset) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.get$Type$Opaque(base, offset); + } finally { + Reference.reachabilityFence(scope); + } + } + @ForceInline + public void put$Type$Opaque(Scope scope, Object base, long offset, $type$ value) { + try { + put$Type$OpaqueInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void put$Type$OpaqueInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.put$Type$Opaque(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } +#if[CAS] + @ForceInline + public boolean compareAndSet$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return compareAndSet$Type$Internal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private boolean compareAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.compareAndSet$Type$(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ compareAndExchange$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return compareAndExchange$Type$Internal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ compareAndExchange$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.compareAndExchange$Type$(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ compareAndExchange$Type$Acquire(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return compareAndExchange$Type$AcquireInternal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ compareAndExchange$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.compareAndExchange$Type$Acquire(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ compareAndExchange$Type$Release(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return compareAndExchange$Type$ReleaseInternal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ compareAndExchange$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.compareAndExchange$Type$Release(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public boolean weakCompareAndSet$Type$Plain(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return weakCompareAndSet$Type$PlainInternal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private boolean weakCompareAndSet$Type$PlainInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.weakCompareAndSet$Type$Plain(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public boolean weakCompareAndSet$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return weakCompareAndSet$Type$Internal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private boolean weakCompareAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.weakCompareAndSet$Type$(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public boolean weakCompareAndSet$Type$Acquire(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return weakCompareAndSet$Type$AcquireInternal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private boolean weakCompareAndSet$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.weakCompareAndSet$Type$Acquire(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public boolean weakCompareAndSet$Type$Release(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + return weakCompareAndSet$Type$ReleaseInternal(scope, base, offset, expected, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private boolean weakCompareAndSet$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.weakCompareAndSet$Type$Release(base, offset, expected, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndSet$Type$(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndSet$Type$Internal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndSet$Type$(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndSet$Type$Acquire(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndSet$Type$AcquireInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndSet$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndSet$Type$Acquire(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndSet$Type$Release(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndSet$Type$ReleaseInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndSet$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndSet$Type$Release(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } +#end[CAS] + +#if[AtomicAdd] + @ForceInline + public $type$ getAndAdd$Type$(Scope scope, Object base, long offset, $type$ delta) { + try { + return getAndAdd$Type$Internal(scope, base, offset, delta); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndAdd$Type$Internal(Scope scope, Object base, long offset, $type$ delta) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndAdd$Type$(base, offset, delta); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndAdd$Type$Acquire(Scope scope, Object base, long offset, $type$ delta) { + try { + return getAndAdd$Type$AcquireInternal(scope, base, offset, delta); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndAdd$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ delta) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndAdd$Type$Acquire(base, offset, delta); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndAdd$Type$Release(Scope scope, Object base, long offset, $type$ delta) { + try { + return getAndAdd$Type$ReleaseInternal(scope, base, offset, delta); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndAdd$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ delta) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndAdd$Type$Release(base, offset, delta); + } finally { + Reference.reachabilityFence(scope); + } + } +#end[AtomicAdd] + +#if[Bitwise] + @ForceInline + public $type$ getAndBitwiseOr$Type$(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseOr$Type$Internal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseOr$Type$Internal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseOr$Type$(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseOr$Type$Acquire(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseOr$Type$AcquireInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseOr$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseOr$Type$Acquire(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseOr$Type$Release(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseOr$Type$ReleaseInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseOr$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseOr$Type$Release(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseAnd$Type$(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseAnd$Type$Internal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseAnd$Type$Internal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseAnd$Type$(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseAnd$Type$Acquire(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseAnd$Type$AcquireInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseAnd$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseAnd$Type$Acquire(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseAnd$Type$Release(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseAnd$Type$ReleaseInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseAnd$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseAnd$Type$Release(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseXor$Type$(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseXor$Type$Internal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseXor$Type$Internal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseXor$Type$(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseXor$Type$Acquire(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseXor$Type$AcquireInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseXor$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseXor$Type$Acquire(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public $type$ getAndBitwiseXor$Type$Release(Scope scope, Object base, long offset, $type$ value) { + try { + return getAndBitwiseXor$Type$ReleaseInternal(scope, base, offset, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private $type$ getAndBitwiseXor$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) { + try { + if (scope != null) { + scope.checkValidState(); + } + return UNSAFE.getAndBitwiseXor$Type$Release(base, offset, value); + } finally { + Reference.reachabilityFence(scope); + } + } +#end[Bitwise] diff --git a/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template new file mode 100644 index 0000000000000..2bf948d33d6ea --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/misc/X-ScopedMemoryAccess.java.template @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.internal.misc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.ref.Reference; +import java.io.FileDescriptor; + +import jdk.internal.access.SharedSecrets; +import jdk.internal.util.ArraysSupport; +import jdk.internal.vm.annotation.ForceInline; + + +/** + * This class defines low-level methods to access on-heap and off-heap memory. The methods in this class + * can be thought of as thin wrappers around methods provided in the {@link Unsafe} class. All the methods in this + * class, accept one or more {@link Scope} parameter, which is used to validate as to whether access to memory + * can be performed in a safe fashion - more specifically, to ensure that the memory being accessed has not + * already been released (which would result in a hard VM crash). + *

    + * Accessing and releasing memory from a single thread is not problematic - after all, a given thread cannot, + * at the same time, access a memory region and free it. But ensuring correctness of memory access + * when multiple threads are involved is much trickier, as there can be cases where a thread is accessing + * a memory region while another thread is releasing it. + *

    + * This class provides tools to manage races when multiple threads are accessing and/or releasing the same memory + * region concurrently. More specifically, when a thread wants to release a memory region, it should call the + * {@link #closeScope(jdk.internal.misc.ScopedMemoryAccess.Scope)} method provided by this class. This method initiates + * thread-local handshakes with all the other VM threads, which are then stopped one by one. If any thread is found + * accessing memory that is associated to the very scope object being closed, that thread execution is asynchronously + * interrupted with a {@link Scope.ScopedAccessError}. + *

    + * This synchronization strategy relies on the idea that accessing memory is atomic with respect to checking the + * validity of the scope associated with that memory region - that is, a thread that wants to perform memory access will be + * suspended either before a scope check or after the memory access. To ensure this atomicity, + * all methods in this class are marked with the special {@link Scoped} annotation, which is recognized by the VM, + * and used during the thread-local handshake to detect (and stop) threads performing potentially problematic memory access + * operations. Additionally, to make sure that the scope object(s) of the memory being accessed is always + * reachable during an access operation, all the methods in this class add reachability fences around the underlying + * unsafe access. + *

    + * This form of synchronization allows APIs to use plain memory access without any other form of synchronization + * which might be deemed to expensive; in other words, this approach prioritizes the performance of memory access over + * that of releasing a shared memory resource. + */ +public class ScopedMemoryAccess { + + private static Unsafe UNSAFE = Unsafe.getUnsafe(); + + private static native void registerNatives(); + static { + registerNatives(); + } + + public boolean closeScope(Scope scope) { + return closeScope0(scope, Scope.ScopedAccessError.INSTANCE); + } + + native boolean closeScope0(Scope scope, Scope.ScopedAccessError exception); + + private ScopedMemoryAccess() {} + + private static final ScopedMemoryAccess theScopedMemoryAccess = new ScopedMemoryAccess(); + + public static ScopedMemoryAccess getScopedMemoryAccess() { + return theScopedMemoryAccess; + } + + /** + * Scope interface used during scoped memory access operations. A scope can be thought of as an object + * which embodies the temporal checks associated with a given memory region. + */ + public interface Scope { + void checkValidState(); + + Thread ownerThread(); + + /** + * Error thrown when memory access fails because the memory has already been released. + * Note: for performance reasons, this exception is never created by client; instead a shared instance + * is thrown (sometimes, this instance can be thrown asynchronously inside VM code). For this reason, + * it is important for clients to always catch this exception and throw a regular exception instead + * (which contains full stack information). + */ + final class ScopedAccessError extends Error { + private ScopedAccessError() { + super("Attempt to access an already released memory resource", null, false, false); + } + static final long serialVersionUID = 1L; + + public static final ScopedAccessError INSTANCE = new ScopedAccessError(); + } + } + + @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) + @Retention(RetentionPolicy.RUNTIME) + @interface Scoped { } + + // bulk ops + + @ForceInline + public void copyMemory(Scope srcScope, Scope dstScope, + Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes) { + try { + copyMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void copyMemoryInternal(Scope srcScope, Scope dstScope, + Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes) { + try { + if (srcScope != null) { + srcScope.checkValidState(); + } + if (dstScope != null) { + dstScope.checkValidState(); + } + UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); + } finally { + Reference.reachabilityFence(srcScope); + Reference.reachabilityFence(dstScope); + } + } + + @ForceInline + public void copySwapMemory(Scope srcScope, Scope dstScope, + Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes, long elemSize) { + try { + copySwapMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes, elemSize); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void copySwapMemoryInternal(Scope srcScope, Scope dstScope, + Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes, long elemSize) { + try { + if (srcScope != null) { + srcScope.checkValidState(); + } + if (dstScope != null) { + dstScope.checkValidState(); + } + UNSAFE.copySwapMemory(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); + } finally { + Reference.reachabilityFence(srcScope); + Reference.reachabilityFence(dstScope); + } + } + + @ForceInline + public void setMemory(Scope scope, Object o, long offset, long bytes, byte value) { + try { + setMemoryInternal(scope, o, offset, bytes, value); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private void setMemoryInternal(Scope scope, Object o, long offset, long bytes, byte value) { + try { + if (scope != null) { + scope.checkValidState(); + } + UNSAFE.setMemory(o, offset, bytes, value); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public int vectorizedMismatch(Scope aScope, Scope bScope, + Object a, long aOffset, + Object b, long bOffset, + int length, + int log2ArrayIndexScale) { + try { + return vectorizedMismatchInternal(aScope, bScope, a, aOffset, b, bOffset, length, log2ArrayIndexScale); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + private int vectorizedMismatchInternal(Scope aScope, Scope bScope, + Object a, long aOffset, + Object b, long bOffset, + int length, + int log2ArrayIndexScale) { + try { + if (aScope != null) { + aScope.checkValidState(); + } + if (bScope != null) { + bScope.checkValidState(); + } + return ArraysSupport.vectorizedMismatch(a, aOffset, b, bOffset, length, log2ArrayIndexScale); + } finally { + Reference.reachabilityFence(aScope); + Reference.reachabilityFence(bScope); + } + } + + @ForceInline + public boolean isLoaded(Scope scope, long address, boolean isSync, long size) { + try { + return isLoadedInternal(scope, address, isSync, size); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + public boolean isLoadedInternal(Scope scope, long address, boolean isSync, long size) { + try { + if (scope != null) { + scope.checkValidState(); + } + return SharedSecrets.getJavaNioAccess().isLoaded(address, isSync, size); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void load(Scope scope, long address, boolean isSync, long size) { + try { + loadInternal(scope, address, isSync, size); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + public void loadInternal(Scope scope, long address, boolean isSync, long size) { + try { + if (scope != null) { + scope.checkValidState(); + } + SharedSecrets.getJavaNioAccess().load(address, isSync, size); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void unload(Scope scope, long address, boolean isSync, long size) { + try { + unloadInternal(scope, address, isSync, size); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + public void unloadInternal(Scope scope, long address, boolean isSync, long size) { + try { + if (scope != null) { + scope.checkValidState(); + } + SharedSecrets.getJavaNioAccess().unload(address, isSync, size); + } finally { + Reference.reachabilityFence(scope); + } + } + + @ForceInline + public void force(Scope scope, FileDescriptor fd, long address, boolean isSync, long index, long length) { + try { + forceInternal(scope, fd, address, isSync, index, length); + } catch (Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @ForceInline @Scoped + public void forceInternal(Scope scope, FileDescriptor fd, long address, boolean isSync, long index, long length) { + try { + if (scope != null) { + scope.checkValidState(); + } + SharedSecrets.getJavaNioAccess().force(fd, address, isSync, index, length); + } finally { + Reference.reachabilityFence(scope); + } + } + // typed-ops here + diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java index e7ffb7a0ed851..777905ea6a303 100644 --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -160,37 +160,6 @@ public static int vectorizedMismatch(Object a, long aOffset, } } - /** - * Mismatch over long lengths. - */ - public static long vectorizedMismatchLargeForBytes(Object a, long aOffset, - Object b, long bOffset, - long length) { - long off = 0; - long remaining = length; - int i, size; - boolean lastSubRange = false; - while (remaining > 7 && !lastSubRange) { - if (remaining > Integer.MAX_VALUE) { - size = Integer.MAX_VALUE; - } else { - size = (int) remaining; - lastSubRange = true; - } - i = vectorizedMismatch( - a, aOffset + off, - b, bOffset + off, - size, LOG2_ARRAY_BYTE_INDEX_SCALE); - if (i >= 0) - return off + i; - - i = size - ~i; - off += i; - remaining -= i; - } - return ~remaining; - } - // Booleans // Each boolean element takes up one byte diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 9d0ae93b47092..501d5c7ee16b4 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -217,7 +217,8 @@ exports jdk.internal.platform to jdk.management; exports jdk.internal.ref to - java.desktop; + java.desktop, + jdk.incubator.foreign; exports jdk.internal.reflect to java.logging, java.sql, diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java new file mode 100644 index 0000000000000..15a62f5e02ee9 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/Addressable.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.foreign; + +/** + * Represents a type which is addressable. An addressable type is one which can be projected down to + * a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment}, + * and {@link MemoryAddress}. + * + * @apiNote In the future, if the Java language permits, {@link Addressable} + * may become a {@code sealed} interface, which would prohibit subclassing except by + * explicitly permitted types, such as {@link MemorySegment} and {@link MemoryAddress}. + * + * @implSpec + * Implementations of this interface value-based. + */ +public interface Addressable { + /** + * Map this object into a {@link MemoryAddress} instance. + * @return the {@link MemoryAddress} instance associated with this object. + */ + MemoryAddress address(); +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegment.java deleted file mode 100644 index 1e573b865b212..0000000000000 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegment.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package jdk.incubator.foreign; - -import java.nio.channels.FileChannel; -import java.nio.file.Path; - -/** - * A mapped memory segment, that is, a memory segment backed by memory-mapped file. - * - *

    Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. - * Mapped memory segments behave like ordinary segments, but provide additional capabilities to manipulate memory-mapped - * memory regions, such as {@link #force()} and {@link #load()}. - *

    - * All implementations of this interface must be value-based; - * use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on - * instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should - * be used for comparisons. - *

    - * Non-platform classes should not implement {@linkplain MappedMemorySegment} directly. - * - *

    The content of a mapped memory segment can change at any time, for example - * if the content of the corresponding region of the mapped file is changed by - * this (or another) program. Whether or not such changes occur, and when they - * occur, is operating-system dependent and therefore unspecified. - * - * All or part of a mapped memory segment may become - * inaccessible at any time, for example if the backing mapped file is truncated. An - * attempt to access an inaccessible region of a mapped memory segment will not - * change the segment's content and will cause an unspecified exception to be - * thrown either at the time of the access or at some later time. It is - * therefore strongly recommended that appropriate precautions be taken to - * avoid the manipulation of a mapped file by this (or another) program, except to read or write - * the file's content. - * - * @apiNote In the future, if the Java language permits, {@link MemorySegment} - * may become a {@code sealed} interface, which would prohibit subclassing except by - * explicitly permitted subtypes. - */ -public interface MappedMemorySegment extends MemorySegment { - - @Override - MappedMemorySegment withAccessModes(int accessModes); - - @Override - MappedMemorySegment asSlice(long offset, long newSize); - - /** - * Forces any changes made to this segment's content to be written to the - * storage device containing the mapped file. - * - *

    If the file mapped into this segment resides on a local storage - * device then when this method returns it is guaranteed that all changes - * made to the segment since it was created, or since this method was last - * invoked, will have been written to that device. - * - *

    If the file does not reside on a local device then no such guarantee - * is made. - * - *

    If this segment was not mapped in read/write mode ({@link - * java.nio.channels.FileChannel.MapMode#READ_WRITE}) then - * invoking this method may have no effect. In particular, the - * method has no effect for segments mapped in read-only or private - * mapping modes. This method may or may not have an effect for - * implementation-specific mapping modes. - *

    - */ - void force(); - - /** - * Loads this segment's content into physical memory. - * - *

    This method makes a best effort to ensure that, when it returns, - * this segment's contents is resident in physical memory. Invoking this - * method may cause some number of page faults and I/O operations to - * occur.

    - */ - void load(); - - /** - * Unloads this segment's content from physical memory. - * - *

    This method makes a best effort to ensure that this segment's contents are - * are no longer resident in physical memory. Accessing this segment's contents - * after invoking this method may cause some number of page faults and I/O operations to - * occur (as this segment's contents might need to be paged back in).

    - */ - void unload(); - - /** - * Tells whether or not this segment's content is resident in physical - * memory. - * - *

    A return value of {@code true} implies that it is highly likely - * that all of the data in this segment is resident in physical memory and - * may therefore be accessed without incurring any virtual-memory page - * faults or I/O operations. A return value of {@code false} does not - * necessarily imply that the segment's content is not resident in physical - * memory. - * - *

    The returned value is a hint, rather than a guarantee, because the - * underlying operating system may have paged out some of the segment's data - * by the time that an invocation of this method returns.

    - * - * @return {@code true} if it is likely that this segment's content - * is resident in physical memory - */ - boolean isLoaded(); -} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegments.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegments.java new file mode 100644 index 0000000000000..5613a2f6be6de --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MappedMemorySegments.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.foreign; + +import jdk.internal.foreign.MappedMemorySegmentImpl; + +import java.nio.MappedByteBuffer; + +/** + * This class provides capabilities to manipulate mapped memory segments, such as {@link #force(MemorySegment)}, + * and {@link #load(MemorySegment)}. The methods in these class are suitable replacements for some of the + * functionality in the {@link java.nio.MappedByteBuffer} class. Note that, while it is possible to map a segment + * into a byte buffer (see {@link MemorySegment#asByteBuffer()}), and call e.g. {@link MappedByteBuffer#force()} that way, + * this can only be done when the source segment is small enough, due to the size limitation inherent to the + * ByteBuffer API. + *

    + * Clients requiring sophisticated, low-level control over mapped memory segments, should consider writing + * custom mapped memory segment factories; using JNI, e.g. on Linux, it is possible to call {@code mmap} + * with the desired parameters; the returned address can be easily wrapped into a memory segment, using + * {@link MemoryAddress#ofLong(long)} and {@link MemoryAddress#asSegmentRestricted(long, Runnable, Object)}. + * + * @implNote + * The behavior of some the methods in this class (see {@link #load(MemorySegment)}, {@link #unload(MemorySegment)} and + * {@link #isLoaded(MemorySegment)}) is highly platform-dependent; as a result, calling these methods might + * be a no-op on certain platforms. + */ +public final class MappedMemorySegments { + private MappedMemorySegments() { + // no thanks + } + + /** + * Tells whether or not the contents of the given segment is resident in physical + * memory. + * + *

    A return value of {@code true} implies that it is highly likely + * that all of the data in the given segment is resident in physical memory and + * may therefore be accessed without incurring any virtual-memory page + * faults or I/O operations. A return value of {@code false} does not + * necessarily imply that the segment's content is not resident in physical + * memory. + * + *

    The returned value is a hint, rather than a guarantee, because the + * underlying operating system may have paged out some of the segment's data + * by the time that an invocation of this method returns.

    + * + * @param segment the segment whose contents are to be tested. + * @return {@code true} if it is likely that the contents of the given segment + * is resident in physical memory + * + * @throws IllegalStateException if the given segment is not alive, or if the given segment is confined + * and this method is called from a thread other than the segment's owner thread. + * @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if + * {@code segment.isMapped() == false}. + */ + public static boolean isLoaded(MemorySegment segment) { + return toMappedSegment(segment).isLoaded(); + } + + /** + * Loads the contents of the given segment into physical memory. + * + *

    This method makes a best effort to ensure that, when it returns, + * this contents of the given segment is resident in physical memory. Invoking this + * method may cause some number of page faults and I/O operations to + * occur.

    + * + * @param segment the segment whose contents are to be loaded. + * + * @throws IllegalStateException if the given segment is not alive, or if the given segment is confined + * and this method is called from a thread other than the segment's owner thread. + * @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if + * {@code segment.isMapped() == false}. + */ + public static void load(MemorySegment segment) { + toMappedSegment(segment).load(); + } + + /** + * Unloads the contents of the given segment from physical memory. + * + *

    This method makes a best effort to ensure that the contents of the given segment are + * are no longer resident in physical memory. Accessing this segment's contents + * after invoking this method may cause some number of page faults and I/O operations to + * occur (as this segment's contents might need to be paged back in).

    + * + * @param segment the segment whose contents are to be unloaded. + * + * @throws IllegalStateException if the given segment is not alive, or if the given segment is confined + * and this method is called from a thread other than the segment's owner thread. + * @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if + * {@code segment.isMapped() == false}. + */ + public static void unload(MemorySegment segment) { + toMappedSegment(segment).unload(); + } + + /** + * Forces any changes made to the contents of the given segment to be written to the + * storage device described by the mapped segment's file descriptor. + * + *

    If this mapping's file descriptor resides on a local storage + * device then when this method returns it is guaranteed that all changes + * made to the segment since it was created, or since this method was last + * invoked, will have been written to that device. + * + *

    If this mapping's file descriptor does not reside on a local device then no such guarantee + * is made. + * + *

    If the given segment was not mapped in read/write mode ({@link + * java.nio.channels.FileChannel.MapMode#READ_WRITE}) then + * invoking this method may have no effect. In particular, the + * method has no effect for segments mapped in read-only or private + * mapping modes. This method may or may not have an effect for + * implementation-specific mapping modes. + *

    + * + * @param segment the segment whose contents are to be written to the storage device described by the + * segment's file descriptor. + * + * @throws IllegalStateException if the given segment is not alive, or if the given segment is confined + * and this method is called from a thread other than the segment's owner thread. + * @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if + * {@code segment.isMapped() == false}. + */ + public static void force(MemorySegment segment) { + toMappedSegment(segment).force(); + } + + static MappedMemorySegmentImpl toMappedSegment(MemorySegment segment) { + if (segment instanceof MappedMemorySegmentImpl) { + return (MappedMemorySegmentImpl)segment; + } else { + throw new UnsupportedOperationException("Not a mapped memory segment"); + } + } +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java new file mode 100644 index 0000000000000..41e9d7d9112a8 --- /dev/null +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAccess.java @@ -0,0 +1,1336 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.incubator.foreign; + +import jdk.internal.access.foreign.MemorySegmentProxy; +import jdk.internal.vm.annotation.ForceInline; + +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +/** + * This class defines ready-made static accessors which can be used to dereference memory segments in many ways. + *

    + * The most primitive accessors (see {@link #getIntAtOffset(MemorySegment, long, ByteOrder)}) take a segment, an offset + * (expressed in bytes) and a byte order. The final address at which the dereference will occur will be computed by offsetting + * the base address by the specified offset, as if by calling {@link MemoryAddress#addOffset(long)} on the specified base address. + *

    + * In cases where no offset is required, overloads are provided (see {@link #getInt(MemorySegment, ByteOrder)}) so that + * clients can omit the offset coordinate. + *

    + * To help dereferencing in array-like use cases (e.g. where the layout of a given memory segment is a sequence + * layout of given size an element count), higher-level overloads are also provided (see {@link #getIntAtIndex(MemorySegment, long, ByteOrder)}), + * which take a segment and a logical element index. The formula to obtain the byte offset {@code O} from an + * index {@code I} is given by {@code O = I * S} where {@code S} is the size (expressed in bytes) of the element to + * be dereferenced. + *

    + * In cases where native byte order is preferred, overloads are provided (see {@link #getIntAtOffset(MemorySegment, long)}) + * so that clients can omit the byte order parameter. + */ +public final class MemoryAccess { + + private MemoryAccess() { + // just the one + } + + private static final VarHandle byte_handle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); + private static final VarHandle char_LE_handle = unalignedHandle(MemoryLayouts.BITS_16_LE, char.class); + private static final VarHandle short_LE_handle = unalignedHandle(MemoryLayouts.BITS_16_LE, short.class); + private static final VarHandle int_LE_handle = unalignedHandle(MemoryLayouts.BITS_32_LE, int.class); + private static final VarHandle float_LE_handle = unalignedHandle(MemoryLayouts.BITS_32_LE, float.class); + private static final VarHandle long_LE_handle = unalignedHandle(MemoryLayouts.BITS_64_LE, long.class); + private static final VarHandle double_LE_handle = unalignedHandle(MemoryLayouts.BITS_64_LE, double.class); + private static final VarHandle char_BE_handle = unalignedHandle(MemoryLayouts.BITS_16_BE, char.class); + private static final VarHandle short_BE_handle = unalignedHandle(MemoryLayouts.BITS_16_BE, short.class); + private static final VarHandle int_BE_handle = unalignedHandle(MemoryLayouts.BITS_32_BE, int.class); + private static final VarHandle float_BE_handle = unalignedHandle(MemoryLayouts.BITS_32_BE, float.class); + private static final VarHandle long_BE_handle = unalignedHandle(MemoryLayouts.BITS_64_BE, long.class); + private static final VarHandle double_BE_handle = unalignedHandle(MemoryLayouts.BITS_64_BE, double.class); + private static final VarHandle address_handle; + + static { + Class carrier = switch ((int) MemoryLayouts.ADDRESS.byteSize()) { + case 4 -> int.class; + case 8 -> long.class; + default -> throw new ExceptionInInitializerError("Unsupported pointer size: " + MemoryLayouts.ADDRESS.byteSize()); + }; + address_handle = MemoryHandles.asAddressVarHandle(unalignedHandle(MemoryLayouts.ADDRESS, carrier)); + } + + private static VarHandle unalignedHandle(ValueLayout elementLayout, Class carrier) { + return MemoryHandles.varHandle(carrier, 1, elementLayout.order()); + } + + /** + * Reads a byte from given segment and offset. + * + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a byte value read from {@code segment}. + */ + public static byte getByteAtOffset(MemorySegment segment, long offset) { + return (byte)byte_handle.get(segment, offset); + } + + /** + * Writes a byte at given segment and offset. + * + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the byte value to be written. + */ + public static void setByteAtOffset(MemorySegment segment, long offset, byte value) { + byte_handle.set(segment, offset, value); + } + + /** + * Reads a char from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getCharAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a char value read from {@code segment}. + */ + public static char getCharAtOffset(MemorySegment segment, long offset) { + return getCharAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes a char at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setCharAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the char value to be written. + */ + public static void setCharAtOffset(MemorySegment segment, long offset, char value) { + setCharAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads a short from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getShortAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a short value read from {@code segment}. + */ + public static short getShortAtOffset(MemorySegment segment, long offset) { + return getShortAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes a short at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setShortAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the short value to be written. + */ + public static void setShortAtOffset(MemorySegment segment, long offset, short value) { + setShortAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads an int from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getIntAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return an int value read from {@code segment}. + */ + public static int getIntAtOffset(MemorySegment segment, long offset) { + return getIntAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes an int at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setIntAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the int value to be written. + */ + public static void setIntAtOffset(MemorySegment segment, long offset, int value) { + setIntAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads a float from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getFloatAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a float value read from {@code segment}. + */ + public static float getFloatAtOffset(MemorySegment segment, long offset) { + return getFloatAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes a float at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setFloatAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the float value to be written. + */ + public static void setFloatAtOffset(MemorySegment segment, long offset, float value) { + setFloatAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads a long from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getLongAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a long value read from {@code segment}. + */ + public static long getLongAtOffset(MemorySegment segment, long offset) { + return getLongAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes a long at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setLongAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the long value to be written. + */ + public static void setLongAtOffset(MemorySegment segment, long offset, long value) { + setLongAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads a double from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    getDoubleAtOffset(segment, offset, ByteOrder.nativeOrder());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a double value read from {@code segment}. + */ + public static double getDoubleAtOffset(MemorySegment segment, long offset) { + return getDoubleAtOffset(segment, offset, ByteOrder.nativeOrder()); + } + + /** + * Writes a double at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setDoubleAtOffset(segment, offset, ByteOrder.nativeOrder(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the double value to be written. + */ + public static void setDoubleAtOffset(MemorySegment segment, long offset, double value) { + setDoubleAtOffset(segment, offset, ByteOrder.nativeOrder(), value); + } + + /** + * Reads a memory address from given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent (e.g. on a 64-bit platform) to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.asAddressHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()));
    +    MemoryAddress value = (MemoryAddress)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @return a memory address read from {@code segment}. + */ + public static MemoryAddress getAddressAtOffset(MemorySegment segment, long offset) { + return (MemoryAddress)address_handle.get(segment, offset); + } + + /** + * Writes a memory address at given segment and offset, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent (e.g. on a 64-bit platform) to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.asAddressHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()));
    +    handle.set(segment, offset, value.address());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param value the memory address to be written (expressed as an {@link Addressable} instance). + */ + public static void setAddressAtOffset(MemorySegment segment, long offset, Addressable value) { + address_handle.set(segment, offset, value.address()); + } + + /** + * Reads a char from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(char.class, 1, order);
    +    char value = (char)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return a char value read from {@code segment}. + */ + public static char getCharAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (char)((order == ByteOrder.BIG_ENDIAN) ? char_BE_handle : char_LE_handle).get(segment, offset); + } + + /** + * Writes a char at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(char.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the char value to be written. + */ + public static void setCharAtOffset(MemorySegment segment, long offset, ByteOrder order, char value) { + ((order == ByteOrder.BIG_ENDIAN) ? char_BE_handle : char_LE_handle).set(segment, offset, value); + } + + /** + * Reads a short from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(short.class, 1, order);
    +    short value = (short)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return a short value read from {@code segment}. + */ + public static short getShortAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (short)((order == ByteOrder.BIG_ENDIAN) ? short_BE_handle : short_LE_handle).get(segment, offset); + } + + /** + * Writes a short at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(short.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the short value to be written. + */ + public static void setShortAtOffset(MemorySegment segment, long offset, ByteOrder order, short value) { + ((order == ByteOrder.BIG_ENDIAN) ? short_BE_handle : short_LE_handle).set(segment, offset, value); + } + + /** + * Reads an int from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(int.class, 1, order);
    +    int value = (int)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return an int value read from {@code segment}. + */ + public static int getIntAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (int)((order == ByteOrder.BIG_ENDIAN) ? int_BE_handle : int_LE_handle).get(segment, offset); + } + + /** + * Writes an int at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(int.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the int value to be written. + */ + public static void setIntAtOffset(MemorySegment segment, long offset, ByteOrder order, int value) { + ((order == ByteOrder.BIG_ENDIAN) ? int_BE_handle : int_LE_handle).set(segment, offset, value); + } + + /** + * Reads a float from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(float.class, 1, order);
    +    float value = (float)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return a float value read from {@code segment}. + */ + public static float getFloatAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (float)((order == ByteOrder.BIG_ENDIAN) ? float_BE_handle : float_LE_handle).get(segment, offset); + } + + /** + * Writes a float at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(float.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the float value to be written. + */ + public static void setFloatAtOffset(MemorySegment segment, long offset, ByteOrder order, float value) { + ((order == ByteOrder.BIG_ENDIAN) ? float_BE_handle : float_LE_handle).set(segment, offset, value); + } + + /** + * Reads a long from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(long.class, 1, order);
    +    long value = (long)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return a long value read from {@code segment}. + */ + public static long getLongAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (long)((order == ByteOrder.BIG_ENDIAN) ? long_BE_handle : long_LE_handle).get(segment, offset); + } + + /** + * Writes a long at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(long.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the long value to be written. + */ + public static void setLongAtOffset(MemorySegment segment, long offset, ByteOrder order, long value) { + ((order == ByteOrder.BIG_ENDIAN) ? long_BE_handle : long_LE_handle).set(segment, offset, value); + } + + /** + * Reads a double from given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(double.class, 1, order);
    +    double value = (double)handle.get(segment, offset);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @return a double value read from {@code segment}. + */ + public static double getDoubleAtOffset(MemorySegment segment, long offset, ByteOrder order) { + return (double)((order == ByteOrder.BIG_ENDIAN) ? double_BE_handle : double_LE_handle).get(segment, offset); + } + + /** + * Writes a double at given segment and offset with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    VarHandle handle = MemoryHandles.varHandle(double.class, 1, order);
    +    handle.set(segment, offset, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param offset offset in bytes (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(offset)}. + * @param order the specified byte order. + * @param value the double value to be written. + */ + public static void setDoubleAtOffset(MemorySegment segment, long offset, ByteOrder order, double value) { + ((order == ByteOrder.BIG_ENDIAN) ? double_BE_handle : double_LE_handle).set(segment, offset, value); + } + + /** + * Reads a byte from given segment. + *

    + * This is equivalent to the following code: + *

    {@code
    +    byte value = getByteAtOffset(segment, 0L);
    +     * }
    + * + * @param segment the segment to be dereferenced. + * @return a byte value read from {@code segment}. + */ + public static byte getByte(MemorySegment segment) { + return getByteAtOffset(segment, 0L); + } + + /** + * Writes a byte at given segment. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setByteAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the byte value to be written. + */ + public static void setByte(MemorySegment segment, byte value) { + setByteAtOffset(segment, 0L, value); + } + + /** + * Reads a char from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    char value = getCharAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a char value read from {@code segment}. + */ + public static char getChar(MemorySegment segment) { + return getCharAtOffset(segment, 0L); + } + + /** + * Writes a char at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setCharAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the char value to be written. + */ + public static void setChar(MemorySegment segment, char value) { + setCharAtOffset(segment, 0L, value); + } + + /** + * Reads a short from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    short value = getShortAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a short value read from {@code segment}. + */ + public static short getShort(MemorySegment segment) { + return getShortAtOffset(segment, 0L); + } + + /** + * Writes a short at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setShortAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the short value to be written. + */ + public static void setShort(MemorySegment segment, short value) { + setShortAtOffset(segment, 0L, value); + } + + /** + * Reads an int from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    int value = getIntAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return an int value read from {@code segment}. + */ + public static int getInt(MemorySegment segment) { + return getIntAtOffset(segment, 0L); + } + + /** + * Writes an int at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setIntAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the int value to be written. + */ + public static void setInt(MemorySegment segment, int value) { + setIntAtOffset(segment, 0L, value); + } + + /** + * Reads a float from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    float value = getFloatAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a float value read from {@code segment}. + */ + public static float getFloat(MemorySegment segment) { + return getFloatAtOffset(segment, 0L); + } + + /** + * Writes a float at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setFloatAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the float value to be written. + */ + public static void setFloat(MemorySegment segment, float value) { + setFloatAtOffset(segment, 0L, value); + } + + /** + * Reads a long from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    long value = getLongAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a long value read from {@code segment}. + */ + public static long getLong(MemorySegment segment) { + return getLongAtOffset(segment, 0L); + } + + /** + * Writes a long at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setLongAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the long value to be written. + */ + public static void setLong(MemorySegment segment, long value) { + setLongAtOffset(segment, 0L, value); + } + + /** + * Reads a double from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    double value = getDoubleAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a double value read from {@code segment}. + */ + public static double getDouble(MemorySegment segment) { + return getDoubleAtOffset(segment, 0L); + } + + /** + * Writes a double at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setDoubleAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the double value to be written. + */ + public static void setDouble(MemorySegment segment, double value) { + setDoubleAtOffset(segment, 0L, value); + } + + /** + * Reads a memory address from given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    MemoryAddress value = getAddressAtOffset(segment, 0L);
    +     * }
    + * @param segment the segment to be dereferenced. + * @return a memory address read from {@code segment}. + */ + public static MemoryAddress getAddress(MemorySegment segment) { + return getAddressAtOffset(segment, 0L); + } + + /** + * Writes a memory address at given segment, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setAddressAtOffset(segment, 0L, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param value the memory address to be written (expressed as an {@link Addressable} instance). + */ + public static void setAddress(MemorySegment segment, Addressable value) { + setAddressAtOffset(segment, 0L, value); + } + + /** + * Reads a char from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    char value = getCharAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return a char value read from {@code segment}. + */ + public static char getChar(MemorySegment segment, ByteOrder order) { + return getCharAtOffset(segment, 0L, order); + } + + /** + * Writes a char at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setCharAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the char value to be written. + */ + public static void setChar(MemorySegment segment, ByteOrder order, char value) { + setCharAtOffset(segment, 0L, order, value); + } + + /** + * Reads a short from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    short value = getShortAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return a short value read from {@code segment}. + */ + public static short getShort(MemorySegment segment, ByteOrder order) { + return getShortAtOffset(segment, 0L, order); + } + + /** + * Writes a short at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setShortAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the short value to be written. + */ + public static void setShort(MemorySegment segment, ByteOrder order, short value) { + setShortAtOffset(segment, 0L, order, value); + } + + /** + * Reads an int from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    int value = getIntAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return an int value read from {@code segment}. + */ + public static int getInt(MemorySegment segment, ByteOrder order) { + return getIntAtOffset(segment, 0L, order); + } + + /** + * Writes an int at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setIntAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the int value to be written. + */ + public static void setInt(MemorySegment segment, ByteOrder order, int value) { + setIntAtOffset(segment, 0L, order, value); + } + + /** + * Reads a float from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    float value = getFloatAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return a float value read from {@code segment}. + */ + public static float getFloat(MemorySegment segment, ByteOrder order) { + return getFloatAtOffset(segment, 0L, order); + } + + /** + * Writes a float at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setFloatAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the float value to be written. + */ + public static void setFloat(MemorySegment segment, ByteOrder order, float value) { + setFloatAtOffset(segment, 0L, order, value); + } + + /** + * Reads a long from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    long value = getLongAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return a long value read from {@code segment}. + */ + public static long getLong(MemorySegment segment, ByteOrder order) { + return getLongAtOffset(segment, 0L, order); + } + + /** + * Writes a long at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setLongAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the long value to be written. + */ + public static void setLong(MemorySegment segment, ByteOrder order, long value) { + setLongAtOffset(segment, 0L, order, value); + } + + /** + * Reads a double from given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    double value = getDoubleAtOffset(segment, 0L, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @return a double value read from {@code segment}. + */ + public static double getDouble(MemorySegment segment, ByteOrder order) { + return getDoubleAtOffset(segment, 0L, order); + } + + /** + * Writes a double at given segment, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setDoubleAtOffset(segment, 0L, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param order the specified byte order. + * @param value the double value to be written. + */ + public static void setDouble(MemorySegment segment, ByteOrder order, double value) { + setDoubleAtOffset(segment, 0L, order, value); + } + + /** + * Reads a char from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    char value = getCharAtOffset(segment, 2 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @return a char value read from {@code segment} at the element index specified by {@code index}. + */ + public static char getCharAtIndex(MemorySegment segment, long index) { + return getCharAtOffset(segment, scale(segment, index, 2)); + } + + /** + * Writes a char at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setCharAtOffset(segment, 2 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param value the char value to be written. + */ + public static void setCharAtIndex(MemorySegment segment, long index, char value) { + setCharAtOffset(segment, scale(segment, index, 2), value); + } + + /** + * Reads a short from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    short value = getShortAtOffset(segment, 2 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @return a short value read from {@code segment} at the element index specified by {@code index}. + */ + public static short getShortAtIndex(MemorySegment segment, long index) { + return getShortAtOffset(segment, scale(segment, index, 2)); + } + + /** + * Writes a short at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setShortAtOffset(segment, 2 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param value the short value to be written. + */ + public static void setShortAtIndex(MemorySegment segment, long index, short value) { + setShortAtOffset(segment, scale(segment, index, 2), value); + } + + /** + * Reads an int from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    int value = getIntAtOffset(segment, 4 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @return an int value read from {@code segment} at the element index specified by {@code index}. + */ + public static int getIntAtIndex(MemorySegment segment, long index) { + return getIntAtOffset(segment, scale(segment, index, 4)); + } + + /** + * Writes an int at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setIntAtOffset(segment, 4 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param value the int value to be written. + */ + public static void setIntAtIndex(MemorySegment segment, long index, int value) { + setIntAtOffset(segment, scale(segment, index, 4), value); + } + + /** + * Reads a float from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    float value = getFloatAtOffset(segment, 4 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @return a float value read from {@code segment} at the element index specified by {@code index}. + */ + public static float getFloatAtIndex(MemorySegment segment, long index) { + return getFloatAtOffset(segment, scale(segment, index, 4)); + } + + /** + * Writes a float at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setFloatAtOffset(segment, 4 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param value the float value to be written. + */ + public static void setFloatAtIndex(MemorySegment segment, long index, float value) { + setFloatAtOffset(segment, scale(segment, index, 4), value); + } + + /** + * Reads a long from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    return getLongAtOffset(segment, 8 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @return a long value read from {@code segment} at the element index specified by {@code index}. + */ + public static long getLongAtIndex(MemorySegment segment, long index) { + return getLongAtOffset(segment, scale(segment, index, 8)); + } + + /** + * Writes a long at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setLongAtOffset(segment, 8 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param value the long value to be written. + */ + public static void setLongAtIndex(MemorySegment segment, long index, long value) { + setLongAtOffset(segment, scale(segment, index, 8), value); + } + + /** + * Reads a double from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    return getDoubleAtOffset(segment, 8 * index);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @return a double value read from {@code segment} at the element index specified by {@code index}. + */ + public static double getDoubleAtIndex(MemorySegment segment, long index) { + return getDoubleAtOffset(segment, scale(segment, index, 8)); + } + + /** + * Reads a memory address from given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    return getAddressAtOffset(segment, index * MemoryLayouts.ADDRESS.byteSize());
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @return a memory address read from {@code segment} at the element index specified by {@code index}. + */ + public static MemoryAddress getAddressAtIndex(MemorySegment segment, long index) { + return getAddressAtOffset(segment, scale(segment, index, (int)MemoryLayouts.ADDRESS.byteSize())); + } + + /** + * Writes a memory address at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setAddressAtOffset(segment, index * MemoryLayouts.ADDRESS.byteSize(), value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param value the memory address to be written (expressed as an {@link Addressable} instance). + */ + public static void setAddressAtIndex(MemorySegment segment, long index, Addressable value) { + setAddressAtOffset(segment, scale(segment, index, (int)MemoryLayouts.ADDRESS.byteSize()), value); + } + + /** + * Writes a double at given segment and element index, with byte order set to {@link ByteOrder#nativeOrder()}. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setDoubleAtOffset(segment, 8 * index, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param value the double value to be written. + */ + public static void setDoubleAtIndex(MemorySegment segment, long index, double value) { + setDoubleAtOffset(segment, scale(segment, index, 8), value); + } + + /** + * Reads a char from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    char value = getCharAtOffset(segment, 2 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param order the specified byte order. + * @return a char value read from {@code segment} at the element index specified by {@code index}. + */ + public static char getCharAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getCharAtOffset(segment, scale(segment, index, 2), order); + } + + /** + * Writes a char at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setCharAtOffset(segment, 2 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param order the specified byte order. + * @param value the char value to be written. + */ + public static void setCharAtIndex(MemorySegment segment, long index, ByteOrder order, char value) { + setCharAtOffset(segment, scale(segment, index, 2), order, value); + } + + /** + * Reads a short from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    short value = getShortAtOffset(segment, 2 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param order the specified byte order. + * @return a short value read from {@code segment} at the element index specified by {@code index}. + */ + public static short getShortAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getShortAtOffset(segment, scale(segment, index, 2), order); + } + + /** + * Writes a short at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setShortAtOffset(segment, 2 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 2)}. + * @param order the specified byte order. + * @param value the short value to be written. + */ + public static void setShortAtIndex(MemorySegment segment, long index, ByteOrder order, short value) { + setShortAtOffset(segment, scale(segment, index, 2), order, value); + } + + /** + * Reads an int from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    int value = getIntAtOffset(segment, 4 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param order the specified byte order. + * @return an int value read from {@code segment} at the element index specified by {@code index}. + */ + public static int getIntAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getIntAtOffset(segment, scale(segment, index, 4), order); + } + + /** + * Writes an int at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setIntAtOffset(segment, 4 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param order the specified byte order. + * @param value the int value to be written. + */ + public static void setIntAtIndex(MemorySegment segment, long index, ByteOrder order, int value) { + setIntAtOffset(segment, scale(segment, index, 4), order, value); + } + + /** + * Reads a float from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    float value = getFloatAtOffset(segment, 4 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param order the specified byte order. + * @return a float value read from {@code segment} at the element index specified by {@code index}. + */ + public static float getFloatAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getFloatAtOffset(segment, scale(segment, index, 4), order); + } + + /** + * Writes a float at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setFloatAtOffset(segment, 4 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 4)}. + * @param order the specified byte order. + * @param value the float value to be written. + */ + public static void setFloatAtIndex(MemorySegment segment, long index, ByteOrder order, float value) { + setFloatAtOffset(segment, scale(segment, index, 4), order, value); + } + + /** + * Reads a long from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    return getLongAtOffset(segment, 8 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param order the specified byte order. + * @return a long value read from {@code segment} at the element index specified by {@code index}. + */ + public static long getLongAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getLongAtOffset(segment, scale(segment, index, 8), order); + } + + /** + * Writes a long at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setLongAtOffset(segment, 8 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param order the specified byte order. + * @param value the long value to be written. + */ + public static void setLongAtIndex(MemorySegment segment, long index, ByteOrder order, long value) { + setLongAtOffset(segment, scale(segment, index, 8), order, value); + } + + /** + * Reads a double from given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    return getDoubleAtOffset(segment, 8 * index, order);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param order the specified byte order. + * @return a double value read from {@code segment} at the element index specified by {@code index}. + */ + public static double getDoubleAtIndex(MemorySegment segment, long index, ByteOrder order) { + return getDoubleAtOffset(segment, scale(segment, index, 8), order); + } + + /** + * Writes a double at given segment and element index, with given byte order. + *

    + * This is equivalent to the following code: + *

    {@code
    +    setDoubleAtOffset(segment, 8 * index, order, value);
    +     * }
    + * @param segment the segment to be dereferenced. + * @param index element index (relative to {@code segment}). The final address of this read operation can be expressed as {@code segment.address().addOffset(index * 8)}. + * @param order the specified byte order. + * @param value the double value to be written. + */ + public static void setDoubleAtIndex(MemorySegment segment, long index, ByteOrder order, double value) { + setDoubleAtOffset(segment, scale(segment, index, 8), order, value); + } + + @ForceInline + private static long scale(MemorySegment address, long index, int size) { + return MemorySegmentProxy.multiplyOffsets(index, size, (MemorySegmentProxy)address); + } +} diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java index f5774d79eb7a0..2cc9905e29d99 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryAddress.java @@ -26,19 +26,18 @@ package jdk.incubator.foreign; +import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.MemoryAddressImpl; +import jdk.internal.foreign.NativeMemorySegmentImpl; +import jdk.internal.foreign.Utils; + +import java.lang.ref.Cleaner; /** * A memory address models a reference into a memory location. Memory addresses are typically obtained using the - * {@link MemorySegment#baseAddress()} method; such addresses are said to be checked, and can be expressed - * as offsets into some underlying memory segment (see {@link #segment()} and {@link #segmentOffset()}). - * Since checked memory addresses feature both spatial and temporal bounds, these addresses can safely be - * dereferenced using a memory access var handle (see {@link MemoryHandles}). - *

    - * If an address does not have any associated segment, it is said to be unchecked. Unchecked memory - * addresses do not feature known spatial or temporal bounds; as such, attempting a memory dereference operation - * using an unchecked memory address will result in a runtime exception. Unchecked addresses can be obtained - * e.g. by calling the {@link #ofLong(long)} method. + * {@link MemorySegment#address()} method, and can refer to either off-heap or on-heap memory. + * Given an address, it is possible to compute its offset relative to a given segment, which can be useful + * when performing memory dereference operations using a memory access var handle (see {@link MemoryHandles}). *

    * All implementations of this interface must be value-based; * use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on @@ -54,7 +53,13 @@ * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. */ -public interface MemoryAddress { +public interface MemoryAddress extends Addressable { + + @Override + default MemoryAddress address() { + return this; + } + /** * Creates a new memory address with given offset (in bytes), which might be negative, from current one. * @param offset specified offset (in bytes), relative to this address, which should be used to create the new address. @@ -63,34 +68,99 @@ public interface MemoryAddress { MemoryAddress addOffset(long offset); /** - * Returns the offset of this memory address into the underlying segment (if any). - * @return the offset of this memory address into the underlying segment (if any). - * @throws UnsupportedOperationException if no segment is associated with this memory address, - * e.g. if {@code segment() == null}. + * Returns the offset of this memory address into the given segment. More specifically, if both the segment's + * base address and this address are off-heap addresses, the result is computed as + * {@code this.toRawLongValue() - segment.address().toRawLongValue()}. Otherwise, if both addresses in the form + * {@code (B, O1)}, {@code (B, O2)}, where {@code B} is the same base heap object and {@code O1}, {@code O2} + * are byte offsets (relative to the base object) associated with this address and the segment's base address, + * the result is computed as {@code O1 - O2}. + *

    + * If the segment's base address and this address are both heap addresses, but with different base objects, the result is undefined + * and an exception is thrown. Similarly, if the segment's base address is an heap address (resp. off-heap) and + * this address is an off-heap (resp. heap) address, the result is undefined and an exception is thrown. + * Otherwise, the result is a byte offset {@code SO}. If this address falls within the + * spatial bounds of the given segment, then {@code 0 <= SO < segment.byteSize()}; otherwise, {@code SO < 0 || SO > segment.byteSize()}. + * @return the offset of this memory address into the given segment. + * @param segment the segment relative to which this address offset should be computed + * @throws IllegalArgumentException if {@code segment} is not compatible with this address; this can happen, for instance, + * when {@code segment} models an heap memory region, while this address models an off-heap memory address. */ - long segmentOffset(); + long segmentOffset(MemorySegment segment); /** - * Returns the raw long value associated to this memory address. - * @return The raw long value associated to this memory address. - * @throws UnsupportedOperationException if this memory address is associated with an heap segment. + * Returns a new confined native memory segment with given size, and whose base address is this address; the returned segment has its own temporal + * bounds, and can therefore be closed. This method can be useful when interacting with custom native memory sources (e.g. custom allocators), + * where an address to some underlying memory region is typically obtained from native code (often as a plain {@code long} value). + *

    + * The returned segment will feature all access modes + * (see {@link MemorySegment#ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). + *

    + * Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and, + * if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, + * have no visible effect, or cause an unspecified exception to be thrown. + *

    + * Calling {@link MemorySegment#close()} on the returned segment will not result in releasing any + * memory resources which might implicitly be associated with the segment. This method is equivalent to the following code: + *

    {@code
    +    asSegmentRestricted(byteSize, null, null);
    +     * }
    + * This method is restricted. Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @param bytesSize the desired size. + * @return a new confined native memory segment with given base address and size. + * @throws IllegalArgumentException if {@code bytesSize <= 0}. + * @throws UnsupportedOperationException if this address is an heap address. + * @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either + * {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}). */ - long toRawLongValue(); + default MemorySegment asSegmentRestricted(long bytesSize) { + return asSegmentRestricted(bytesSize, null, null); + } /** - * Returns the memory segment (if any) this address belongs to. - * @return The memory segment this address belongs to, or {@code null} if no such segment exists. + * Returns a new confined native memory segment with given size, and whose base address is this address; the returned segment has its own temporal + * bounds, and can therefore be closed. This method can be useful when interacting with custom native memory sources (e.g. custom allocators), + * where an address to some underlying memory region is typically obtained from native code (often as a plain {@code long} value). + *

    + * The returned segment will feature all access modes + * (see {@link MemorySegment#ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). + * Moreover, the returned segment will keep a strong reference to the supplied attachment object (if any), which can + * be useful in cases where the lifecycle of the segment is dependent on that of some other external resource. + *

    + * Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and, + * if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value, + * have no visible effect, or cause an unspecified exception to be thrown. + *

    + * Calling {@link MemorySegment#close()} on the returned segment will not result in releasing any + * memory resources which might implicitly be associated with the segment, but will result in calling the + * provided cleanup action (if any). + *

    + * Both the cleanup action and the attachment object (if any) will be preserved under terminal operations such as + * {@link MemorySegment#handoff(Thread)}, {@link MemorySegment#share()} and {@link MemorySegment#registerCleaner(Cleaner)}. + *

    + * This method is restricted. Restricted methods are unsafe, and, if used incorrectly, their use might crash + * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on + * restricted methods, and use safe and supported functionalities, where possible. + * + * @param bytesSize the desired size. + * @param cleanupAction the cleanup action; can be {@code null}. + * @param attachment an attachment object that will be kept strongly reachable by the returned segment; can be {@code null}. + * @return a new confined native memory segment with given base address and size. + * @throws IllegalArgumentException if {@code bytesSize <= 0}. + * @throws UnsupportedOperationException if this address is an heap address. + * @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either + * {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}). */ - MemorySegment segment(); + MemorySegment asSegmentRestricted(long bytesSize, Runnable cleanupAction, Object attachment); /** - * Reinterpret this address as an offset into the provided segment. - * @param segment the segment to be rebased - * @return a new address pointing to the same memory location through the provided segment - * @throws IllegalArgumentException if the provided segment is not a valid rebase target for this address. This - * can happen, for instance, if an heap-based addressed is rebased to an off-heap memory segment. + * Returns the raw long value associated with this memory address. + * @return The raw long value associated with this memory address. + * @throws UnsupportedOperationException if this memory address is an heap address. */ - MemoryAddress rebase(MemorySegment segment); + long toRawLongValue(); /** * Compares the specified object with this address for equality. Returns {@code true} if and only if the specified @@ -116,21 +186,18 @@ public interface MemoryAddress { int hashCode(); /** - * The unchecked memory address instance modelling the {@code NULL} address. This address is not backed by - * a memory segment and hence it cannot be dereferenced. + * The off-heap memory address instance modelling the {@code NULL} address. */ - MemoryAddress NULL = new MemoryAddressImpl( 0L); + MemoryAddress NULL = new MemoryAddressImpl(null, 0L); /** - * Obtain a new unchecked memory address instance from given long address. The returned address is not backed by - * a memory segment and hence it cannot be dereferenced. + * Obtain an off-heap memory address instance from given long address. * @param value the long address. * @return the new memory address instance. */ static MemoryAddress ofLong(long value) { return value == 0 ? NULL : - new MemoryAddressImpl(value); + new MemoryAddressImpl(null, value); } - } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java index 9e0abe8e1aa75..07f65eb1e3cc7 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryHandles.java @@ -46,44 +46,22 @@ * (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the * byte order associated to a memory access var handle. The resulting memory access var handle can then be combined in various ways * to emulate different addressing modes. The var handles created by this class feature a mandatory coordinate type - * (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate - * multi-dimensional array indexing. + * (of type {@link MemorySegment}), and one {@code long} coordinate type, which represents the offset, in bytes, relative + * to the segment, at which dereference should occur. *

    - * As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows: + * As an example, consider the memory layout expressed by a {@link GroupLayout} instance constructed as follows: *

    {@code
    -SequenceLayout seq = MemoryLayout.ofSequence(5,
    -    MemoryLayout.ofStruct(
    +GroupLayout seq = MemoryLayout.ofStruct(
             MemoryLayout.ofPaddingBits(32),
             MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
    -    ));
    +);
      * }
    * To access the member layout named {@code value}, we can construct a memory access var handle as follows: *
    {@code
    -VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemoryAddress) -> int
    -handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
    -handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
    +VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemorySegment, long) -> int
    +handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
      * }
    * - *

    Addressing mode

    - * - * The final memory location accessed by a memory access var handle can be computed as follows: - * - *
    {@code
    -address = base + offset
    - * }
    - * - * where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset} - * can be expressed in the following form: - * - *
    {@code
    -offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    - * }
    - * - * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as optional {@code long} - * access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are - * static constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)} - * and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively. - * *

    Alignment and access modes

    * * A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B} @@ -130,9 +108,6 @@ private MemoryHandles() { private static final MethodHandle LONG_TO_ADDRESS; private static final MethodHandle ADDRESS_TO_LONG; - private static final MethodHandle ADD_OFFSET; - private static final MethodHandle ADD_STRIDE; - private static final MethodHandle INT_TO_BYTE; private static final MethodHandle BYTE_TO_UNSIGNED_INT; private static final MethodHandle INT_TO_SHORT; @@ -150,12 +125,6 @@ private MemoryHandles() { MethodType.methodType(MemoryAddress.class, long.class)); ADDRESS_TO_LONG = MethodHandles.lookup().findStatic(MemoryHandles.class, "addressToLong", MethodType.methodType(long.class, MemoryAddress.class)); - ADD_OFFSET = MethodHandles.lookup().findStatic(MemoryHandles.class, "addOffset", - MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class)); - - ADD_STRIDE = MethodHandles.lookup().findStatic(MemoryHandles.class, "addStride", - MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class, long.class)); - INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class), MethodType.methodType(byte.class, int.class)); BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt", @@ -184,11 +153,12 @@ private MemoryHandles() { /** * Creates a memory access var handle with the given carrier type and byte order. * - * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate, - * and its variable type is set by the given carrier type. - * - * The alignment constraint for the resulting memory access var handle is the same as the in memory size of the - * carrier type, and the accessed offset is set at zero. + * The returned var handle's type is {@code carrier} and the list of coordinate types is + * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into + * a given memory segment. The returned var handle accesses bytes at an offset in a given + * memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness; + * the alignment constraint (in bytes) for the resulting memory access var handle is the same as the size (in bytes) of the + * carrier type {@code carrier}. * * @apiNote the resulting var handle features certain access mode restrictions, * which are common to all memory access var handles. @@ -209,10 +179,11 @@ public static VarHandle varHandle(Class carrier, ByteOrder byteOrder) { /** * Creates a memory access var handle with the given carrier type, alignment constraint, and byte order. * - * The resulting memory access var handle features a single {@link MemoryAddress} access coordinate, - * and its variable type is set by the given carrier type. - * - * The accessed offset is zero. + * The returned var handle's type is {@code carrier} and the list of coordinate types is + * {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into + * a given memory segment. The returned var handle accesses bytes at an offset in a given + * memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness; + * the alignment constraint (in bytes) for the resulting memory access var handle is given by {@code alignmentBytes}. * * @apiNote the resulting var handle features certain access mode restrictions, * which are common to all memory access var handles. @@ -232,94 +203,11 @@ public static VarHandle varHandle(Class carrier, long alignmentBytes, ByteOrd throw new IllegalArgumentException("Bad alignment: " + alignmentBytes); } - return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{})); - } - - /** - * Returns a var handle that adds a fixed offset to the incoming {@link MemoryAddress} coordinate - * and then propagates such value to the target var handle. That is, - * when the returned var handle receives a memory address coordinate pointing at a memory location at - * offset O, a memory address coordinate pointing at a memory location at offset O' + O - * is created, and then passed to the target var handle. - * - * The returned var handle will feature the same type and access coordinates as the target var handle. - * - * @param target the target memory access handle to access after the offset adjustment. - * @param bytesOffset the offset, in bytes. Must be positive or zero. - * @return the adapted var handle. - * @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}. - */ - public static VarHandle withOffset(VarHandle target, long bytesOffset) { - if (bytesOffset == 0) { - return target; //nothing to do - } - - checkAddressFirstCoordinate(target); - - if (JLI.isMemoryAccessVarHandle(target) && - (bytesOffset & JLI.memoryAddressAlignmentMask(target)) == 0) { - //flatten - return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle( - JLI.memoryAddressCarrier(target), - JLI.memoryAddressAlignmentMask(target), - JLI.memoryAddressByteOrder(target), - JLI.memoryAddressOffset(target) + bytesOffset, - JLI.memoryAddressStrides(target))); - } else { - //slow path - VarHandle res = collectCoordinates(target, 0, ADD_OFFSET); - return insertCoordinates(res, 1, bytesOffset); - } - } - - /** - * Returns a var handle which adds a variable offset to the incoming {@link MemoryAddress} - * access coordinate value and then propagates such value to the target var handle. - * That is, when the returned var handle receives a memory address coordinate pointing at a memory location at - * offset O, a new memory address coordinate pointing at a memory location at offset (S * X) + O - * is created, and then passed to the target var handle, - * where S is a constant stride, whereas X is a dynamic value that will be - * provided as an additional access coordinate (of type {@code long}). - * - * The returned var handle will feature the same type as the target var handle; an additional access coordinate - * of type {@code long} will be added to the access coordinate types of the target var handle at the position - * immediately following the leading access coordinate of type {@link MemoryAddress}. - * - * @param target the target memory access handle to access after the scale adjustment. - * @param bytesStride the stride, in bytes, by which to multiply the coordinate value. - * @return the adapted var handle. - * @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}. - */ - public static VarHandle withStride(VarHandle target, long bytesStride) { - if (bytesStride == 0) { - return dropCoordinates(target, 1, long.class); // dummy coordinate - } - - checkAddressFirstCoordinate(target); - - if (JLI.isMemoryAccessVarHandle(target) && - (bytesStride & JLI.memoryAddressAlignmentMask(target)) == 0) { - //flatten - long[] strides = JLI.memoryAddressStrides(target); - long[] newStrides = new long[strides.length + 1]; - System.arraycopy(strides, 0, newStrides, 1, strides.length); - newStrides[0] = bytesStride; - - return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle( - JLI.memoryAddressCarrier(target), - JLI.memoryAddressAlignmentMask(target), - JLI.memoryAddressByteOrder(target), - JLI.memoryAddressOffset(target), - newStrides)); - } else { - //slow path - VarHandle res = collectCoordinates(target, 0, ADD_STRIDE); - return insertCoordinates(res, 2, bytesStride); - } + return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, false, alignmentBytes - 1, byteOrder)); } /** - * Adapt an existing var handle into a new var handle whose carrier type is {@link MemoryAddress}. + * Adapt an existing var handle into a new var handle whose carrier type is {@link MemorySegment}. * That is, when calling {@link VarHandle#get(Object...)} on the returned var handle, * the read numeric value will be turned into a memory address (as if by calling {@link MemoryAddress#ofLong(long)}); * similarly, when calling {@link VarHandle#set(Object...)}, the memory address to be set will be converted @@ -362,8 +250,8 @@ public static VarHandle asAddressVarHandle(VarHandle target) { MemorySegment segment = MemorySegment.allocateNative(2); VarHandle SHORT_VH = MemoryLayouts.JAVA_SHORT.varHandle(short.class); VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class); - SHORT_VH.set(segment.baseAddress(), (short)-1); - INT_VH.get(segment.baseAddress()); // returns 65535 + SHORT_VH.set(segment, (short)-1); + INT_VH.get(segment); // returns 65535 * } *

    * When calling e.g. {@link VarHandle#set(Object...)} on the resulting var @@ -620,8 +508,8 @@ public static VarHandle dropCoordinates(VarHandle target, int pos, Class... v private static void checkAddressFirstCoordinate(VarHandle handle) { if (handle.coordinateTypes().size() < 1 || - handle.coordinateTypes().get(0) != MemoryAddress.class) { - throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemoryAddress"); + handle.coordinateTypes().get(0) != MemorySegment.class) { + throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemorySegment"); } } @@ -662,12 +550,4 @@ private static MemoryAddress longToAddress(long value) { private static long addressToLong(MemoryAddress value) { return value.toRawLongValue(); } - - private static MemoryAddress addOffset(MemoryAddress address, long offset) { - return address.addOffset(offset); - } - - private static MemoryAddress addStride(MemoryAddress address, long index, long stride) { - return address.addOffset(index * stride); - } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java index b9a6d145f39cd..45487c7d76acd 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java @@ -68,9 +68,9 @@ *

    {@code
     SequenceLayout taggedValues = MemoryLayout.ofSequence(5,
         MemoryLayout.ofStruct(
    -        MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind"),
    +        MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder()).withName("kind"),
             MemoryLayout.ofPaddingBits(24),
    -        MemoryLayout.ofValueBits(32, ByteOrder.NATIVE_ORDER).withName("value")
    +        MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder()).withName("value")
         )
     ).withName("TaggedValues");
      * }
    @@ -147,7 +147,7 @@ *
    {@code
     MemoryLayout taggedValuesWithHole = MemoryLayout.ofSequence(5,
         MemoryLayout.ofStruct(
    -        MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind").
    +        MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder()).withName("kind").
             MemoryLayout.ofPaddingBits(32),
             MemoryLayout.ofPaddingBits(32)
     ));
    @@ -370,6 +370,24 @@ default long byteOffset(PathElement... elements) {
         /**
          * Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
          * where the path is considered rooted in this layout.
    +     * 

    + * The final memory location accessed by the returned memory access var handle can be computed as follows: + * + *

    {@code
    +    address = base + offset
    +     * }
    + * + * where {@code base} denotes the base address expressed by the {@link MemorySegment} access coordinate + * (see {@link MemorySegment#address()} and {@link MemoryAddress#toRawLongValue()}) and {@code offset} + * can be expressed in the following form: + * + *
    {@code
    +    offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
    +     * }
    + * + * where {@code x_1}, {@code x_2}, ... {@code x_n} are dynamic values provided as optional {@code long} + * access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are + * static stride constants which are derived from the layout path. * * @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java index f6ed7dd265f8a..70a8edbcee804 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayouts.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,8 @@ package jdk.incubator.foreign; +import jdk.internal.misc.Unsafe; + import java.nio.ByteOrder; /** @@ -101,6 +103,11 @@ private MemoryLayouts() { */ public static final MemoryLayout PAD_64 = MemoryLayout.ofPaddingBits(64); + /** + * A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}), and byte order set to {@link ByteOrder#nativeOrder()}. + */ + public static final ValueLayout ADDRESS = MemoryLayout.ofValueBits(Unsafe.ADDRESS_SIZE * 8, ByteOrder.nativeOrder()); + /** * A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}. */ @@ -123,8 +130,14 @@ private MemoryLayouts() { /** * A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}. + * The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following + * invariant holds: + *
    {@code
    +    MemoryLayouts.JAVA_LONG.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
    +     * }
    */ - public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder()); + public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder()) + .withBitAlignment(ADDRESS.bitSize()); /** * A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}. @@ -133,6 +146,12 @@ private MemoryLayouts() { /** * A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}. - */ - public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder()); + * The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following + * invariant holds: + *
    {@code
    +    MemoryLayouts.JAVA_DOUBLE.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
    +     * }
    + */ + public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder()) + .withBitAlignment(ADDRESS.bitSize()); } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java index 239d14b8f5e1e..76303bb58facc 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemorySegment.java @@ -26,6 +26,8 @@ package jdk.incubator.foreign; +import java.io.FileDescriptor; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; import jdk.internal.foreign.AbstractMemorySegmentImpl; @@ -38,8 +40,8 @@ import java.nio.channels.FileChannel; import java.nio.file.Path; import java.util.Objects; +import java.util.Optional; import java.util.Spliterator; -import java.util.function.Consumer; /** * A memory segment models a contiguous region of memory. A memory segment is associated with both spatial @@ -54,7 +56,7 @@ *

    * Non-platform classes should not implement {@linkplain MemorySegment} directly. * - *

    Constructing memory segments from different sources

    + *

    Constructing memory segments

    * * There are multiple ways to obtain a memory segment. First, memory segments backed by off-heap memory can * be allocated using one of the many factory methods provided (see {@link MemorySegment#allocateNative(MemoryLayout)}, @@ -74,8 +76,9 @@ * by native memory. *

    * Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method - * {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. Such memory segments are called mapped memory segments - * (see {@link MappedMemorySegment}). + * {@link MemorySegment#mapFile(Path, long, long, FileChannel.MapMode)}. Such memory segments are called mapped memory segments; + * mapped memory segments are associated with an underlying file descriptor. For more operations on mapped memory segments, please refer to the + * {@link MappedMemorySegments} class. *

    * Array and buffer segments are effectively views over existing memory regions which might outlive the * lifecycle of the segments derived from them, and can even be manipulated directly (e.g. via array access, or direct use @@ -84,7 +87,7 @@ * associated with such segments remain inaccessible, and that said memory sources are never aliased by more than one segment * at a time - e.g. so as to prevent concurrent modifications of the contents of an array, or buffer segment. * - *

    Closing a memory segment

    + *

    Explicit deallocation

    * * Memory segments are closed explicitly (see {@link MemorySegment#close()}). When a segment is closed, it is no longer * alive (see {@link #isAlive()}, and subsequent operation on the segment (or on any {@link MemoryAddress} instance @@ -105,7 +108,7 @@ *

    Access modes

    * * Memory segments supports zero or more access modes. Supported access modes are {@link #READ}, - * {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. The set of access modes supported by a segment alters the + * {@link #WRITE}, {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}. The set of access modes supported by a segment alters the * set of operations that are supported by that segment. For instance, attempting to call {@link #close()} on * a segment which does not support the {@link #CLOSE} access mode will result in an exception. *

    @@ -144,68 +147,112 @@ * the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the * owner thread will result in a runtime failure. *

    - * Memory segments support serial thread confinement; that is, ownership of a memory segment can change (see - * {@link #withOwnerThread(Thread)}). This allows, for instance, for two threads {@code A} and {@code B} to share - * a segment in a controlled, cooperative and race-free fashion. + * The {@link #handoff(Thread)} method can be used to change the thread-confinement properties of a memory segment. + * This method is, like {@link #close()}, a terminal operation which marks the original segment as not alive + * (see {@link #isAlive()}) and creates a new segment with the desired thread-confinement properties. Calling + * {@link #handoff(Thread)} is only possible if the segment features the corresponding {@link #HANDOFF} access mode. *

    - * In some cases, it might be useful for multiple threads to process the contents of the same memory segment concurrently - * (e.g. in the case of parallel processing); while memory segments provide strong confinement guarantees, it is possible - * to obtain a {@link Spliterator} from a segment, which can be used to slice the segment and allow multiple thread to - * work in parallel on disjoint segment slices (this assumes that the access mode {@link #ACQUIRE} is set). - * For instance, the following code can be used to sum all int values in a memory segment in parallel: + * For instance, if a client wants to transfer ownership of a segment to another (known) thread, it can do so as follows: + * + *

    {@code
    +MemorySegment segment = ...
    +MemorySegment aSegment = segment.handoff(threadA);
    + * }
    + * + * By doing so, the original segment is marked as not alive, and a new segment is returned whose owner thread + * is {@code threadA}; this allows, for instance, for two threads {@code A} and {@code B} to share + * a segment in a controlled, cooperative and race-free fashion (also known as serial thread confinement). + *

    + * Alternatively, the {@link #share()} method can be used to remove thread ownership altogether; this is only possible + * if the segment features the corresponding {@link #SHARE} access mode. The following code shows how clients can + * obtain a shared segment: + * *

    {@code
     MemorySegment segment = ...
    +MemorySegment sharedSegment = segment.share();
    + * }
    + * + * Again here, the original segment is marked as not alive, and a new shared segment is returned which features no owner + * thread (e.g. {@link #ownerThread()} returns {@code null}). This might be useful when multiple threads need to process + * the contents of the same memory segment concurrently (e.g. in the case of parallel processing). For instance, a client + * might obtain a {@link Spliterator} from a shared segment, which can then be used to slice the segment and allow multiple + * threads to work in parallel on disjoint segment slices. The following code can be used to sum all int values in a memory segment in parallel: + * + *
    {@code
     SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
    -VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
    -int sum = StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
    -                       .mapToInt(s -> (int)VH_int.get(s.baseAddress()))
    -                       .sum();
    +try (MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT).share()) {
    +    VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
    +    int sum = StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
    +                           .mapToInt(s -> (int)VH_int.get(s.address()))
    +                           .sum();
    +}
    + * }
    + * + * Once shared, a segment can be claimed back by a given thread (again using {@link #handoff(Thread)}); in fact, many threads + * can attempt to gain ownership of the same segment, concurrently, and only one of them is guaranteed to succeed. + *

    + * When using shared segments, clients should make sure that no other thread is accessing the segment while + * the segment is being closed. If one or more threads attempts to access a segment concurrently while the + * segment is being closed, an exception might occur on both the accessing and the closing threads. Clients should + * refrain from attempting to close a segment repeatedly (e.g. keep calling {@link #close()} until no exception is thrown); + * such exceptions should instead be seen as an indication that the client code is lacking appropriate synchronization between the threads + * accessing/closing the segment. + * + *

    Implicit deallocation

    + * + * Clients can register a memory segment against a {@link Cleaner}, to make sure that underlying resources associated with + * that segment will be released when the segment becomes unreachable, which can be useful to prevent native memory + * leaks. This can be achieved using the {@link #registerCleaner(Cleaner)} method, as follows: + * + *
    {@code
    +MemorySegment segment = ...
    +MemorySegment gcSegment = segment.registerCleaner(cleaner);
      * }
    * + * Here, the original segment is marked as not alive, and a new segment is returned (the owner thread of the returned + * segment set is set to that of the current thread, see {@link #ownerThread()}); the new segment + * will also be registered with the the {@link Cleaner} instance provided to the {@link #registerCleaner(Cleaner)} method; + * as such, if not closed explicitly (see {@link #close()}), the new segment will be automatically closed by the cleaner. + * * @apiNote In the future, if the Java language permits, {@link MemorySegment} - * may become a {@code sealed} interface, which would prohibit subclassing except by - * {@link MappedMemorySegment} and other explicitly permitted subtypes. + * may become a {@code sealed} interface, which would prohibit subclassing except by other explicitly permitted subtypes. * * @implSpec * Implementations of this interface are immutable, thread-safe and value-based. */ -public interface MemorySegment extends AutoCloseable { +public interface MemorySegment extends Addressable, AutoCloseable { /** * The base memory address associated with this memory segment. The returned address is - * a checked memory address and can therefore be used in derefrence operations + * a checked memory address and can therefore be used in dereference operations * (see {@link MemoryAddress}). * @return The base memory address. + * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the + * thread owning this segment */ - MemoryAddress baseAddress(); + @Override + MemoryAddress address(); /** - * Returns a spliterator for the given memory segment. The returned spliterator reports {@link Spliterator#SIZED}, + * Returns a spliterator for this memory segment. The returned spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#IMMUTABLE}, {@link Spliterator#NONNULL} and {@link Spliterator#ORDERED} * characteristics. *

    - * The returned spliterator splits the segment according to the specified sequence layout; that is, + * The returned spliterator splits this segment according to the specified sequence layout; that is, * if the supplied layout is a sequence layout whose element count is {@code N}, then calling {@link Spliterator#trySplit()} * will result in a spliterator serving approximatively {@code N/2} elements (depending on whether N is even or not). * As such, splitting is possible as long as {@code N >= 2}. The spliterator returns segments that feature the same * access modes as the given segment less the {@link #CLOSE} access mode. *

    - * The returned spliterator effectively allows to slice a segment into disjoint sub-segments, which can then - * be processed in parallel by multiple threads (if the access mode {@link #ACQUIRE} is set). - * While closing the segment (see {@link #close()}) during pending concurrent execution will generally - * fail with an exception, it is possible to close a segment when a spliterator has been obtained but no thread - * is actively working on it using {@link Spliterator#tryAdvance(Consumer)}; in such cases, any subsequent call - * to {@link Spliterator#tryAdvance(Consumer)} will fail with an exception. - * @param segment the segment to be used for splitting. + * The returned spliterator effectively allows to slice this segment into disjoint sub-segments, which can then + * be processed in parallel by multiple threads (if the segment is shared). + * * @param layout the layout to be used for splitting. - * @param the memory segment type * @return the element spliterator for this segment * @throws IllegalStateException if the segment is not alive, or if access occurs from a thread other than the * thread owning this segment */ - static Spliterator spliterator(S segment, SequenceLayout layout) { - return AbstractMemorySegmentImpl.spliterator(segment, layout); - } + Spliterator spliterator(SequenceLayout layout); /** * The thread owning this segment. @@ -213,27 +260,6 @@ static Spliterator spliterator(S segment, SequenceL */ Thread ownerThread(); - /** - * Obtains a new memory segment backed by the same underlying memory region as this segment, - * but with different owner thread. As a side-effect, this segment will be marked as not alive, - * and subsequent operations on this segment will result in runtime errors. - *

    - * Write accesses to the segment's content happens-before - * hand-over from the current owner thread to the new owner thread, which in turn happens before read accesses to the segment's contents on - * the new owner thread. - * - * @param newOwner the new owner thread. - * @return a new memory segment backed by the same underlying memory region as this segment, - * owned by {@code newOwner}. - * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the - * thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different - * thread (see {@link #spliterator(MemorySegment, SequenceLayout)}). - * @throws NullPointerException if {@code newOwner == null} - * @throws IllegalArgumentException if the segment is already a confined segment owner by {@code newOnwer}. - * @throws UnsupportedOperationException if this segment does not support the {@link #HANDOFF} access mode. - */ - MemorySegment withOwnerThread(Thread newOwner); - /** * The size (in bytes) of this memory segment. * @return The size (in bytes) of this memory segment. @@ -242,7 +268,7 @@ static Spliterator spliterator(S segment, SequenceL /** * Obtains a segment view with specific access modes. Supported access modes are {@link #READ}, {@link #WRITE}, - * {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. It is generally not possible to go from a segment with stricter access modes + * {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}. It is generally not possible to go from a segment with stricter access modes * to one with less strict access modes. For instance, attempting to add {@link #WRITE} access mode to a read-only segment * will be met with an exception. * @param accessModes an ORed mask of zero or more access modes. @@ -262,7 +288,7 @@ static Spliterator spliterator(S segment, SequenceL /** * Returns the access modes associated with this segment; the result is represented as ORed values from - * {@link #READ}, {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. + * {@link #READ}, {@link #WRITE}, {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}. * @return the access modes associated with this segment. */ int accessModes(); @@ -270,6 +296,11 @@ static Spliterator spliterator(S segment, SequenceL /** * Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset, * and whose new size is specified by the given argument. + * + * @see #asSlice(long) + * @see #asSlice(MemoryAddress) + * @see #asSlice(MemoryAddress, long) + * * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. * @param newSize The new segment size, specified in bytes. * @return a new memory segment view with updated base/limit addresses. @@ -277,6 +308,81 @@ static Spliterator spliterator(S segment, SequenceL */ MemorySegment asSlice(long offset, long newSize); + /** + * Obtains a new memory segment view whose base address is the given address, and whose new size is specified by the given argument. + *

    + * Equivalent to the following code: + *

    {@code
    +    asSlice(newBase.segmentOffset(this), newSize);
    +     * }
    + * + * @see #asSlice(long) + * @see #asSlice(MemoryAddress) + * @see #asSlice(long, long) + * + * @param newBase The new segment base address. + * @param newSize The new segment size, specified in bytes. + * @return a new memory segment view with updated base/limit addresses. + * @throws NullPointerException if {@code newBase == null}. + * @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset} + */ + default MemorySegment asSlice(MemoryAddress newBase, long newSize) { + Objects.requireNonNull(newBase); + return asSlice(newBase.segmentOffset(this), newSize); + } + + /** + * Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset, + * and whose new size is computed by subtracting the specified offset from this segment size. + *

    + * Equivalent to the following code: + *

    {@code
    +    asSlice(offset, byteSize() - offset);
    +     * }
    + * + * @see #asSlice(MemoryAddress) + * @see #asSlice(MemoryAddress, long) + * @see #asSlice(long, long) + * + * @param offset The new segment base offset (relative to the current segment base address), specified in bytes. + * @return a new memory segment view with updated base/limit addresses. + * @throws IndexOutOfBoundsException if {@code offset < 0}, or {@code offset > byteSize()}. + */ + default MemorySegment asSlice(long offset) { + return asSlice(offset, byteSize() - offset); + } + + /** + * Obtains a new memory segment view whose base address is the given address, and whose new size is computed by subtracting + * the address offset relative to this segment (see {@link MemoryAddress#segmentOffset(MemorySegment)}) from this segment size. + *

    + * Equivalent to the following code: + *

    {@code
    +    asSlice(newBase.segmentOffset(this));
    +     * }
    + * + * @see #asSlice(long) + * @see #asSlice(MemoryAddress, long) + * @see #asSlice(long, long) + * + * @param newBase The new segment base offset (relative to the current segment base address), specified in bytes. + * @return a new memory segment view with updated base/limit addresses. + * @throws NullPointerException if {@code newBase == null}. + * @throws IndexOutOfBoundsException if {@code address.segmentOffset(this) < 0}, or {@code address.segmentOffset(this) > byteSize()}. + */ + default MemorySegment asSlice(MemoryAddress newBase) { + Objects.requireNonNull(newBase); + return asSlice(newBase.segmentOffset(this)); + } + + /** + * Is this a mapped segment? Returns true if this segment is a mapped memory segment, + * created using the {@link #mapFile(Path, long, long, FileChannel.MapMode)} factory, or a buffer segment + * derived from a {@link java.nio.MappedByteBuffer} using the {@link #ofByteBuffer(ByteBuffer)} factory. + * @return {@code true} if this segment is a mapped segment. + */ + boolean isMapped(); + /** * Is this segment alive? * @return true, if the segment is alive. @@ -285,17 +391,93 @@ static Spliterator spliterator(S segment, SequenceL boolean isAlive(); /** - * Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment, - * or to access any {@link MemoryAddress} instance associated with it will fail with {@link IllegalStateException}. + * Closes this memory segment. This is a terminal operation; as a side-effect, if this operation completes + * without exceptions, this segment will be marked as not alive, and subsequent operations on this segment + * will fail with {@link IllegalStateException}. + *

    * Depending on the kind of memory segment being closed, calling this method further triggers deallocation of all the resources * associated with the memory segment. + * + * @apiNote This operation is not idempotent; that is, closing an already closed segment always results in an + * exception being thrown. This reflects a deliberate design choice: segment state transitions should be + * manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application + * logic. This is especially useful when reasoning about the lifecycle of dependent segment views (see {@link #asSlice(MemoryAddress)}, + * where closing one segment might side-effect multiple segments. In such cases it might in fact not be obvious, looking + * at the code, as to whether a given segment is alive or not. + * * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the - * thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different - * thread (see {@link #spliterator(MemorySegment, SequenceLayout)}). + * thread owning this segment, or if this segment is shared and the segment is concurrently accessed while this method is + * called. * @throws UnsupportedOperationException if this segment does not support the {@link #CLOSE} access mode. */ void close(); + /** + * Obtains a new confined memory segment backed by the same underlying memory region as this segment. The returned segment will + * be confined on the specified thread, and will feature the same spatial bounds and access modes (see {@link #accessModes()}) + * as this segment. + *

    + * This is a terminal operation; as a side-effect, if this operation completes + * without exceptions, this segment will be marked as not alive, and subsequent operations on this segment + * will fail with {@link IllegalStateException}. + *

    + * In case where the owner thread of the returned segment differs from that of this segment, write accesses to this + * segment's content happens-before + * hand-over from the current owner thread to the new owner thread, which in turn happens before read accesses + * to the returned segment's contents on the new owner thread. + * + * @param thread the new owner thread + * @return a new confined memory segment whose owner thread is set to {@code thread}. + * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the + * thread owning this segment. + * @throws UnsupportedOperationException if this segment does not support the {@link #HANDOFF} access mode. + * @throws NullPointerException if {@code thread == null} + */ + MemorySegment handoff(Thread thread); + + /** + * Obtains a new shared memory segment backed by the same underlying memory region as this segment. The returned segment will + * not be confined on any thread and can therefore be accessed concurrently from multiple threads; moreover, the + * returned segment will feature the same spatial bounds and access modes (see {@link #accessModes()}) + * as this segment. + *

    + * This is a terminal operation; as a side-effect, if this operation completes + * without exceptions, this segment will be marked as not alive, and subsequent operations on this segment + * will fail with {@link IllegalStateException}. + *

    + * Write accesses to this segment's content happens-before + * hand-over from the current owner thread to the new owner thread, which in turn happens before read accesses + * to the returned segment's contents on a new thread. + * + * @return a new memory shared segment backed by the same underlying memory region as this segment. + * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the + * thread owning this segment. + */ + MemorySegment share(); + + /** + * Register this memory segment instance against a {@link Cleaner} object, by returning a new memory segment backed + * by the same underlying memory region as this segment. The returned segment will feature the same confinement, + * spatial bounds and access modes (see {@link #accessModes()}) as this segment. Moreover, the returned segment + * will be associated with the specified {@link Cleaner} object; this allows for the segment to be closed + * as soon as it becomes unreachable, which might be helpful in preventing native memory leaks. + *

    + * This is a terminal operation; as a side-effect, if this operation completes + * without exceptions, this segment will be marked as not alive, and subsequent operations on this segment + * will fail with {@link IllegalStateException}. + *

    + * The implicit deallocation behavior associated with the returned segment will be preserved under terminal + * operations such as {@link #handoff(Thread)} and {@link #share()}. + * + * @param cleaner the cleaner object, responsible for implicit deallocation of the returned segment. + * @return a new memory segment backed by the same underlying memory region as this segment, which features + * implicit deallocation. + * @throws IllegalStateException if this segment is not alive, or if access occurs from a thread other than the + * thread owning this segment, or if this segment is already associated with a cleaner. + * @throws UnsupportedOperationException if this segment does not support the {@link #CLOSE} access mode. + */ + MemorySegment registerCleaner(Cleaner cleaner); + /** * Fills a value into this memory segment. *

    @@ -306,7 +488,7 @@ static Spliterator spliterator(S segment, SequenceL byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); for (long l = 0; l < segment.byteSize(); l++) { - byteHandle.set(segment.baseAddress(), l, value); + byteHandle.set(segment.address(), l, value); } * }

    * @@ -334,7 +516,7 @@ static Spliterator spliterator(S segment, SequenceL *

    * The result of a bulk copy is unspecified if, in the uncommon case, the source segment and this segment * do not overlap, but refer to overlapping regions of the same backing storage using different addresses. - * For example, this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments. + * For example, this may occur if the same file is {@link MemorySegment#mapFile mapped} to two segments. * * @param src the source segment. * @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}. @@ -349,7 +531,7 @@ static Spliterator spliterator(S segment, SequenceL /** * Finds and returns the offset, in bytes, of the first mismatch between * this segment and a given other segment. The offset is relative to the - * {@link #baseAddress() base address} of each segment and will be in the + * {@link #address() base address} of each segment and will be in the * range of 0 (inclusive) up to the {@link #byteSize() size} (in bytes) of * the smaller memory segment (exclusive). *

    @@ -379,11 +561,21 @@ static Spliterator spliterator(S segment, SequenceL * (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is * direct (see {@link ByteBuffer#isDirect()}). *

    + * The returned buffer's position (see {@link ByteBuffer#position()} is initially set to zero, while + * the returned buffer's capacity and limit (see {@link ByteBuffer#capacity()} and {@link ByteBuffer#limit()}, respectively) + * are set to this segment' size (see {@link MemorySegment#byteSize()}). For this reason, a byte buffer cannot be + * returned if this segment' size is greater than {@link Integer#MAX_VALUE}. + *

    * The life-cycle of the returned buffer will be tied to that of this segment. That means that if the this segment * is closed (see {@link MemorySegment#close()}, accessing the returned * buffer will throw an {@link IllegalStateException}. *

    - * The resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using + * If this segment is shared, calling certain I/O operations on the resulting buffer might result in + * an unspecified exception being thrown. Examples of such problematic operations are {@link FileChannel#read(ByteBuffer)}, + * {@link FileChannel#write(ByteBuffer)}, {@link java.nio.channels.SocketChannel#read(ByteBuffer)} and + * {@link java.nio.channels.SocketChannel#write(ByteBuffer)}. + *

    + * Finally, the resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using * {@link ByteBuffer#order(java.nio.ByteOrder)}. * * @return a {@link ByteBuffer} view of this memory segment. @@ -404,123 +596,190 @@ static Spliterator spliterator(S segment, SequenceL byte[] toByteArray(); /** - * Creates a new buffer memory segment that models the memory associated with the given byte + * Copy the contents of this memory segment into a fresh short array. + * @return a fresh short array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link short[]} instance, e.g. because {@code byteSize() % 2 != 0}, + * or {@code byteSize() / 2 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + short[] toShortArray(); + + /** + * Copy the contents of this memory segment into a fresh char array. + * @return a fresh char array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link char[]} instance, e.g. because {@code byteSize() % 2 != 0}, + * or {@code byteSize() / 2 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + char[] toCharArray(); + + /** + * Copy the contents of this memory segment into a fresh int array. + * @return a fresh int array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link int[]} instance, e.g. because {@code byteSize() % 4 != 0}, + * or {@code byteSize() / 4 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + int[] toIntArray(); + + /** + * Copy the contents of this memory segment into a fresh float array. + * @return a fresh float array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link float[]} instance, e.g. because {@code byteSize() % 4 != 0}, + * or {@code byteSize() / 4 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + float[] toFloatArray(); + + /** + * Copy the contents of this memory segment into a fresh long array. + * @return a fresh long array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link long[]} instance, e.g. because {@code byteSize() % 8 != 0}, + * or {@code byteSize() / 8 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + long[] toLongArray(); + + /** + * Copy the contents of this memory segment into a fresh double array. + * @return a fresh double array copy of this memory segment. + * @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this + * segment's contents cannot be copied into a {@link double[]} instance, e.g. because {@code byteSize() % 8 != 0}, + * or {@code byteSize() / 8 > Integer#MAX_VALUE}. + * @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the + * thread owning this segment. + */ + double[] toDoubleArray(); + + /** + * Creates a new confined buffer memory segment that models the memory associated with the given byte * buffer. The segment starts relative to the buffer's position (inclusive) * and ends relative to the buffer's limit (exclusive). *

    * The segment will feature all access modes (see {@link #ALL_ACCESS}), * unless the given buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will - * not feature the {@link #WRITE} access mode. + * not feature the {@link #WRITE} access mode, and its confinement thread is the current thread (see {@link Thread#currentThread()}). *

    * The resulting memory segment keeps a reference to the backing buffer, to ensure it remains reachable * for the life-time of the segment. * * @param bb the byte buffer backing the buffer memory segment. - * @return a new buffer memory segment. + * @return a new confined buffer memory segment. */ static MemorySegment ofByteBuffer(ByteBuffer bb) { return AbstractMemorySegmentImpl.ofBuffer(bb); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated byte array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated byte array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(byte[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated char array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated char array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(char[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated short array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated short array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(short[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated int array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated int array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable - * for the life-time of the segment. The segment will feature all access modes. + * for the life-time of the segment. The segment will feature all access modes + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(int[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated float array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated float array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(float[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated long array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated long array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(long[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new array memory segment that models the memory associated with a given heap-allocated double array. + * Creates a new confined array memory segment that models the memory associated with a given heap-allocated double array. *

    * The resulting memory segment keeps a reference to the backing array, to ensure it remains reachable * for the life-time of the segment. The segment will feature all access modes * (see {@link #ALL_ACCESS}). * * @param arr the primitive array backing the array memory segment. - * @return a new array memory segment. + * @return a new confined array memory segment. */ static MemorySegment ofArray(double[] arr) { return HeapMemorySegmentImpl.makeArraySegment(arr); } /** - * Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout. + * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given layout. *

    * This is equivalent to the following code: *

    {@code
    @@ -540,7 +799,7 @@ static MemorySegment allocateNative(MemoryLayout layout) {
         }
     
         /**
    -     * Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
    +     * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
          * 

    * This is equivalent to the following code: *

    {@code
    @@ -552,7 +811,7 @@ static MemorySegment allocateNative(MemoryLayout layout) {
          * to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so will result in off-heap memory leaks.
          *
          * @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
    -     * @return a new native memory segment.
    +     * @return a new confined native memory segment.
          * @throws IllegalArgumentException if {@code bytesSize < 0}.
          */
         static MemorySegment allocateNative(long bytesSize) {
    @@ -560,11 +819,25 @@ static MemorySegment allocateNative(long bytesSize) {
         }
     
         /**
    -     * Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
    +     * Creates a new confined mapped memory segment that models a memory-mapped region of a file from a given path.
          * 

    * The segment will feature all access modes (see {@link #ALL_ACCESS}), * unless the given mapping mode is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case - * the segment will not feature the {@link #WRITE} access mode. + * the segment will not feature the {@link #WRITE} access mode, and its confinement thread is the current thread (see {@link Thread#currentThread()}). + *

    + * The content of a mapped memory segment can change at any time, for example + * if the content of the corresponding region of the mapped file is changed by + * this (or another) program. Whether or not such changes occur, and when they + * occur, is operating-system dependent and therefore unspecified. + *

    + * All or part of a mapped memory segment may become + * inaccessible at any time, for example if the backing mapped file is truncated. An + * attempt to access an inaccessible region of a mapped memory segment will not + * change the segment's content and will cause an unspecified exception to be + * thrown either at the time of the access or at some later time. It is + * therefore strongly recommended that appropriate precautions be taken to + * avoid the manipulation of a mapped file by this (or another) program, except to read or write + * the file's content. * * @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block * of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon. @@ -573,21 +846,26 @@ static MemorySegment allocateNative(long bytesSize) { * @param bytesOffset the offset (expressed in bytes) within the file at which the mapped segment is to start. * @param bytesSize the size (in bytes) of the mapped memory backing the memory segment. * @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the chosen mapping mode - * might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegment#force()}). - * @return a new mapped memory segment. + * might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegments#force(MemorySegment)}). + * @return a new confined mapped memory segment. * @throws IllegalArgumentException if {@code bytesOffset < 0}. * @throws IllegalArgumentException if {@code bytesSize < 0}. - * @throws UnsupportedOperationException if an unsupported map mode is specified. + * @throws UnsupportedOperationException if an unsupported map mode is specified, or if the {@code path} is associated + * with a provider that does not support creating file channels. * @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs. + * @throws SecurityException If a security manager is installed and it denies an unspecified permission required by the implementation. + * In the case of the default provider, the {@link SecurityManager#checkRead(String)} method is invoked to check + * read access if the file is opened for reading. The {@link SecurityManager#checkWrite(String)} method is invoked to check + * write access if the file is opened for writing. */ - static MappedMemorySegment mapFromPath(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException { + static MemorySegment mapFile(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException { return MappedMemorySegmentImpl.makeMappedSegment(path, bytesOffset, bytesSize, mapMode); } /** - * Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and + * Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size and * alignment constraint (in bytes). The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}). * * @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero. * Moreover, a client is responsible to call the {@link MemorySegment#close()} on a native memory segment, @@ -595,7 +873,7 @@ static MappedMemorySegment mapFromPath(Path path, long bytesOffset, long bytesSi * * @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment. * @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment. - * @return a new native memory segment. + * @return a new confined native memory segment. * @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes < 0}, or if {@code alignmentBytes} * is not a power of 2. */ @@ -613,39 +891,27 @@ static MemorySegment allocateNative(long bytesSize, long alignmentBytes) { } /** - * Returns a new native memory segment with given base address and size; the returned segment has its own temporal - * bounds, and can therefore be closed; closing such a segment can optionally result in calling an user-provided cleanup - * action. This method can be very useful when interacting with custom native memory sources (e.g. custom allocators, - * GPU memory, etc.), where an address to some underlying memory region is typically obtained from native code - * (often as a plain {@code long} value). The segment will feature all access modes - * (see {@link #ALL_ACCESS}). + * Returns a shared native memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}. + * This method can be very useful when dereferencing memory addresses obtained when interacting with native libraries. + * The segment will feature the {@link #READ} and {@link #WRITE} access modes. + * Equivalent to (but likely more efficient than) the following code: + *

    {@code
    +    MemoryAddress.NULL.asSegmentRestricted(Long.MAX_VALUE)
    +                 .withOwnerThread(null)
    +                 .withAccessModes(READ | WRITE);
    +     * }
    *

    * This method is restricted. Restricted methods are unsafe, and, if used incorrectly, their use might crash * the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on * restricted methods, and use safe and supported functionalities, where possible. * - * @param addr the desired base address - * @param bytesSize the desired size. - * @param owner the desired owner thread. If {@code owner == null}, the returned segment is not confined. - * @param cleanup a cleanup action to be executed when the {@link MemorySegment#close()} method is called on the - * returned segment. If {@code cleanup == null}, no cleanup action is executed. - * @param attachment an object that must be kept alive by the returned segment; this can be useful when - * the returned segment depends on memory which could be released if a certain object - * is determined to be unreacheable. In most cases this will be set to {@code null}. - * @return a new native memory segment with given base address, size, owner, cleanup action and object attachment. - * @throws IllegalArgumentException if {@code bytesSize <= 0}. - * @throws UnsupportedOperationException if {@code addr} is associated with an heap segment. + * @return a memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}. * @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either * {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}). - * @throws NullPointerException if {@code addr == null}. */ - static MemorySegment ofNativeRestricted(MemoryAddress addr, long bytesSize, Thread owner, Runnable cleanup, Object attachment) { - Objects.requireNonNull(addr); - if (bytesSize <= 0) { - throw new IllegalArgumentException("Invalid size : " + bytesSize); - } + static MemorySegment ofNativeRestricted() { Utils.checkRestrictedAccess("MemorySegment.ofNativeRestricted"); - return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, bytesSize, owner, cleanup, attachment); + return NativeMemorySegmentImpl.EVERYTHING; } // access mode masks @@ -672,25 +938,24 @@ static MemorySegment ofNativeRestricted(MemoryAddress addr, long bytesSize, Thre int CLOSE = WRITE << 1; /** - * Acquire access mode; this segment support sharing with threads other than the owner thread, via spliterator - * (see {@link #spliterator(MemorySegment, SequenceLayout)}). + * Share access mode; this segment support sharing with threads other than the owner thread (see {@link #share()}). * @see MemorySegment#accessModes() * @see MemorySegment#withAccessModes(int) */ - int ACQUIRE = CLOSE << 1; + int SHARE = CLOSE << 1; /** * Handoff access mode; this segment support serial thread-confinement via thread ownership changes - * (see {@link #withOwnerThread(Thread)}). + * (see {@link #handoff(Thread)}). * @see MemorySegment#accessModes() * @see MemorySegment#withAccessModes(int) */ - int HANDOFF = ACQUIRE << 1; + int HANDOFF = SHARE << 1; /** * Default access mode; this is a union of all the access modes supported by memory segments. * @see MemorySegment#accessModes() * @see MemorySegment#withAccessModes(int) */ - int ALL_ACCESS = READ | WRITE | CLOSE | ACQUIRE | HANDOFF; + int ALL_ACCESS = READ | WRITE | CLOSE | SHARE | HANDOFF; } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java index d7906221e94b0..5d4000e0aa230 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/package-info.java @@ -28,9 +28,12 @@ *

    Classes to support low-level, safe and efficient memory access. *

    * The key abstractions introduced by this package are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}. - * The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which can - * sometimes be expressed as an offset into a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained - * using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class + * The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which also can + * reside either inside or outside the Java heap (and can sometimes be expressed as an offset into a given segment). + * A memory segment represents the main access coordinate of a memory access var handle, which can be obtained + * using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class; a set of + * common dereference operations is provided also by the {@link jdk.incubator.foreign.MemoryAccess} class, which can + * be useful for simple, non-structured access. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class * hierarchy enables description of memory layouts and basic operations such as computing the size in bytes of a given * layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce * memory access var handles, e.g. using layout paths. @@ -39,25 +42,21 @@ * ranging from {@code 0} to {@code 9}, we can use the following code: * *

    {@code
    -static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
    -
     try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
    -    MemoryAddress base = segment.baseAddress();
    -    for (long i = 0 ; i < 10 ; i++) {
    -       intHandle.set(base.addOffset(i * 4), (int)i);
    +    for (int i = 0 ; i < 10 ; i++) {
    +       MemoryAccess.setIntAtIndex(segment, i);
         }
     }
      * }
    * - * Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at - * a given memory location. Also, {@code intHandle} is stored in a {@code static} and {@code final} field, to achieve - * better performance and allow for inlining of the memory access operation through the {@link java.lang.invoke.VarHandle} - * instance. We then create a native memory segment, that is, a memory segment backed by + * Here create a native memory segment, that is, a memory segment backed by * off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}. * The segment is created inside a try-with-resources construct: this idiom ensures that all the memory resources * associated with the segment will be released at the end of the block, according to the semantics described in * Section {@jls 14.20.3} of The Java Language Specification. Inside the try-with-resources block, we initialize - * the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots, + * the contents of the memory segment using the + * {@link jdk.incubator.foreign.MemoryAccess#setIntAtIndex(jdk.incubator.foreign.MemorySegment, long, int)} helper method; + * more specifically, if we view the memory segment as a set of 10 adjacent slots, * {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot * so that {@code s[i] = i}, again where {@code 0 <= i < 10}. * @@ -77,18 +76,18 @@ * *

    Safety

    * - * This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment using - * a memory address, such an address is validated (upon access), to make sure that it does not point to a memory location - * which resides outside the boundaries of the memory segment it refers to. We call this guarantee spatial safety; + * This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment, + * the access coordinates are validated (upon access), to make sure that access does not occur at an address which resides + * outside the boundaries of the memory segment used by the dereference operation. We call this guarantee spatial safety; * in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in * Section {@jls 15.10.4} of The Java Language Specification. *

    - * Since memory segments can be closed (see above), a memory address is also validated (upon access) to make sure that - * the segment it belongs to has not been closed prematurely. We call this guarantee temporal safety. Note that, + * Since memory segments can be closed (see above), segments are also validated (upon access) to make sure that + * the segment being accessed has not been closed prematurely. We call this guarantee temporal safety. Note that, * in the general case, guaranteeing temporal safety can be hard, as multiple threads could attempt to access and/or close * the same memory segment concurrently. The memory access API addresses this problem by imposing strong - * thread-confinement guarantees on memory segments: each - * memory segment is associated with an owner thread, which is the only thread that can either access or close the segment. + * thread-confinement guarantees on memory segments: upon creation, a memory segment is associated with an owner thread, + * which is the only thread that can either access or close the segment. *

    * Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid * memory location - or fails. diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java index 9c45b02e5c2b7..2833f406b3fb8 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java @@ -25,28 +25,24 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.SequenceLayout; +import jdk.incubator.foreign.*; import jdk.internal.access.JavaNioAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.access.foreign.UnmapperProxy; -import jdk.internal.misc.Unsafe; +import jdk.internal.misc.ScopedMemoryAccess; import jdk.internal.util.ArraysSupport; import jdk.internal.vm.annotation.ForceInline; import sun.security.action.GetPropertyAction; +import java.io.FileDescriptor; import java.lang.invoke.VarHandle; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Random; -import java.util.Spliterator; +import java.util.*; import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; /** * This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information @@ -59,7 +55,7 @@ */ public abstract class AbstractMemorySegmentImpl implements MemorySegment, MemorySegmentProxy { - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); private static final boolean enableSmallSegments = Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true")); @@ -101,58 +97,62 @@ public AbstractMemorySegmentImpl asSlice(long offset, long newSize) { return asSliceNoCheck(offset, newSize); } + @Override + public AbstractMemorySegmentImpl asSlice(long offset) { + checkBounds(offset, 0); + return asSliceNoCheck(offset, length - offset); + } + private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) { return dup(offset, newSize, mask, scope); } - @SuppressWarnings("unchecked") - public static Spliterator spliterator(S segment, SequenceLayout sequenceLayout) { - ((AbstractMemorySegmentImpl)segment).checkValidState(); - if (sequenceLayout.byteSize() != segment.byteSize()) { + @Override + public Spliterator spliterator(SequenceLayout sequenceLayout) { + checkValidState(); + if (sequenceLayout.byteSize() != byteSize()) { throw new IllegalArgumentException(); } - return (Spliterator)new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(), - (AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE)); + return new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(), + withAccessModes(accessModes() & ~CLOSE)); } @Override public final MemorySegment fill(byte value){ - checkRange(0, length, true); - UNSAFE.setMemory(base(), min(), length, value); + checkAccess(0, length, false); + SCOPED_MEMORY_ACCESS.setMemory(scope, base(), min(), length, value); return this; } public void copyFrom(MemorySegment src) { AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src; long size = that.byteSize(); - checkRange(0, size, true); - that.checkRange(0, size, false); - UNSAFE.copyMemory( + checkAccess(0, size, false); + that.checkAccess(0, size, true); + SCOPED_MEMORY_ACCESS.copyMemory(scope, that.scope, that.base(), that.min(), base(), min(), size); } - private final static VarHandle BYTE_HANDLE = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) - .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); - @Override public long mismatch(MemorySegment other) { AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)other; final long thisSize = this.byteSize(); final long thatSize = that.byteSize(); final long length = Math.min(thisSize, thatSize); - this.checkRange(0, length, false); - that.checkRange(0, length, false); + this.checkAccess(0, length, true); + that.checkAccess(0, length, true); if (this == other) { + checkValidState(); return -1; } long i = 0; if (length > 7) { - if ((byte) BYTE_HANDLE.get(this.baseAddress(), 0) != (byte) BYTE_HANDLE.get(that.baseAddress(), 0)) { + if (MemoryAccess.getByte(this) != MemoryAccess.getByte(that)) { return 0; } - i = ArraysSupport.vectorizedMismatchLargeForBytes( + i = vectorizedMismatchLargeForBytes(scope, that.scope, this.base(), this.min(), that.base(), that.min(), length); @@ -163,20 +163,51 @@ public long mismatch(MemorySegment other) { assert remaining < 8 : "remaining greater than 7: " + remaining; i = length - remaining; } - MemoryAddress thisAddress = this.baseAddress(); - MemoryAddress thatAddress = that.baseAddress(); for (; i < length; i++) { - if ((byte) BYTE_HANDLE.get(thisAddress, i) != (byte) BYTE_HANDLE.get(thatAddress, i)) { + if (MemoryAccess.getByteAtOffset(this, i) != MemoryAccess.getByteAtOffset(that, i)) { return i; } } return thisSize != thatSize ? length : -1; } + /** + * Mismatch over long lengths. + */ + private static long vectorizedMismatchLargeForBytes(MemoryScope aScope, MemoryScope bScope, + Object a, long aOffset, + Object b, long bOffset, + long length) { + long off = 0; + long remaining = length; + int i, size; + boolean lastSubRange = false; + while (remaining > 7 && !lastSubRange) { + if (remaining > Integer.MAX_VALUE) { + size = Integer.MAX_VALUE; + } else { + size = (int) remaining; + lastSubRange = true; + } + i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(aScope, bScope, + a, aOffset + off, + b, bOffset + off, + size, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE); + if (i >= 0) + return off + i; + + i = size - ~i; + off += i; + remaining -= i; + } + return ~remaining; + } + @Override @ForceInline - public final MemoryAddress baseAddress() { - return new MemoryAddressImpl(this, 0); + public final MemoryAddress address() { + checkValidState(); + return new MemoryAddressImpl(base(), min()); } @Override @@ -184,7 +215,7 @@ public final ByteBuffer asByteBuffer() { if (!isSet(READ)) { throw unsupportedAccessMode(READ); } - checkIntSize("ByteBuffer"); + checkArraySize("ByteBuffer", 1); ByteBuffer _bb = makeByteBuffer(); if (!isSet(WRITE)) { //scope is IMMUTABLE - obtain a RO byte buffer @@ -234,69 +265,137 @@ private void checkAccessModes(int accessModes) { } } - @Override - public MemorySegment withOwnerThread(Thread newOwner) { - Objects.requireNonNull(newOwner); + public MemorySegment handoff(Thread thread) { + Objects.requireNonNull(thread); + checkValidState(); if (!isSet(HANDOFF)) { throw unsupportedAccessMode(HANDOFF); } - if (scope.ownerThread() == newOwner) { - throw new IllegalArgumentException("Segment already owned by thread: " + newOwner); - } else { - try { - return dup(0L, length, mask, scope.dup(newOwner)); - } finally { - //flush read/writes to segment memory before returning the new segment - VarHandle.fullFence(); - } + try { + return dup(0L, length, mask, scope.confineTo(thread)); + } finally { + //flush read/writes to segment memory before returning the new segment + VarHandle.fullFence(); } } @Override - public final void close() { + public MemorySegment share() { + checkValidState(); + if (!isSet(SHARE)) { + throw unsupportedAccessMode(SHARE); + } + try { + return dup(0L, length, mask, scope.share()); + } finally { + //flush read/writes to segment memory before returning the new segment + VarHandle.fullFence(); + } + } + + @Override + public MemorySegment registerCleaner(Cleaner cleaner) { + Objects.requireNonNull(cleaner); + checkValidState(); if (!isSet(CLOSE)) { throw unsupportedAccessMode(CLOSE); } - closeNoCheck(); + return dup(0L, length, mask, scope.cleanable(cleaner)); } - private final void closeNoCheck() { + @Override + public final void close() { + checkValidState(); + if (!isSet(CLOSE)) { + throw unsupportedAccessMode(CLOSE); + } scope.close(); } - final AbstractMemorySegmentImpl acquire() { - if (Thread.currentThread() != ownerThread() && !isSet(ACQUIRE)) { - throw unsupportedAccessMode(ACQUIRE); - } - return dup(0, length, mask, scope.acquire()); + @Override + public boolean isMapped() { + return false; } @Override public final byte[] toByteArray() { - checkIntSize("byte[]"); - byte[] arr = new byte[(int)length]; - MemorySegment arrSegment = MemorySegment.ofArray(arr); + return toArray(byte[].class, 1, byte[]::new, MemorySegment::ofArray); + } + + @Override + public final short[] toShortArray() { + return toArray(short[].class, 2, short[]::new, MemorySegment::ofArray); + } + + @Override + public final char[] toCharArray() { + return toArray(char[].class, 2, char[]::new, MemorySegment::ofArray); + } + + @Override + public final int[] toIntArray() { + return toArray(int[].class, 4, int[]::new, MemorySegment::ofArray); + } + + @Override + public final float[] toFloatArray() { + return toArray(float[].class, 4, float[]::new, MemorySegment::ofArray); + } + + @Override + public final long[] toLongArray() { + return toArray(long[].class, 8, long[]::new, MemorySegment::ofArray); + } + + @Override + public final double[] toDoubleArray() { + return toArray(double[].class, 8, double[]::new, MemorySegment::ofArray); + } + + private Z toArray(Class arrayClass, int elemSize, IntFunction arrayFactory, Function segmentFactory) { + int size = checkArraySize(arrayClass.getSimpleName(), elemSize); + Z arr = arrayFactory.apply(size); + MemorySegment arrSegment = segmentFactory.apply(arr); arrSegment.copyFrom(this); return arr; } - boolean isSmall() { + @Override + public boolean isSmall() { return isSet(SMALL); } - void checkRange(long offset, long length, boolean writeAccess) { - scope.checkValidState(); - if (writeAccess && !isSet(WRITE)) { + @Override + public void checkAccess(long offset, long length, boolean readOnly) { + if (!readOnly && !isSet(WRITE)) { throw unsupportedAccessMode(WRITE); - } else if (!writeAccess && !isSet(READ)) { + } else if (readOnly && !isSet(READ)) { throw unsupportedAccessMode(READ); } checkBounds(offset, length); } + private void checkAccessAndScope(long offset, long length, boolean readOnly) { + checkValidState(); + checkAccess(offset, length, readOnly); + } + + private void checkValidState() { + try { + scope.checkValidState(); + } catch (ScopedMemoryAccess.Scope.ScopedAccessError ex) { + throw new IllegalStateException("This segment is already closed"); + } + } + + @Override + public long unsafeGetOffset() { + return min(); + } + @Override - public final void checkValidState() { - scope.checkValidState(); + public Object unsafeGetBase() { + return base(); } // Helper methods @@ -305,10 +404,15 @@ private boolean isSet(int mask) { return (this.mask & mask) != 0; } - private void checkIntSize(String typeName) { - if (length > (Integer.MAX_VALUE - 8)) { //conservative check + private int checkArraySize(String typeName, int elemSize) { + if (length % elemSize != 0) { + throw new UnsupportedOperationException(String.format("Segment size is not a multiple of %d. Size: %d", elemSize, length)); + } + long arraySize = length / elemSize; + if (arraySize > (Integer.MAX_VALUE - 8)) { //conservative check throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length)); } + return (int)arraySize; } private void checkBounds(long offset, long length) { @@ -323,6 +427,11 @@ private void checkBounds(long offset, long length) { } } + @Override + public MemoryScope scope() { + return scope; + } + private void checkBoundsSmall(int offset, int length) { if (length < 0 || offset < 0 || @@ -347,8 +456,8 @@ private List modeStrings(int mode) { if ((mode & CLOSE) != 0) { modes.add("CLOSE"); } - if ((mode & ACQUIRE) != 0) { - modes.add("ACQUIRE"); + if ((mode & SHARE) != 0) { + modes.add("SHARE"); } if ((mode & HANDOFF) != 0) { modes.add("HANDOFF"); @@ -398,11 +507,10 @@ public SegmentSplitter trySplit() { public boolean tryAdvance(Consumer action) { Objects.requireNonNull(action); if (currentIndex < elemCount) { - AbstractMemorySegmentImpl acquired = segment.acquire(); + AbstractMemorySegmentImpl acquired = segment; try { action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize)); } finally { - acquired.closeNoCheck(); currentIndex++; if (currentIndex == elemCount) { segment = null; @@ -418,7 +526,7 @@ public boolean tryAdvance(Consumer action) { public void forEachRemaining(Consumer action) { Objects.requireNonNull(action); if (currentIndex < elemCount) { - AbstractMemorySegmentImpl acquired = segment.acquire(); + AbstractMemorySegmentImpl acquired = segment; try { if (acquired.isSmall()) { int index = (int) currentIndex; @@ -433,7 +541,6 @@ public void forEachRemaining(Consumer action) { } } } finally { - acquired.closeNoCheck(); currentIndex = elemCount; segment = null; } @@ -474,7 +581,7 @@ public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) { bufferScope = bufferSegment.scope; modes = bufferSegment.mask; } else { - bufferScope = MemoryScope.create(bb, null); + bufferScope = MemoryScope.createConfined(bb, MemoryScope.DUMMY_CLEANUP_ACTION, null); modes = defaultAccessModes(size); } if (bb.isReadOnly()) { @@ -488,28 +595,4 @@ public static AbstractMemorySegmentImpl ofBuffer(ByteBuffer bb) { return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferScope); } } - - public static final AbstractMemorySegmentImpl NOTHING = new AbstractMemorySegmentImpl( - 0, 0, MemoryScope.createUnchecked(null, null, null) - ) { - @Override - ByteBuffer makeByteBuffer() { - throw new UnsupportedOperationException(); - } - - @Override - long min() { - return 0; - } - - @Override - Object base() { - return null; - } - - @Override - AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) { - throw new UnsupportedOperationException(); - } - }; } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java index 27e8429515c62..1a88f5f1635cd 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/HeapMemorySegmentImpl.java @@ -121,7 +121,7 @@ public static MemorySegment makeArraySegment(double[] arr) { static HeapMemorySegmentImpl makeHeapSegment(Supplier obj, int length, int base, int scale) { int byteSize = length * scale; - MemoryScope scope = MemoryScope.create(null, null); + MemoryScope scope = MemoryScope.createConfined(null, MemoryScope.DUMMY_CLEANUP_ACTION, null); return new HeapMemorySegmentImpl<>(base, obj, byteSize, defaultAccessModes(byteSize), scope); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java index d789957f9437a..a0b6b5ebce574 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/LayoutPath.java @@ -25,21 +25,28 @@ */ package jdk.internal.foreign; +import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; import jdk.internal.access.JavaLangInvokeAccess; import jdk.internal.access.SharedSecrets; -import sun.invoke.util.Wrapper; +import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; +import sun.invoke.util.Wrapper; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.invoke.VarHandle; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.List; import java.util.function.ToLongFunction; import java.util.function.UnaryOperator; -import java.util.stream.LongStream; /** * This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout, ToLongFunction)}, @@ -53,6 +60,17 @@ public class LayoutPath { private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess(); + private static final MethodHandle ADD_STRIDE; + + static { + try { + ADD_STRIDE = MethodHandles.lookup().findStatic(LayoutPath.class, "addStride", + MethodType.methodType(long.class, MemorySegment.class, long.class, long.class, long.class)); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); + } + } + private final MemoryLayout layout; private final long offset; private final LayoutPath enclosing; @@ -141,12 +159,30 @@ public VarHandle dereferenceHandle(Class carrier) { checkAlignment(this); - return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle( - carrier, - layout.byteAlignment() - 1, //mask - ((ValueLayout) layout).order(), - Utils.bitsToBytesOrThrow(offset, IllegalStateException::new), - LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray())); + List> expectedCoordinates = new ArrayList<>(); + Deque perms = new ArrayDeque<>(); + perms.addFirst(0); + expectedCoordinates.add(MemorySegment.class); + + VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1, + ((ValueLayout)layout).order())); + + for (int i = 0 ; i < strides.length ; i++) { + expectedCoordinates.add(long.class); + perms.addFirst(0); + perms.addLast(i + 1); + //add stride + handle = MemoryHandles.collectCoordinates(handle, 1 + i, + MethodHandles.insertArguments(ADD_STRIDE, 1, Utils.bitsToBytesOrThrow(strides[strides.length - 1 - i], IllegalStateException::new))); // MS, long, MS_n, long_n, long + } + //add offset + handle = MemoryHandles.insertCoordinates(handle, 1 + strides.length, Utils.bitsToBytesOrThrow(offset, IllegalStateException::new)); + + if (strides.length > 0) { + // remove duplicate MS args + handle = MemoryHandles.permuteCoordinates(handle, expectedCoordinates, perms.stream().mapToInt(i -> i).toArray()); + } + return handle; } public MemoryLayout layout() { @@ -284,4 +320,9 @@ public PathKind kind() { return kind; } } + + private static long addStride(MemorySegment segment, long stride, long base, long index) { + return MemorySegmentProxy.addOffsets(base, + MemorySegmentProxy.multiplyOffsets(stride, index, ((MemorySegmentProxy)segment)), (MemorySegmentProxy)segment); + } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java index c5321195b099f..0607bf5e3e3e6 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MappedMemorySegmentImpl.java @@ -25,18 +25,19 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.MappedMemorySegment; -import jdk.internal.access.JavaNioAccess; -import jdk.internal.access.SharedSecrets; +import jdk.incubator.foreign.MemorySegment; import jdk.internal.access.foreign.UnmapperProxy; +import jdk.internal.misc.ScopedMemoryAccess; import sun.nio.ch.FileChannelImpl; +import java.io.FileDescriptor; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.Optional; /** * Implementation for a mapped memory segments. A mapped memory segment is a native memory segment, which @@ -44,10 +45,12 @@ * memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial * in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}). */ -public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements MappedMemorySegment { +public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl { private final UnmapperProxy unmapper; + static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, MemoryScope scope) { super(min, length, mask, scope); this.unmapper = unmapper; @@ -55,7 +58,6 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements @Override ByteBuffer makeByteBuffer() { - JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); return nioAccess.newMappedByteBuffer(unmapper, min, (int)length, null, this); } @@ -78,33 +80,40 @@ public MappedMemorySegmentImpl withAccessModes(int accessModes) { } @Override + public boolean isMapped() { + return true; + } + + // support for mapped segments + + public MemorySegment segment() { + return MappedMemorySegmentImpl.this; + } + public void load() { - nioAccess.load(min, unmapper.isSync(), length); + SCOPED_MEMORY_ACCESS.load(scope, min, unmapper.isSync(), length); } - @Override public void unload() { - nioAccess.unload(min, unmapper.isSync(), length); + SCOPED_MEMORY_ACCESS.unload(scope, min, unmapper.isSync(), length); } - @Override public boolean isLoaded() { - return nioAccess.isLoaded(min, unmapper.isSync(), length); + return SCOPED_MEMORY_ACCESS.isLoaded(scope, min, unmapper.isSync(), length); } - @Override public void force() { - nioAccess.force(unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length); + SCOPED_MEMORY_ACCESS.force(scope, unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length); } // factories - public static MappedMemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException { + public static MemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException { if (bytesSize < 0) throw new IllegalArgumentException("Requested bytes size must be >= 0."); if (bytesOffset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0."); try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) { UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, bytesOffset, bytesSize); - MemoryScope scope = MemoryScope.create(null, unmapperProxy::unmap); + MemoryScope scope = MemoryScope.createConfined(null, unmapperProxy::unmap, null); int modes = defaultAccessModes(bytesSize); if (mapMode == FileChannel.MapMode.READ_ONLY) { modes &= ~WRITE; diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java index feed358562ddd..dc76c0e87fa3c 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryAddressImpl.java @@ -25,9 +25,6 @@ */ package jdk.internal.foreign; -import jdk.internal.access.foreign.MemoryAddressProxy; -import jdk.internal.misc.Unsafe; - import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; @@ -37,93 +34,54 @@ * This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information * about the segment this address is associated with, as well as an offset into such segment. */ -public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProxy { - - private static final Unsafe UNSAFE = Unsafe.getUnsafe(); +public final class MemoryAddressImpl implements MemoryAddress { - private final AbstractMemorySegmentImpl segment; + private final Object base; private final long offset; - public MemoryAddressImpl(long offset) { - this.segment = AbstractMemorySegmentImpl.NOTHING; - this.offset = offset; - } - - public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) { - this.segment = Objects.requireNonNull(segment); + public MemoryAddressImpl(Object base, long offset) { + this.base = base; this.offset = offset; } // MemoryAddress methods @Override - public long segmentOffset() { - if (segment() == null) { - throw new UnsupportedOperationException("Address does not have a segment"); + public long segmentOffset(MemorySegment segment) { + Objects.requireNonNull(segment); + AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment; + if (segmentImpl.base() != base) { + throw new IllegalArgumentException("Invalid segment: " + segment); } - return offset; + return offset - segmentImpl.min(); } @Override public long toRawLongValue() { - if (unsafeGetBase() != null) { + if (base != null) { throw new UnsupportedOperationException("Not a native address"); } - return unsafeGetOffset(); - } - - @Override - public MemorySegment segment() { - return segment != AbstractMemorySegmentImpl.NOTHING ? - segment : null; + return offset; } @Override public MemoryAddress addOffset(long bytes) { - return new MemoryAddressImpl(segment, offset + bytes); + return new MemoryAddressImpl(base, offset + bytes); } - @Override - public MemoryAddress rebase(MemorySegment segment) { - AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment; - if (segmentImpl.base() != this.segment.base()) { - throw new IllegalArgumentException("Invalid rebase target: " + segment); - } - return new MemoryAddressImpl((AbstractMemorySegmentImpl)segment, - unsafeGetOffset() - ((MemoryAddressImpl)segment.baseAddress()).unsafeGetOffset()); - } - - // MemoryAddressProxy methods - - public void checkAccess(long offset, long length, boolean readOnly) { - segment.checkRange(MemoryAddressProxy.addOffsets(this.offset, offset, this), length, !readOnly); - } - - public long unsafeGetOffset() { - return segment.min() + offset; - } - - public Object unsafeGetBase() { - return segment.base(); - } - - @Override - public boolean isSmall() { - return segment.isSmall(); - } // Object methods @Override public int hashCode() { - return Objects.hash(unsafeGetBase(), unsafeGetOffset()); + return Objects.hash(base, offset); } @Override public boolean equals(Object that) { if (that instanceof MemoryAddressImpl) { MemoryAddressImpl addr = (MemoryAddressImpl)that; - return Objects.equals(unsafeGetBase(), ((MemoryAddressImpl) that).unsafeGetBase()) && - unsafeGetOffset() == addr.unsafeGetOffset(); + return Objects.equals(base, addr.base) && + offset == addr.offset; } else { return false; } @@ -131,6 +89,15 @@ public boolean equals(Object that) { @Override public String toString() { - return "MemoryAddress{ region: " + segment + " offset=0x" + Long.toHexString(offset) + " }"; + return "MemoryAddress{ base: " + base + " offset=0x" + Long.toHexString(offset) + " }"; + } + + @Override + public MemorySegment asSegmentRestricted(long bytesSize, Runnable cleanupAction, Object attachment) { + Utils.checkRestrictedAccess("MemoryAddress.asSegmentRestricted"); + if (bytesSize <= 0) { + throw new IllegalArgumentException("Invalid size : " + bytesSize); + } + return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(this, bytesSize, cleanupAction, attachment); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryScope.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryScope.java index 43706a97588c9..624e5e5ba7ca4 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryScope.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/MemoryScope.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,287 +26,282 @@ package jdk.internal.foreign; +import jdk.internal.misc.ScopedMemoryAccess; +import jdk.internal.ref.PhantomCleanable; import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; +import java.lang.ref.Cleaner; +import java.lang.ref.Reference; import java.util.Objects; -import java.util.concurrent.atomic.LongAdder; -import java.util.concurrent.locks.StampedLock; /** * This class manages the temporal bounds associated with a memory segment as well - * as thread confinement. - * A scope has a liveness bit, which is updated when the scope is closed - * (this operation is triggered by {@link AbstractMemorySegmentImpl#close()}). - * A scope may also have an associated "owner" thread that confines some operations to - * associated owner thread such as {@link #close()} or {@link #dup(Thread)}. - * Furthermore, a scope is either root scope ({@link #create(Object, Runnable) created} - * when memory segment is allocated) or child scope ({@link #acquire() acquired} from root scope). - * When a child scope is acquired from another child scope, it is actually acquired from - * the root scope. There is only a single level of children. All child scopes are peers. - * A child scope can be {@link #close() closed} at any time, but root scope can only - * be closed after all its children have been closed, at which time any associated - * cleanup action is executed (the associated memory segment is freed). - * Besides thread-confined checked scopes, {@linkplain #createUnchecked(Thread, Object, Runnable)} - * method may be used passing {@code null} as the "owner" thread to create a - * scope that doesn't check for thread-confinement while its temporal bounds are - * enforced reliably only under condition that thread that closes the scope is also - * the single thread performing the checked access or there is an external synchronization - * in place that prevents concurrent access and closing of the scope. + * as thread confinement. A scope has a liveness bit, which is updated when the scope is closed + * (this operation is triggered by {@link AbstractMemorySegmentImpl#close()}). This bit is consulted prior + * to memory access (see {@link #checkValidState()}). + * There are two kinds of memory scope: confined memory scope and shared memory scope. + * A confined memory scope has an associated owner thread that confines some operations to + * associated owner thread such as {@link #close()} or {@link #checkValidState()}. + * Shared scopes do not feature an owner thread - meaning their operations can be called, in a racy + * manner, by multiple threads. To guarantee temporal safety in the presence of concurrent thread, + * shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent + * access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}). */ -abstract class MemoryScope { +abstract class MemoryScope implements ScopedMemoryAccess.Scope { + + static final Runnable DUMMY_CLEANUP_ACTION = () -> { }; + + private MemoryScope(Object ref, Runnable cleanupAction, Cleaner cleaner) { + Objects.requireNonNull(cleanupAction); + this.ref = ref; + this.cleanupAction = cleanupAction; + this.scopeCleanable = cleaner != null ? + new ScopeCleanable(this, cleaner, cleanupAction) : + null; + } /** - * Creates a root MemoryScope with given ref, cleanupAction and current - * thread as the "owner" thread. - * This method may be called in any thread. - * The returned instance may be published unsafely to and used in any thread, - * but methods that explicitly state that they may only be called in "owner" thread, - * must strictly be called in the thread that created the scope - * or else IllegalStateException is thrown. - * + * Creates a confined memory scope with given attachment and cleanup action. The returned scope + * is assumed to be confined on the current thread. * @param ref an optional reference to an instance that needs to be kept reachable - * @param cleanupAction an optional cleanup action to be executed when returned scope is closed - * @return a root MemoryScope + * @param cleanupAction a cleanup action to be executed when returned scope is closed + * @return a confined memory scope */ - static MemoryScope create(Object ref, Runnable cleanupAction) { - return new Root(Thread.currentThread(), ref, cleanupAction); + static MemoryScope createConfined(Object ref, Runnable cleanupAction, Cleaner cleaner) { + return new ConfinedScope(Thread.currentThread(), ref, cleanupAction, cleaner); } /** - * Creates a root MemoryScope with given ref, cleanupAction and "owner" thread. - * This method may be called in any thread. - * The returned instance may be published unsafely to and used in any thread, - * but methods that explicitly state that they may only be called in "owner" thread, - * must strictly be called in given owner thread or else IllegalStateException is thrown. - * If given owner thread is null, the returned MemoryScope is unchecked, meaning - * that all methods may be called in any thread and that {@link #checkValidState()} - * does not check for temporal bounds. - * - * @param owner the desired owner thread. If {@code owner == null}, - * the returned scope is not thread-confined and not checked. + * Creates a shared memory scope with given attachment and cleanup action. * @param ref an optional reference to an instance that needs to be kept reachable - * @param cleanupAction an optional cleanup action to be executed when returned scope is closed - * @return a root MemoryScope + * @param cleanupAction a cleanup action to be executed when returned scope is closed + * @return a shared memory scope */ - static MemoryScope createUnchecked(Thread owner, Object ref, Runnable cleanupAction) { - return new Root(owner, ref, cleanupAction); + static MemoryScope createShared(Object ref, Runnable cleanupAction, Cleaner cleaner) { + return new SharedScope(ref, cleanupAction, cleaner); } - private final Thread owner; - private boolean closed; // = false - private static final VarHandle CLOSED; + protected final Object ref; + protected final ScopeCleanable scopeCleanable; + protected final Runnable cleanupAction; - static { + /** + * Closes this scope, executing any cleanup action (where provided). + * @throws IllegalStateException if this scope is already closed or if this is + * a confined scope and this method is called outside of the owner thread. + */ + final void close() { try { - CLOSED = MethodHandles.lookup().findVarHandle(MemoryScope.class, "closed", boolean.class); - } catch (Throwable ex) { - throw new ExceptionInInitializerError(ex); + justClose(); + cleanupAction.run(); + if (scopeCleanable != null) { + scopeCleanable.clear(); + } + } finally { + Reference.reachabilityFence(this); } } - private MemoryScope(Thread owner) { - this.owner = owner; - } + abstract void justClose(); /** - * Acquires a child scope (or peer scope if this is a child) with current - * thread as the "owner" thread. - * This method may be called in any thread. - * The returned instance may be published unsafely to and used in any thread, - * but methods that explicitly state that they may only be called in "owner" thread, - * must strictly be called in the thread that acquired the scope - * or else IllegalStateException is thrown. - * - * @return a child (or peer) scope - * @throws IllegalStateException if root scope is already closed - */ - abstract MemoryScope acquire(); - - /** - * Closes this scope, executing any cleanup action if this is the root scope. - * This method may only be called in the "owner" thread of this scope unless the - * scope is a root scope with no owner thread - i.e. is not checked. - * + * Duplicates this scope with given new "owner" thread and {@link #close() closes} it. + * @param newOwner new owner thread of the returned memory scope + * @return a new confined scope, which is a duplicate of this scope, but with a new owner thread. * @throws IllegalStateException if this scope is already closed or if this is - * a root scope and there is/are still active child - * scope(s) or if this method is called outside of - * owner thread in checked scope + * a confined scope and this method is called outside of the owner thread. */ - abstract void close(); + final MemoryScope confineTo(Thread newOwner) { + try { + justClose(); + if (scopeCleanable != null) { + scopeCleanable.clear(); + } + return new ConfinedScope(newOwner, ref, cleanupAction, scopeCleanable != null ? + scopeCleanable.cleaner : null); + } finally { + Reference.reachabilityFence(this); + } + } /** * Duplicates this scope with given new "owner" thread and {@link #close() closes} it. - * If this is a root scope, a new root scope is returned; this root scope is closed, but - * without executing the cleanup action, which is instead transferred to the duped scope. - * If this is a child scope, a new child scope is returned. - * This method may only be called in the "owner" thread of this scope unless the - * scope is a root scope with no owner thread - i.e. is not checked. - * The returned instance may be published unsafely to and used in any thread, - * but methods that explicitly state that they may only be called in "owner" thread, - * must strictly be called in given new "owner" thread - * or else IllegalStateException is thrown. - * - * @param newOwner new owner thread of the returned MemoryScope - * @return a duplicate of this scope - * @throws NullPointerException if given owner thread is null + * @return a new shared scope, which is a duplicate of this scope. * @throws IllegalStateException if this scope is already closed or if this is - * a root scope and there is/are still active child - * scope(s) or if this method is called outside of - * owner thread in checked scope + * a confined scope and this method is called outside of the owner thread, + * or if this is already a shared scope. */ - abstract MemoryScope dup(Thread newOwner); + final MemoryScope share() { + try { + justClose(); + if (scopeCleanable != null) { + scopeCleanable.clear(); + } + return new SharedScope(ref, cleanupAction, scopeCleanable != null ? + scopeCleanable.cleaner : null); + } finally { + Reference.reachabilityFence(this); + } + } + + final MemoryScope cleanable(Cleaner cleaner) { + if (scopeCleanable != null) { + throw new IllegalStateException("Already registered with a cleaner"); + } + try { + justClose(); + return ownerThread() == null ? + new SharedScope(ref, cleanupAction, cleaner) : + new ConfinedScope(ownerThread(), ref, cleanupAction, cleaner); + } finally { + Reference.reachabilityFence(this); + } + } /** * Returns "owner" thread of this scope. - * - * @return owner thread (or null for unchecked scope) + * @return owner thread (or null for a shared scope) */ - final Thread ownerThread() { - return owner; - } + public abstract Thread ownerThread(); /** - * This method may be called in any thread. - * + * Returns true, if this scope is still alive. This method may be called in any thread. * @return {@code true} if this scope is not closed yet. */ - final boolean isAlive() { - return !((boolean)CLOSED.getVolatile(this)); - } + public abstract boolean isAlive(); /** - * Checks that this scope is still alive and that this method is executed - * in the "owner" thread of this scope or this scope is unchecked (not associated - * with owner thread). - * - * @throws IllegalStateException if this scope is already closed or this - * method is executed outside owning thread - * in checked scope + * Checks that this scope is still alive (see {@link #isAlive()}). + * @throws IllegalStateException if this scope is already closed or if this is + * a confined scope and this method is called outside of the owner thread. */ - @ForceInline - final void checkValidState() { - if (owner != null && owner != Thread.currentThread()) { - throw new IllegalStateException("Attempted access outside owning thread"); - } - checkAliveConfined(this); + public abstract void checkValidState(); + + @Override + protected Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); } /** - * Checks that this scope is still alive. - * - * @throws IllegalStateException if this scope is already closed + * A confined scope, which features an owner thread. The liveness check features an additional + * confinement check - that is, calling any operation on this scope from a thread other than the + * owner thread will result in an exception. Because of this restriction, checking the liveness bit + * can be performed in plain mode (see {@link #checkAliveRaw(MemoryScope)}). */ - @ForceInline - private static void checkAliveConfined(MemoryScope scope) { - if (scope.closed) { - throw new IllegalStateException("This segment is already closed"); - } - } + static class ConfinedScope extends MemoryScope { - private static final class Root extends MemoryScope { - private final StampedLock lock = new StampedLock(); - private final LongAdder acquired = new LongAdder(); - private final Object ref; - private final Runnable cleanupAction; + private boolean closed; // = false + final Thread owner; - private Root(Thread owner, Object ref, Runnable cleanupAction) { - super(owner); - this.ref = ref; - this.cleanupAction = cleanupAction; + public ConfinedScope(Thread owner, Object ref, Runnable cleanupAction, Cleaner cleaner) { + super(ref, cleanupAction, cleaner); + this.owner = owner; } - @Override - MemoryScope acquire() { - // try to optimistically acquire the lock - long stamp = lock.tryOptimisticRead(); - try { - for (; ; stamp = lock.readLock()) { - if (stamp == 0L) - continue; - checkAliveConfined(this); // plain read is enough here (either successful optimistic read, or full read lock) - - // increment acquires - acquired.increment(); - // did a call to close() occur since we acquired the lock? - if (lock.validate(stamp)) { - // no, just return the acquired scope - return new Child(Thread.currentThread()); - } else { - // yes, just back off and retry (close might have failed, after all) - acquired.decrement(); - } - } - } finally { - if (StampedLock.isReadLockStamp(stamp)) - lock.unlockRead(stamp); + @ForceInline + public final void checkValidState() { + if (owner != Thread.currentThread()) { + throw new IllegalStateException("Attempted access outside owning thread"); + } + if (closed) { + throw ScopedAccessError.INSTANCE; } } @Override - MemoryScope dup(Thread newOwner) { - Objects.requireNonNull(newOwner, "newOwner"); - // pre-allocate duped scope so we don't get OOME later and be left with this scope closed - var duped = new Root(newOwner, ref, cleanupAction); - justClose(); - return duped; + public boolean isAlive() { + return !closed; + } + + void justClose() { + checkValidState(); + closed = true; } @Override - void close() { - justClose(); - if (cleanupAction != null) { - cleanupAction.run(); - } + public Thread ownerThread() { + return owner; } + } - @ForceInline - private void justClose() { - // enter critical section - no acquires are possible past this point - long stamp = lock.writeLock(); + /** + * A shared scope, which can be shared across multiple threads. Closing a shared scope has to ensure that + * (i) only one thread can successfully close a scope (e.g. in a close vs. close race) and that + * (ii) no other thread is accessing the memory associated with this scope while the segment is being + * closed. To ensure the former condition, a CAS is performed on the liveness bit. Ensuring the latter + * is trickier, and require a complex synchronization protocol (see {@link jdk.internal.misc.ScopedMemoryAccess}). + * Since it is the responsibility of the closing thread to make sure that no concurrent access is possible, + * checking the liveness bit upon access can be performed in plain mode (see {@link #checkAliveRaw(MemoryScope)}), + * as in the confined case. + */ + static class SharedScope extends MemoryScope { + + static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess(); + + final static int ALIVE = 0; + final static int CLOSING = 1; + final static int CLOSED = 2; + + int state = ALIVE; + + private static final VarHandle STATE; + + static { try { - checkValidState(); // plain read is enough here (full write lock) - // check for absence of active acquired children - if (acquired.sum() > 0) { - throw new IllegalStateException("Cannot close this scope as it has active acquired children"); - } - // now that we made sure there's no active acquired children, we can mark scope as closed - CLOSED.set(this, true); // plain write is enough here (full write lock) - } finally { - // leave critical section - lock.unlockWrite(stamp); + STATE = MethodHandles.lookup().findVarHandle(SharedScope.class, "state", int.class); + } catch (Throwable ex) { + throw new ExceptionInInitializerError(ex); } } - private final class Child extends MemoryScope { + SharedScope(Object ref, Runnable cleanupAction, Cleaner cleaner) { + super(ref, cleanupAction, cleaner); + } - private Child(Thread owner) { - super(owner); - } + @Override + public Thread ownerThread() { + return null; + } - @Override - MemoryScope acquire() { - return Root.this.acquire(); + @Override + public void checkValidState() { + if (state != ALIVE) { + throw ScopedAccessError.INSTANCE; } + } - @Override - MemoryScope dup(Thread newOwner) { - checkValidState(); // child scope is always checked - // pre-allocate duped scope so we don't get OOME later and be left with this scope closed - var duped = new Child(newOwner); - CLOSED.setVolatile(this, true); - return duped; + void justClose() { + if (!STATE.compareAndSet(this, ALIVE, CLOSING)) { + throw new IllegalStateException("Already closed"); } - - @Override - void close() { - checkValidState(); // child scope is always checked - CLOSED.set(this, true); - // following acts as a volatile write after plain write above so - // plain write gets flushed too (which is important for isAliveThreadSafe()) - Root.this.acquired.decrement(); + boolean success = SCOPED_MEMORY_ACCESS.closeScope(this); + STATE.setVolatile(this, success ? CLOSED : ALIVE); + if (!success) { + throw new IllegalStateException("Cannot close while another thread is accessing the segment"); } } + + @Override + public boolean isAlive() { + return (int)STATE.getVolatile(this) != CLOSED; + } + } + + static class ScopeCleanable extends PhantomCleanable { + final Cleaner cleaner; + final Runnable cleanupAction; + + public ScopeCleanable(MemoryScope referent, Cleaner cleaner, Runnable cleanupAction) { + super(referent, cleaner); + this.cleaner = cleaner; + this.cleanupAction = cleanupAction; + } + + @Override + protected void performCleanup() { + cleanupAction.run(); + } } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java index e650bbd188649..395f21f4d51bd 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/NativeMemorySegmentImpl.java @@ -28,9 +28,8 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; -import jdk.internal.access.JavaNioAccess; -import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; +import jdk.internal.misc.VM; import jdk.internal.vm.annotation.ForceInline; import sun.security.action.GetBooleanAction; @@ -42,11 +41,15 @@ */ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl { + public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, MemoryScope.DUMMY_CLEANUP_ACTION, null) + .share() + .withAccessModes(READ | WRITE); + private static final Unsafe unsafe = Unsafe.getUnsafe(); // The maximum alignment supported by malloc - typically 16 on // 64-bit platforms and 8 on 32-bit platforms. - private final static long MAX_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; + private final static long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16; private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory"); @@ -65,7 +68,6 @@ NativeMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) @Override ByteBuffer makeByteBuffer() { - JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess(); return nioAccess.newDirectByteBuffer(min(), (int) this.length, null, this); } @@ -82,18 +84,24 @@ Object base() { // factories public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes) { - long alignedSize = bytesSize; - - if (alignmentBytes > MAX_ALIGN) { - alignedSize = bytesSize + (alignmentBytes - 1); + if (VM.isDirectMemoryPageAligned()) { + alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize()); } + long alignedSize = alignmentBytes > MAX_MALLOC_ALIGN ? + bytesSize + (alignmentBytes - 1) : + bytesSize; + + nioAccess.reserveMemory(alignedSize, bytesSize); long buf = unsafe.allocateMemory(alignedSize); if (!skipZeroMemory) { unsafe.setMemory(buf, alignedSize, (byte)0); } long alignedBuf = Utils.alignUp(buf, alignmentBytes); - MemoryScope scope = MemoryScope.create(null, () -> unsafe.freeMemory(buf)); + MemoryScope scope = MemoryScope.createConfined(null, () -> { + unsafe.freeMemory(buf); + nioAccess.unreserveMemory(alignedSize, bytesSize); + }, null); MemorySegment segment = new NativeMemorySegmentImpl(buf, alignedSize, defaultAccessModes(alignedSize), scope); if (alignedSize != bytesSize) { @@ -103,8 +111,8 @@ public static MemorySegment makeNativeSegment(long bytesSize, long alignmentByte return segment; } - public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Thread owner, Runnable cleanup, Object attachment) { - MemoryScope scope = MemoryScope.createUnchecked(owner, attachment, cleanup); - return new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope); + public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Runnable cleanupAction, Object ref) { + return new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), + MemoryScope.createConfined(ref, cleanupAction == null ? MemoryScope.DUMMY_CLEANUP_ACTION : cleanupAction, null)); } } diff --git a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java index 8ddbb4cca03f0..dd4d7b8bbfc91 100644 --- a/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java +++ b/src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/Utils.java @@ -26,9 +26,9 @@ package jdk.internal.foreign; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryHandles; -import jdk.internal.access.foreign.MemoryAddressProxy; +import jdk.incubator.foreign.MemorySegment; +import jdk.internal.access.foreign.MemorySegmentProxy; import jdk.internal.misc.VM; import java.lang.invoke.MethodHandle; @@ -46,12 +46,12 @@ public final class Utils { private static final String foreignRestrictedAccess = Optional.ofNullable(VM.getSavedProperty("foreign.restricted")) .orElse("deny"); - private static final MethodHandle ADDRESS_FILTER; + private static final MethodHandle SEGMENT_FILTER; static { try { - ADDRESS_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterAddress", - MethodType.methodType(MemoryAddressProxy.class, MemoryAddress.class)); + SEGMENT_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterSegment", + MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } @@ -70,13 +70,13 @@ public static long bitsToBytesOrThrow(long bits, Supplier exFa } public static VarHandle fixUpVarHandle(VarHandle handle) { - // This adaptation is required, otherwise the memory access var handle will have type MemoryAddressProxy, - // and not MemoryAddress (which the user expects), which causes performance issues with asType() adaptations. - return MemoryHandles.filterCoordinates(handle, 0, ADDRESS_FILTER); + // This adaptation is required, otherwise the memory access var handle will have type MemorySegmentProxy, + // and not MemorySegment (which the user expects), which causes performance issues with asType() adaptations. + return MemoryHandles.filterCoordinates(handle, 0, SEGMENT_FILTER); } - private static MemoryAddressProxy filterAddress(MemoryAddress addr) { - return (MemoryAddressImpl)addr; + private static MemorySegmentProxy filterSegment(MemorySegment segment) { + return (AbstractMemorySegmentImpl)segment; } public static void checkRestrictedAccess(String method) { diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 3cc33d44c6147..47329e8dcb7d5 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -559,7 +559,6 @@ java/beans/XMLEncoder/Test6570354.java 8015593 macosx-all # jdk_foreign java/foreign/TestMismatch.java 8249684 macosx-all -java/foreign/TestMismatch.java 8255270 generic-i586 ############################################################################ diff --git a/test/jdk/java/foreign/TestAdaptVarHandles.java b/test/jdk/java/foreign/TestAdaptVarHandles.java index 0ab4adf4656ce..709aef3fc8836 100644 --- a/test/jdk/java/foreign/TestAdaptVarHandles.java +++ b/test/jdk/java/foreign/TestAdaptVarHandles.java @@ -33,6 +33,7 @@ import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryHandles; +import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.ValueLayout; @@ -70,7 +71,7 @@ public class TestAdaptVarHandles { I2O = MethodHandles.explicitCastArguments(I2S, MethodType.methodType(Object.class, int.class)); S2L = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLong", MethodType.methodType(long.class, String.class)); S2L_EX = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLongException", MethodType.methodType(long.class, String.class)); - BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemoryAddress.class, MemorySegment.class)); + BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemorySegment.class, MemorySegment.class)); SUM_OFFSETS = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "sumOffsets", MethodType.methodType(long.class, long.class, long.class)); VOID_FILTER = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "void_filter", MethodType.methodType(void.class, String.class)); @@ -86,22 +87,29 @@ public class TestAdaptVarHandles { } } + static final VarHandle intHandleIndexed = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT) + .varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); + + static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); + + static final VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); + @Test public void testFilterValue() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); VarHandle intHandle = layout.varHandle(int.class); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S); - i2SHandle.set(segment.baseAddress(), "1"); - String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42"); + i2SHandle.set(segment, "1"); + String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); assertEquals(oldValue, "1"); - String value = (String)i2SHandle.get(segment.baseAddress()); + String value = (String)i2SHandle.get(segment); assertEquals(value, "43"); - boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12"); + boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12"); assertTrue(swapped); - oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42"); + oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42"); assertEquals(oldValue, "12"); - value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress()); + value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment); assertEquals(value, "42"); } @@ -113,16 +121,16 @@ public void testFilterValueComposite() throws Throwable { MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S); i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b"); - i2SHandle.set(segment.baseAddress(), "1"); - String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42"); + i2SHandle.set(segment, "1"); + String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); assertEquals(oldValue, "ab1"); - String value = (String)i2SHandle.get(segment.baseAddress()); + String value = (String)i2SHandle.get(segment); assertEquals(value, "ab43"); - boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12"); + boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12"); assertTrue(swapped); - oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42"); + oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42"); assertEquals(oldValue, "ab12"); - value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress()); + value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment); assertEquals(value, "ab42"); } @@ -132,16 +140,16 @@ public void testFilterValueLoose() throws Throwable { MemorySegment segment = MemorySegment.allocateNative(layout); VarHandle intHandle = layout.varHandle(int.class); VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O); - i2SHandle.set(segment.baseAddress(), "1"); - String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42"); + i2SHandle.set(segment, "1"); + String oldValue = (String)i2SHandle.getAndAdd(segment, "42"); assertEquals(oldValue, "1"); - String value = (String)i2SHandle.get(segment.baseAddress()); + String value = (String)i2SHandle.get(segment); assertEquals(value, "43"); - boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12"); + boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12"); assertTrue(swapped); - oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42"); + oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42"); assertEquals(oldValue, "12"); - value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress()); + value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment); assertEquals(value, "42"); } @@ -152,19 +160,16 @@ public void testBadFilterNullTarget() { @Test(expectedExceptions = NullPointerException.class) public void testBadFilterNullUnbox() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.filterValue(intHandle, null, I2S); } @Test(expectedExceptions = NullPointerException.class) public void testBadFilterNullBox() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.filterValue(intHandle, S2I, null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCarrier() { - VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); MemoryHandles.filterValue(floatHandle, S2I, I2S); } @@ -216,8 +221,7 @@ public void testBadFilterUnboxHandleException() { public void testFilterCoordinates() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); - VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4); - VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandle, 0, BASE_ADDR, S2L); + VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L); intHandle_longIndex.set(segment, "0", 1); int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42); assertEquals(oldValue, 1); @@ -238,46 +242,39 @@ public void testBadFilterCoordinatesNullTarget() { @Test(expectedExceptions = NullPointerException.class) public void testBadFilterCoordinatesNullFilters() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.filterCoordinates(intHandle, 0, null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCoordinatesNegativePos() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.filterCoordinates(intHandle, -1, SUM_OFFSETS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCoordinatesPosTooBig() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.filterCoordinates(intHandle, 1, SUM_OFFSETS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCoordinatesWrongFilterType() { - VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4); - MemoryHandles.filterCoordinates(intHandle, 1, S2I); + MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2I); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCoordinatesWrongFilterException() { - VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4); - MemoryHandles.filterCoordinates(intHandle, 1, S2L_EX); + MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2L_EX); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadFilterCoordinatesTooManyFilters() { - VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4); - MemoryHandles.filterCoordinates(intHandle, 1, S2L, S2L); + MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2L, S2L); } @Test public void testInsertCoordinates() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); - VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4); - VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandle, 0, segment.baseAddress(), 0L); + VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L); intHandle_longIndex.set(1); int oldValue = (int)intHandle_longIndex.getAndAdd(42); assertEquals(oldValue, 1); @@ -298,51 +295,45 @@ public void testBadInsertCoordinatesNullTarget() { @Test(expectedExceptions = NullPointerException.class) public void testBadInsertCoordinatesNullValues() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.insertCoordinates(intHandle, 0, null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadInsertCoordinatesNegativePos() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.insertCoordinates(intHandle, -1, 42); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadInsertCoordinatesPosTooBig() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.insertCoordinates(intHandle, 1, 42); } @Test(expectedExceptions = ClassCastException.class) public void testBadInsertCoordinatesWrongCoordinateType() { - VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4); - MemoryHandles.insertCoordinates(intHandle, 1, "Hello"); + MemoryHandles.insertCoordinates(intHandleIndexed, 1, "Hello"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadInsertCoordinatesTooManyValues() { - VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4); - MemoryHandles.insertCoordinates(intHandle, 1, 0L, 0L); + MemoryHandles.insertCoordinates(intHandleIndexed, 1, 0L, 0L); } @Test public void testPermuteCoordinates() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); - VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4); - VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandle, - List.of(long.class, MemoryAddress.class), 1, 0); - intHandle_swap.set(0L, segment.baseAddress(), 1); - int oldValue = (int)intHandle_swap.getAndAdd(0L, segment.baseAddress(), 42); + VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed, + List.of(long.class, MemorySegment.class), 1, 0); + intHandle_swap.set(0L, segment, 1); + int oldValue = (int)intHandle_swap.getAndAdd(0L, segment, 42); assertEquals(oldValue, 1); - int value = (int)intHandle_swap.get(0L, segment.baseAddress()); + int value = (int)intHandle_swap.get(0L, segment); assertEquals(value, 43); - boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment.baseAddress(), 43, 12); + boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment, 43, 12); assertTrue(swapped); - oldValue = (int)intHandle_swap.compareAndExchange(0L, segment.baseAddress(), 12, 42); + oldValue = (int)intHandle_swap.compareAndExchange(0L, segment, 12, 42); assertEquals(oldValue, 12); - value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment.baseAddress()); + value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment); assertEquals(value, 42); } @@ -353,37 +344,31 @@ public void testBadPermuteCoordinatesNullTarget() { @Test(expectedExceptions = NullPointerException.class) public void testBadPermuteCoordinatesNullCoordinates() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, null); } @Test(expectedExceptions = NullPointerException.class) public void testBadPermuteCoordinatesNullReorder() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, List.of(int.class), null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadPermuteCoordinatesTooManyCoordinates() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), new int[2]); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadPermuteCoordinatesTooFewCoordinates() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, List.of()); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadPermuteCoordinatesIndexTooBig() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), 3); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadPermuteCoordinatesIndexTooSmall() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), -1); } @@ -391,18 +376,17 @@ public void testBadPermuteCoordinatesIndexTooSmall() { public void testCollectCoordinates() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); - VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4); - VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS); - intHandle_sum.set(segment.baseAddress(), -2L, 2L, 1); - int oldValue = (int)intHandle_sum.getAndAdd(segment.baseAddress(), -2L, 2L, 42); + VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS); + intHandle_sum.set(segment, -2L, 2L, 1); + int oldValue = (int)intHandle_sum.getAndAdd(segment, -2L, 2L, 42); assertEquals(oldValue, 1); - int value = (int)intHandle_sum.get(segment.baseAddress(), -2L, 2L); + int value = (int)intHandle_sum.get(segment, -2L, 2L); assertEquals(value, 43); - boolean swapped = (boolean)intHandle_sum.compareAndSet(segment.baseAddress(), -2L, 2L, 43, 12); + boolean swapped = (boolean)intHandle_sum.compareAndSet(segment, -2L, 2L, 43, 12); assertTrue(swapped); - oldValue = (int)intHandle_sum.compareAndExchange(segment.baseAddress(), -2L, 2L, 12, 42); + oldValue = (int)intHandle_sum.compareAndExchange(segment, -2L, 2L, 12, 42); assertEquals(oldValue, 12); - value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), -2L, 2L); + value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, -2L, 2L); assertEquals(value, 42); } @@ -413,37 +397,31 @@ public void testBadCollectCoordinatesNullTarget() { @Test(expectedExceptions = NullPointerException.class) public void testBadCollectCoordinatesNullFilters() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, 0, null); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadCollectCoordinatesNegativePos() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, -1, SUM_OFFSETS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadCollectCoordinatesPosTooBig() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadCollectCoordinatesWrongFilterType() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, 0, SUM_OFFSETS); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadCollectCoordinatesWrongVoidFilterType() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, 0, VOID_FILTER); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadCollectCoordinatesWrongFilterException() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.collectCoordinates(intHandle, 0, S2L_EX); } @@ -451,36 +429,32 @@ public void testBadCollectCoordinatesWrongFilterException() { public void testDropCoordinates() throws Throwable { ValueLayout layout = MemoryLayouts.JAVA_INT; MemorySegment segment = MemorySegment.allocateNative(layout); - VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4); - VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandle, 1, float.class, String.class); - intHandle_dummy.set(segment.baseAddress(), 1f, "hello", 0L, 1); - int oldValue = (int)intHandle_dummy.getAndAdd(segment.baseAddress(), 1f, "hello", 0L, 42); + VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class); + intHandle_dummy.set(segment, 1f, "hello", 0L, 1); + int oldValue = (int)intHandle_dummy.getAndAdd(segment, 1f, "hello", 0L, 42); assertEquals(oldValue, 1); - int value = (int)intHandle_dummy.get(segment.baseAddress(), 1f, "hello", 0L); + int value = (int)intHandle_dummy.get(segment, 1f, "hello", 0L); assertEquals(value, 43); - boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment.baseAddress(), 1f, "hello", 0L, 43, 12); + boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment, 1f, "hello", 0L, 43, 12); assertTrue(swapped); - oldValue = (int)intHandle_dummy.compareAndExchange(segment.baseAddress(), 1f, "hello", 0L, 12, 42); + oldValue = (int)intHandle_dummy.compareAndExchange(segment, 1f, "hello", 0L, 12, 42); assertEquals(oldValue, 12); - value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), 1f, "hello", 0L); + value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 1f, "hello", 0L); assertEquals(value, 42); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadDropCoordinatesNegativePos() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.dropCoordinates(intHandle, -1); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadDropCoordinatesPosTooBig() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.dropCoordinates(intHandle, 2); } @Test(expectedExceptions = NullPointerException.class) public void testBadDropCoordinatesNullValueTypes() { - VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); MemoryHandles.dropCoordinates(intHandle, 1, null); } @@ -507,8 +481,8 @@ static long stringToLongException(String s) throws Throwable { return Long.valueOf(s); } - static MemoryAddress baseAddress(MemorySegment segment) { - return segment.baseAddress(); + static MemorySegment baseAddress(MemorySegment segment) { + return segment; } static long sumOffsets(long l1, long l2) { diff --git a/test/jdk/java/foreign/TestAddressHandle.java b/test/jdk/java/foreign/TestAddressHandle.java index ca97946391572..13abca5420a4c 100644 --- a/test/jdk/java/foreign/TestAddressHandle.java +++ b/test/jdk/java/foreign/TestAddressHandle.java @@ -61,58 +61,52 @@ public class TestAddressHandle { @Test(dataProvider = "addressHandles") public void testAddressHandle(VarHandle addrHandle, int byteSize) { - VarHandle longHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()); + VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); try (MemorySegment segment = MemorySegment.allocateNative(8)) { - MemoryAddress target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? - segment.baseAddress().addOffset(8 - byteSize) : - segment.baseAddress(); - longHandle.set(segment.baseAddress(), 42L); + MemorySegment target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ? + segment.asSlice(8 - byteSize) : + segment; + longHandle.set(segment, 42L); MemoryAddress address = (MemoryAddress)addrHandle.get(target); assertEquals(address.toRawLongValue(), 42L); - try { - longHandle.get(address); // check that address cannot be de-referenced - fail(); - } catch (UnsupportedOperationException ex) { - assertTrue(true); - } addrHandle.set(target, address.addOffset(1)); - long result = (long)longHandle.get(segment.baseAddress()); + long result = (long)longHandle.get(segment); assertEquals(43L, result); } } @Test(dataProvider = "addressHandles") public void testNull(VarHandle addrHandle, int byteSize) { - VarHandle longHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()); + VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); try (MemorySegment segment = MemorySegment.allocateNative(8)) { - longHandle.set(segment.baseAddress(), 0L); - MemoryAddress address = (MemoryAddress)addrHandle.get(segment.baseAddress()); + longHandle.set(segment, 0L); + MemoryAddress address = (MemoryAddress)addrHandle.get(segment); assertTrue(address == MemoryAddress.NULL); } } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadAdaptFloat() { - VarHandle floatHandle = MemoryHandles.varHandle(float.class, ByteOrder.nativeOrder()); + VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class); MemoryHandles.asAddressVarHandle(floatHandle); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadAdaptDouble() { - VarHandle doubleHandle = MemoryHandles.varHandle(double.class, ByteOrder.nativeOrder()); + VarHandle doubleHandle = MemoryLayouts.JAVA_DOUBLE.varHandle(double.class); MemoryHandles.asAddressVarHandle(doubleHandle); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadAdaptBoolean() { - VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); + VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle boolHandle = MemoryHandles.filterValue(intHandle, BOOL_TO_INT, INT_TO_BOOL); MemoryHandles.asAddressVarHandle(boolHandle); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadAdaptString() { - VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); + VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class); VarHandle stringHandle = MemoryHandles.filterValue(intHandle, STRING_TO_INT, INT_TO_STRING); MemoryHandles.asAddressVarHandle(stringHandle); } @@ -121,32 +115,31 @@ public void testBadAdaptString() { static Object[][] addressHandles() { return new Object[][] { // long - { MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder())), 8 }, - { MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 }, + { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 }, { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_LONG.varHandle(long.class)), 8 }, // int - { MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder())), 4 }, - { MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 }, + { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 }, { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_INT.varHandle(int.class)), 4 }, // short - { MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder())), 2 }, - { MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 }, + { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 }, { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_SHORT.varHandle(short.class)), 2 }, // char - { MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder())), 2 }, - { MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 }, + { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 }, { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_CHAR.varHandle(char.class)), 2 }, // byte - { MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder())), 1 }, - { MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 }, + { MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 }, { MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_BYTE.varHandle(byte.class)), 1 } }; } + static VarHandle at(VarHandle handle, long offset) { + return MemoryHandles.insertCoordinates(handle, 1, offset); + } + static int boolToInt(boolean value) { return value ? 1 : 0; } diff --git a/test/jdk/java/foreign/TestArrays.java b/test/jdk/java/foreign/TestArrays.java index 2a5d597b858a5..b8be313ba4d4c 100644 --- a/test/jdk/java/foreign/TestArrays.java +++ b/test/jdk/java/foreign/TestArrays.java @@ -36,7 +36,9 @@ import java.lang.invoke.VarHandle; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; import org.testng.annotations.*; @@ -81,83 +83,126 @@ public class TestArrays { static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement()); static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement()); - static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer handleSetter) { + static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer handleSetter) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { handleSetter.accept(base, i); } } - static void checkBytes(MemoryAddress base, SequenceLayout layout) { - long nBytes = layout.elementCount().getAsLong() * layout.elementLayout().byteSize(); - byte[] arr = base.segment().toByteArray(); - for (long i = 0 ; i < nBytes ; i++) { - byte expected = (byte)byteHandle.get(base, i); - byte found = arr[(int)i]; + static void checkBytes(MemorySegment base, SequenceLayout layout, Function arrayFactory, BiFunction handleGetter) { + int nelems = (int)layout.elementCount().getAsLong(); + Object arr = arrayFactory.apply(base); + for (int i = 0; i < nelems; i++) { + Object found = handleGetter.apply(base, (long) i); + Object expected = java.lang.reflect.Array.get(arr, i); assertEquals(expected, found); } } @Test(dataProvider = "arrays") - public void testArrays(Consumer init, SequenceLayout layout) { + public void testArrays(Consumer init, Consumer checker, MemoryLayout layout) { try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - init.accept(segment.baseAddress()); - checkBytes(segment.baseAddress(), layout); + init.accept(segment); + checker.accept(segment); } } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testTooBigForArray() { - try (MemorySegment segment = MemorySegment.ofNativeRestricted(MemoryAddress.NULL, (long)Integer.MAX_VALUE + 10L, null, null, null)) { - segment.toByteArray(); + @Test(dataProvider = "elemLayouts", + expectedExceptions = UnsupportedOperationException.class) + public void testTooBigForArray(MemoryLayout layout, Function arrayFactory) { + MemoryLayout seq = MemoryLayout.ofSequence((Integer.MAX_VALUE * layout.byteSize()) + 1, layout); + //do not really allocate here, as it's way too much memory + try (MemorySegment segment = MemoryAddress.NULL.asSegmentRestricted(seq.byteSize())) { + arrayFactory.apply(segment); } } - @Test(expectedExceptions = IllegalStateException.class) - public void testArrayFromClosedSegment() { - MemorySegment segment = MemorySegment.allocateNative(8); + @Test(dataProvider = "elemLayouts", + expectedExceptions = UnsupportedOperationException.class) + public void testBadSize(MemoryLayout layout, Function arrayFactory) { + if (layout.byteSize() == 1) throw new UnsupportedOperationException(); //make it fail + try (MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1)) { + arrayFactory.apply(segment); + } + } + + @Test(dataProvider = "elemLayouts", + expectedExceptions = IllegalStateException.class) + public void testArrayFromClosedSegment(MemoryLayout layout, Function arrayFactory) { + MemorySegment segment = MemorySegment.allocateNative(layout); segment.close(); - segment.toByteArray(); + arrayFactory.apply(segment); } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testArrayFromHeapSegmentWithoutAccess() { - MemorySegment segment = MemorySegment.ofArray(new byte[8]); - segment = segment.withAccessModes(segment.accessModes() & ~READ); - segment.toByteArray(); + @Test(dataProvider = "elemLayouts", + expectedExceptions = UnsupportedOperationException.class) + public void testArrayFromHeapSegmentWithoutAccess(MemoryLayout layout, Function arrayFactory) { + MemorySegment segment = MemorySegment.ofArray(new byte[(int)layout.byteSize()]); + segment = segment.withAccessModes(MemorySegment.ALL_ACCESS & ~READ); + arrayFactory.apply(segment); } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testArrayFromNativeSegmentWithoutAccess() { - MemorySegment segment = MemorySegment.allocateNative(8); - segment = segment.withAccessModes(segment.accessModes() & ~READ); - segment.toByteArray(); + @Test(dataProvider = "elemLayouts", + expectedExceptions = UnsupportedOperationException.class) + public void testArrayFromNativeSegmentWithoutAccess(MemoryLayout layout, Function arrayFactory) { + try (MemorySegment segment = MemorySegment.allocateNative(layout).withAccessModes(MemorySegment.ALL_ACCESS & ~READ)) { + arrayFactory.apply(segment); + } } @DataProvider(name = "arrays") public Object[][] nativeAccessOps() { - Consumer byteInitializer = + Consumer byteInitializer = (base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos)); - Consumer charInitializer = + Consumer charInitializer = (base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos)); - Consumer shortInitializer = + Consumer shortInitializer = (base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos)); - Consumer intInitializer = + Consumer intInitializer = (base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos)); - Consumer floatInitializer = + Consumer floatInitializer = (base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos)); - Consumer longInitializer = + Consumer longInitializer = (base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos)); - Consumer doubleInitializer = + Consumer doubleInitializer = (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos)); + Consumer byteChecker = + (base) -> checkBytes(base, bytes, MemorySegment::toByteArray, (addr, pos) -> (byte)byteHandle.get(addr, pos)); + Consumer shortChecker = + (base) -> checkBytes(base, shorts, MemorySegment::toShortArray, (addr, pos) -> (short)shortHandle.get(addr, pos)); + Consumer charChecker = + (base) -> checkBytes(base, chars, MemorySegment::toCharArray, (addr, pos) -> (char)charHandle.get(addr, pos)); + Consumer intChecker = + (base) -> checkBytes(base, ints, MemorySegment::toIntArray, (addr, pos) -> (int)intHandle.get(addr, pos)); + Consumer floatChecker = + (base) -> checkBytes(base, floats, MemorySegment::toFloatArray, (addr, pos) -> (float)floatHandle.get(addr, pos)); + Consumer longChecker = + (base) -> checkBytes(base, longs, MemorySegment::toLongArray, (addr, pos) -> (long)longHandle.get(addr, pos)); + Consumer doubleChecker = + (base) -> checkBytes(base, doubles, MemorySegment::toDoubleArray, (addr, pos) -> (double)doubleHandle.get(addr, pos)); + return new Object[][]{ - {byteInitializer, bytes}, - {charInitializer, chars}, - {shortInitializer, shorts}, - {intInitializer, ints}, - {floatInitializer, floats}, - {longInitializer, longs}, - {doubleInitializer, doubles} + {byteInitializer, byteChecker, bytes}, + {charInitializer, charChecker, chars}, + {shortInitializer, shortChecker, shorts}, + {intInitializer, intChecker, ints}, + {floatInitializer, floatChecker, floats}, + {longInitializer, longChecker, longs}, + {doubleInitializer, doubleChecker, doubles} + }; + } + + @DataProvider(name = "elemLayouts") + public Object[][] elemLayouts() { + return new Object[][] { + { MemoryLayouts.JAVA_BYTE, (Function) MemorySegment::toByteArray }, + { MemoryLayouts.JAVA_SHORT, (Function) MemorySegment::toShortArray }, + { MemoryLayouts.JAVA_CHAR, (Function) MemorySegment::toCharArray }, + { MemoryLayouts.JAVA_INT, (Function) MemorySegment::toIntArray }, + { MemoryLayouts.JAVA_FLOAT, (Function) MemorySegment::toFloatArray }, + { MemoryLayouts.JAVA_LONG, (Function) MemorySegment::toLongArray }, + { MemoryLayouts.JAVA_DOUBLE, (Function) MemorySegment::toDoubleArray } }; } } diff --git a/test/jdk/java/foreign/TestByteBuffer.java b/test/jdk/java/foreign/TestByteBuffer.java index 5960d95a5d409..8c71aeb0d1af5 100644 --- a/test/jdk/java/foreign/TestByteBuffer.java +++ b/test/jdk/java/foreign/TestByteBuffer.java @@ -4,9 +4,7 @@ * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -31,7 +29,8 @@ */ -import jdk.incubator.foreign.MappedMemorySegment; +import jdk.incubator.foreign.MappedMemorySegments; +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryAddress; @@ -62,7 +61,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -73,7 +74,6 @@ import jdk.internal.foreign.HeapMemorySegmentImpl; import jdk.internal.foreign.MappedMemorySegmentImpl; -import jdk.internal.foreign.MemoryAddressImpl; import jdk.internal.foreign.NativeMemorySegmentImpl; import org.testng.SkipException; import org.testng.annotations.*; @@ -134,23 +134,14 @@ public class TestByteBuffer { static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index")); static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value")); - static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement()); - static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement()); - static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement()); - static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement()); - static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement()); - static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement()); - static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement()); - - - static void initTuples(MemoryAddress base, long count) { + static void initTuples(MemorySegment base, long count) { for (long i = 0; i < count ; i++) { indexHandle.set(base, i, (int)i); valueHandle.set(base, i, (float)(i / 500f)); } } - static void checkTuples(MemoryAddress base, ByteBuffer bb, long count) { + static void checkTuples(MemorySegment base, ByteBuffer bb, long count) { for (long i = 0; i < count ; i++) { int index; float value; @@ -160,25 +151,25 @@ static void checkTuples(MemoryAddress base, ByteBuffer bb, long count) { } } - static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer handleSetter) { + static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer handleSetter) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { handleSetter.accept(base, i); } } - static void checkBytes(MemoryAddress base, SequenceLayout layout, + static void checkBytes(MemorySegment base, SequenceLayout layout, Function bufFactory, - BiFunction handleExtractor, + BiFunction handleExtractor, Function bufferExtractor) { long nelems = layout.elementCount().getAsLong(); long elemSize = layout.elementLayout().byteSize(); for (long i = 0 ; i < nelems ; i++) { long limit = nelems - i; - MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize); + MemorySegment resizedSegment = base.asSlice(i * elemSize, limit * elemSize); ByteBuffer bb = resizedSegment.asByteBuffer(); Z z = bufFactory.apply(bb); for (long j = i ; j < limit ; j++) { - Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i); + Object handleValue = handleExtractor.apply(resizedSegment, j - i); Object bufferValue = bufferExtractor.apply(z); if (handleValue instanceof Number) { assertEquals(((Number)handleValue).longValue(), j); @@ -194,11 +185,10 @@ static void checkBytes(MemoryAddress base, SequenceLayout lay @Test public void testOffheap() { try (MemorySegment segment = MemorySegment.allocateNative(tuples)) { - MemoryAddress base = segment.baseAddress(); - initTuples(base, tuples.elementCount().getAsLong()); + initTuples(segment, tuples.elementCount().getAsLong()); ByteBuffer bb = segment.asByteBuffer(); - checkTuples(base, bb, tuples.elementCount().getAsLong()); + checkTuples(segment, bb, tuples.elementCount().getAsLong()); } } @@ -206,11 +196,10 @@ public void testOffheap() { public void testHeap() { byte[] arr = new byte[(int) tuples.byteSize()]; MemorySegment region = MemorySegment.ofArray(arr); - MemoryAddress base = region.baseAddress(); - initTuples(base, tuples.elementCount().getAsLong()); + initTuples(region, tuples.elementCount().getAsLong()); ByteBuffer bb = region.asByteBuffer(); - checkTuples(base, bb, tuples.elementCount().getAsLong()); + checkTuples(region, bb, tuples.elementCount().getAsLong()); } @Test @@ -223,8 +212,7 @@ public void testChannel() throws Throwable { try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) { withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> { MemorySegment segment = MemorySegment.ofByteBuffer(mbb); - MemoryAddress base = segment.baseAddress(); - initTuples(base, tuples.elementCount().getAsLong()); + initTuples(segment, tuples.elementCount().getAsLong()); mbb.force(); }); } @@ -233,20 +221,19 @@ public void testChannel() throws Throwable { try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) { withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> { MemorySegment segment = MemorySegment.ofByteBuffer(mbb); - MemoryAddress base = segment.baseAddress(); - checkTuples(base, mbb, tuples.elementCount().getAsLong()); + checkTuples(segment, mbb, tuples.elementCount().getAsLong()); }); } } @Test public void testDefaultAccessModesMappedSegment() throws Throwable { - try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE)) { + try (MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE)) { assertTrue(segment.hasAccessModes(ALL_ACCESS)); assertEquals(segment.accessModes(), ALL_ACCESS); } - try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY)) { + try (MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY)) { assertTrue(segment.hasAccessModes(ALL_ACCESS & ~WRITE)); assertEquals(segment.accessModes(), ALL_ACCESS & ~WRITE); } @@ -259,19 +246,29 @@ public void testMappedSegment() throws Throwable { f.deleteOnExit(); //write to channel - try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) { - MemoryAddress base = segment.baseAddress(); - initTuples(base, tuples.elementCount().getAsLong()); - segment.force(); + try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) { + initTuples(segment, tuples.elementCount().getAsLong()); + MappedMemorySegments.force(segment); } //read from channel - try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) { - MemoryAddress base = segment.baseAddress(); - checkTuples(base, segment.asByteBuffer(), tuples.elementCount().getAsLong()); + try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) { + checkTuples(segment, segment.asByteBuffer(), tuples.elementCount().getAsLong()); } } + @Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class) + public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable { + File f = new File("test3.out"); + f.createNewFile(); + f.deleteOnExit(); + + MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 8, FileChannel.MapMode.READ_WRITE); + assertTrue(segment.isMapped()); + segment.close(); + mappedBufferOp.apply(segment); + } + @Test public void testMappedSegmentOffset() throws Throwable { File f = new File("test3.out"); @@ -283,19 +280,17 @@ public void testMappedSegmentOffset() throws Throwable { // write one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { //write to channel - try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) { - MemoryAddress base = segment.baseAddress(); - initTuples(base, 1); - segment.force(); + try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) { + initTuples(segment, 1); + MappedMemorySegments.force(segment); } } // check one at a time for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) { //read from channel - try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) { - MemoryAddress base = segment.baseAddress(); - checkTuples(base, segment.asByteBuffer(), 1); + try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) { + checkTuples(segment, segment.asByteBuffer(), 1); } } } @@ -320,35 +315,27 @@ static void checkByteArrayAlignment(MemoryLayout layout) { } @Test(dataProvider = "bufferOps") - public void testScopedBuffer(Function bufferFactory, Map members) { + public void testScopedBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { Buffer bb; try (MemorySegment segment = MemorySegment.allocateNative(bytes)) { - MemoryAddress base = segment.baseAddress(); bb = bufferFactory.apply(segment.asByteBuffer()); } //outside of scope!! - for (Map.Entry e : members.entrySet()) { - if (!e.getKey().getName().contains("get") && - !e.getKey().getName().contains("put")) { - //skip - return; - } - try { - e.getKey().invoke(bb, e.getValue()); - assertTrue(false); - } catch (InvocationTargetException ex) { - Throwable cause = ex.getCause(); - if (cause instanceof IllegalStateException) { - //all get/set buffer operation should fail because of the scope check - assertTrue(ex.getCause().getMessage().contains("already closed")); - } else { - //all other exceptions were unexpected - fail - assertTrue(false); - } - } catch (Throwable ex) { - //unexpected exception - fail - assertTrue(false); + try { + method.invoke(bb, args); + fail("Exception expected"); + } catch (InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof IllegalStateException) { + //all get/set buffer operation should fail because of the scope check + assertTrue(ex.getCause().getMessage().contains("already closed")); + } else { + //all other exceptions were unexpected - fail + fail("Unexpected exception", cause); } + } catch (Throwable ex) { + //unexpected exception - fail + fail("Unexpected exception", ex); } } @@ -387,63 +374,59 @@ public void testScopedBufferAndVarHandle(VarHandle bufferHandle) { } @Test(dataProvider = "bufferOps") - public void testDirectBuffer(Function bufferFactory, Map members) { + public void testDirectBuffer(Function bufferFactory, @NoInjection Method method, Object[] args) { try (MemorySegment segment = MemorySegment.allocateNative(bytes)) { - MemoryAddress base = segment.baseAddress(); Buffer bb = bufferFactory.apply(segment.asByteBuffer()); assertTrue(bb.isDirect()); DirectBuffer directBuffer = ((DirectBuffer)bb); - assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset()); + assertEquals(directBuffer.address(), segment.address().toRawLongValue()); assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer)); assertTrue(directBuffer.cleaner() == null); } } @Test(dataProvider="resizeOps") - public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testResizeOffheap(Consumer checker, Consumer initializer, SequenceLayout seq) { try (MemorySegment segment = MemorySegment.allocateNative(seq)) { - MemoryAddress base = segment.baseAddress(); - initializer.accept(base); - checker.accept(base); + initializer.accept(segment); + checker.accept(segment); } } @Test(dataProvider="resizeOps") - public void testResizeHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testResizeHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int capacity = (int)seq.byteSize(); - MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress(); + MemorySegment base = MemorySegment.ofArray(new byte[capacity]); initializer.accept(base); checker.accept(base); } @Test(dataProvider="resizeOps") - public void testResizeBuffer(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testResizeBuffer(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int capacity = (int)seq.byteSize(); - MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress(); + MemorySegment base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])); initializer.accept(base); checker.accept(base); } @Test(dataProvider="resizeOps") - public void testResizeRoundtripHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testResizeRoundtripHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int capacity = (int)seq.byteSize(); byte[] arr = new byte[capacity]; MemorySegment segment = MemorySegment.ofArray(arr); - MemoryAddress first = segment.baseAddress(); - initializer.accept(first); - MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress(); + initializer.accept(segment); + MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer()); checker.accept(second); } @Test(dataProvider="resizeOps") - public void testResizeRoundtripNative(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testResizeRoundtripNative(Consumer checker, Consumer initializer, SequenceLayout seq) { try (MemorySegment segment = MemorySegment.allocateNative(seq)) { - MemoryAddress first = segment.baseAddress(); - initializer.accept(first); - MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress(); + initializer.accept(segment); + MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer()); checker.accept(second); } } @@ -460,7 +443,7 @@ public void testBufferOnClosedScope() { @Test(expectedExceptions = UnsupportedOperationException.class) public void testTooBigForByteBuffer() { - try (MemorySegment segment = MemorySegment.ofNativeRestricted(MemoryAddress.NULL, (long)Integer.MAX_VALUE + 10L, null, null, null)) { + try (MemorySegment segment = MemoryAddress.NULL.asSegmentRestricted(Integer.MAX_VALUE + 10L)) { segment.asByteBuffer(); } } @@ -470,7 +453,7 @@ public void testBadMapNegativeSize() throws IOException { File f = new File("testNeg1.out"); f.createNewFile(); f.deleteOnExit(); - MemorySegment.mapFromPath(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE); + MemorySegment.mapFile(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -478,39 +461,39 @@ public void testBadMapNegativeOffset() throws IOException { File f = new File("testNeg2.out"); f.createNewFile(); f.deleteOnExit(); - MemorySegment.mapFromPath(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE); + MemorySegment.mapFile(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE); } public void testMapZeroSize() throws IOException { File f = new File("testPos1.out"); f.createNewFile(); f.deleteOnExit(); - try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE)) { + try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE)) { assertEquals(segment.byteSize(), 0); } } @Test(dataProvider="resizeOps") - public void testCopyHeapToNative(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testCopyHeapToNative(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes); MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) { - initializer.accept(heapArray.baseAddress()); + initializer.accept(heapArray); nativeArray.copyFrom(heapArray); - checker.accept(nativeArray.baseAddress()); + checker.accept(nativeArray); } } @Test(dataProvider="resizeOps") - public void testCopyNativeToHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testCopyNativeToHeap(Consumer checker, Consumer initializer, SequenceLayout seq) { checkByteArrayAlignment(seq.elementLayout()); int bytes = (int)seq.byteSize(); try (MemorySegment nativeArray = MemorySegment.allocateNative(seq); MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) { - initializer.accept(nativeArray.baseAddress()); + initializer.accept(nativeArray); heapArray.copyFrom(nativeArray); - checker.accept(heapArray.baseAddress()); + checker.accept(heapArray); } } @@ -546,6 +529,16 @@ public void testBufferToSegment(ByteBuffer bb, Predicate segmentC assertEquals(bb.capacity(), segment.byteSize()); } + @Test(dataProvider="bufferSources") + public void bufferProperties(ByteBuffer bb, Predicate _unused) { + try (MemorySegment segment = MemorySegment.ofByteBuffer(bb)) { + ByteBuffer buffer = segment.asByteBuffer(); + assertEquals(buffer.position(), 0); + assertEquals(buffer.capacity(), segment.byteSize()); + assertEquals(buffer.limit(), segment.byteSize()); + } + } + @Test public void testRoundTripAccess() { try(MemorySegment ms = MemorySegment.allocateNative(4)) { @@ -562,34 +555,77 @@ public void testDeadAccessOnClosedBufferSegment() { s1.close(); // memory freed - intHandle.set(s2.baseAddress(), 0L, 10); // Dead access! + MemoryAccess.setInt(s2, 10); // Dead access! } - @DataProvider(name = "bufferOps") - public static Object[][] bufferOps() throws Throwable { - return new Object[][]{ - { (Function) bb -> bb, bufferMembers(ByteBuffer.class)}, - { (Function) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)}, - { (Function) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)}, - { (Function) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)}, - { (Function) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)}, - { (Function) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)}, - { (Function) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)}, - }; + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testIOOnSharedSegmentBuffer() throws IOException { + File tmp = File.createTempFile("tmp", "txt"); + tmp.deleteOnExit(); + try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) { + MemorySegment segment = MemorySegment.allocateNative(10).share(); + for (int i = 0; i < 10; i++) { + MemoryAccess.setByteAtOffset(segment, i, (byte) i); + } + ByteBuffer bb = segment.asByteBuffer(); + segment.close(); + channel.write(bb); + } + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testIOOnClosedConfinedSegmentBuffer() throws IOException { + File tmp = File.createTempFile("tmp", "txt"); + tmp.deleteOnExit(); + try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) { + MemorySegment segment = MemorySegment.allocateNative(10); + for (int i = 0; i < 10; i++) { + MemoryAccess.setByteAtOffset(segment, i, (byte) i); + } + ByteBuffer bb = segment.asByteBuffer(); + segment.close(); + channel.write(bb); + } + } + + public void testIOOnClosedConfinedSegment() throws IOException { + File tmp = File.createTempFile("tmp", "txt"); + tmp.deleteOnExit(); + try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) { + MemorySegment segment = MemorySegment.allocateNative(10); + for (int i = 0; i < 10; i++) { + MemoryAccess.setByteAtOffset(segment, i, (byte) i); + } + ByteBuffer bb = segment.asByteBuffer(); + channel.write(bb); + } } - static Map bufferMembers(Class bufferClass) { - Map members = new HashMap<>(); + @DataProvider(name = "bufferOps") + public static Object[][] bufferOps() throws Throwable { + List args = new ArrayList<>(); + bufferOpsArgs(args, bb -> bb, ByteBuffer.class); + bufferOpsArgs(args, ByteBuffer::asCharBuffer, CharBuffer.class); + bufferOpsArgs(args, ByteBuffer::asShortBuffer, ShortBuffer.class); + bufferOpsArgs(args, ByteBuffer::asIntBuffer, IntBuffer.class); + bufferOpsArgs(args, ByteBuffer::asFloatBuffer, FloatBuffer.class); + bufferOpsArgs(args, ByteBuffer::asLongBuffer, LongBuffer.class); + bufferOpsArgs(args, ByteBuffer::asDoubleBuffer, DoubleBuffer.class); + return args.toArray(Object[][]::new); + } + + static void bufferOpsArgs(List argsList, Function factory, Class bufferClass) { for (Method m : bufferClass.getMethods()) { //skip statics and method declared in j.l.Object - if (m.getDeclaringClass().equals(Object.class) || - (m.getModifiers() & Modifier.STATIC) != 0) continue; + if (m.getDeclaringClass().equals(Object.class) + || ((m.getModifiers() & Modifier.STATIC) != 0) + || (!m.getName().contains("get") && !m.getName().contains("put")) + || m.getParameterCount() > 2) continue; Object[] args = Stream.of(m.getParameterTypes()) .map(TestByteBuffer::defaultValue) .toArray(); - members.put(m, args); + argsList.add(new Object[] { factory, m, args }); } - return members; } @DataProvider(name = "bufferHandleOps") @@ -622,35 +658,35 @@ static Map varHandleMembers(ByteBuffer bb, VarHandle han @DataProvider(name = "resizeOps") public Object[][] resizeOps() { - Consumer byteInitializer = - (base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos)); - Consumer charInitializer = - (base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos)); - Consumer shortInitializer = - (base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos)); - Consumer intInitializer = - (base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos)); - Consumer floatInitializer = - (base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos)); - Consumer longInitializer = - (base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos)); - Consumer doubleInitializer = - (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos)); - - Consumer byteChecker = - (base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get); - Consumer charChecker = - (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get); - Consumer shortChecker = - (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get); - Consumer intChecker = - (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get); - Consumer floatChecker = - (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get); - Consumer longChecker = - (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get); - Consumer doubleChecker = - (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get); + Consumer byteInitializer = + (base) -> initBytes(base, bytes, (addr, pos) -> MemoryAccess.setByteAtOffset(addr, pos, (byte)(long)pos)); + Consumer charInitializer = + (base) -> initBytes(base, chars, (addr, pos) -> MemoryAccess.setCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (char)(long)pos)); + Consumer shortInitializer = + (base) -> initBytes(base, shorts, (addr, pos) -> MemoryAccess.setShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (short)(long)pos)); + Consumer intInitializer = + (base) -> initBytes(base, ints, (addr, pos) -> MemoryAccess.setIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (int)(long)pos)); + Consumer floatInitializer = + (base) -> initBytes(base, floats, (addr, pos) -> MemoryAccess.setFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (float)(long)pos)); + Consumer longInitializer = + (base) -> initBytes(base, longs, (addr, pos) -> MemoryAccess.setLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (long)pos)); + Consumer doubleInitializer = + (base) -> initBytes(base, doubles, (addr, pos) -> MemoryAccess.setDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (double)(long)pos)); + + Consumer byteChecker = + (base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> MemoryAccess.getByteAtOffset(addr, pos), ByteBuffer::get); + Consumer charChecker = + (base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> MemoryAccess.getCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), CharBuffer::get); + Consumer shortChecker = + (base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> MemoryAccess.getShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), ShortBuffer::get); + Consumer intChecker = + (base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> MemoryAccess.getIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), IntBuffer::get); + Consumer floatChecker = + (base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> MemoryAccess.getFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), FloatBuffer::get); + Consumer longChecker = + (base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> MemoryAccess.getLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), LongBuffer::get); + Consumer doubleChecker = + (base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> MemoryAccess.getDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), DoubleBuffer::get); return new Object[][]{ {byteChecker, byteInitializer, bytes}, @@ -704,6 +740,22 @@ static Object defaultValue(Class c) { } else { throw new IllegalStateException(); } + } else if (c == String.class) { + return "asdf"; + } else if (c == ByteBuffer.class) { + return ByteBuffer.wrap(new byte[1]); + } else if (c == CharBuffer.class) { + return CharBuffer.wrap(new char[1]); + } else if (c == ShortBuffer.class) { + return ShortBuffer.wrap(new short[1]); + } else if (c == IntBuffer.class) { + return IntBuffer.wrap(new int[1]); + } else if (c == FloatBuffer.class) { + return FloatBuffer.wrap(new float[1]); + } else if (c == LongBuffer.class) { + return LongBuffer.wrap(new long[1]); + } else if (c == DoubleBuffer.class) { + return DoubleBuffer.wrap(new double[1]); } else { return null; } @@ -731,4 +783,32 @@ public static Object[][] bufferSources() { throw new ExceptionInInitializerError(ex); } } + + enum MappedSegmentOp { + LOAD(MappedMemorySegments::load), + UNLOAD(MappedMemorySegments::unload), + IS_LOADED(MappedMemorySegments::isLoaded), + FORCE(MappedMemorySegments::force), + BUFFER_LOAD(m -> ((MappedByteBuffer)m.asByteBuffer()).load()), + BUFFER_IS_LOADED(m -> ((MappedByteBuffer)m.asByteBuffer()).isLoaded()), + BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force()); + + + private Consumer segmentOp; + + MappedSegmentOp(Consumer segmentOp) { + this.segmentOp = segmentOp; + } + + void apply(MemorySegment segment) { + segmentOp.accept(segment); + } + } + + @DataProvider(name = "mappedOps") + public static Object[][] mappedOps() { + return Stream.of(MappedSegmentOp.values()) + .map(op -> new Object[] { op }) + .toArray(Object[][]::new); + } } diff --git a/test/jdk/java/foreign/TestCleaner.java b/test/jdk/java/foreign/TestCleaner.java new file mode 100644 index 0000000000000..8ec8e9e0c721c --- /dev/null +++ b/test/jdk/java/foreign/TestCleaner.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules java.base/jdk.internal.ref + * jdk.incubator.foreign/jdk.incubator.foreign + * @run testng/othervm -Dforeign.restricted=permit TestCleaner + */ + +import jdk.incubator.foreign.MemorySegment; +import java.lang.ref.Cleaner; +import jdk.internal.ref.CleanerFactory; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public class TestCleaner { + + static class SegmentState { + private AtomicInteger cleanupCalls = new AtomicInteger(0); + + void cleanup() { + cleanupCalls.incrementAndGet(); + } + + int cleanupCalls() { + return cleanupCalls.get(); + } + } + + @Test(dataProvider = "cleaners") + public void testAtMostOnce(RegisterKind registerKind, Supplier cleanerFactory, SegmentFunction segmentFunction) { + SegmentState segmentState = new SegmentState(); + MemorySegment root = MemorySegment.allocateNative(10).share(); + MemorySegment segment = root.address().asSegmentRestricted(10, () -> { + root.close(); + segmentState.cleanup(); + }, null); + + if (registerKind == RegisterKind.BEFORE) { + // register cleaners before + segment = segment.registerCleaner(cleanerFactory.get()); + } + + kickGCAndCheck(segmentState, segment); + + segment = segmentFunction.apply(segment); + + kickGCAndCheck(segmentState, segment); + + if (segment.isAlive() && registerKind == RegisterKind.AFTER) { + // register cleaners after + segment = segment.registerCleaner(cleanerFactory.get()); + } + + kickGCAndCheck(segmentState, segment); + segment = null; + while (segmentState.cleanupCalls() == 0) { + byte[] b = new byte[100]; + System.gc(); + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + } + assertEquals(segmentState.cleanupCalls(), 1); + } + + private void kickGCAndCheck(SegmentState segmentState, MemorySegment segment) { + for (int i = 0 ; i < 100 ; i++) { + byte[] b = new byte[100]; + System.gc(); + Thread.onSpinWait(); + } + //check that cleanup has not been called by any cleaner yet! + assertEquals(segmentState.cleanupCalls(), segment.isAlive() ? 0 : 1); + } + + @Test(dataProvider = "segmentFunctions") + public void testBadDoubleRegister(Supplier cleanerFactory, SegmentFunction segmentFunction) { + MemorySegment segment = MemorySegment.allocateNative(10); + segment = segment.registerCleaner(cleanerFactory.get()); + segment = segmentFunction.apply(segment); + try { + segment.registerCleaner(cleanerFactory.get()); // error here! + fail(); + } catch (IllegalStateException ex) { + if (!segment.isAlive()) { + assertTrue(ex.getMessage().contains("This segment is already closed")); + } else { + assertTrue(ex.getMessage().contains("Already registered with a cleaner")); + } + } + } + + enum SegmentFunction implements Function { + IDENTITY(Function.identity()), + CLOSE(s -> { s.close(); return s; }), + SHARE(s -> { return s.share(); }); + + private final Function segmentFunction; + + SegmentFunction(Function segmentFunction) { + this.segmentFunction = segmentFunction; + } + + @Override + public MemorySegment apply(MemorySegment segment) { + return segmentFunction.apply(segment); + } + } + + @DataProvider + static Object[][] segmentFunctions() { + Supplier[] cleaners = { + (Supplier)Cleaner::create, + (Supplier)CleanerFactory::cleaner + }; + + SegmentFunction[] segmentFunctions = SegmentFunction.values(); + Object[][] data = new Object[cleaners.length * segmentFunctions.length][3]; + + for (int cleaner = 0 ; cleaner < cleaners.length ; cleaner++) { + for (int segmentFunction = 0 ; segmentFunction < segmentFunctions.length ; segmentFunction++) { + data[cleaner + (cleaners.length * segmentFunction)] = + new Object[] { cleaners[cleaner], segmentFunctions[segmentFunction] }; + } + } + + return data; + } + + enum RegisterKind { + BEFORE, + AFTER; + } + + @DataProvider + static Object[][] cleaners() { + Supplier[] cleaners = { + (Supplier)Cleaner::create, + (Supplier)CleanerFactory::cleaner + }; + + List data = new ArrayList<>(); + for (RegisterKind kind : RegisterKind.values()) { + for (Object cleaner : cleaners) { + for (SegmentFunction segmentFunction : SegmentFunction.values()) { + data.add(new Object[] {kind, cleaner, segmentFunction}); + } + } + } + return data.toArray(Object[][]::new); + } +} diff --git a/test/jdk/java/foreign/TestHandshake.java b/test/jdk/java/foreign/TestHandshake.java new file mode 100644 index 0000000000000..00cf9f40f3ad1 --- /dev/null +++ b/test/jdk/java/foreign/TestHandshake.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @modules jdk.incubator.foreign java.base/jdk.internal.vm.annotation java.base/jdk.internal.misc + * @key randomness + * @run testng/othervm TestHandshake + * @run testng/othervm -Xint TestHandshake + * @run testng/othervm -XX:TieredStopAtLevel=1 TestHandshake + * @run testng/othervm -XX:-TieredCompilation TestHandshake + */ + +import jdk.incubator.foreign.MemoryAccess; +import jdk.incubator.foreign.MemorySegment; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class TestHandshake { + + static final int ITERATIONS = 5; + static final int SEGMENT_SIZE = 1_000_000; + static final int MAX_DELAY_MILLIS = 500; + static final int MAX_EXECUTOR_WAIT_SECONDS = 10; + static final int MAX_THREAD_SPIN_WAIT_MILLIS = 200; + + static final AtomicLong start = new AtomicLong(); + + @Test(dataProvider = "accessors") + public void testHandshake(String testName, AccessorFactory accessorFactory) throws InterruptedException { + for (int it = 0 ; it < ITERATIONS ; it++) { + MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE).share(); + System.out.println("ITERATION " + it); + ExecutorService accessExecutor = Executors.newCachedThreadPool(); + start.set(System.currentTimeMillis()); + for (int i = 0; i < Runtime.getRuntime().availableProcessors() ; i++) { + accessExecutor.execute(accessorFactory.make(i, segment)); + } + int delay = ThreadLocalRandom.current().nextInt(MAX_DELAY_MILLIS); + System.out.println("Starting handshaker with delay set to " + delay + " millis"); + Thread.sleep(delay); + accessExecutor.execute(new Handshaker(segment)); + accessExecutor.shutdown(); + assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS)); + assertTrue(!segment.isAlive()); + } + } + + static abstract class AbstractSegmentAccessor implements Runnable { + final MemorySegment segment; + final int id; + + AbstractSegmentAccessor(int id, MemorySegment segment) { + this.id = id; + this.segment = segment; + } + + @Override + public final void run() { + outer: while (segment.isAlive()) { + try { + doAccess(); + } catch (IllegalStateException ex) { + long delay = System.currentTimeMillis() - start.get(); + System.out.println("Accessor #" + id + " suspending - delay (ms): " + delay); + backoff(); + delay = System.currentTimeMillis() - start.get(); + System.out.println("Accessor #" + id + " resuming - delay (ms): " + delay); + continue outer; + } + } + long delay = System.currentTimeMillis() - start.get(); + System.out.println("Accessor #" + id + " terminated - delay (ms): " + delay); + } + + abstract void doAccess(); + + private void backoff() { + try { + Thread.sleep(ThreadLocalRandom.current().nextInt(MAX_THREAD_SPIN_WAIT_MILLIS)); + } catch (InterruptedException ex) { + throw new AssertionError(ex); + } + } + } + + static abstract class AbstractBufferAccessor extends AbstractSegmentAccessor { + final ByteBuffer bb; + + AbstractBufferAccessor(int id, MemorySegment segment) { + super(id, segment); + this.bb = segment.asByteBuffer(); + } + } + + static class SegmentAccessor extends AbstractSegmentAccessor { + + SegmentAccessor(int id, MemorySegment segment) { + super(id, segment); + } + + @Override + void doAccess() { + int sum = 0; + for (int i = 0; i < segment.byteSize(); i++) { + sum += MemoryAccess.getByteAtOffset(segment, i); + } + } + } + + static class SegmentCopyAccessor extends AbstractSegmentAccessor { + + MemorySegment first, second; + + + SegmentCopyAccessor(int id, MemorySegment segment) { + super(id, segment); + long split = segment.byteSize() / 2; + first = segment.asSlice(0, split); + second = segment.asSlice(split); + } + + @Override + public void doAccess() { + first.copyFrom(second); + } + } + + static class SegmentFillAccessor extends AbstractSegmentAccessor { + + SegmentFillAccessor(int id, MemorySegment segment) { + super(id, segment); + } + + @Override + public void doAccess() { + segment.fill((byte) ThreadLocalRandom.current().nextInt(10)); + } + } + + static class SegmentMismatchAccessor extends AbstractSegmentAccessor { + + final MemorySegment copy; + + SegmentMismatchAccessor(int id, MemorySegment segment) { + super(id, segment); + this.copy = MemorySegment.allocateNative(SEGMENT_SIZE).share(); + copy.copyFrom(segment); + MemoryAccess.setByteAtOffset(copy, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42); + } + + @Override + public void doAccess() { + segment.mismatch(copy); + } + } + + static class BufferAccessor extends AbstractBufferAccessor { + + BufferAccessor(int id, MemorySegment segment) { + super(id, segment); + } + + @Override + public void doAccess() { + int sum = 0; + for (int i = 0; i < bb.capacity(); i++) { + sum += bb.get(i); + } + } + } + + static class BufferHandleAccessor extends AbstractBufferAccessor { + + static VarHandle handle = MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder()); + + public BufferHandleAccessor(int id, MemorySegment segment) { + super(id, segment); + } + + @Override + public void doAccess() { + int sum = 0; + for (int i = 0; i < bb.capacity() / 2; i++) { + sum += (short) handle.get(bb, i); + } + } + }; + + static class Handshaker implements Runnable { + + final MemorySegment segment; + + Handshaker(MemorySegment segment) { + this.segment = segment; + } + + @Override + public void run() { + while (true) { + try { + segment.close(); + break; + } catch (IllegalStateException ex) { + Thread.onSpinWait(); + } + } + long delay = System.currentTimeMillis() - start.get(); + System.out.println("Segment closed - delay (ms): " + delay); + } + } + + interface AccessorFactory { + AbstractSegmentAccessor make(int id, MemorySegment segment); + } + + @DataProvider + static Object[][] accessors() { + return new Object[][] { + { "SegmentAccessor", (AccessorFactory)SegmentAccessor::new }, + { "SegmentCopyAccessor", (AccessorFactory)SegmentCopyAccessor::new }, + { "SegmentMismatchAccesor", (AccessorFactory)SegmentMismatchAccessor::new }, + { "SegmentFillAccessor", (AccessorFactory)SegmentFillAccessor::new }, + { "BufferAccessor", (AccessorFactory)BufferAccessor::new }, + { "BufferHandleAccessor", (AccessorFactory)BufferHandleAccessor::new } + }; + } +} diff --git a/test/jdk/java/foreign/TestLayouts.java b/test/jdk/java/foreign/TestLayouts.java index 9567c7b09cab9..d1b2f29b23d87 100644 --- a/test/jdk/java/foreign/TestLayouts.java +++ b/test/jdk/java/foreign/TestLayouts.java @@ -26,16 +26,13 @@ * @run testng TestLayouts */ -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.*; import java.lang.invoke.VarHandle; import java.nio.ByteOrder; import java.util.function.LongFunction; import java.util.stream.Stream; -import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.SequenceLayout; import org.testng.annotations.*; import static org.testng.Assert.*; @@ -64,14 +61,14 @@ public void testVLAInStruct() { MemoryLayout.PathElement.sequenceElement()); try (MemorySegment segment = MemorySegment.allocateNative( layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr")))) { - size_handle.set(segment.baseAddress(), 4); + size_handle.set(segment, 4); for (int i = 0 ; i < 4 ; i++) { - array_elem_handle.set(segment.baseAddress(), i, (double)i); + array_elem_handle.set(segment, i, (double)i); } //check - assertEquals(4, (int)size_handle.get(segment.baseAddress())); + assertEquals(4, (int)size_handle.get(segment)); for (int i = 0 ; i < 4 ; i++) { - assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i)); + assertEquals((double)i, (double)array_elem_handle.get(segment, i)); } } } @@ -90,14 +87,14 @@ public void testVLAInSequence() { MemoryLayout.PathElement.sequenceElement()); try (MemorySegment segment = MemorySegment.allocateNative( layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement()))) { - size_handle.set(segment.baseAddress(), 4); + size_handle.set(segment, 4); for (int i = 0 ; i < 4 ; i++) { - array_elem_handle.set(segment.baseAddress(), i, (double)i); + array_elem_handle.set(segment, i, (double)i); } //check - assertEquals(4, (int)size_handle.get(segment.baseAddress())); + assertEquals(4, (int)size_handle.get(segment)); for (int i = 0 ; i < 4 ; i++) { - assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i)); + assertEquals((double)i, (double)array_elem_handle.get(segment, i)); } } } @@ -109,13 +106,13 @@ public void testIndexedSequencePath() { VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement()); // init segment for (int i = 0 ; i < 10 ; i++) { - indexHandle.set(segment.baseAddress(), (long)i, i); + indexHandle.set(segment, (long)i, i); } //check statically indexed handles for (int i = 0 ; i < 10 ; i++) { VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i)); - int expected = (int)indexHandle.get(segment.baseAddress(), (long)i); - int found = (int)preindexHandle.get(segment.baseAddress()); + int expected = (int)indexHandle.get(segment, (long)i); + int found = (int)preindexHandle.get(segment); assertEquals(expected, found); } } @@ -174,7 +171,7 @@ public void testStructSizeAndAlign() { MemoryLayouts.JAVA_LONG ); assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8); - assertEquals(struct.byteAlignment(), 8); + assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); } @Test(dataProvider="basicLayouts") @@ -205,7 +202,7 @@ public void testUnionSizeAndAlign() { MemoryLayouts.JAVA_LONG ); assertEquals(struct.byteSize(), 8); - assertEquals(struct.byteAlignment(), 8); + assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment()); } @Test(dataProvider = "layoutKinds") @@ -217,8 +214,10 @@ public void testPadding(LayoutKind kind) { public void testAlignmentString(MemoryLayout layout, long bitAlign) { long[] alignments = { 8, 16, 32, 64, 128 }; for (long a : alignments) { - assertFalse(layout.toString().contains("%")); - assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign); + if (layout.bitAlignment() == layout.bitSize()) { + assertFalse(layout.toString().contains("%")); + assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign); + } } } @@ -309,16 +308,12 @@ public Object[][] basicLayouts() { @DataProvider(name = "layoutsAndAlignments") public Object[][] layoutsAndAlignments() { - Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 5][]; + Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 4][]; int i = 0; //add basic layouts for (MemoryLayout l : basicLayouts) { layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() }; } - //add basic layouts wrapped in a sequence with unspecified size - for (MemoryLayout l : basicLayouts) { - layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(l), l.bitAlignment() }; - } //add basic layouts wrapped in a sequence with given size for (MemoryLayout l : basicLayouts) { layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(4, l), l.bitAlignment() }; diff --git a/test/jdk/java/foreign/TestMemoryAccess.java b/test/jdk/java/foreign/TestMemoryAccess.java index d0d57af38c172..bcaf2b1756fc7 100644 --- a/test/jdk/java/foreign/TestMemoryAccess.java +++ b/test/jdk/java/foreign/TestMemoryAccess.java @@ -36,7 +36,7 @@ import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; -import jdk.incubator.foreign.MemoryAddress; + import java.lang.invoke.VarHandle; import java.util.function.Function; @@ -82,12 +82,11 @@ public void testPaddedArrayAccessByIndexSeq(Function viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) { - MemoryAddress outer_address; + MemorySegment outer_segment; try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout))) { boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE); - MemoryAddress addr = segment.baseAddress(); try { - checker.check(handle, addr); + checker.check(handle, segment); if (isRO) { throw new AssertionError(); //not ok, memory should be immutable } @@ -98,15 +97,15 @@ private void testAccessInternal(Function viewFacto return; } try { - checker.check(handle, addr.addOffset(layout.byteSize())); + checker.check(handle, segment.asSlice(layout.byteSize())); throw new AssertionError(); //not ok, out of bounds } catch (IndexOutOfBoundsException ex) { //ok, should fail (out of bounds) } - outer_address = addr; //leak! + outer_segment = segment; //leak! } try { - checker.check(handle, outer_address); + checker.check(handle, outer_segment); throw new AssertionError(); //not ok, scope is closed } catch (IllegalStateException ex) { //ok, should fail (scope is closed) @@ -114,13 +113,12 @@ private void testAccessInternal(Function viewFacto } private void testArrayAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) { - MemoryAddress outer_address; + MemorySegment outer_segment; try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) { boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE); - MemoryAddress addr = segment.baseAddress(); try { for (int i = 0; i < seq.elementCount().getAsLong(); i++) { - checker.check(handle, addr, i); + checker.check(handle, segment, i); } if (isRO) { throw new AssertionError(); //not ok, memory should be immutable @@ -132,15 +130,15 @@ private void testArrayAccessInternal(Function view return; } try { - checker.check(handle, addr, seq.elementCount().getAsLong()); + checker.check(handle, segment, seq.elementCount().getAsLong()); throw new AssertionError(); //not ok, out of bounds } catch (IndexOutOfBoundsException ex) { //ok, should fail (out of bounds) } - outer_address = addr; //leak! + outer_segment = segment; //leak! } try { - checker.check(handle, outer_address, 0); + checker.check(handle, outer_segment, 0); throw new AssertionError(); //not ok, scope is closed } catch (IllegalStateException ex) { //ok, should fail (scope is closed) @@ -183,14 +181,13 @@ public void testBadCarriers(Class carrier) { } private void testMatrixAccessInternal(Function viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) { - MemoryAddress outer_address; + MemorySegment outer_segment; try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) { boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE); - MemoryAddress addr = segment.baseAddress(); try { for (int i = 0; i < seq.elementCount().getAsLong(); i++) { for (int j = 0; j < ((SequenceLayout) seq.elementLayout()).elementCount().getAsLong(); j++) { - checker.check(handle, addr, i, j); + checker.check(handle, segment, i, j); } } if (isRO) { @@ -203,16 +200,16 @@ private void testMatrixAccessInternal(Function vie return; } try { - checker.check(handle, addr, seq.elementCount().getAsLong(), + checker.check(handle, segment, seq.elementCount().getAsLong(), ((SequenceLayout)seq.elementLayout()).elementCount().getAsLong()); throw new AssertionError(); //not ok, out of bounds } catch (IndexOutOfBoundsException ex) { //ok, should fail (out of bounds) } - outer_address = addr; //leak! + outer_segment = segment; //leak! } try { - checker.check(handle, outer_address, 0, 0); + checker.check(handle, outer_segment, 0, 0); throw new AssertionError(); //not ok, scope is closed } catch (IllegalStateException ex) { //ok, should fail (scope is closed) @@ -261,41 +258,41 @@ public Object[][] createData() { } interface Checker { - void check(VarHandle handle, MemoryAddress addr); + void check(VarHandle handle, MemorySegment segment); - Checker BYTE = (handle, addr) -> { - handle.set(addr, (byte)42); - assertEquals(42, (byte)handle.get(addr)); + Checker BYTE = (handle, segment) -> { + handle.set(segment, (byte)42); + assertEquals(42, (byte)handle.get(segment)); }; - Checker SHORT = (handle, addr) -> { - handle.set(addr, (short)42); - assertEquals(42, (short)handle.get(addr)); + Checker SHORT = (handle, segment) -> { + handle.set(segment, (short)42); + assertEquals(42, (short)handle.get(segment)); }; - Checker CHAR = (handle, addr) -> { - handle.set(addr, (char)42); - assertEquals(42, (char)handle.get(addr)); + Checker CHAR = (handle, segment) -> { + handle.set(segment, (char)42); + assertEquals(42, (char)handle.get(segment)); }; - Checker INT = (handle, addr) -> { - handle.set(addr, 42); - assertEquals(42, (int)handle.get(addr)); + Checker INT = (handle, segment) -> { + handle.set(segment, 42); + assertEquals(42, (int)handle.get(segment)); }; - Checker LONG = (handle, addr) -> { - handle.set(addr, (long)42); - assertEquals(42, (long)handle.get(addr)); + Checker LONG = (handle, segment) -> { + handle.set(segment, (long)42); + assertEquals(42, (long)handle.get(segment)); }; - Checker FLOAT = (handle, addr) -> { - handle.set(addr, (float)42); - assertEquals((float)42, (float)handle.get(addr)); + Checker FLOAT = (handle, segment) -> { + handle.set(segment, (float)42); + assertEquals((float)42, (float)handle.get(segment)); }; - Checker DOUBLE = (handle, addr) -> { - handle.set(addr, (double)42); - assertEquals((double)42, (double)handle.get(addr)); + Checker DOUBLE = (handle, segment) -> { + handle.set(segment, (double)42); + assertEquals((double)42, (double)handle.get(segment)); }; } @@ -338,41 +335,41 @@ public Object[][] createArrayData() { } interface ArrayChecker { - void check(VarHandle handle, MemoryAddress addr, long index); + void check(VarHandle handle, MemorySegment segment, long index); - ArrayChecker BYTE = (handle, addr, i) -> { - handle.set(addr, i, (byte)i); - assertEquals(i, (byte)handle.get(addr, i)); + ArrayChecker BYTE = (handle, segment, i) -> { + handle.set(segment, i, (byte)i); + assertEquals(i, (byte)handle.get(segment, i)); }; - ArrayChecker SHORT = (handle, addr, i) -> { - handle.set(addr, i, (short)i); - assertEquals(i, (short)handle.get(addr, i)); + ArrayChecker SHORT = (handle, segment, i) -> { + handle.set(segment, i, (short)i); + assertEquals(i, (short)handle.get(segment, i)); }; - ArrayChecker CHAR = (handle, addr, i) -> { - handle.set(addr, i, (char)i); - assertEquals(i, (char)handle.get(addr, i)); + ArrayChecker CHAR = (handle, segment, i) -> { + handle.set(segment, i, (char)i); + assertEquals(i, (char)handle.get(segment, i)); }; - ArrayChecker INT = (handle, addr, i) -> { - handle.set(addr, i, (int)i); - assertEquals(i, (int)handle.get(addr, i)); + ArrayChecker INT = (handle, segment, i) -> { + handle.set(segment, i, (int)i); + assertEquals(i, (int)handle.get(segment, i)); }; - ArrayChecker LONG = (handle, addr, i) -> { - handle.set(addr, i, (long)i); - assertEquals(i, (long)handle.get(addr, i)); + ArrayChecker LONG = (handle, segment, i) -> { + handle.set(segment, i, (long)i); + assertEquals(i, (long)handle.get(segment, i)); }; - ArrayChecker FLOAT = (handle, addr, i) -> { - handle.set(addr, i, (float)i); - assertEquals((float)i, (float)handle.get(addr, i)); + ArrayChecker FLOAT = (handle, segment, i) -> { + handle.set(segment, i, (float)i); + assertEquals((float)i, (float)handle.get(segment, i)); }; - ArrayChecker DOUBLE = (handle, addr, i) -> { - handle.set(addr, i, (double)i); - assertEquals((double)i, (double)handle.get(addr, i)); + ArrayChecker DOUBLE = (handle, segment, i) -> { + handle.set(segment, i, (double)i); + assertEquals((double)i, (double)handle.get(segment, i)); }; } @@ -415,41 +412,41 @@ public Object[][] createMatrixData() { } interface MatrixChecker { - void check(VarHandle handle, MemoryAddress addr, long row, long col); + void check(VarHandle handle, MemorySegment segment, long row, long col); - MatrixChecker BYTE = (handle, addr, r, c) -> { - handle.set(addr, r, c, (byte)(r + c)); - assertEquals(r + c, (byte)handle.get(addr, r, c)); + MatrixChecker BYTE = (handle, segment, r, c) -> { + handle.set(segment, r, c, (byte)(r + c)); + assertEquals(r + c, (byte)handle.get(segment, r, c)); }; - MatrixChecker SHORT = (handle, addr, r, c) -> { - handle.set(addr, r, c, (short)(r + c)); - assertEquals(r + c, (short)handle.get(addr, r, c)); + MatrixChecker SHORT = (handle, segment, r, c) -> { + handle.set(segment, r, c, (short)(r + c)); + assertEquals(r + c, (short)handle.get(segment, r, c)); }; - MatrixChecker CHAR = (handle, addr, r, c) -> { - handle.set(addr, r, c, (char)(r + c)); - assertEquals(r + c, (char)handle.get(addr, r, c)); + MatrixChecker CHAR = (handle, segment, r, c) -> { + handle.set(segment, r, c, (char)(r + c)); + assertEquals(r + c, (char)handle.get(segment, r, c)); }; - MatrixChecker INT = (handle, addr, r, c) -> { - handle.set(addr, r, c, (int)(r + c)); - assertEquals(r + c, (int)handle.get(addr, r, c)); + MatrixChecker INT = (handle, segment, r, c) -> { + handle.set(segment, r, c, (int)(r + c)); + assertEquals(r + c, (int)handle.get(segment, r, c)); }; - MatrixChecker LONG = (handle, addr, r, c) -> { - handle.set(addr, r, c, r + c); - assertEquals(r + c, (long)handle.get(addr, r, c)); + MatrixChecker LONG = (handle, segment, r, c) -> { + handle.set(segment, r, c, r + c); + assertEquals(r + c, (long)handle.get(segment, r, c)); }; - MatrixChecker FLOAT = (handle, addr, r, c) -> { - handle.set(addr, r, c, (float)(r + c)); - assertEquals((float)(r + c), (float)handle.get(addr, r, c)); + MatrixChecker FLOAT = (handle, segment, r, c) -> { + handle.set(segment, r, c, (float)(r + c)); + assertEquals((float)(r + c), (float)handle.get(segment, r, c)); }; - MatrixChecker DOUBLE = (handle, addr, r, c) -> { - handle.set(addr, r, c, (double)(r + c)); - assertEquals((double)(r + c), (double)handle.get(addr, r, c)); + MatrixChecker DOUBLE = (handle, segment, r, c) -> { + handle.set(segment, r, c, (double)(r + c)); + assertEquals((double)(r + c), (double)handle.get(segment, r, c)); }; } diff --git a/test/jdk/java/foreign/TestMemoryAlignment.java b/test/jdk/java/foreign/TestMemoryAlignment.java index 5fa7da33c00d0..2b6ee427151d6 100644 --- a/test/jdk/java/foreign/TestMemoryAlignment.java +++ b/test/jdk/java/foreign/TestMemoryAlignment.java @@ -31,7 +31,6 @@ import jdk.incubator.foreign.GroupLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.SequenceLayout; import jdk.incubator.foreign.ValueLayout; @@ -51,9 +50,8 @@ public void testAlignedAccess(long align) { assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws VarHandle vh = aligned.varHandle(int.class); try (MemorySegment segment = MemorySegment.allocateNative(aligned)) { - MemoryAddress addr = segment.baseAddress(); - vh.set(addr, -42); - int val = (int)vh.get(addr); + vh.set(segment, -42); + int val = (int)vh.get(segment); assertEquals(val, -42); } } @@ -67,8 +65,7 @@ public void testUnalignedAccess(long align) { assertEquals(alignedGroup.bitAlignment(), align); VarHandle vh = aligned.varHandle(int.class); try (MemorySegment segment = MemorySegment.allocateNative(alignedGroup)) { - MemoryAddress addr = segment.baseAddress(); - vh.set(addr.addOffset(1L), -42); + vh.set(segment.asSlice(1L), -42); assertEquals(align, 8); //this is the only case where access is aligned } catch (IllegalStateException ex) { assertNotEquals(align, 8); //if align != 8, access is always unaligned @@ -94,9 +91,8 @@ public void testUnalignedSequence(long align) { try { VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement()); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - MemoryAddress addr = segment.baseAddress(); for (long i = 0 ; i < 5 ; i++) { - vh.set(addr, i, -42); + vh.set(segment, i, -42); } } } catch (UnsupportedOperationException ex) { @@ -118,13 +114,12 @@ public void testPackedAccess() { VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b")); VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c")); try (MemorySegment segment = MemorySegment.allocateNative(g)) { - MemoryAddress addr = segment.baseAddress(); - vh_c.set(addr, Byte.MIN_VALUE); - assertEquals(vh_c.get(addr), Byte.MIN_VALUE); - vh_s.set(addr, Short.MIN_VALUE); - assertEquals(vh_s.get(addr), Short.MIN_VALUE); - vh_i.set(addr, Integer.MIN_VALUE); - assertEquals(vh_i.get(addr), Integer.MIN_VALUE); + vh_c.set(segment, Byte.MIN_VALUE); + assertEquals(vh_c.get(segment), Byte.MIN_VALUE); + vh_s.set(segment, Short.MIN_VALUE); + assertEquals(vh_s.get(segment), Short.MIN_VALUE); + vh_i.set(segment, Integer.MIN_VALUE); + assertEquals(vh_i.get(segment), Integer.MIN_VALUE); } } diff --git a/test/jdk/java/foreign/TestMemoryCopy.java b/test/jdk/java/foreign/TestMemoryCopy.java index b11bd52073399..4e5bfc974b9be 100644 --- a/test/jdk/java/foreign/TestMemoryCopy.java +++ b/test/jdk/java/foreign/TestMemoryCopy.java @@ -27,7 +27,6 @@ * @run testng TestMemoryCopy */ -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import org.testng.annotations.DataProvider; @@ -46,21 +45,19 @@ public class TestMemoryCopy { @Test(dataProvider = "slices") public void testCopy(SegmentSlice s1, SegmentSlice s2) { - MemoryAddress addr1 = s1.segment.baseAddress(); - MemoryAddress addr2 = s2.segment.baseAddress(); int size = Math.min(s1.size(), s2.size()); //prepare source and target segments for (int i = 0 ; i < size ; i++) { - BYTE_HANDLE.set(addr2.addOffset(i), (byte)0); + BYTE_HANDLE.set(s2.segment.asSlice(i), (byte)0); } for (int i = 0 ; i < size ; i++) { - BYTE_HANDLE.set(addr1.addOffset(i), (byte) i); + BYTE_HANDLE.set(s1.segment.asSlice(i), (byte) i); } //perform copy s2.segment.copyFrom(s1.segment.asSlice(0, size)); //check that copy actually worked for (int i = 0 ; i < size ; i++) { - assertEquals((byte)i, BYTE_HANDLE.get(addr2.addOffset(i))); + assertEquals((byte)i, BYTE_HANDLE.get(s2.segment.asSlice(i))); } } diff --git a/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java b/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java index f601d9683c7fa..a0678a7083b18 100644 --- a/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java +++ b/test/jdk/java/foreign/TestMemoryHandleAsUnsigned.java @@ -59,10 +59,10 @@ public void testUnsignedIntToByte(int intValue) { VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - intHandle.set(segment.baseAddress(), intValue); + intHandle.set(segment, intValue); int expectedIntValue = Byte.toUnsignedInt(byteValue); - assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue); - assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue); + assertEquals((int) intHandle.get(segment), expectedIntValue); + assertEquals((byte) byteHandle.get(segment), byteValue); } } @@ -81,10 +81,10 @@ public void testUnsignedLongToByte(long longValue) { VarHandle longHandle = MemoryHandles.asUnsigned(byteHandle, long.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - longHandle.set(segment.baseAddress(), longValue); + longHandle.set(segment, longValue); long expectedLongValue = Byte.toUnsignedLong(byteValue); - assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue); - assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue); + assertEquals((long) longHandle.get(segment), expectedLongValue); + assertEquals((byte) byteHandle.get(segment), byteValue); } } @@ -103,10 +103,10 @@ public void testUnsignedIntToShort(int intValue) { VarHandle intHandle = MemoryHandles.asUnsigned(shortHandle, int.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - intHandle.set(segment.baseAddress(), intValue); + intHandle.set(segment, intValue); int expectedIntValue = Short.toUnsignedInt(shortValue); - assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue); - assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue); + assertEquals((int) intHandle.get(segment), expectedIntValue); + assertEquals((short) shortHandle.get(segment), shortValue); } } @@ -125,10 +125,10 @@ public void testUnsignedLongToShort(long longValue) { VarHandle longHandle = MemoryHandles.asUnsigned(shortHandle, long.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - longHandle.set(segment.baseAddress(), longValue); + longHandle.set(segment, longValue); long expectedLongValue = Short.toUnsignedLong(shortValue); - assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue); - assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue); + assertEquals((long) longHandle.get(segment), expectedLongValue); + assertEquals((short) shortHandle.get(segment), shortValue); } } @@ -151,10 +151,10 @@ public void testUnsignedLongToInt(long longValue) { VarHandle longHandle = MemoryHandles.asUnsigned(intHandle, long.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - longHandle.set(segment.baseAddress(), longValue); + longHandle.set(segment, longValue); long expectedLongValue = Integer.toUnsignedLong(intValue); - assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue); - assertEquals((int) intHandle.get(segment.baseAddress()), intValue); + assertEquals((long) longHandle.get(segment), expectedLongValue); + assertEquals((int) intHandle.get(segment), intValue); } } @@ -165,10 +165,10 @@ public void testCoordinatesSequenceLayout() { VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); try (MemorySegment segment = MemorySegment.allocateNative(layout)) { - intHandle.set(segment.baseAddress(), 0L, (int) -1); - assertEquals((int) intHandle.get(segment.baseAddress(), 0L), 255); - intHandle.set(segment.baseAddress(), 1L, (int) 200); - assertEquals((int) intHandle.get(segment.baseAddress(), 1L), 200); + intHandle.set(segment, 0L, (int) -1); + assertEquals((int) intHandle.get(segment, 0L), 255); + intHandle.set(segment, 1L, (int) 200); + assertEquals((int) intHandle.get(segment, 1L), 200); } } @@ -176,19 +176,18 @@ public void testCoordinatesSequenceLayout() { public void testCoordinatesStride() { byte[] arr = { 0, 0, (byte) 129, 0 }; MemorySegment segment = MemorySegment.ofArray(arr); - MemoryAddress addr = segment.baseAddress(); { - VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); + VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) + .varHandle(byte.class, PathElement.sequenceElement()); VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); - VarHandle strideHandle = MemoryHandles.withStride(intHandle, 1); - assertEquals((int) strideHandle.get(addr, 2L), 129); + assertEquals((int) intHandle.get(segment, 2L), 129); } { - VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); - VarHandle strideHandle = MemoryHandles.withStride(byteHandle, 1); - VarHandle intHandle = MemoryHandles.asUnsigned(strideHandle, int.class); - assertEquals((int) intHandle.get(addr, 2L), 129); + VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) + .varHandle(byte.class, PathElement.sequenceElement()); + VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class); + assertEquals((int) intHandle.get(segment, 2L), 129); } } diff --git a/test/jdk/java/foreign/TestMismatch.java b/test/jdk/java/foreign/TestMismatch.java index 5c361a82fe985..dc35054e411a0 100644 --- a/test/jdk/java/foreign/TestMismatch.java +++ b/test/jdk/java/foreign/TestMismatch.java @@ -47,9 +47,8 @@ public class TestMismatch { // stores a increasing sequence of values into the memory of the given segment static MemorySegment initializeSegment(MemorySegment segment) { - MemoryAddress addr = segment.baseAddress(); for (int i = 0 ; i < segment.byteSize() ; i++) { - BYTE_HANDLE.set(addr.addOffset(i), (byte)i); + BYTE_HANDLE.set(segment.asSlice(i), (byte)i); } return segment; } @@ -81,7 +80,7 @@ public void testDifferentValues(MemorySegment s1, MemorySegment s2) { for (long i = s2.byteSize() -1 ; i >= 0; i--) { long expectedMismatchOffset = i; - BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF); + BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF); if (s1.byteSize() == s2.byteSize()) { assertEquals(s1.mismatch(s2), expectedMismatchOffset); @@ -111,15 +110,18 @@ public void testEmpty() { @Test public void testLarge() { - try (var s1 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L); - var s2 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L)) { - assertEquals(s1.mismatch(s1), -1); - assertEquals(s1.mismatch(s2), -1); - assertEquals(s2.mismatch(s1), -1); + // skip if not on 64 bits + if (MemoryLayouts.ADDRESS.byteSize() > 32) { + try (var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L); + var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L)) { + assertEquals(s1.mismatch(s1), -1); + assertEquals(s1.mismatch(s2), -1); + assertEquals(s2.mismatch(s1), -1); - testLargeAcrossMaxBoundary(s1, s2); + testLargeAcrossMaxBoundary(s1, s2); - testLargeMismatchAcrossMaxBoundary(s1, s2); + testLargeMismatchAcrossMaxBoundary(s1, s2); + } } } @@ -135,7 +137,7 @@ private void testLargeAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) { private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) { for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) { - BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF); + BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF); long expectedMismatchOffset = i; assertEquals(s1.mismatch(s2), expectedMismatchOffset); assertEquals(s2.mismatch(s1), expectedMismatchOffset); diff --git a/test/jdk/java/foreign/TestNative.java b/test/jdk/java/foreign/TestNative.java index 5d7a37e010b9b..c922f4bf70391 100644 --- a/test/jdk/java/foreign/TestNative.java +++ b/test/jdk/java/foreign/TestNative.java @@ -24,18 +24,17 @@ /* * @test - * @modules java.base/jdk.internal.misc - * jdk.incubator.foreign/jdk.internal.foreign + * @modules jdk.incubator.foreign/jdk.internal.foreign * @run testng/othervm -Dforeign.restricted=permit TestNative */ +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayout.PathElement; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; import jdk.incubator.foreign.SequenceLayout; -import jdk.internal.misc.Unsafe; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -58,12 +57,6 @@ public class TestNative { - static Unsafe UNSAFE; - - static { - UNSAFE = Unsafe.getUnsafe(); - } - static SequenceLayout bytes = MemoryLayout.ofSequence(100, MemoryLayouts.JAVA_BYTE.withOrder(ByteOrder.nativeOrder()) ); @@ -100,24 +93,24 @@ public class TestNative { static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement()); static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement()); - static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer handleSetter) { + static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer handleSetter) { for (long i = 0; i < seq.elementCount().getAsLong() ; i++) { handleSetter.accept(base, i); } } - static void checkBytes(MemoryAddress base, SequenceLayout layout, - BiFunction handleExtractor, + static void checkBytes(MemorySegment base, SequenceLayout layout, + BiFunction handleExtractor, Function bufferFactory, BiFunction nativeBufferExtractor, BiFunction nativeRawExtractor) { long nelems = layout.elementCount().getAsLong(); - ByteBuffer bb = base.segment().asSlice(base.segmentOffset(), (int)layout.byteSize()).asByteBuffer(); + ByteBuffer bb = base.asByteBuffer(); Z z = bufferFactory.apply(bb); for (long i = 0 ; i < nelems ; i++) { Object handleValue = handleExtractor.apply(base, i); Object bufferValue = nativeBufferExtractor.apply(z, (int)i); - Object rawValue = nativeRawExtractor.apply(base.toRawLongValue(), (int)i); + Object rawValue = nativeRawExtractor.apply(base.address().toRawLongValue(), (int)i); if (handleValue instanceof Number) { assertEquals(((Number)handleValue).longValue(), i); assertEquals(((Number)bufferValue).longValue(), i); @@ -152,11 +145,10 @@ static void checkBytes(MemoryAddress base, SequenceLayout lay public static native void free(long address); @Test(dataProvider="nativeAccessOps") - public void testNativeAccess(Consumer checker, Consumer initializer, SequenceLayout seq) { + public void testNativeAccess(Consumer checker, Consumer initializer, SequenceLayout seq) { try (MemorySegment segment = MemorySegment.allocateNative(seq)) { - MemoryAddress address = segment.baseAddress(); - initializer.accept(address); - checker.accept(address); + initializer.accept(segment); + checker.accept(segment); } } @@ -175,71 +167,79 @@ public void testNativeCapacity(Function bufferFunction, int @Test public void testDefaultAccessModes() { MemoryAddress addr = MemoryAddress.ofLong(allocate(12)); - MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null, - () -> free(addr.toRawLongValue()), null); + MemorySegment mallocSegment = addr.asSegmentRestricted(12, () -> free(addr.toRawLongValue()), null); try (MemorySegment segment = mallocSegment) { assertTrue(segment.hasAccessModes(ALL_ACCESS)); assertEquals(segment.accessModes(), ALL_ACCESS); } } + @Test + public void testDefaultAccessModesEverthing() { + MemorySegment everything = MemorySegment.ofNativeRestricted(); + assertTrue(everything.hasAccessModes(READ | WRITE)); + assertEquals(everything.accessModes(), READ | WRITE); + } + @Test public void testMallocSegment() { MemoryAddress addr = MemoryAddress.ofLong(allocate(12)); - assertNull(addr.segment()); - MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null, - () -> free(addr.toRawLongValue()), null); + MemorySegment mallocSegment = addr.asSegmentRestricted(12, () -> free(addr.toRawLongValue()), null); assertEquals(mallocSegment.byteSize(), 12); mallocSegment.close(); //free here assertTrue(!mallocSegment.isAlive()); } + @Test + public void testEverythingSegment() { + MemoryAddress addr = MemoryAddress.ofLong(allocate(4)); + MemorySegment everything = MemorySegment.ofNativeRestricted(); + MemoryAccess.setIntAtOffset(everything, addr.toRawLongValue(), 42); + assertEquals(MemoryAccess.getIntAtOffset(everything, addr.toRawLongValue()), 42); + free(addr.toRawLongValue()); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testBadResize() { try (MemorySegment segment = MemorySegment.allocateNative(4)) { - MemorySegment.ofNativeRestricted(segment.baseAddress(), 0, null, null, null); + segment.address().asSegmentRestricted(0); } } - @Test(expectedExceptions = NullPointerException.class) - public void testNullUnsafeSegment() { - MemorySegment.ofNativeRestricted(null, 10, null, null, null); - } - static { System.loadLibrary("NativeAccess"); } @DataProvider(name = "nativeAccessOps") public Object[][] nativeAccessOps() { - Consumer byteInitializer = + Consumer byteInitializer = (base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos)); - Consumer charInitializer = + Consumer charInitializer = (base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos)); - Consumer shortInitializer = + Consumer shortInitializer = (base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos)); - Consumer intInitializer = + Consumer intInitializer = (base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos)); - Consumer floatInitializer = + Consumer floatInitializer = (base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos)); - Consumer longInitializer = + Consumer longInitializer = (base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos)); - Consumer doubleInitializer = + Consumer doubleInitializer = (base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos)); - Consumer byteChecker = + Consumer byteChecker = (base) -> checkBytes(base, bytes, byteHandle::get, bb -> bb, TestNative::getByteBuffer, TestNative::getByteRaw); - Consumer charChecker = + Consumer charChecker = (base) -> checkBytes(base, chars, charHandle::get, ByteBuffer::asCharBuffer, TestNative::getCharBuffer, TestNative::getCharRaw); - Consumer shortChecker = + Consumer shortChecker = (base) -> checkBytes(base, shorts, shortHandle::get, ByteBuffer::asShortBuffer, TestNative::getShortBuffer, TestNative::getShortRaw); - Consumer intChecker = + Consumer intChecker = (base) -> checkBytes(base, ints, intHandle::get, ByteBuffer::asIntBuffer, TestNative::getIntBuffer, TestNative::getIntRaw); - Consumer floatChecker = + Consumer floatChecker = (base) -> checkBytes(base, floats, floatHandle::get, ByteBuffer::asFloatBuffer, TestNative::getFloatBuffer, TestNative::getFloatRaw); - Consumer longChecker = + Consumer longChecker = (base) -> checkBytes(base, longs, longHandle::get, ByteBuffer::asLongBuffer, TestNative::getLongBuffer, TestNative::getLongRaw); - Consumer doubleChecker = + Consumer doubleChecker = (base) -> checkBytes(base, doubles, doubleHandle::get, ByteBuffer::asDoubleBuffer, TestNative::getDoubleBuffer, TestNative::getDoubleRaw); return new Object[][]{ diff --git a/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java b/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java index ac7752e9f5cd8..072506e42bf78 100644 --- a/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java +++ b/test/jdk/java/foreign/TestNoForeignUnsafeOverride.java @@ -29,8 +29,8 @@ */ import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemorySegment; +import jdk.incubator.foreign.MemorySegment; import org.testng.annotations.Test; public class TestNoForeignUnsafeOverride { @@ -40,6 +40,6 @@ public class TestNoForeignUnsafeOverride { @Test(expectedExceptions = IllegalAccessError.class) public void testUnsafeAccess() { - MemorySegment.ofNativeRestricted(MemoryAddress.ofLong(42), 10, null, null, null); + MemorySegment.ofNativeRestricted(); } } diff --git a/test/jdk/java/foreign/TestRebase.java b/test/jdk/java/foreign/TestRebase.java index de7c4777a9800..c30bc3ec0689d 100644 --- a/test/jdk/java/foreign/TestRebase.java +++ b/test/jdk/java/foreign/TestRebase.java @@ -27,6 +27,7 @@ * @run testng TestRebase */ +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; @@ -44,35 +45,33 @@ public class TestRebase { - static VarHandle BYTE_VH = MemoryLayouts.JAVA_BYTE.varHandle(byte.class); - @Test(dataProvider = "slices") public void testRebase(SegmentSlice s1, SegmentSlice s2) { if (s1.contains(s2)) { //check that an address and its rebased counterpart point to same element - MemoryAddress base = s2.segment.baseAddress(); - MemoryAddress rebased = base.rebase(s1.segment); + MemoryAddress base = s2.segment.address(); + long offset = base.segmentOffset(s1.segment); for (int i = 0; i < s2.size(); i++) { - int expected = (int) BYTE_VH.get(base.addOffset(i)); - int found = (int) BYTE_VH.get(rebased.addOffset(i)); + int expected = MemoryAccess.getByteAtOffset(s2.segment, i); + int found = (int)MemoryAccess.getByteAtOffset(s1.segment, i + offset); assertEquals(found, expected); } } else if (s1.kind != s2.kind) { // check that rebase s1 to s2 fails try { - s1.segment.baseAddress().rebase(s2.segment); + s1.segment.address().segmentOffset(s2.segment); fail("Rebase unexpectedly passed!"); } catch (IllegalArgumentException ex) { assertTrue(true); } } else if (!s2.contains(s1)) { //disjoint segments - check that rebased address is out of bounds - MemoryAddress base = s2.segment.baseAddress(); - MemoryAddress rebased = base.rebase(s1.segment); + MemoryAddress base = s2.segment.address(); + long offset = base.segmentOffset(s1.segment); for (int i = 0; i < s2.size(); i++) { - BYTE_VH.get(base.addOffset(i)); + MemoryAccess.getByteAtOffset(s2.segment, i); try { - BYTE_VH.get(rebased.addOffset(i)); + MemoryAccess.getByteAtOffset(s1.segment, i + offset); fail("Rebased address on a disjoint segment is not out of bounds!"); } catch (IndexOutOfBoundsException ex) { assertTrue(true); @@ -129,7 +128,7 @@ static Object[][] slices() { //init root segment MemorySegment segment = kind.makeSegment(16); for (int i = 0 ; i < 16 ; i++) { - BYTE_VH.set(segment.baseAddress().addOffset(i), (byte)i); + MemoryAccess.setByteAtOffset(segment, i, (byte)i); } //compute all slices for (int size : sizes) { diff --git a/test/jdk/java/foreign/TestSegments.java b/test/jdk/java/foreign/TestSegments.java index ffd8a1cd2b4d1..5c702fb4626b1 100644 --- a/test/jdk/java/foreign/TestSegments.java +++ b/test/jdk/java/foreign/TestSegments.java @@ -23,10 +23,9 @@ /* * @test - * @run testng TestSegments + * @run testng/othervm -XX:MaxDirectMemorySize=1M TestSegments */ -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; @@ -34,13 +33,13 @@ import org.testng.annotations.Test; import java.lang.invoke.VarHandle; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; -import java.util.Spliterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.LongFunction; @@ -74,9 +73,6 @@ public void testOpOutsideConfinement(SegmentMember member) throws Throwable { Thread t = new Thread(() -> { try { Object o = member.method.invoke(segment, member.params); - if (member.method.getName().equals("acquire")) { - ((MemorySegment)o).close(); - } } catch (ReflectiveOperationException ex) { throw new IllegalStateException(ex); } @@ -88,38 +84,40 @@ public void testOpOutsideConfinement(SegmentMember member) throws Throwable { } } + @Test(dataProvider = "segmentOperations") + public void testOpAfterClose(SegmentMember member) throws Throwable { + MemorySegment segment = MemorySegment.allocateNative(4); + segment.close(); + try { + Object o = member.method.invoke(segment, member.params); + assertFalse(member.isConfined()); + } catch (InvocationTargetException ex) { + assertTrue(member.isConfined()); + Throwable target = ex.getTargetException(); + assertTrue(target instanceof NullPointerException || + target instanceof UnsupportedOperationException || + target instanceof IllegalStateException); + } + } + + @Test(expectedExceptions = OutOfMemoryError.class) + public void testNativeAllocationTooBig() { + try (MemorySegment segment = MemorySegment.allocateNative(1024 * 1024 * 8 * 2)) { // 2M + // do nothing + } + } + @Test public void testNativeSegmentIsZeroed() { VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) .varHandle(byte.class, MemoryLayout.PathElement.sequenceElement()); try (MemorySegment segment = MemorySegment.allocateNative(1000)) { for (long i = 0 ; i < segment.byteSize() ; i++) { - assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i)); - } - } - } - - @Test - public void testNothingSegmentAccess() { - VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class); - long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 }; - for (long value : values) { - MemoryAddress addr = MemoryAddress.ofLong(value); - try { - longHandle.get(addr); - } catch (UnsupportedOperationException ex) { - assertTrue(ex.getMessage().contains("Required access mode")); + assertEquals(0, (byte)byteHandle.get(segment, i)); } } } - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testNothingSegmentOffset() { - MemoryAddress addr = MemoryAddress.ofLong(42); - assertNull(addr.segment()); - addr.segmentOffset(); - } - @Test public void testSlices() { VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE) @@ -127,21 +125,16 @@ public void testSlices() { try (MemorySegment segment = MemorySegment.allocateNative(10)) { //init for (byte i = 0 ; i < segment.byteSize() ; i++) { - byteHandle.set(segment.baseAddress(), (long)i, i); + byteHandle.set(segment, (long)i, i); } - long start = 0; - MemoryAddress base = segment.baseAddress(); - MemoryAddress last = base.addOffset(10); - while (!base.equals(last)) { - MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start); - for (long i = start ; i < 10 ; i++) { + for (int offset = 0 ; offset < 10 ; offset++) { + MemorySegment slice = segment.asSlice(offset); + for (long i = offset ; i < 10 ; i++) { assertEquals( - byteHandle.get(segment.baseAddress(), i), - byteHandle.get(slice.baseAddress(), i - start) + byteHandle.get(segment, i), + byteHandle.get(slice, i - offset) ); } - base = base.addOffset(1); - start++; } } } @@ -197,20 +190,20 @@ public void testFill(Supplier memorySegmentSupplier) { try (MemorySegment segment = memorySegmentSupplier.get()) { segment.fill(value); for (long l = 0; l < segment.byteSize(); l++) { - assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value); + assertEquals((byte) byteHandle.get(segment, l), value); } // fill a slice var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value); for (long l = 0; l < sliceSegment.byteSize(); l++) { - assertEquals((byte) byteHandle.get(sliceSegment.baseAddress(), l), ~value); + assertEquals((byte) byteHandle.get(sliceSegment, l), ~value); } // assert enclosing slice - assertEquals((byte) byteHandle.get(segment.baseAddress(), 0L), value); + assertEquals((byte) byteHandle.get(segment, 0L), value); for (long l = 1; l < segment.byteSize() - 2; l++) { - assertEquals((byte) byteHandle.get(segment.baseAddress(), l), (byte) ~value); + assertEquals((byte) byteHandle.get(segment, l), (byte) ~value); } - assertEquals((byte) byteHandle.get(segment.baseAddress(), segment.byteSize() - 1L), value); + assertEquals((byte) byteHandle.get(segment, segment.byteSize() - 1L), value); } } } @@ -319,8 +312,9 @@ MemoryLayout make(long size) { static Object[][] segmentMembers() { List members = new ArrayList<>(); for (Method m : MemorySegment.class.getDeclaredMethods()) { - //skip statics and method declared in j.l.Object - if (m.getDeclaringClass().equals(Object.class) || + //skip defaults, statics and method declared in j.l.Object + if (m.isDefault() || + m.getDeclaringClass().equals(Object.class) || (m.getModifiers() & Modifier.STATIC) != 0) continue; Object[] args = Stream.of(m.getParameterTypes()) .map(TestSegments::defaultValue) @@ -335,12 +329,22 @@ static class SegmentMember { final Object[] params; final static List CONFINED_NAMES = List.of( + "address", "close", + "share", + "handoff", + "registerCleaner", "fill", + "spliterator", "copyFrom", "mismatch", "toByteArray", - "withOwnerThread" + "toCharArray", + "toShortArray", + "toIntArray", + "toFloatArray", + "toLongArray", + "toDoubleArray" ); public SegmentMember(Method method, Object[] params) { @@ -395,30 +399,10 @@ public Object[][] accessModes() { } enum AccessActions { - ACQUIRE(MemorySegment.ACQUIRE) { + SHARE(MemorySegment.SHARE) { @Override void run(MemorySegment segment) { - Spliterator spliterator = - MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE)); - AtomicReference exception = new AtomicReference<>(); - Runnable action = () -> { - try { - spliterator.tryAdvance(s -> { }); - } catch (RuntimeException e) { - exception.set(e); - } - }; - Thread thread = new Thread(action); - thread.start(); - try { - thread.join(); - } catch (InterruptedException ex) { - throw new AssertionError(ex); - } - RuntimeException e = exception.get(); - if (e != null) { - throw e; - } + segment.share(); } }, CLOSE(MemorySegment.CLOSE) { @@ -430,19 +414,19 @@ void run(MemorySegment segment) { READ(MemorySegment.READ) { @Override void run(MemorySegment segment) { - INT_HANDLE.get(segment.baseAddress()); + INT_HANDLE.get(segment); } }, WRITE(MemorySegment.WRITE) { @Override void run(MemorySegment segment) { - INT_HANDLE.set(segment.baseAddress(), 42); + INT_HANDLE.set(segment, 42); } }, HANDOFF(MemorySegment.HANDOFF) { @Override void run(MemorySegment segment) { - segment.withOwnerThread(new Thread()); + segment.handoff(new Thread()); } }; diff --git a/test/jdk/java/foreign/TestSharedAccess.java b/test/jdk/java/foreign/TestSharedAccess.java index 24243e52b827f..29e1fbeb2191b 100644 --- a/test/jdk/java/foreign/TestSharedAccess.java +++ b/test/jdk/java/foreign/TestSharedAccess.java @@ -27,12 +27,8 @@ * @run testng/othervm -Dforeign.restricted=permit TestSharedAccess */ -import jdk.incubator.foreign.MemoryAddress; -import jdk.incubator.foreign.MemoryLayout; -import jdk.incubator.foreign.MemoryLayouts; -import jdk.incubator.foreign.MemorySegment; -import jdk.incubator.foreign.SequenceLayout; -import org.testng.annotations.Test; +import jdk.incubator.foreign.*; +import org.testng.annotations.*; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; @@ -41,14 +37,10 @@ import java.util.Spliterator; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; +import static org.testng.Assert.*; public class TestSharedAccess { @@ -59,17 +51,17 @@ public void testConfined() throws Throwable { Thread owner = Thread.currentThread(); MemorySegment s = MemorySegment.allocateNative(4); AtomicReference confined = new AtomicReference<>(s); - setInt(s.baseAddress(), 42); - assertEquals(getInt(s.baseAddress()), 42); + setInt(s, 42); + assertEquals(getInt(s), 42); List threads = new ArrayList<>(); for (int i = 0 ; i < 1000 ; i++) { threads.add(new Thread(() -> { - assertEquals(getInt(confined.get().baseAddress()), 42); - confined.set(confined.get().withOwnerThread(owner)); + assertEquals(getInt(confined.get()), 42); + confined.set(confined.get().handoff(owner)); })); } threads.forEach(t -> { - confined.set(confined.get().withOwnerThread(t)); + confined.set(confined.get().handoff(t)); t.start(); try { t.join(); @@ -83,13 +75,13 @@ public void testConfined() throws Throwable { @Test public void testShared() throws Throwable { SequenceLayout layout = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT); - try (MemorySegment s = MemorySegment.allocateNative(layout)) { + try (MemorySegment s = MemorySegment.allocateNative(layout).share()) { for (int i = 0 ; i < layout.elementCount().getAsLong() ; i++) { - setInt(s.baseAddress().addOffset(i * 4), 42); + setInt(s.asSlice(i * 4), 42); } List threads = new ArrayList<>(); List> spliterators = new ArrayList<>(); - spliterators.add(MemorySegment.spliterator(s, layout)); + spliterators.add(s.spliterator(layout)); while (true) { boolean progress = false; List> newSpliterators = new ArrayList<>(); @@ -108,7 +100,7 @@ public void testShared() throws Throwable { for (Spliterator spliterator : spliterators) { threads.add(new Thread(() -> { spliterator.tryAdvance(local -> { - assertEquals(getInt(local.baseAddress()), 42); + assertEquals(getInt(local), 42); accessCount.incrementAndGet(); }); })); @@ -128,14 +120,13 @@ public void testShared() throws Throwable { @Test public void testSharedUnsafe() throws Throwable { try (MemorySegment s = MemorySegment.allocateNative(4)) { - setInt(s.baseAddress(), 42); - assertEquals(getInt(s.baseAddress()), 42); + setInt(s, 42); + assertEquals(getInt(s), 42); List threads = new ArrayList<>(); - MemorySegment sharedSegment = MemorySegment.ofNativeRestricted( - s.baseAddress(), s.byteSize(), null, null, null); + MemorySegment sharedSegment = s.address().asSegmentRestricted(s.byteSize()).share(); for (int i = 0 ; i < 1000 ; i++) { threads.add(new Thread(() -> { - assertEquals(getInt(sharedSegment.baseAddress()), 42); + assertEquals(getInt(sharedSegment), 42); })); } threads.forEach(Thread::start); @@ -149,65 +140,32 @@ public void testSharedUnsafe() throws Throwable { } } - @Test(expectedExceptions=IllegalStateException.class) - public void testBadCloseWithPendingAcquire() { - withAcquired(MemorySegment::close); - } - - @Test(expectedExceptions=IllegalStateException.class) - public void testBadCloseWithPendingAcquireBuffer() { - withAcquired(segment -> { - segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost - segment.close(); // this should still fail - }); - } - - @Test(expectedExceptions=IllegalStateException.class) - public void testBadHandoffWithPendingAcquire() { - withAcquired(segment -> segment.withOwnerThread(new Thread())); - } - - @Test(expectedExceptions=IllegalStateException.class) - public void testBadHandoffWithPendingAcquireBuffer() { - withAcquired(segment -> { - segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost - segment.withOwnerThread(new Thread()); // this should still fail - }); + @Test + public void testHandoffToSelf() { + MemorySegment s1 = MemorySegment.ofArray(new int[4]); + MemorySegment s2 = s1.handoff(Thread.currentThread()); + assertFalse(s1.isAlive()); + assertTrue(s2.isAlive()); } - @Test(expectedExceptions=IllegalArgumentException.class) - public void testBadHandoffSameThread() { - MemorySegment.ofArray(new int[4]).withOwnerThread(Thread.currentThread()); + @Test + public void testShareTwice() { + MemorySegment s1 = MemorySegment.ofArray(new int[4]).share(); + MemorySegment s2 = s1.share(); + assertFalse(s1.isAlive()); + assertTrue(s2.isAlive()); } - @Test(expectedExceptions=NullPointerException.class) - public void testBadHandoffNullThread() { - MemorySegment.ofArray(new int[4]).withOwnerThread(null); + @Test(expectedExceptions=UnsupportedOperationException.class) + public void testBadHandoffNoAccess() { + MemorySegment.ofArray(new int[4]) + .withAccessModes(MemorySegment.CLOSE).handoff(new Thread()); } - private void withAcquired(Consumer acquiredAction) { - CountDownLatch holder = new CountDownLatch(1); - MemorySegment segment = MemorySegment.allocateNative(16); - Spliterator spliterator = MemorySegment.spliterator(segment, - MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_BYTE)); - CountDownLatch acquired = new CountDownLatch(1); - Runnable r = () -> spliterator.tryAdvance(s -> { - try { - acquired.countDown(); - holder.await(); - } catch (InterruptedException ex) { - throw new AssertionError(ex); - } - }); - new Thread(r).start(); - try { - acquired.await(); - acquiredAction.accept(segment); - } catch (InterruptedException ex) { - throw new AssertionError(ex); - } finally { - holder.countDown(); - } + @Test(expectedExceptions=UnsupportedOperationException.class) + public void testBadShareNoAccess() { + MemorySegment.ofArray(new int[4]) + .withAccessModes(MemorySegment.CLOSE).share(); } @Test @@ -228,8 +186,7 @@ public void testOutsideConfinementThread() throws Throwable { } catch (InterruptedException e) { } - MemoryAddress base = s2.baseAddress(); - setInt(base.addOffset(4), -42); + setInt(s2.asSlice(4), -42); fail(); } catch (IllegalStateException ex) { assertTrue(ex.getMessage().contains("owning thread")); @@ -237,19 +194,18 @@ public void testOutsideConfinementThread() throws Throwable { }); a.await(); - MemoryAddress base = s1.baseAddress(); - setInt(base.addOffset(4), 42); + setInt(s1.asSlice(4), 42); } b.countDown(); r.get(); } - static int getInt(MemoryAddress address) { - return (int)intHandle.getVolatile(address); + static int getInt(MemorySegment base) { + return (int)intHandle.getVolatile(base); } - static void setInt(MemoryAddress address, int value) { - intHandle.setVolatile(address, value); + static void setInt(MemorySegment base, int value) { + intHandle.setVolatile(base, value); } } diff --git a/test/jdk/java/foreign/TestSlices.java b/test/jdk/java/foreign/TestSlices.java index 7fe6436425b61..8225bf95a9336 100644 --- a/test/jdk/java/foreign/TestSlices.java +++ b/test/jdk/java/foreign/TestSlices.java @@ -44,15 +44,13 @@ public class TestSlices { static VarHandle VH_ALL = LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement()); - static VarHandle VH_INT = MemoryLayouts.JAVA_INT.varHandle(int.class); - @Test(dataProvider = "slices") public void testSlices(VarHandle handle, int lo, int hi, int[] values) { try (MemorySegment segment = MemorySegment.allocateNative(LAYOUT)) { //init for (long i = 0 ; i < 2 ; i++) { for (long j = 0 ; j < 5 ; j++) { - VH_ALL.set(segment.baseAddress(), i, j, (int)j + 1 + ((int)i * 5)); + VH_ALL.set(segment, i, j, (int)j + 1 + ((int)i * 5)); } } @@ -64,7 +62,7 @@ static void checkSlice(MemorySegment segment, VarHandle handle, long i_max, long int index = 0; for (long i = 0 ; i < i_max ; i++) { for (long j = 0 ; j < j_max ; j++) { - int x = (int) handle.get(segment.baseAddress(), i, j); + int x = (int) handle.get(segment, i, j); assertEquals(x, values[index++]); } } @@ -79,19 +77,15 @@ static Object[][] slices() { // x[0::2] { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } }, - { MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } }, // x[1::2] { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } }, - { MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 4), 2, 2, new int[] { 2, 4, 7, 9 } }, // x[4::-2] { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } }, - { MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 16), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } }, // x[3::-2] { LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } }, - { MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 12), 2, 2, new int[] { 4, 2, 9, 7 } }, }; } } diff --git a/test/jdk/java/foreign/TestSpliterator.java b/test/jdk/java/foreign/TestSpliterator.java index f0922b23ef3b9..233b2ed6a6382 100644 --- a/test/jdk/java/foreign/TestSpliterator.java +++ b/test/jdk/java/foreign/TestSpliterator.java @@ -26,7 +26,6 @@ * @run testng TestSpliterator */ -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; @@ -61,22 +60,22 @@ public void testSum(int size, int threshold) { SequenceLayout layout = MemoryLayout.ofSequence(size, MemoryLayouts.JAVA_INT); //setup - MemorySegment segment = MemorySegment.allocateNative(layout); + MemorySegment segment = MemorySegment.allocateNative(layout).share(); for (int i = 0; i < layout.elementCount().getAsLong(); i++) { - INT_HANDLE.set(segment.baseAddress(), (long) i, i); + INT_HANDLE.set(segment, (long) i, i); } long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum(); //serial long serial = sum(0, segment); assertEquals(serial, expected); //parallel counted completer - long parallelCounted = new SumSegmentCounted(null, MemorySegment.spliterator(segment, layout), threshold).invoke(); + long parallelCounted = new SumSegmentCounted(null, segment.spliterator(layout), threshold).invoke(); assertEquals(parallelCounted, expected); //parallel recursive action - long parallelRecursive = new SumSegmentRecursive(MemorySegment.spliterator(segment, layout), threshold).invoke(); + long parallelRecursive = new SumSegmentRecursive(segment.spliterator(layout), threshold).invoke(); assertEquals(parallelRecursive, expected); //parallel stream - long streamParallel = StreamSupport.stream(MemorySegment.spliterator(segment, layout), true) + long streamParallel = StreamSupport.stream(segment.spliterator(layout), true) .reduce(0L, TestSpliterator::sumSingle, Long::sum); assertEquals(streamParallel, expected); segment.close(); @@ -88,27 +87,26 @@ public void testSumSameThread() { //setup MemorySegment segment = MemorySegment.allocateNative(layout); for (int i = 0; i < layout.elementCount().getAsLong(); i++) { - INT_HANDLE.set(segment.baseAddress(), (long) i, i); + INT_HANDLE.set(segment, (long) i, i); } long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum(); //check that a segment w/o ACQUIRE access mode can still be used from same thread AtomicLong spliteratorSum = new AtomicLong(); - spliterator(segment.withAccessModes(MemorySegment.READ), layout) + segment.withAccessModes(MemorySegment.READ).spliterator(layout) .forEachRemaining(s -> spliteratorSum.addAndGet(sumSingle(0L, s))); assertEquals(spliteratorSum.get(), expected); } static long sumSingle(long acc, MemorySegment segment) { - return acc + (int)INT_HANDLE.get(segment.baseAddress(), 0L); + return acc + (int)INT_HANDLE.get(segment, 0L); } static long sum(long start, MemorySegment segment) { long sum = start; - MemoryAddress base = segment.baseAddress(); int length = (int)segment.byteSize(); for (int i = 0 ; i < length / CARRIER_SIZE ; i++) { - sum += (int)INT_HANDLE.get(base, (long)i); + sum += (int)INT_HANDLE.get(segment, (long)i); } return sum; } @@ -211,13 +209,13 @@ public Object[][] accessScenarios() { var mallocSegment = MemorySegment.allocateNative(layout); Map>,Integer> l = Map.of( - () -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS), layout), ALL_ACCESS, - () -> spliterator(mallocSegment.withAccessModes(0), layout), 0, - () -> spliterator(mallocSegment.withAccessModes(READ), layout), READ, - () -> spliterator(mallocSegment.withAccessModes(CLOSE), layout), 0, - () -> spliterator(mallocSegment.withAccessModes(READ|WRITE), layout), READ|WRITE, - () -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE), layout), READ|WRITE|ACQUIRE, - () -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE|HANDOFF), layout), READ|WRITE|ACQUIRE|HANDOFF + () -> mallocSegment.withAccessModes(ALL_ACCESS).spliterator(layout), ALL_ACCESS, + () -> mallocSegment.withAccessModes(0).spliterator(layout), 0, + () -> mallocSegment.withAccessModes(READ).spliterator(layout), READ, + () -> mallocSegment.withAccessModes(CLOSE).spliterator(layout), 0, + () -> mallocSegment.withAccessModes(READ|WRITE).spliterator(layout), READ|WRITE, + () -> mallocSegment.withAccessModes(READ|WRITE| SHARE).spliterator(layout), READ|WRITE| SHARE, + () -> mallocSegment.withAccessModes(READ|WRITE| SHARE |HANDOFF).spliterator(layout), READ|WRITE| SHARE |HANDOFF ); return l.entrySet().stream().map(e -> new Object[] { e.getKey(), e.getValue() }).toArray(Object[][]::new); diff --git a/test/jdk/java/foreign/TestVarHandleCombinators.java b/test/jdk/java/foreign/TestVarHandleCombinators.java index e9e69398f543e..c3840dbad3821 100644 --- a/test/jdk/java/foreign/TestVarHandleCombinators.java +++ b/test/jdk/java/foreign/TestVarHandleCombinators.java @@ -31,7 +31,6 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemorySegment; import java.lang.invoke.MethodHandles; @@ -45,36 +44,20 @@ public class TestVarHandleCombinators { @Test public void testElementAccess() { VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); - vh = MemoryHandles.withStride(vh, 1); byte[] arr = { 0, 0, -1, 0 }; MemorySegment segment = MemorySegment.ofArray(arr); - MemoryAddress addr = segment.baseAddress(); - - assertEquals((byte) vh.get(addr, 2), (byte) -1); + assertEquals((byte) vh.get(segment, 2), (byte) -1); } @Test(expectedExceptions = IllegalStateException.class) public void testUnalignedElement() { VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder()); - vh = MemoryHandles.withStride(vh, 2); MemorySegment segment = MemorySegment.ofArray(new byte[4]); - vh.get(segment.baseAddress(), 1L); //should throw - } - - public void testZeroStrideElement() { - VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); - VarHandle strided_vh = MemoryHandles.withStride(vh, 0); - MemorySegment segment = MemorySegment.ofArray(new int[] { 42 }); - for (int i = 0 ; i < 100 ; i++) { - assertEquals((int)vh.get(segment.baseAddress()), strided_vh.get(segment.baseAddress(), (long)i)); - } - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testStrideWrongHandle() { - VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder()); - MemoryHandles.withStride(vh, 10); + vh.get(segment, 2L); //should throw + //FIXME: the VH only checks the alignment of the segment, which is fine if the VH is derived from layouts, + //FIXME: but not if the VH is just created from scratch - we need a VH variable to govern this property, + //FIXME: at least until the VM is fixed } @Test(expectedExceptions = IllegalArgumentException.class) @@ -92,56 +75,8 @@ public void testAlign() { VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder()); MemorySegment segment = MemorySegment.allocateNative(1, 2); - MemoryAddress address = segment.baseAddress(); - - vh.set(address, (byte) 10); // fine, memory region is aligned - assertEquals((byte) vh.get(address), (byte) 10); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testAlignBadAccess() { - VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder()); - vh = MemoryHandles.withOffset(vh, 1); // offset by 1 byte - - MemorySegment segment = MemorySegment.allocateNative(2, 2); - MemoryAddress address = segment.baseAddress(); - - vh.set(address, (byte) 10); // should be bad align - } - - public void testZeroOffsetElement() { - VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); - VarHandle offset_vh = MemoryHandles.withOffset(vh, 0); - MemorySegment segment = MemorySegment.ofArray(new int[] { 42 }); - for (int i = 0 ; i < 100 ; i++) { - assertEquals((int)vh.get(segment.baseAddress()), offset_vh.get(segment.baseAddress(), (long)i)); - } - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testOffsetWrongHandle() { - VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder()); - MemoryHandles.withOffset(vh, 1); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testUnalignedOffset() { - VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder()); - vh = MemoryHandles.withOffset(vh, 2); - MemorySegment segment = MemorySegment.ofArray(new byte[4]); - vh.get(segment.baseAddress()); //should throw - } - - @Test - public void testOffset() { - VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()); - vh = MemoryHandles.withOffset(vh, 1); - - MemorySegment segment = MemorySegment.ofArray(new byte[2]); - MemoryAddress address = segment.baseAddress(); - - vh.set(address, (byte) 10); - assertEquals((byte) vh.get(address), (byte) 10); + vh.set(segment, 0L, (byte) 10); // fine, memory region is aligned + assertEquals((byte) vh.get(segment, 0L), (byte) 10); } @Test @@ -149,9 +84,7 @@ public void testByteOrderLE() { VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.LITTLE_ENDIAN); byte[] arr = new byte[2]; MemorySegment segment = MemorySegment.ofArray(arr); - MemoryAddress address = segment.baseAddress(); - - vh.set(address, (short) 0xFF); + vh.set(segment, 0L, (short) 0xFF); assertEquals(arr[0], (byte) 0xFF); assertEquals(arr[1], (byte) 0); } @@ -161,9 +94,7 @@ public void testByteOrderBE() { VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.BIG_ENDIAN); byte[] arr = new byte[2]; MemorySegment segment = MemorySegment.ofArray(arr); - MemoryAddress address = segment.baseAddress(); - - vh.set(address, (short) 0xFF); + vh.set(segment, 0L, (short) 0xFF); assertEquals(arr[0], (byte) 0); assertEquals(arr[1], (byte) 0xFF); } @@ -176,16 +107,13 @@ public void testNestedSequenceAccess() { //[10 : [5 : [x32 i32]]] VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); - vh = MemoryHandles.withOffset(vh, 4); - VarHandle inner_vh = MemoryHandles.withStride(vh, 8); - VarHandle outer_vh = MemoryHandles.withStride(inner_vh, 5 * 8); int count = 0; try (MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8)) { for (long i = 0; i < outer_size; i++) { for (long j = 0; j < inner_size; j++) { - outer_vh.set(segment.baseAddress(), i, j, count); + vh.set(segment, i * 40 + j * 8, count); assertEquals( - (int)inner_vh.get(segment.baseAddress().addOffset(i * inner_size * 8), j), + (int)vh.get(segment.asSlice(i * inner_size * 8), j * 8), count); count++; } @@ -205,7 +133,7 @@ public Object[][] createBadCarriers() { { boolean.class }, { Object.class }, { int[].class }, - { MemoryAddress.class } + { MemorySegment.class } }; } diff --git a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java index f40885f8cbe34..b879bdd4da9ac 100644 --- a/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java +++ b/test/jdk/java/lang/invoke/VarHandles/VarHandleTestExact.java @@ -230,10 +230,9 @@ public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX VarHandle vh = MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder()); assertFalse(vh.hasInvokeExactBehavior()); try (MemorySegment seg = MemorySegment.allocateNative(8)) { - MemoryAddress base = seg.baseAddress(); try { - vh.set(base, testValue); - vh.withInvokeBehavior().set(base, testValue); + vh.set(seg, 0L, testValue); + vh.withInvokeBehavior().set(seg, 0L, testValue); } catch (WrongMethodTypeException wmte) { fail("Unexpected exception", wmte); } @@ -241,11 +240,11 @@ public void testExactSegmentSet(Class carrier, Object testValue, SetSegmentX vh = vh.withInvokeExactBehavior(); assertTrue(vh.hasInvokeExactBehavior()); try { - setter.set(vh, base, testValue); // should throw + setter.set(vh, seg, 0L, testValue); // should throw fail("Exception expected"); } catch (WrongMethodTypeException wmte) { assertMatches(wmte.getMessage(), - ".*\\Qexpected (MemoryAddress," + carrier.getSimpleName() + ")void \\E.*"); + ".*\\Qexpected (MemorySegment,long," + carrier.getSimpleName() + ")void \\E.*"); } } } @@ -281,7 +280,7 @@ private interface SetBufferX { } private interface SetSegmentX { - void set(VarHandle vh, MemoryAddress addr, Object testValue); + void set(VarHandle vh, MemorySegment segment, long offser, Object testValue); } private static void consume(Object o) {} @@ -418,11 +417,11 @@ public static Object[][] dataSetMemorySegment() { List cases = new ArrayList<>(); // create a bunch of different sig-poly call sites - testCaseSegmentSet(cases, long.class, 1234, (vh, addr, tv) -> vh.set(addr, (int) tv)); - testCaseSegmentSet(cases, long.class, (char) 1234, (vh, addr, tv) -> vh.set(addr, (char) tv)); - testCaseSegmentSet(cases, long.class, (short) 1234, (vh, addr, tv) -> vh.set(addr, (short) tv)); - testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, addr, tv) -> vh.set(addr, (byte) tv)); - testCaseSegmentSet(cases, double.class, 1234F, (vh, addr, tv) -> vh.set(addr, (float) tv)); + testCaseSegmentSet(cases, long.class, 1234, (vh, seg, off, tv) -> vh.set(seg, off, (int) tv)); + testCaseSegmentSet(cases, long.class, (char) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (char) tv)); + testCaseSegmentSet(cases, long.class, (short) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (short) tv)); + testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (byte) tv)); + testCaseSegmentSet(cases, double.class, 1234F, (vh, seg, off, tv) -> vh.set(seg, off, (float) tv)); return cases.toArray(Object[][]::new); } diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java index c5410865a2b67..8c7d8c48921a7 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SegmentTestDataProvider.java @@ -23,6 +23,7 @@ package org.openjdk.tests.java.util.stream; +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemoryLayouts; import jdk.incubator.foreign.MemorySegment; @@ -39,16 +40,8 @@ public class SegmentTestDataProvider { - static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class); - static VarHandle CHAR_HANDLE = MemoryLayouts.JAVA_CHAR.withBitAlignment(8).varHandle(char.class); - static VarHandle SHORT_HANDLE = MemoryLayouts.JAVA_SHORT.withBitAlignment(8).varHandle(short.class); - static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.withBitAlignment(8).varHandle(int.class); - static VarHandle LONG_HANDLE = MemoryLayouts.JAVA_LONG.withBitAlignment(8).varHandle(long.class); - static VarHandle FLOAT_HANDLE = MemoryLayouts.JAVA_FLOAT.withBitAlignment(8).varHandle(float.class); - static VarHandle DOUBLE_HANDLE = MemoryLayouts.JAVA_DOUBLE.withBitAlignment(8).varHandle(double.class); - static boolean compareSegmentsByte(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (byte)BYTE_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getByte; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -59,7 +52,7 @@ static boolean compareSegmentsByte(Collection segments1, Collecti } static boolean compareSegmentsChar(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (char)CHAR_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getChar; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -70,7 +63,7 @@ static boolean compareSegmentsChar(Collection segments1, Collecti } static boolean compareSegmentsShort(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (short)SHORT_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getShort; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -81,7 +74,7 @@ static boolean compareSegmentsShort(Collection segments1, Collect } static boolean compareSegmentsInt(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (int)INT_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getInt; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -92,7 +85,7 @@ static boolean compareSegmentsInt(Collection segments1, Collectio } static boolean compareSegmentsLong(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (long)LONG_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getLong; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -103,7 +96,7 @@ static boolean compareSegmentsLong(Collection segments1, Collecti } static boolean compareSegmentsFloat(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (float)FLOAT_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getFloat; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -122,7 +115,7 @@ static Consumer segmentCopier(Consumer input) { } static boolean compareSegmentsDouble(Collection segments1, Collection segments2, boolean isOrdered) { - Function mapper = segment -> (double)DOUBLE_HANDLE.get(segment.baseAddress()); + Function mapper = MemoryAccess::getDouble; List list1 = segments1.stream() .map(mapper) .collect(Collectors.toList()); @@ -134,7 +127,7 @@ static boolean compareSegmentsDouble(Collection segments1, Collec static void initSegment(MemorySegment segment) { for (int i = 0 ; i < segment.byteSize() ; i++) { - BYTE_HANDLE.set(segment.baseAddress(), (byte)i); + MemoryAccess.setByte(segment, (byte)i); } } diff --git a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java index 1a18ae4e7a253..1f9f52ff8e027 100644 --- a/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java +++ b/test/jdk/java/util/stream/test/org/openjdk/tests/java/util/stream/SpliteratorTest.java @@ -66,7 +66,7 @@ public void testDoubleSpliterator(String name, Supplier su public void testSegmentSpliterator(String name, SequenceLayout layout, SpliteratorTestHelper.ContentAsserter contentAsserter) { try (MemorySegment segment = MemorySegment.allocateNative(layout)) { SegmentTestDataProvider.initSegment(segment); - SpliteratorTestHelper.testSpliterator(() -> MemorySegment.spliterator(segment, layout), + SpliteratorTestHelper.testSpliterator(() -> segment.spliterator(layout), SegmentTestDataProvider::segmentCopier, contentAsserter); } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java index 8837fa8045a90..f79b019b1a2cf 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverConstant.java @@ -72,12 +72,12 @@ public class LoopOverConstant { //setup native memory segment - static final MemoryAddress segment_addr = MemorySegment.allocateNative(ALLOC_SIZE).baseAddress(); + static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE); static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement()); static { for (int i = 0; i < ELEM_SIZE; i++) { - VH_int.set(segment_addr, (long) i, i); + VH_int.set(segment, (long) i, i); } } @@ -100,7 +100,7 @@ public int unsafe_get() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public int segment_get() { - return (int)VH_int.get(segment_addr, 0L); + return (int)VH_int.get(segment, 0L); } @Benchmark @@ -122,7 +122,7 @@ public int unsafe_loop() { public int segment_loop() { int res = 0; for (int i = 0; i < ELEM_SIZE; i++) { - res += (int) VH_int.get(segment_addr, (long)i); + res += (int) VH_int.get(segment, (long)i); } return res; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java index 194d7ab6ecf68..101f3dde8e96f 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNew.java @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -74,7 +73,16 @@ public void unsafe_loop() { public void segment_loop() { MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE); for (int i = 0; i < ELEM_SIZE; i++) { - VH_int.set(segment.baseAddress(), (long) i, i); + VH_int.set(segment, (long) i, i); + } + segment.close(); + } + + @Benchmark + public void segment_loop_shared() { + MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE).share(); + for (int i = 0; i < ELEM_SIZE; i++) { + VH_int.set(segment, (long) i, i); } segment.close(); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java index f68e7892bd9a0..03cc9dab946de 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstant.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; @@ -73,7 +74,7 @@ public void setup() { } segment = MemorySegment.allocateNative(ALLOC_SIZE); for (int i = 0; i < ELEM_SIZE; i++) { - VH_int.set(segment.baseAddress(), (long) i, i); + VH_int.set(segment, (long) i, i); } byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder()); for (int i = 0; i < ELEM_SIZE; i++) { @@ -97,7 +98,7 @@ public int unsafe_get() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public int segment_get() { - return (int) VH_int.get(segment.baseAddress(), 0L); + return (int) VH_int.get(segment, 0L); } @Benchmark @@ -115,12 +116,20 @@ public int unsafe_loop() { return res; } + @Benchmark + public int segment_loop_static() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += MemoryAccess.getIntAtIndex(segment, i); + } + return res; + } + @Benchmark public int segment_loop() { int sum = 0; - MemoryAddress base = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - sum += (int) VH_int.get(base, (long) i); + sum += (int) VH_int.get(segment, (long) i); } return sum; } @@ -128,7 +137,7 @@ public int segment_loop() { @Benchmark public int segment_loop_slice() { int sum = 0; - MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress(); + MemorySegment base = segment.asSlice(0, segment.byteSize()); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } @@ -138,7 +147,7 @@ public int segment_loop_slice() { @Benchmark public int segment_loop_readonly() { int sum = 0; - MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress(); + MemorySegment base = segment.withAccessModes(MemorySegment.READ); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java index 8fb4c2d6e77cb..61fd7cb132012 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantHeap.java @@ -22,6 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; @@ -90,7 +91,7 @@ public int unsafe_get() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public int segment_get() { - return (int) VH_int.get(segment.baseAddress(), 0L); + return (int) VH_int.get(segment, 0L); } @Benchmark @@ -111,17 +112,25 @@ public int unsafe_loop() { @Benchmark public int segment_loop() { int sum = 0; - MemoryAddress base = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - sum += (int) VH_int.get(base, (long) i); + sum += (int) VH_int.get(segment, (long) i); } return sum; } + @Benchmark + public int segment_loop_static() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += MemoryAccess.getIntAtIndex(segment, i); + } + return res; + } + @Benchmark public int segment_loop_slice() { int sum = 0; - MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress(); + MemorySegment base = segment.asSlice(0, segment.byteSize()); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } @@ -131,7 +140,7 @@ public int segment_loop_slice() { @Benchmark public int segment_loop_readonly() { int sum = 0; - MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress(); + MemorySegment base = segment.withAccessModes(MemorySegment.READ); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java index fb6b44274c875..96f404fc6170e 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantMapped.java @@ -22,7 +22,7 @@ */ package org.openjdk.bench.jdk.incubator.foreign; -import jdk.incubator.foreign.MemoryAddress; +import jdk.incubator.foreign.MemoryAccess; import jdk.incubator.foreign.MemoryLayout; import jdk.incubator.foreign.MemorySegment; import org.openjdk.jmh.annotations.Benchmark; @@ -95,8 +95,8 @@ public void setup() throws IOException { } ((MappedByteBuffer)byteBuffer).force(); } - segment = MemorySegment.mapFromPath(tempPath, 0L, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE); - unsafe_addr = segment.baseAddress().toRawLongValue(); + segment = MemorySegment.mapFile(tempPath, 0L, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE); + unsafe_addr = segment.address().toRawLongValue(); } @TearDown @@ -114,7 +114,7 @@ public int unsafe_get() { @Benchmark @OutputTimeUnit(TimeUnit.NANOSECONDS) public int segment_get() { - return (int) VH_int.get(segment.baseAddress(), 0L); + return (int) VH_int.get(segment, 0L); } @Benchmark @@ -135,17 +135,25 @@ public int unsafe_loop() { @Benchmark public int segment_loop() { int sum = 0; - MemoryAddress base = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - sum += (int) VH_int.get(base, (long) i); + sum += (int) VH_int.get(segment, (long) i); } return sum; } + @Benchmark + public int segment_loop_static() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += MemoryAccess.getIntAtIndex(segment, i); + } + return res; + } + @Benchmark public int segment_loop_slice() { int sum = 0; - MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress(); + MemorySegment base = segment.asSlice(0, segment.byteSize()); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } @@ -155,7 +163,7 @@ public int segment_loop_slice() { @Benchmark public int segment_loop_readonly() { int sum = 0; - MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress(); + MemorySegment base = segment.withAccessModes(MemorySegment.READ); for (int i = 0; i < ELEM_SIZE; i++) { sum += (int) VH_int.get(base, (long) i); } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java new file mode 100644 index 0000000000000..0d629a16dc913 --- /dev/null +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/LoopOverNonConstantShared.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.bench.jdk.incubator.foreign; + +import jdk.incubator.foreign.MemoryAccess; +import jdk.incubator.foreign.MemoryLayout; +import jdk.incubator.foreign.MemorySegment; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import sun.misc.Unsafe; + +import java.lang.invoke.VarHandle; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.TimeUnit; + +import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement; +import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(3) +public class LoopOverNonConstantShared { + + static final Unsafe unsafe = Utils.unsafe; + + static final int ELEM_SIZE = 1_000_000; + static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); + static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; + + static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement()); + MemorySegment segment; + long unsafe_addr; + + ByteBuffer byteBuffer; + + @Setup + public void setup() { + unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); + for (int i = 0; i < ELEM_SIZE; i++) { + unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i); + } + segment = MemorySegment.allocateNative(ALLOC_SIZE).share(); + for (int i = 0; i < ELEM_SIZE; i++) { + VH_int.set(segment, (long) i, i); + } + byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder()); + for (int i = 0; i < ELEM_SIZE; i++) { + byteBuffer.putInt(i * CARRIER_SIZE , i); + } + } + + @TearDown + public void tearDown() { + segment.close(); + unsafe.invokeCleaner(byteBuffer); + unsafe.freeMemory(unsafe_addr); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public int unsafe_get() { + return unsafe.getInt(unsafe_addr); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public int segment_get() { + return (int) VH_int.get(segment, 0L); + } + + @Benchmark + @OutputTimeUnit(TimeUnit.NANOSECONDS) + public int BB_get() { + return byteBuffer.getInt(0); + } + + @Benchmark + public int unsafe_loop() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += unsafe.getInt(unsafe_addr + (i * CARRIER_SIZE)); + } + return res; + } + + @Benchmark + public int segment_loop_static() { + int res = 0; + for (int i = 0; i < ELEM_SIZE; i ++) { + res += MemoryAccess.getIntAtIndex(segment, i); + } + return res; + } + + @Benchmark + public int segment_loop() { + int sum = 0; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += (int) VH_int.get(segment, (long) i); + } + return sum; + } + + @Benchmark + public int segment_loop_slice() { + int sum = 0; + MemorySegment base = segment.asSlice(0, segment.byteSize()); + for (int i = 0; i < ELEM_SIZE; i++) { + sum += (int) VH_int.get(base, (long) i); + } + return sum; + } + + @Benchmark + public int segment_loop_readonly() { + int sum = 0; + MemorySegment base = segment.withAccessModes(MemorySegment.READ); + for (int i = 0; i < ELEM_SIZE; i++) { + sum += (int) VH_int.get(base, (long) i); + } + return sum; + } + + @Benchmark + public int BB_loop() { + int sum = 0; + ByteBuffer bb = byteBuffer; + for (int i = 0; i < ELEM_SIZE; i++) { + sum += bb.getInt(i * CARRIER_SIZE); + } + return sum; + } + +} diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java index b84ff169bc04b..4bebe01de09f7 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/ParallelSum.java @@ -85,9 +85,9 @@ public void setup() { for (int i = 0; i < ELEM_SIZE; i++) { unsafe.putInt(address + (i * CARRIER_SIZE), i); } - segment = MemorySegment.allocateNative(ALLOC_SIZE); + segment = MemorySegment.allocateNative(ALLOC_SIZE).share(); for (int i = 0; i < ELEM_SIZE; i++) { - VH_int.set(segment.baseAddress(), (long) i, i); + VH_int.set(segment, (long) i, i); } } @@ -100,9 +100,8 @@ public void tearDown() throws Throwable { @Benchmark public int segment_serial() { int res = 0; - MemoryAddress base = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - res += (int)VH_int.get(base, (long) i); + res += (int)VH_int.get(segment, (long) i); } return res; } @@ -118,73 +117,71 @@ public int unsafe_serial() { @Benchmark public int segment_parallel() { - return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), SEGMENT_TO_INT).invoke(); + return new SumSegment(segment.spliterator(SEQUENCE_LAYOUT), SEGMENT_TO_INT).invoke(); } @Benchmark public int segment_parallel_bulk() { - return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), SEGMENT_TO_INT_BULK).invoke(); + return new SumSegment(segment.spliterator(SEQUENCE_LAYOUT_BULK), SEGMENT_TO_INT_BULK).invoke(); } @Benchmark public int segment_stream_parallel() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true) .mapToInt(SEGMENT_TO_INT).sum(); } @Benchmark public int segment_stream_parallel_bulk() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), true) .mapToInt(SEGMENT_TO_INT_BULK).sum(); } final static ToIntFunction SEGMENT_TO_INT = slice -> - (int) VH_int.get(slice.baseAddress(), 0L); + (int) VH_int.get(slice, 0L); final static ToIntFunction SEGMENT_TO_INT_BULK = slice -> { int res = 0; - MemoryAddress base = slice.baseAddress(); for (int i = 0; i < BULK_FACTOR ; i++) { - res += (int)VH_int.get(base, (long) i); + res += (int)VH_int.get(slice, (long) i); } return res; }; @Benchmark public Optional segment_stream_findany_serial() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), false) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), false) .filter(FIND_SINGLE) .findAny(); } @Benchmark public Optional segment_stream_findany_parallel() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true) .filter(FIND_SINGLE) .findAny(); } @Benchmark public Optional segment_stream_findany_serial_bulk() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), false) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), false) .filter(FIND_BULK) .findAny(); } @Benchmark public Optional segment_stream_findany_parallel_bulk() { - return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true) + return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), true) .filter(FIND_BULK) .findAny(); } final static Predicate FIND_SINGLE = slice -> - (int)VH_int.get(slice.baseAddress(), 0L) == (ELEM_SIZE - 1); + (int)VH_int.get(slice, 0L) == (ELEM_SIZE - 1); final static Predicate FIND_BULK = slice -> { - MemoryAddress base = slice.baseAddress(); for (int i = 0; i < BULK_FACTOR ; i++) { - if ((int)VH_int.get(base, (long)i) == (ELEM_SIZE - 1)) { + if ((int)VH_int.get(slice, (long)i) == (ELEM_SIZE - 1)) { return true; } } @@ -193,7 +190,7 @@ public Optional segment_stream_findany_parallel_bulk() { @Benchmark public int unsafe_parallel() { - return new SumUnsafe(address, 0, ALLOC_SIZE).invoke(); + return new SumUnsafe(address, 0, ALLOC_SIZE / CARRIER_SIZE).invoke(); } static class SumUnsafe extends RecursiveTask { @@ -212,15 +209,19 @@ static class SumUnsafe extends RecursiveTask { @Override protected Integer compute() { if (length > SPLIT_THRESHOLD) { - SumUnsafe s1 = new SumUnsafe(address, start, length / 2); - SumUnsafe s2 = new SumUnsafe(address, length / 2, length / 2); + int rem = length % 2; + int split = length / 2; + int lobound = split; + int hibound = lobound + rem; + SumUnsafe s1 = new SumUnsafe(address, start, lobound); + SumUnsafe s2 = new SumUnsafe(address, start + lobound, hibound); s1.fork(); s2.fork(); return s1.join() + s2.join(); } else { int res = 0; - for (int i = 0; i < length; i += CARRIER_SIZE) { - res += unsafe.getInt(start + address + i); + for (int i = 0; i < length; i ++) { + res += unsafe.getInt(address + (start + i) * CARRIER_SIZE); } return res; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java index bcda35b2ce526..2a35b36955ea4 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/TestAdaptVarHandles.java @@ -144,9 +144,8 @@ public int mh_box_loop() throws Throwable { @Benchmark public int segment_loop() throws Throwable { int sum = 0; - MemoryAddress baseAddress = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - sum += (int)VH_addr_int.get(baseAddress, (long)i); + sum += (int)VH_addr_int.get(segment, (long)i); } return sum; } @@ -154,9 +153,8 @@ public int segment_loop() throws Throwable { @Benchmark public int segment_box_loop() throws Throwable { int sum = 0; - MemoryAddress baseAddress = segment.baseAddress(); for (int i = 0; i < ELEM_SIZE; i++) { - sum += ((IntBox)VH_addr_box_int.get(baseAddress, (long)i)).intValue(); + sum += ((IntBox)VH_addr_box_int.get(segment, (long)i)).intValue(); } return sum; } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java index ec920b90ae82c..12ae364f14860 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/VarHandleExact.java @@ -53,7 +53,7 @@ public class VarHandleExact { static final VarHandle generic; static { - generic = MemoryHandles.withStride(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 4); + generic = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()); exact = generic.withInvokeExactBehavior(); } @@ -71,16 +71,16 @@ public void tearDown() { @Benchmark public void exact_exactInvocation() { - exact.set(data.baseAddress(), (long) 0, 42); + exact.set(data, (long) 0, 42); } @Benchmark public void generic_genericInvocation() { - generic.set(data.baseAddress(), 0, 42); + generic.set(data, 0, 42); } @Benchmark public void generic_exactInvocation() { - generic.set(data.baseAddress(), (long) 0, 42); + generic.set(data, (long) 0, 42); } } diff --git a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java index 6b199ab62cfc9..0a3127bb50800 100644 --- a/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java +++ b/test/micro/org/openjdk/bench/jdk/incubator/foreign/points/support/PanamaPoint.java @@ -58,19 +58,19 @@ public PanamaPoint(MemorySegment segment) { } public void setX(int x) { - VH_x.set(segment.baseAddress(), x); + VH_x.set(segment, x); } public int getX() { - return (int) VH_x.get(segment.baseAddress()); + return (int) VH_x.get(segment); } public void setY(int y) { - VH_y.set(segment.baseAddress(), y); + VH_y.set(segment, y); } public int getY() { - return (int) VH_y.get(segment.baseAddress()); + return (int) VH_y.get(segment); } @Override From 1c0b490c28c0d24642fbb7d72c26535b46ed3c27 Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 12 Nov 2020 17:00:15 +0000 Subject: [PATCH 090/124] 8256201: java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java failed Reviewed-by: jdv --- .../FullscreenWindowProps/FullscreenWindowProps.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java index d444ee5f0f1be..1160c54bf329c 100644 --- a/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java +++ b/test/jdk/java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java @@ -55,6 +55,7 @@ public void paint(Graphics g) { } }; try { + frame.setUndecorated(true); // workaround JDK-8256257 frame.setBackground(Color.MAGENTA); frame.setVisible(true); gd.setFullScreenWindow(frame); @@ -99,4 +100,4 @@ private static void checkSize(int actual, int expected, String prop) { throw new RuntimeException(prop + " is wrong"); } } -} \ No newline at end of file +} From 90f9a7053aab785361a15461bc64065d34d0c54e Mon Sep 17 00:00:00 2001 From: Fernando Guallini Date: Thu, 12 Nov 2020 17:04:16 +0000 Subject: [PATCH 091/124] 8255546: Missing coverage for javax.smartcardio.CardPermission and ResponseAPDU Reviewed-by: xuelei --- .../javax/smartcardio/ResponseAPDUTest.java | 11 +- .../javax/smartcardio/TestCardPermission.java | 139 +++++++++++++----- 2 files changed, 110 insertions(+), 40 deletions(-) diff --git a/test/jdk/javax/smartcardio/ResponseAPDUTest.java b/test/jdk/javax/smartcardio/ResponseAPDUTest.java index bec322ea436f4..1bb2ed058946f 100644 --- a/test/jdk/javax/smartcardio/ResponseAPDUTest.java +++ b/test/jdk/javax/smartcardio/ResponseAPDUTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ /* * @test - * @bug 8049021 - * @summary Construct ResponseAPDU from byte array and check NR< SW, SW1 and SW2 + * @bug 8049021 8255546 + * @summary Construct ResponseAPDU from byte array and check NR< SW, SW1, SW2 and toString * @run testng ResponseAPDUTest */ import javax.smartcardio.ResponseAPDU; @@ -42,6 +42,7 @@ public class ResponseAPDUTest { static final ResponseAPDU RAPDU = new ResponseAPDU(R1); static byte[] expectedData; static int expectedNr, expectedSw1, expectedSw2, expectedSw; + static String expectedToString; @BeforeClass public static void setUpClass() throws Exception { @@ -57,6 +58,9 @@ public static void setUpClass() throws Exception { expectedSw1 = R1[apduLen - 2] & 0xff; expectedSw2 = R1[apduLen - 1] & 0xff; expectedSw = (expectedSw1 << 8) | expectedSw2; + + expectedToString = "ResponseAPDU: " + R1.length + + " bytes, SW=" + Integer.toHexString(expectedSw); } @Test @@ -67,5 +71,6 @@ public static void test() { assertEquals(RAPDU.getSW(), expectedSw); assertEquals(RAPDU.getSW1(), expectedSw1); assertEquals(RAPDU.getSW2(), expectedSw2); + assertEquals(RAPDU.toString(), expectedToString); } } diff --git a/test/jdk/javax/smartcardio/TestCardPermission.java b/test/jdk/javax/smartcardio/TestCardPermission.java index cbeb15dcffbdc..15ec536eac5ae 100644 --- a/test/jdk/javax/smartcardio/TestCardPermission.java +++ b/test/jdk/javax/smartcardio/TestCardPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,60 +23,125 @@ /** * @test - * @bug 6293767 6469513 + * @bug 6293767 6469513 8255546 * @summary Test for the CardPermission class * @author Andreas Sterbenz + * @run testng TestCardPermission */ +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + import javax.smartcardio.*; +import java.security.Permission; + +import static org.testng.Assert.*; public class TestCardPermission { - public static void main(String[] args) throws Exception { - CardPermission perm; - - test("*"); - test("connect"); - test("reset"); - test("exclusive"); - test("transmitControl"); - test("getBasicChannel"); - test("openLogicalChannel"); - - test("connect,reset"); - test("Reset,coNnect", "connect,reset"); - test("exclusive,*,connect", "*"); - test("connect,reset,exclusive,transmitControl,getBasicChannel,openLogicalChannel", "*"); - test(null, null); - - invalid(""); - invalid("foo"); - invalid("connect, reset"); - invalid("connect,,reset"); - invalid("connect,"); - invalid(",connect"); + @DataProvider(name = "actions") + Object[][] getActions() { + return new Object[][]{ + {"*"}, + {"connect"}, + {"reset"}, + {"exclusive"}, + {"transmitControl"}, + {"getBasicChannel"}, + {"openLogicalChannel"}, + {"connect,reset"} + }; } - private static void invalid(String s) throws Exception { - try { - CardPermission c = new CardPermission("*", s); - throw new Exception("Created invalid action: " + c); - } catch (IllegalArgumentException e) { - System.out.println("OK: " + e); - } + @DataProvider(name = "actionsCanon") + Object[][] getActionsCanon() { + return new Object[][]{ + {"Reset,coNnect", "connect,reset"}, + {"exclusive,*,connect", "*"}, + {"connect,reset,exclusive,transmitControl,getBasicChannel,openLogicalChannel", "*"}, + {null, null} + }; + } + + @DataProvider(name = "invalidActions") + Object[][] getInvalidActions() { + return new Object[][]{ + {""}, + {"foo"}, + {"connect, reset"}, + {"connect,,reset"}, + {"connect,"}, + {",connect"} + }; + } + + @Test(dataProvider = "actions") + public void testActions(String actions) throws Exception { + testActions(actions, actions); + } + + @Test(dataProvider = "actionsCanon") + public void testActionsCanon(String actions, String canon) throws Exception { + testActions(actions, canon); + } + + @Test(dataProvider = "invalidActions") + public void testInvalidActions(String actions) { + assertThrows(IllegalArgumentException.class, () -> new CardPermission("*", actions)); + } + + // Should return false since p2 is not a CardPermission instance + @Test + public void testImpliesNotCardPermissionInstance() { + String actions = "connect"; + CardPermission p1 = new CardPermission("*", actions); + Permission p2 = new Permission(actions) { + @Override public boolean implies(Permission permission) { return false; } + @Override public boolean equals(Object obj) { return false; } + @Override public int hashCode() { return 0; } + @Override public String getActions() { return null; } + }; + assertFalse(p1.implies(p2)); + } + + // Should return false since p2 actions are not a subset of p1 + @Test + public void testImpliesNotSubsetCardPermission() { + CardPermission p1 = new CardPermission("*", "connect,reset"); + CardPermission p2 = new CardPermission("*", "transmitControl"); + assertFalse(p1.implies(p2)); } - private static void test(String actions) throws Exception { - test(actions, actions); + // Should return true since p1 name is * and p2 actions are a subset of p1 + @Test + public void testImpliesNameEqualsAll() { + CardPermission p1 = new CardPermission("*", "connect,reset"); + CardPermission p2 = new CardPermission("None", "reset"); + assertTrue(p1.implies(p2)); } - private static void test(String actions, String canon) throws Exception { + // Should return true since p1 and p2 names are equal + @Test + public void testImpliesBothSameNameNotAll() { + CardPermission p1 = new CardPermission("None", "connect,reset"); + CardPermission p2 = new CardPermission("None", "reset"); + assertTrue(p1.implies(p2)); + } + + // Should return false since p1 and p2 names are not equal + @Test + public void testImpliesNameNotSameNotAll() { + CardPermission p1 = new CardPermission("None", "connect,reset"); + CardPermission p2 = new CardPermission("Other", "reset"); + assertFalse(p1.implies(p2)); + } + + private void testActions(String actions, String canon) throws Exception { CardPermission p = new CardPermission("*", actions); System.out.println(p); String a = p.getActions(); - if (canon != null && canon.equals(a) == false) { + if (canon != null && !canon.equals(a)) { throw new Exception("Canonical actions mismatch: " + canon + " != " + a); } } - } From b5a9c92b7a783aa3c99877fd055ed3c8da2ba301 Mon Sep 17 00:00:00 2001 From: Roger Riggs Date: Thu, 12 Nov 2020 17:06:00 +0000 Subject: [PATCH 092/124] 8256244: java/lang/ProcessHandle/PermissionTest.java fails with TestNG 7.1 Reviewed-by: lancea, iignatyev --- .../lang/ProcessHandle/PermissionTest.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/test/jdk/java/lang/ProcessHandle/PermissionTest.java b/test/jdk/java/lang/ProcessHandle/PermissionTest.java index 055152ebcaa0a..54c51a2c1d418 100644 --- a/test/jdk/java/lang/ProcessHandle/PermissionTest.java +++ b/test/jdk/java/lang/ProcessHandle/PermissionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,6 @@ import org.testng.Assert; import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; /* @@ -118,45 +117,54 @@ public void processToHandleWithPermission() throws IOException { } } - @BeforeGroups (groups = {"NoManageProcessPermission"}) + /** + * Setup a policy that would reject ProcessHandle requests without Permissions ManageProcess. + */ public void noPermissionsSetup(){ Policy.setPolicy(new TestPolicy()); SecurityManager sm = new SecurityManager(); System.setSecurityManager(sm); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionAllChildren() { + noPermissionsSetup(); currentHndl.descendants(); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionAllProcesses() { + noPermissionsSetup(); ProcessHandle.allProcesses(); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionChildren() { + noPermissionsSetup(); currentHndl.children(); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionCurrent() { + noPermissionsSetup(); ProcessHandle.current(); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionOf() { + noPermissionsSetup(); ProcessHandle.of(0); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionParent() { + noPermissionsSetup(); currentHndl.parent(); } - @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class) + @Test(expectedExceptions = SecurityException.class) public void noPermissionProcessToHandle() throws IOException { + noPermissionsSetup(); Process p = null; try { ProcessBuilder pb = new ProcessBuilder("sleep", "30"); From 531c56ea65091d6a77899b6c816e68a0dc5adc64 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Thu, 12 Nov 2020 21:37:30 +0000 Subject: [PATCH 093/124] 8256278: Shenandoah: Avoid num of dead callback from weak processor in Shenandoah root verifier Reviewed-by: rkennke, shade --- src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp index b1f6d175cd908..b986ff7f4aadf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRootVerifier.cpp @@ -36,7 +36,6 @@ #include "gc/shenandoah/shenandoahUtils.hpp" #include "gc/shared/oopStorage.inline.hpp" #include "gc/shared/oopStorageSet.hpp" -#include "gc/shared/weakProcessor.inline.hpp" #include "runtime/thread.hpp" #include "utilities/debug.hpp" @@ -100,8 +99,8 @@ void ShenandoahRootVerifier::oops_do(OopClosure* oops) { if (verify(WeakRoots)) { shenandoah_assert_safepoint(); - AlwaysTrueClosure always_true; - WeakProcessor::weak_oops_do(&always_true, oops); + serial_weak_roots_do(oops); + concurrent_weak_roots_do(oops); } else if (verify(SerialWeakRoots)) { shenandoah_assert_safepoint(); serial_weak_roots_do(oops); From dff26a489dbc1cf6eddd83e310cb34d0f0a77dd2 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Fri, 13 Nov 2020 00:32:29 +0000 Subject: [PATCH 094/124] 8256063: Module::getPackages on an unnamed module may return packages that are in a named module Reviewed-by: alanb, chegar --- .../share/classes/java/lang/Module.java | 10 ++-- .../lang/module/Packages/GetPackagesTest.java | 58 +++++++++++++++++++ .../lang/module/Packages/m/module-info.java | 26 +++++++++ .../java/lang/module/Packages/m/p/Main.java | 33 +++++++++++ 4 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 test/jdk/java/lang/module/Packages/GetPackagesTest.java create mode 100644 test/jdk/java/lang/module/Packages/m/module-info.java create mode 100644 test/jdk/java/lang/module/Packages/m/p/Main.java diff --git a/src/java.base/share/classes/java/lang/Module.java b/src/java.base/share/classes/java/lang/Module.java index c1452c098b6ba..e501094ca9328 100644 --- a/src/java.base/share/classes/java/lang/Module.java +++ b/src/java.base/share/classes/java/lang/Module.java @@ -1077,9 +1077,9 @@ public boolean canUse(Class service) { *

    For named modules, the returned set contains an element for each * package in the module.

    * - *

    For unnamed modules, this method is the equivalent to invoking the - * {@link ClassLoader#getDefinedPackages() getDefinedPackages} method of - * this module's class loader and returning the set of package names.

    + *

    For unnamed modules, the returned set contains an element for + * each package that {@link ClassLoader#getDefinedPackages() has been defined} + * in the unnamed module.

    * * @return the set of the package names of the packages in this module */ @@ -1094,11 +1094,11 @@ public Set getPackages() { } else { packages = loader.packages(); } - return packages.map(Package::getName).collect(Collectors.toSet()); + return packages.filter(p -> p.module() == this) + .map(Package::getName).collect(Collectors.toSet()); } } - // -- creating Module objects -- /** diff --git a/test/jdk/java/lang/module/Packages/GetPackagesTest.java b/test/jdk/java/lang/module/Packages/GetPackagesTest.java new file mode 100644 index 0000000000000..4d156dcec3cca --- /dev/null +++ b/test/jdk/java/lang/module/Packages/GetPackagesTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @library /test/lib + * @build m/* + * @run main/othervm --add-modules m test.GetPackagesTest + * @summary test the packages returned by Module::getPackages for an unnamed + * module does not include the packages for named modules + */ + +package test; + +import java.util.Set; +import static jdk.test.lib.Asserts.*; + +public class GetPackagesTest { + public static void main(String... args) throws Exception { + // module m contains the package "p" + Class c = Class.forName("p.Main"); + Module m = c.getModule(); + Module test = GetPackagesTest.class.getModule(); + + // module m and unnamed module are defined by the same class loader + assertTrue(m.isNamed()); + assertFalse(test.isNamed()); + assertTrue(m.getClassLoader() == test.getClassLoader()); + + // verify Module::getPackages on an unnamed module only contains + // the packages defined to the unnamed module + assertEquals(m.getPackages(), Set.of("p")); + + Set pkgs = test.getPackages(); + assertTrue(pkgs.contains("test")); + assertFalse(pkgs.contains("p")); + } +} diff --git a/test/jdk/java/lang/module/Packages/m/module-info.java b/test/jdk/java/lang/module/Packages/m/module-info.java new file mode 100644 index 0000000000000..eac015b12037a --- /dev/null +++ b/test/jdk/java/lang/module/Packages/m/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m { + exports p; +} diff --git a/test/jdk/java/lang/module/Packages/m/p/Main.java b/test/jdk/java/lang/module/Packages/m/p/Main.java new file mode 100644 index 0000000000000..1063494867f16 --- /dev/null +++ b/test/jdk/java/lang/module/Packages/m/p/Main.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package p; + +/** + * Main class for dummy module. + */ + +public class Main { + public static void main(String[] args) { + } +} From e32a4ea4efc284eb004201a6cb77eb0404460bfd Mon Sep 17 00:00:00 2001 From: Alexandre Iline Date: Fri, 13 Nov 2020 02:27:32 +0000 Subject: [PATCH 095/124] 8253820: Save test images and dumps with timestamps from client sanity suite Reviewed-by: serb --- .../src/ButtonDemoScreenshotTest.java | 41 ++---- .../SwingSet/src/EditorPaneDemoTest.java | 5 +- .../src/org/jemmy2ext/JemmyExt.java | 135 +++++++++--------- 3 files changed, 86 insertions(+), 95 deletions(-) diff --git a/test/jdk/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java b/test/jdk/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java index a440abcb1ff39..0bb0044f392d9 100644 --- a/test/jdk/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java +++ b/test/jdk/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java @@ -22,22 +22,16 @@ */ import com.sun.swingset3.demos.button.ButtonDemo; -import org.jemmy2ext.JemmyExt; import org.jtregext.GuiTestListener; import org.netbeans.jemmy.ClassReference; -import org.netbeans.jemmy.ComponentChooser; -import org.netbeans.jemmy.image.StrictImageComparator; import org.netbeans.jemmy.operators.JButtonOperator; import org.netbeans.jemmy.operators.JFrameOperator; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import java.awt.Component; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; -import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; @@ -65,25 +59,18 @@ public class ButtonDemoScreenshotTest { private static final int[] BUTTONS = {0, 1, 2, 3, 4, 5}; // "open browser" buttons (6, 7) open a browser, so ignore - private static StrictImageComparator sComparator = null; - - @BeforeClass - public void init() { - sComparator = new StrictImageComparator(); - } @Test(dataProvider = "availableLookAndFeels", dataProviderClass = TestHelpers.class) public void test(String lookAndFeel) throws Exception { UIManager.setLookAndFeel(lookAndFeel); - Robot rob = new Robot(); //capture some of the background Dimension screeSize = Toolkit.getDefaultToolkit().getScreenSize(); Point screenCenter = new Point(screeSize.width / 2, screeSize.height / 2); Rectangle center = new Rectangle( screenCenter.x - 50, screenCenter.y - 50, - screenCenter.x + 50, screenCenter.y + 50); - BufferedImage background = rob.createScreenCapture(center); + 100, 100); + BufferedImage background = getRobot().createScreenCapture(center); new ClassReference(ButtonDemo.class.getCanonicalName()).startApplication(); @@ -91,18 +78,18 @@ public void test(String lookAndFeel) throws Exception { mainFrame.waitComponentShowing(true); //make sure the frame is already painted - waitChangedImage(rob, () -> rob.createScreenCapture(center), - background, mainFrame.getTimeouts(), "background.png"); + waitChangedImage(() -> getRobot().createScreenCapture(center), + background, mainFrame.getTimeouts(), "background"); //make sure the frame is painted completely - waitStillImage(rob, mainFrame, "frame.png"); + waitStillImage(mainFrame, "frame"); // Check all the buttons for (int i : BUTTONS) { - checkButton(mainFrame, i, rob); + checkButton(mainFrame, i); } } - private void checkButton(JFrameOperator jfo, int i, Robot rob) throws InterruptedException { + private void checkButton(JFrameOperator jfo, int i) throws InterruptedException { JButtonOperator button = new JButtonOperator(jfo, i); //additional instrumentation for JDK-8198920. To be removed after the bug is fixed @@ -113,9 +100,7 @@ private void checkButton(JFrameOperator jfo, int i, Robot rob) throws Interrupte button.moveMouse(button.getCenterX(), button.getCenterY()); BufferedImage notPressed, pressed = null; - notPressed = waitStillImage(rob, button, "not-pressed-" + i + ".png"); - - BufferedImage[] pressedImage = new BufferedImage[1]; + notPressed = waitStillImage(button, "not-pressed-" + i); button.pressMouse(); //additional instrumentation for JDK-8198920. To be removed after the bug is fixed @@ -126,14 +111,14 @@ private void checkButton(JFrameOperator jfo, int i, Robot rob) throws Interrupte //additional instrumentation for JDK-8198920. To be removed after the bug is fixed button.getOutput().printTrace("JDK-8198920: Button press confirmed by " + System.currentTimeMillis()); //end of instrumentation for JDK-8198920 - waitChangedImage(rob, () -> capture(rob, button), notPressed, - button.getTimeouts(), "pressed-" + i + ".png"); - pressed = waitStillImage(rob, button, "pressed.png"); + waitChangedImage(() -> capture(button), notPressed, + button.getTimeouts(), "after-press-" + i); + pressed = waitStillImage(button, "pressed-" + i); } finally { button.releaseMouse(); if(pressed != null) { - waitChangedImage(rob, () -> capture(rob, button), pressed, - button.getTimeouts(), "released-" + i + ".png"); + waitChangedImage(() -> capture(button), pressed, + button.getTimeouts(), "released-" + i); } //additional instrumentation for JDK-8198920. To be removed after the bug is fixed button.getOutput().printTrace("JDK-8198920: Button released at " + System.currentTimeMillis()); diff --git a/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java b/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java index 268ea254eb35a..b769c6d826878 100644 --- a/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java +++ b/test/jdk/sanity/client/SwingSet/src/EditorPaneDemoTest.java @@ -149,7 +149,7 @@ private void checkImage(JEditorPaneOperator editorPaneOperator, final int xGap = 100, yGap = 40, columns = 2, rows = 5; editorPaneOperator.waitState(comp -> { BufferedImage capturedImage = ImageTool.getImage(imageRect); - save(capturedImage, "editor.png"); + save(capturedImage, "editor"); assertFalse(isBlack(capturedImage), "image blackness"); int x = 0, y = 0, i = 0, j; for (; i < columns; i++) { @@ -159,8 +159,7 @@ private void checkImage(JEditorPaneOperator editorPaneOperator, y += yGap; if(capturedImage.getRGB(x, y) == Color.WHITE.getRGB()) { // saving image for failure case - JemmyExt.save(capturedImage, "capturedimage_" + pageName + "_" + - UIManager.getLookAndFeel().getClass().getSimpleName() + ".png"); + save(capturedImage, "capturedimage-" + pageName); return false; } } diff --git a/test/jdk/sanity/client/lib/Extensions/src/org/jemmy2ext/JemmyExt.java b/test/jdk/sanity/client/lib/Extensions/src/org/jemmy2ext/JemmyExt.java index 948e1541fcad4..a443ccc26ff5b 100644 --- a/test/jdk/sanity/client/lib/Extensions/src/org/jemmy2ext/JemmyExt.java +++ b/test/jdk/sanity/client/lib/Extensions/src/org/jemmy2ext/JemmyExt.java @@ -22,12 +22,14 @@ */ package org.jemmy2ext; +import java.awt.AWTException; import java.awt.Component; import java.awt.EventQueue; import java.awt.Frame; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Robot; +import java.awt.Toolkit; import java.awt.Window; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; @@ -36,8 +38,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; @@ -83,6 +88,19 @@ */ public class JemmyExt { + private static Robot robot = null; + + public static Robot getRobot() { + try { + if(robot == null) { + robot = new Robot(); + } + return robot; + } catch (AWTException e) { + throw new RuntimeException(e); + } + } + /** * Statically referencing all the classes that are needed by tests so that * they're compiled by jtreg @@ -151,78 +169,62 @@ public Boolean launch() throws Exception { }); } - public static void assertEquals(String string, StrictImageComparator comparator, BufferedImage expected, BufferedImage actual) { - try { - assertTrue(string, comparator.compare(expected, actual)); - } catch (Error err) { - save(expected, "expected.png"); - save(actual, "actual.png"); - throw err; - } + private static final DateFormat timestampFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + private static String timeStamp() { + return timestampFormat.format(new Date()); } - public static void assertNotEquals(String string, StrictImageComparator comparator, BufferedImage notExpected, BufferedImage actual) { - try { - assertFalse(string, comparator.compare(notExpected, actual)); - } catch (Error err) { - save(notExpected, "notExpected.png"); - save(actual, "actual.png"); - throw err; - } + /** + * Constructs filename with a timestamp. + * @param name File name or a path without the extension + * @param extension File extension (without the dot). Could be null, + * in which case timestamp is simply added to the filename (no trailing dot). + * @return file name + */ + public static String timeStamp(String name, String extension) { + return name + "-" + timeStamp() + + ((extension != null) ? ("." + extension) : ""); + } + + /** + * Saves an image into a file. Filename will be constructed from the given fileID and + * a timestamp. + * @param image + * @param fileID + */ + public static void save(BufferedImage image, String fileID) { + doSave(image, timeStamp(fileID + "-" + lafShortName(), "png")); } - public static void save(BufferedImage image, String filename) { - String filepath = filename; + //Saves an image into a file with the provided filename + private static void doSave(BufferedImage image, String filename) { try { - filepath = new File(filename).getCanonicalPath(); + String filepath = new File(filename).getCanonicalPath(); System.out.println("Saving screenshot to " + filepath); BufferedOutputStream file = new BufferedOutputStream(new FileOutputStream(filepath)); new PNGEncoder(file, PNGEncoder.COLOR_MODE).encode(image); } catch (IOException ioe) { - throw new RuntimeException("Failed to save image to " + filepath, ioe); + throw new RuntimeException("Failed to save image to " + filename, ioe); } } - /** - * Waits for a screen area taken by a component to not be completely black rectangle. - * @return last (non-black) image - * @throws TimeoutExpiredException if the waiting is unsuccessful - */ - public static BufferedImage waitNotBlack(Robot rob, ComponentOperator operator, String imageName) { - class NonBlackImageChooser implements ComponentChooser { - private BufferedImage image = null; - @Override - public boolean checkComponent(Component comp) { - image = capture(rob, operator); - save(image, imageName); - return !isBlack(image); - } - - @Override - public String getDescription() { - return "A non-black Image of " + operator; - } - } - NonBlackImageChooser chooser = new NonBlackImageChooser(); - operator.waitState(chooser); - return chooser.image; - } - /** * Waits for the displayed image to be still. + * @param imageID an image ID with no extension. Timestamp and LAF information is added to the ID when saving. * @return last still image * @throws TimeoutExpiredException if the waiting is unsuccessful */ - public static BufferedImage waitStillImage(Robot rob, ComponentOperator operator, String imageName) { + public static BufferedImage waitStillImage(ComponentOperator operator, String imageID) { operator.getTimeouts().setTimeout("Waiter.TimeDelta", 1000); + String timestampName = timeStamp(imageID + "-" + lafShortName(), "png"); class StillImageChooser implements ComponentChooser { private BufferedImage previousImage = null; private final StrictImageComparator sComparator = new StrictImageComparator(); @Override public boolean checkComponent(Component comp) { - BufferedImage currentImage = capture(rob, operator); - save(currentImage, imageName); + BufferedImage currentImage = capture(operator); + doSave(currentImage, timestampName); boolean compareResult = previousImage == null ? false : sComparator.compare(currentImage, previousImage); previousImage = currentImage; return compareResult; @@ -241,22 +243,23 @@ public String getDescription() { /** * Waits for the displayed image to change. * @param reference image to compare to + * @param imageID an image ID with no extension. Timestamp and LAF information is added to the ID when saving. * @return last (changed) image * @throws TimeoutExpiredException if the waiting is unsuccessful */ - public static BufferedImage waitChangedImage(Robot rob, - Supplier supplier, + public static BufferedImage waitChangedImage(Supplier supplier, BufferedImage reference, Timeouts timeouts, - String imageName) throws InterruptedException { + String imageID) throws InterruptedException { ImageComparator comparator = new StrictImageComparator(); + String timestampName = timeStamp(imageID + "-" + lafShortName(), "png"); class ImageWaitable implements Waitable { BufferedImage image; @Override public Object actionProduced(Object obj) { image = supplier.get(); - save(image, imageName); + doSave(image, timestampName); return comparator.compare(reference, image) ? null : image; } @@ -316,10 +319,10 @@ public String getDescription() { } } - public static BufferedImage capture(Robot rob, ComponentOperator operator) { + public static BufferedImage capture(ComponentOperator operator) { Rectangle boundary = new Rectangle(operator.getLocationOnScreen(), operator.getSize()); - return rob.createScreenCapture(boundary); + return getRobot().createScreenCapture(boundary); } /** @@ -400,26 +403,26 @@ public void registerRoot(Throwable t) { } } + private static String lafShortName() { return UIManager.getLookAndFeel().getClass().getSimpleName(); } + /** * Trying to capture as much information as possible. Currently it includes * full dump and a screenshot of the whole screen. */ public static void captureAll() { - String lookAndFeelClassName = UIManager.getLookAndFeel().getClass().getSimpleName(); - PNGEncoder.captureScreen("failure_" + lookAndFeelClassName + ".png", PNGEncoder.COLOR_MODE); + save(getRobot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())), "failure"); try { - Dumper.dumpAll("dumpAll_" + lookAndFeelClassName + ".xml"); + Dumper.dumpAll(timeStamp("dumpAll-" + lafShortName(), "xml")); } catch (FileNotFoundException ex) { Logger.getLogger(JemmyExt.class.getName()).log(Level.SEVERE, null, ex); } - captureWindows(lookAndFeelClassName); + captureWindows(); } /** * Captures each showing window image using Window.paint() method. - * @param lookAndFeelClassName */ - private static void captureWindows(String lookAndFeelClassName) { + private static void captureWindows() { try { EventQueue.invokeAndWait(() -> { Window[] windows = Window.getWindows(); @@ -434,10 +437,14 @@ private static void captureWindows(String lookAndFeelClassName) { g.dispose(); try { - ImageIO.write(img, "png", new File("window_" + lookAndFeelClassName - + "_" + index++ + ".png")); - } catch (IOException e) { - e.printStackTrace(); + save(img, "window-" + index++); + } catch (RuntimeException e) { + if (e.getCause() instanceof IOException) { + System.err.println("Failed to save screen images"); + e.printStackTrace(); + } else { + throw e; + } } } }); From 1d3d64f34c6ca792322bb9f0754b9b1b7e2de51c Mon Sep 17 00:00:00 2001 From: Yumin Qi Date: Fri, 13 Nov 2020 03:44:41 +0000 Subject: [PATCH 096/124] 8255973: Add more logging to debug JDK-8255917 Reviewed-by: ccheung, stuefe, iklam --- src/hotspot/share/logging/logTag.hpp | 1 + src/hotspot/share/runtime/os.cpp | 3 ++ .../share/services/virtualMemoryTracker.cpp | 44 +++++++++++++++---- .../share/services/virtualMemoryTracker.hpp | 2 + .../jtreg/runtime/cds/SharedBaseAddress.java | 2 + 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp index e2d92ce20de99..fea94d89d2d37 100644 --- a/src/hotspot/share/logging/logTag.hpp +++ b/src/hotspot/share/logging/logTag.hpp @@ -118,6 +118,7 @@ LOG_TAG(monitormismatch) \ LOG_TAG(nestmates) \ LOG_TAG(nmethod) \ + LOG_TAG(nmt) \ LOG_TAG(normalize) \ LOG_TAG(numa) \ LOG_TAG(objecttagging) \ diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp index bca50071ef976..7d807b2025787 100644 --- a/src/hotspot/share/runtime/os.cpp +++ b/src/hotspot/share/runtime/os.cpp @@ -1668,6 +1668,9 @@ char* os::attempt_reserve_memory_at(char* addr, size_t bytes) { char* result = pd_attempt_reserve_memory_at(addr, bytes); if (result != NULL) { MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); + } else { + log_debug(os)("Attempt to reserve memory at " INTPTR_FORMAT " for " + SIZE_FORMAT " bytes failed, errno %d", p2i(addr), bytes, get_last_error()); } return result; } diff --git a/src/hotspot/share/services/virtualMemoryTracker.cpp b/src/hotspot/share/services/virtualMemoryTracker.cpp index cc98bfa1efe0e..930fb54300f1d 100644 --- a/src/hotspot/share/services/virtualMemoryTracker.cpp +++ b/src/hotspot/share/services/virtualMemoryTracker.cpp @@ -290,7 +290,7 @@ size_t ReservedMemoryRegion::committed_size() const { void ReservedMemoryRegion::set_flag(MEMFLAGS f) { assert((flag() == mtNone || flag() == f), - "Overwrite memory type for region [" PTR_FORMAT "-" PTR_FORMAT "), %u->%u.", + "Overwrite memory type for region [" INTPTR_FORMAT "-" INTPTR_FORMAT "), %u->%u.", p2i(base()), p2i(end()), (unsigned)flag(), (unsigned)f); if (flag() != f) { VirtualMemorySummary::move_reserved_memory(flag(), f, size()); @@ -343,6 +343,8 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, ReservedMemoryRegion rgn(base_addr, size, stack, flag); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + log_debug(nmt)("Add reserved region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ")", + rgn.flag_name(), p2i(rgn.base()), rgn.size()); if (reserved_rgn == NULL) { VirtualMemorySummary::record_reserved_memory(size, flag); return _reserved_regions->add(rgn) != NULL; @@ -381,6 +383,8 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, // CDS reserves the whole region for mapping CDS archive, then maps each section into the region. // NMT reports CDS as a whole. if (reserved_rgn->flag() == mtClassShared) { + log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", " SIZE_FORMAT ")", + reserved_rgn->flag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); assert(reserved_rgn->contain_region(base_addr, size), "Reserved CDS region should contain this mapping region"); return true; } @@ -388,14 +392,16 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size, // Mapped CDS string region. // The string region(s) is part of the java heap. if (reserved_rgn->flag() == mtJavaHeap) { + log_debug(nmt)("CDS reserved region \'%s\' as a whole (" INTPTR_FORMAT ", " SIZE_FORMAT ")", + reserved_rgn->flag_name(), p2i(reserved_rgn->base()), reserved_rgn->size()); assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region"); return true; } // Print some more details. Don't use UL here to avoid circularities. #ifdef ASSERT - tty->print_cr("Error: existing region: [" PTR_FORMAT "-" PTR_FORMAT "), flag %u.\n" - " new region: [" PTR_FORMAT "-" PTR_FORMAT "), flag %u.", + tty->print_cr("Error: existing region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), flag %u.\n" + " new region: [" INTPTR_FORMAT "-" INTPTR_FORMAT "), flag %u.", p2i(reserved_rgn->base()), p2i(reserved_rgn->end()), (unsigned)reserved_rgn->flag(), p2i(base_addr), p2i(base_addr + size), (unsigned)flag); #endif @@ -430,9 +436,15 @@ bool VirtualMemoryTracker::add_committed_region(address addr, size_t size, ReservedMemoryRegion rgn(addr, size); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); - assert(reserved_rgn != NULL, "No reserved region"); + if (reserved_rgn == NULL) { + log_debug(nmt)("Add committed region \'%s\', No reserved region found for (" INTPTR_FORMAT ", " SIZE_FORMAT ")", + rgn.flag_name(), p2i(rgn.base()), rgn.size()); + } + assert(reserved_rgn != NULL, "Add committed region, No reserved region found"); assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); bool result = reserved_rgn->add_committed_region(addr, size, stack); + log_debug(nmt)("Add committed region \'%s\'(" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", + rgn.flag_name(), p2i(rgn.base()), rgn.size(), (result ? "Succeeded" : "Failed")); return result; } @@ -443,9 +455,12 @@ bool VirtualMemoryTracker::remove_uncommitted_region(address addr, size_t size) ReservedMemoryRegion rgn(addr, size); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); - assert(reserved_rgn != NULL, "No reserved region"); + assert(reserved_rgn != NULL, "No reserved region (" INTPTR_FORMAT ", " SIZE_FORMAT ")", p2i(addr), size); assert(reserved_rgn->contain_region(addr, size), "Not completely contained"); + const char* flag_name = reserved_rgn->flag_name(); // after remove, info is not complete bool result = reserved_rgn->remove_uncommitted_region(addr, size); + log_debug(nmt)("Removed uncommitted region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", + flag_name, p2i(addr), size, (result ? " Succeeded" : "Failed")); return result; } @@ -454,12 +469,19 @@ bool VirtualMemoryTracker::remove_released_region(ReservedMemoryRegion* rgn) { assert(_reserved_regions != NULL, "Sanity check"); // uncommit regions within the released region - if (!rgn->remove_uncommitted_region(rgn->base(), rgn->size())) { + ReservedMemoryRegion backup(*rgn); + bool result = rgn->remove_uncommitted_region(rgn->base(), rgn->size()); + log_debug(nmt)("Remove uncommitted region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") %s", + backup.flag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); + if (!result) { return false; } VirtualMemorySummary::record_released_memory(rgn->size(), rgn->flag()); - return _reserved_regions->remove(*rgn); + result = _reserved_regions->remove(*rgn); + log_debug(nmt)("Removed region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") from _resvered_regions %s" , + backup.flag_name(), p2i(backup.base()), backup.size(), (result ? "Succeeded" : "Failed")); + return result; } bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { @@ -470,6 +492,10 @@ bool VirtualMemoryTracker::remove_released_region(address addr, size_t size) { ReservedMemoryRegion rgn(addr, size); ReservedMemoryRegion* reserved_rgn = _reserved_regions->find(rgn); + if (reserved_rgn == NULL) { + log_debug(nmt)("No reserved region found for (" INTPTR_FORMAT ", " SIZE_FORMAT ")!", + p2i(rgn.base()), rgn.size()); + } assert(reserved_rgn != NULL, "No reserved region"); if (reserved_rgn->same_region(addr, size)) { return remove_released_region(reserved_rgn); @@ -527,8 +553,10 @@ bool VirtualMemoryTracker::split_reserved_region(address addr, size_t size, size NativeCallStack original_stack = *reserved_rgn->call_stack(); MEMFLAGS original_flags = reserved_rgn->flag(); + const char* name = reserved_rgn->flag_name(); remove_released_region(reserved_rgn); - + log_debug(nmt)("Split region \'%s\' (" INTPTR_FORMAT ", " SIZE_FORMAT ") with size " SIZE_FORMAT, + name, p2i(rgn.base()), rgn.size(), split); // Now, create two new regions. add_reserved_region(addr, split, original_stack, original_flags); add_reserved_region(addr + split, size - split, original_stack, original_flags); diff --git a/src/hotspot/share/services/virtualMemoryTracker.hpp b/src/hotspot/share/services/virtualMemoryTracker.hpp index 3e773a7d1799b..60fbc64988a53 100644 --- a/src/hotspot/share/services/virtualMemoryTracker.hpp +++ b/src/hotspot/share/services/virtualMemoryTracker.hpp @@ -340,6 +340,8 @@ class ReservedMemoryRegion : public VirtualMemoryRegion { return *this; } + const char* flag_name() { return NMTUtil::flag_to_name(_flag); } + private: // The committed region contains the uncommitted region, subtract the uncommitted // region from this committed region diff --git a/test/hotspot/jtreg/runtime/cds/SharedBaseAddress.java b/test/hotspot/jtreg/runtime/cds/SharedBaseAddress.java index 6f662038debb3..510cdee13f726 100644 --- a/test/hotspot/jtreg/runtime/cds/SharedBaseAddress.java +++ b/test/hotspot/jtreg/runtime/cds/SharedBaseAddress.java @@ -60,6 +60,8 @@ public static void main(String[] args) throws Exception { .addPrefix("-XX:SharedBaseAddress=" + testEntry) .addPrefix("-Xlog:cds=debug") .addPrefix("-Xlog:cds+reloc=debug") + .addPrefix("-Xlog:nmt=debug") + .addPrefix("-Xlog:os=debug") .addPrefix("-XX:NativeMemoryTracking=detail"); CDSTestUtils.createArchiveAndCheck(opts); From c3139abe3854a76c73f92a87b7d8f728562619ab Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 13 Nov 2020 07:39:29 +0000 Subject: [PATCH 097/124] 8256220: C1: x86_32 fails with -XX:UseSSE=1 after JDK-8210764 due to mishandled lir_neg Reviewed-by: chagedorn --- src/hotspot/cpu/x86/c1_LinearScan_x86.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/hotspot/cpu/x86/c1_LinearScan_x86.cpp b/src/hotspot/cpu/x86/c1_LinearScan_x86.cpp index 14172cee8fa26..a262c55aff4e0 100644 --- a/src/hotspot/cpu/x86/c1_LinearScan_x86.cpp +++ b/src/hotspot/cpu/x86/c1_LinearScan_x86.cpp @@ -550,21 +550,6 @@ void FpuStackAllocator::handle_op1(LIR_Op1* op1) { break; } - case lir_neg: { - if (in->is_fpu_register() && !in->is_xmm_register()) { - assert(res->is_fpu_register() && !res->is_xmm_register(), "must be"); - assert(in->is_last_use(), "old value gets destroyed"); - - insert_free_if_dead(res, in); - insert_exchange(in); - new_in = to_fpu_stack_top(in); - - do_rename(in, res); - new_res = to_fpu_stack_top(res); - } - break; - } - case lir_convert: { Bytecodes::Code bc = op1->as_OpConvert()->bytecode(); switch (bc) { @@ -772,7 +757,8 @@ void FpuStackAllocator::handle_op2(LIR_Op2* op2) { } case lir_abs: - case lir_sqrt: { + case lir_sqrt: + case lir_neg: { // Right argument appears to be unused assert(right->is_illegal(), "must be"); assert(left->is_fpu_register(), "must be"); From 05b824567c346a7d136c01d23f56a908e7efc6d7 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 13 Nov 2020 07:40:19 +0000 Subject: [PATCH 098/124] 8256290: javac/lambda/T8031967.java fails with StackOverflowError on x86_32 Reviewed-by: mcimadamore --- test/langtools/tools/javac/lambda/T8031967.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/langtools/tools/javac/lambda/T8031967.java b/test/langtools/tools/javac/lambda/T8031967.java index 2a6a084902c32..06d75daf91408 100644 --- a/test/langtools/tools/javac/lambda/T8031967.java +++ b/test/langtools/tools/javac/lambda/T8031967.java @@ -27,7 +27,7 @@ * @summary Ensure javac can handle very deeply nested chain of method invocations occurring as * a parameter to other method invocations. * @modules jdk.compiler - * @run main T8031967 + * @run main/othervm -Xss1m T8031967 */ import java.io.IOException; From ea576ddbd4c8ad5815c29466fa9c51c258bcc0df Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Fri, 13 Nov 2020 08:19:33 +0000 Subject: [PATCH 099/124] 8254887: C2: assert(cl->trip_count() > 0) failed: peeling a fully unrolled loop Reviewed-by: chagedorn, thartmann --- src/hotspot/share/opto/loopTransform.cpp | 3 +- .../loopopts/TestPeelingNeverEnteredLoop.java | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestPeelingNeverEnteredLoop.java diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 8ab78ce620d32..d41b48a5a3abf 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -1064,7 +1064,8 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop *phase) const { Node *trip_counter = cl->phi(); // check for vectorized loops, some opts are no longer needed - if (cl->is_unroll_only()) return false; + // RCE needs pre/main/post loops. Don't apply it on a single iteration loop. + if (cl->is_unroll_only() || (cl->is_normal_loop() && cl->trip_count() == 1)) return false; // Check loop body for tests of trip-counter plus loop-invariant vs // loop-invariant. diff --git a/test/hotspot/jtreg/compiler/loopopts/TestPeelingNeverEnteredLoop.java b/test/hotspot/jtreg/compiler/loopopts/TestPeelingNeverEnteredLoop.java new file mode 100644 index 0000000000000..31a214464c62a --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestPeelingNeverEnteredLoop.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8254887 + * @summary C2: assert(cl->trip_count() > 0) failed: peeling a fully unrolled loop + * + * @run main/othervm -Xbatch TestPeelingNeverEnteredLoop + * + */ + +public class TestPeelingNeverEnteredLoop { + + public static final int N = 400; + + public static byte byFld=83; + + public static void lMeth() { + + int iArr1[][]=new int[N][N]; + byte byArr[][]=new byte[N][N]; + + int i10 = 1; + do { + int i11 = 1; + do { + iArr1[i10 - 1][i11] = TestPeelingNeverEnteredLoop.byFld; + byArr[i10][i11] -= (byte)-20046; + for (int i12 = 1; 1 > i12; ++i12) { + } + } while (++i11 < 8); + } while (++i10 < 212); + } + + public static void main(String[] strArr) { + TestPeelingNeverEnteredLoop _instance = new TestPeelingNeverEnteredLoop(); + for (int i = 0; i < 1500; i++ ) { + _instance.lMeth(); + } + } +} From b4d0186718c617ebf39333dff6824526612f9247 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 13 Nov 2020 08:20:11 +0000 Subject: [PATCH 100/124] 8253525: Implement getInstanceSize/sizeOf intrinsics Reviewed-by: kvn, sspitsyn --- src/hotspot/share/c1/c1_Compiler.cpp | 2 + src/hotspot/share/c1/c1_LIRGenerator.cpp | 103 +++++ src/hotspot/share/c1/c1_LIRGenerator.hpp | 1 + src/hotspot/share/classfile/vmIntrinsics.hpp | 4 + src/hotspot/share/classfile/vmSymbols.hpp | 2 + src/hotspot/share/opto/c2compiler.cpp | 1 + src/hotspot/share/opto/library_call.cpp | 119 +++++ src/hotspot/share/opto/library_call.hpp | 2 + src/java.base/share/classes/module-info.java | 1 + .../sun/instrument/InstrumentationImpl.java | 2 + .../hotspot/test/CheckGraalIntrinsics.java | 5 + .../GetObjectSizeIntrinsicsTest.java | 413 ++++++++++++++++++ 12 files changed, 655 insertions(+) create mode 100644 test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index dbfcc610daf87..f245594c5a689 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -228,6 +228,8 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) { #endif #endif break; + case vmIntrinsics::_getObjectSize: + break; default: return false; // Intrinsics not on the previous list are not available. } diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index c9da98aab1823..432b1a47bd0a7 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1323,6 +1323,108 @@ void LIRGenerator::do_currentThread(Intrinsic* x) { LIR_OprFact::address(new LIR_Address(temp, T_OBJECT)), reg); } +void LIRGenerator::do_getObjectSize(Intrinsic* x) { + assert(x->number_of_arguments() == 3, "wrong type"); + LIR_Opr result_reg = rlock_result(x); + + LIRItem value(x->argument_at(2), this); + value.load_item(); + + LIR_Opr klass = new_register(T_METADATA); + __ move(new LIR_Address(value.result(), oopDesc::klass_offset_in_bytes(), T_ADDRESS), klass, NULL); + LIR_Opr layout = new_register(T_INT); + __ move(new LIR_Address(klass, in_bytes(Klass::layout_helper_offset()), T_INT), layout); + + LabelObj* L_done = new LabelObj(); + LabelObj* L_array = new LabelObj(); + + __ cmp(lir_cond_lessEqual, layout, 0); + __ branch(lir_cond_lessEqual, L_array->label()); + + // Instance case: the layout helper gives us instance size almost directly, + // but we need to mask out the _lh_instance_slow_path_bit. + __ convert(Bytecodes::_i2l, layout, result_reg); + + assert((int) Klass::_lh_instance_slow_path_bit < BytesPerLong, "clear bit"); + jlong mask = ~(jlong) right_n_bits(LogBytesPerLong); + __ logical_and(result_reg, LIR_OprFact::longConst(mask), result_reg); + + __ branch(lir_cond_always, L_done->label()); + + // Array case: size is round(header + element_size*arraylength). + // Since arraylength is different for every array instance, we have to + // compute the whole thing at runtime. + + __ branch_destination(L_array->label()); + + int round_mask = MinObjAlignmentInBytes - 1; + + // Figure out header sizes first. + LIR_Opr hss = LIR_OprFact::intConst(Klass::_lh_header_size_shift); + LIR_Opr hsm = LIR_OprFact::intConst(Klass::_lh_header_size_mask); + + LIR_Opr header_size = new_register(T_INT); + __ move(layout, header_size); + LIR_Opr tmp = new_register(T_INT); + __ unsigned_shift_right(header_size, hss, header_size, tmp); + __ logical_and(header_size, hsm, header_size); + __ add(header_size, LIR_OprFact::intConst(round_mask), header_size); + + // Figure out the array length in bytes + assert(Klass::_lh_log2_element_size_shift == 0, "use shift in place"); + LIR_Opr l2esm = LIR_OprFact::intConst(Klass::_lh_log2_element_size_mask); + __ logical_and(layout, l2esm, layout); + + LIR_Opr length_int = new_register(T_INT); + __ move(new LIR_Address(value.result(), arrayOopDesc::length_offset_in_bytes(), T_INT), length_int); + +#ifdef _LP64 + LIR_Opr length = new_register(T_LONG); + __ convert(Bytecodes::_i2l, length_int, length); +#endif + + // Shift-left awkwardness. Normally it is just: + // __ shift_left(length, layout, length); + // But C1 cannot perform shift_left with non-constant count, so we end up + // doing the per-bit loop dance here. x86_32 also does not know how to shift + // longs, so we have to act on ints. + LabelObj* L_shift_loop = new LabelObj(); + LabelObj* L_shift_exit = new LabelObj(); + + __ branch_destination(L_shift_loop->label()); + __ cmp(lir_cond_equal, layout, 0); + __ branch(lir_cond_equal, L_shift_exit->label()); + +#ifdef _LP64 + __ shift_left(length, 1, length); +#else + __ shift_left(length_int, 1, length_int); +#endif + + __ sub(layout, LIR_OprFact::intConst(1), layout); + + __ branch(lir_cond_always, L_shift_loop->label()); + __ branch_destination(L_shift_exit->label()); + + // Mix all up, round, and push to the result. +#ifdef _LP64 + LIR_Opr header_size_long = new_register(T_LONG); + __ convert(Bytecodes::_i2l, header_size, header_size_long); + __ add(length, header_size_long, length); + if (round_mask != 0) { + __ logical_and(length, LIR_OprFact::longConst(~round_mask), length); + } + __ move(length, result_reg); +#else + __ add(length_int, header_size, length_int); + if (round_mask != 0) { + __ logical_and(length_int, LIR_OprFact::intConst(~round_mask), length_int); + } + __ convert(Bytecodes::_i2l, length_int, result_reg); +#endif + + __ branch_destination(L_done->label()); +} void LIRGenerator::do_RegisterFinalizer(Intrinsic* x) { assert(x->number_of_arguments() == 1, "wrong type"); @@ -3044,6 +3146,7 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) { case vmIntrinsics::_isPrimitive: do_isPrimitive(x); break; case vmIntrinsics::_getClass: do_getClass(x); break; case vmIntrinsics::_currentThread: do_currentThread(x); break; + case vmIntrinsics::_getObjectSize: do_getObjectSize(x); break; case vmIntrinsics::_dlog: // fall through case vmIntrinsics::_dlog10: // fall through diff --git a/src/hotspot/share/c1/c1_LIRGenerator.hpp b/src/hotspot/share/c1/c1_LIRGenerator.hpp index 9908d916ef1de..f1b89e542fc02 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.hpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.hpp @@ -253,6 +253,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure { void do_isPrimitive(Intrinsic* x); void do_getClass(Intrinsic* x); void do_currentThread(Intrinsic* x); + void do_getObjectSize(Intrinsic* x); void do_FmaIntrinsic(Intrinsic* x); void do_MathIntrinsic(Intrinsic* x); void do_LibmIntrinsic(Intrinsic* x); diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index b76bc161782dd..f165adf39fcf4 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -524,6 +524,10 @@ class methodHandle; do_name( isCompileConstant_name, "isCompileConstant") \ do_alias( isCompileConstant_signature, object_boolean_signature) \ \ + do_intrinsic(_getObjectSize, sun_instrument_InstrumentationImpl, getObjectSize_name, getObjectSize_signature, F_RN) \ + do_name( getObjectSize_name, "getObjectSize0") \ + do_alias( getObjectSize_signature, long_object_long_signature) \ + \ /* unsafe memory references (there are a lot of them...) */ \ do_signature(getReference_signature, "(Ljava/lang/Object;J)Ljava/lang/Object;") \ do_signature(putReference_signature, "(Ljava/lang/Object;JLjava/lang/Object;)V") \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp index d7f81ff966d53..49665ecb46140 100644 --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -139,6 +139,7 @@ template(sun_net_www_ParseUtil, "sun/net/www/ParseUtil") \ template(java_util_Iterator, "java/util/Iterator") \ template(java_lang_Record, "java/lang/Record") \ + template(sun_instrument_InstrumentationImpl, "sun/instrument/InstrumentationImpl") \ \ template(jdk_internal_loader_NativeLibraries, "jdk/internal/loader/NativeLibraries") \ template(jdk_internal_loader_BuiltinClassLoader, "jdk/internal/loader/BuiltinClassLoader") \ @@ -521,6 +522,7 @@ template(int_array_signature, "[I") \ template(object_void_signature, "(Ljava/lang/Object;)V") \ template(object_int_signature, "(Ljava/lang/Object;)I") \ + template(long_object_long_signature, "(JLjava/lang/Object;)J") \ template(object_boolean_signature, "(Ljava/lang/Object;)Z") \ template(object_object_signature, "(Ljava/lang/Object;)Ljava/lang/Object;") \ template(string_void_signature, "(Ljava/lang/String;)V") \ diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp index 34fc0a257d116..c4af0c9981ed7 100644 --- a/src/hotspot/share/opto/c2compiler.cpp +++ b/src/hotspot/share/opto/c2compiler.cpp @@ -649,6 +649,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt case vmIntrinsics::_profileBoolean: case vmIntrinsics::_isCompileConstant: case vmIntrinsics::_Preconditions_checkIndex: + case vmIntrinsics::_getObjectSize: break; case vmIntrinsics::_VectorUnaryOp: diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 43028fdb56f56..5206e6220e556 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -661,6 +661,9 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_VectorExtract: return inline_vector_extract(); + case vmIntrinsics::_getObjectSize: + return inline_getObjectSize(); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -6692,3 +6695,119 @@ bool LibraryCallKit::inline_isCompileConstant() { set_result(n->is_Con() ? intcon(1) : intcon(0)); return true; } + +//------------------------------- inline_getObjectSize -------------------------------------- +// +// Calculate the runtime size of the object/array. +// native long sun.instrument.InstrumentationImpl.getObjectSize0(long nativeAgent, Object objectToSize); +// +bool LibraryCallKit::inline_getObjectSize() { + Node* obj = argument(3); + Node* klass_node = load_object_klass(obj); + + jint layout_con = Klass::_lh_neutral_value; + Node* layout_val = get_layout_helper(klass_node, layout_con); + int layout_is_con = (layout_val == NULL); + + if (layout_is_con) { + // Layout helper is constant, can figure out things at compile time. + + if (Klass::layout_helper_is_instance(layout_con)) { + // Instance case: layout_con contains the size itself. + Node *size = longcon(Klass::layout_helper_size_in_bytes(layout_con)); + set_result(size); + } else { + // Array case: size is round(header + element_size*arraylength). + // Since arraylength is different for every array instance, we have to + // compute the whole thing at runtime. + + Node* arr_length = load_array_length(obj); + + int round_mask = MinObjAlignmentInBytes - 1; + int hsize = Klass::layout_helper_header_size(layout_con); + int eshift = Klass::layout_helper_log2_element_size(layout_con); + + if ((round_mask & ~right_n_bits(eshift)) == 0) { + round_mask = 0; // strength-reduce it if it goes away completely + } + assert((hsize & right_n_bits(eshift)) == 0, "hsize is pre-rounded"); + Node* header_size = intcon(hsize + round_mask); + + Node* lengthx = ConvI2X(arr_length); + Node* headerx = ConvI2X(header_size); + + Node* abody = lengthx; + if (eshift != 0) { + abody = _gvn.transform(new LShiftXNode(lengthx, intcon(eshift))); + } + Node* size = _gvn.transform( new AddXNode(headerx, abody) ); + if (round_mask != 0) { + size = _gvn.transform( new AndXNode(size, MakeConX(~round_mask)) ); + } + size = ConvX2L(size); + set_result(size); + } + } else { + // Layout helper is not constant, need to test for array-ness at runtime. + + enum { _instance_path = 1, _array_path, PATH_LIMIT }; + RegionNode* result_reg = new RegionNode(PATH_LIMIT); + PhiNode* result_val = new PhiNode(result_reg, TypeLong::LONG); + record_for_igvn(result_reg); + + Node* array_ctl = generate_array_guard(klass_node, NULL); + if (array_ctl != NULL) { + // Array case: size is round(header + element_size*arraylength). + // Since arraylength is different for every array instance, we have to + // compute the whole thing at runtime. + + PreserveJVMState pjvms(this); + set_control(array_ctl); + Node* arr_length = load_array_length(obj); + + int round_mask = MinObjAlignmentInBytes - 1; + Node* mask = intcon(round_mask); + + Node* hss = intcon(Klass::_lh_header_size_shift); + Node* hsm = intcon(Klass::_lh_header_size_mask); + Node* header_size = _gvn.transform(new URShiftINode(layout_val, hss)); + header_size = _gvn.transform(new AndINode(header_size, hsm)); + header_size = _gvn.transform(new AddINode(header_size, mask)); + + // There is no need to mask or shift this value. + // The semantics of LShiftINode include an implicit mask to 0x1F. + assert(Klass::_lh_log2_element_size_shift == 0, "use shift in place"); + Node* elem_shift = layout_val; + + Node* lengthx = ConvI2X(arr_length); + Node* headerx = ConvI2X(header_size); + + Node* abody = _gvn.transform(new LShiftXNode(lengthx, elem_shift)); + Node* size = _gvn.transform(new AddXNode(headerx, abody)); + if (round_mask != 0) { + size = _gvn.transform(new AndXNode(size, MakeConX(~round_mask))); + } + size = ConvX2L(size); + + result_reg->init_req(_array_path, control()); + result_val->init_req(_array_path, size); + } + + if (!stopped()) { + // Instance case: the layout helper gives us instance size almost directly, + // but we need to mask out the _lh_instance_slow_path_bit. + Node* size = ConvI2X(layout_val); + assert((int) Klass::_lh_instance_slow_path_bit < BytesPerLong, "clear bit"); + Node* mask = MakeConX(~(intptr_t) right_n_bits(LogBytesPerLong)); + size = _gvn.transform(new AndXNode(size, mask)); + size = ConvX2L(size); + + result_reg->init_req(_instance_path, control()); + result_val->init_req(_instance_path, size); + } + + set_result(result_reg, result_val); + } + + return true; +} diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index cfff1d1052b17..9565e319fc2a1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -344,5 +344,7 @@ class LibraryCallKit : public GraphKit { } #endif } + + bool inline_getObjectSize(); }; diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java index 501d5c7ee16b4..26afc4b2e1cb9 100644 --- a/src/java.base/share/classes/module-info.java +++ b/src/java.base/share/classes/module-info.java @@ -230,6 +230,7 @@ jdk.internal.jvmstat, jdk.management.agent; exports jdk.internal.vm.annotation to + java.instrument, jdk.internal.vm.ci, jdk.incubator.vector, jdk.incubator.foreign, diff --git a/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java b/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java index 3241c086d905e..d95bd0f21c475 100644 --- a/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java +++ b/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java @@ -44,6 +44,7 @@ import java.util.jar.JarFile; import jdk.internal.module.Modules; +import jdk.internal.vm.annotation.IntrinsicCandidate; /* * Copyright 2003 Wily Technology, Inc. @@ -392,6 +393,7 @@ public void redefineModule(Module module, private native Class[] getInitiatedClasses0(long nativeAgent, ClassLoader loader); + @IntrinsicCandidate private native long getObjectSize0(long nativeAgent, Object objectToSize); diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java index f98b11a9755cd..285a8732685ed 100644 --- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java +++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java @@ -539,6 +539,11 @@ public CheckGraalIntrinsics() { "jdk/internal/vm/vector/VectorSupport.unaryOp(ILjava/lang/Class;Ljava/lang/Class;ILjava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;"); } + if (isJDK16OrHigher()) { + add(toBeInvestigated, + "sun/instrument/InstrumentationImpl.getObjectSize0(JLjava/lang/Object;)J"); + } + /* * The intrinsics down here are known to be implemented but they are not always enabled on * the HotSpot side (e.g., because they require certain CPU features). So, we are ignoring diff --git a/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java new file mode 100644 index 0000000000000..09cc1677a2601 --- /dev/null +++ b/test/jdk/java/lang/instrument/GetObjectSizeIntrinsicsTest.java @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8253525 + * @summary Test for fInst.getObjectSize with 32-bit compressed oops + * @library /test/lib + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize with zero-based compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize without compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize with 32-bit compressed oops + * @library /test/lib + * @requires vm.debug + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize with zero-based compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * @requires vm.debug + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize without compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * @requires vm.debug + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m -XX:-UseCompressedOops + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:FastAllocateSizeLimit=0 + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize with 32-bit compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * @requires vm.debug + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx128m + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +/* + * @test + * @summary Test for fInst.getObjectSize with zero-based compressed oops + * @library /test/lib + * @requires vm.bits == 64 + * @requires vm.debug + * + * @build sun.hotspot.WhiteBox + * @run build GetObjectSizeIntrinsicsTest + * @run shell MakeJAR.sh basicAgent + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xint + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xbatch -XX:TieredStopAtLevel=1 + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + * + * @run main/othervm -Xmx4g + * -XX:+UnlockDiagnosticVMOptions -XX:+AbortVMOnCompilationFailure -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:ObjectAlignmentInBytes=32 + * -Xbatch -XX:-TieredCompilation + * -javaagent:basicAgent.jar GetObjectSizeIntrinsicsTest GetObjectSizeIntrinsicsTest + */ + +import java.util.*; + +import jdk.test.lib.Platform; +import sun.hotspot.WhiteBox; + +public class GetObjectSizeIntrinsicsTest extends ASimpleInstrumentationTestCase { + + static final Boolean compressedOops = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompressedOops"); + static final int REF_SIZE = (compressedOops == null || compressedOops == true) ? 4 : 8; + + static final Long align = WhiteBox.getWhiteBox().getIntxVMFlag("ObjectAlignmentInBytes"); + static final int OBJ_ALIGN = (align == null ? 8 : align.intValue()); + + public GetObjectSizeIntrinsicsTest(String name) { + super(name); + } + + public static void main(String[] args)throws Throwable { + new GetObjectSizeIntrinsicsTest(args[0]).runTest(); + } + + public static final int ITERS = 200_000; + + public static void assertEquals(long expected, long actual) { + if (expected != actual) { + throw new IllegalStateException( + "Error: expected: " + expected + " (" + Long.toHexString(expected) + + "), actual: " + actual + " (" + Long.toHexString(actual) + ")"); + } + } + + public static void assertNotEquals(long notExpected, long actual) { + if (notExpected == actual) { + throw new IllegalStateException( + "Error: not expected: " + notExpected + " (" + Long.toHexString(notExpected) + + "), actual: " + actual + " (" + Long.toHexString(actual) + ")"); + } + } + + public static void assertFail() { + throw new IllegalStateException("Should not be here"); + } + + protected final void doRunTest() throws Throwable { + testSize_newObject(); + testSize_localObject(); + testSize_fieldObject(); + + testSize_newSmallByteArray(); + testSize_localSmallByteArray(); + testSize_fieldSmallByteArray(); + + testSize_newSmallObjArray(); + testSize_localSmallObjArray(); + testSize_fieldSmallObjArray(); + + testNulls(); + } + + private static int roundUp(int v, int a) { + return (v + a - 1) / a * a; + } + + private void testSize_newObject() { + int expected = roundUp(Platform.is64bit() ? 16 : 8, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(new Object())); + } + } + + private void testSize_localObject() { + int expected = roundUp(Platform.is64bit() ? 16 : 8, OBJ_ALIGN); + Object o = new Object(); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(o)); + } + } + + static Object staticO = new Object(); + + private void testSize_fieldObject() { + int expected = roundUp(Platform.is64bit() ? 16 : 8, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(staticO)); + } + } + + private void testSize_newSmallByteArray() { + int expected = roundUp(1024 + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(new byte[1024])); + } + } + + private void testSize_localSmallByteArray() { + byte[] arr = new byte[1024]; + int expected = roundUp(arr.length + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(arr)); + } + } + + static byte[] smallArr = new byte[1024]; + + private void testSize_fieldSmallByteArray() { + int expected = roundUp(smallArr.length + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(smallArr)); + } + } + + private void testSize_newSmallObjArray() { + int expected = roundUp(1024*REF_SIZE + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(new Object[1024])); + } + } + + private void testSize_localSmallObjArray() { + Object[] arr = new Object[1024]; + int expected = roundUp(arr.length*REF_SIZE + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(arr)); + } + } + + static Object[] smallObjArr = new Object[1024]; + + private void testSize_fieldSmallObjArray() { + int expected = roundUp(smallArr.length*REF_SIZE + 16, OBJ_ALIGN); + for (int c = 0; c < ITERS; c++) { + assertEquals(expected, fInst.getObjectSize(smallObjArr)); + } + } + + private void testNulls() { + for (int c = 0; c < ITERS; c++) { + try { + fInst.getObjectSize(null); + assertFail(); + } catch (NullPointerException e) { + // expected + } + } + } + +} From 41139e31c02d78b359d4c562a4ace957b339b612 Mon Sep 17 00:00:00 2001 From: Nils Eliasson Date: Fri, 13 Nov 2020 09:40:06 +0000 Subject: [PATCH 101/124] 8255964: Add all details to jstack log in jtreg timeout handler Reviewed-by: iignatyev --- test/failure_handler/src/share/conf/common.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/test/failure_handler/src/share/conf/common.properties b/test/failure_handler/src/share/conf/common.properties index b0bbe9b530398..c53b122d07838 100644 --- a/test/failure_handler/src/share/conf/common.properties +++ b/test/failure_handler/src/share/conf/common.properties @@ -57,6 +57,7 @@ jcmd.gc.finalizer_info.args=%p GC.finalizer_info jcmd.gc.heap_info.args=%p GC.heap_info jstack.app=jstack +jstack.args=-e -l %p jstack.params.repeat=6 ################################################################################ From b0c28fadaa235ce9dd0c89fc9ff2998df50473bb Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Fri, 13 Nov 2020 09:47:00 +0000 Subject: [PATCH 102/124] 8256011: Shenandoah: Don't resurrect finalizably reachable objects Reviewed-by: shade, zgu --- .../c1/shenandoahBarrierSetC1_aarch64.cpp | 2 +- .../shenandoahBarrierSetAssembler_aarch64.cpp | 144 ++++++++------- .../shenandoahBarrierSetAssembler_aarch64.hpp | 4 +- .../c1/shenandoahBarrierSetC1_x86.cpp | 3 +- .../shenandoahBarrierSetAssembler_x86.cpp | 165 +++++++++--------- .../shenandoahBarrierSetAssembler_x86.hpp | 4 +- .../shenandoah/c1/shenandoahBarrierSetC1.cpp | 51 +++--- .../shenandoah/c1/shenandoahBarrierSetC1.hpp | 35 ++-- .../shenandoah/c2/shenandoahBarrierSetC2.cpp | 22 ++- .../gc/shenandoah/c2/shenandoahSupport.cpp | 79 +++++---- .../gc/shenandoah/c2/shenandoahSupport.hpp | 8 +- .../gc/shenandoah/shenandoahBarrierSet.cpp | 10 -- .../gc/shenandoah/shenandoahBarrierSet.hpp | 29 +-- .../shenandoahBarrierSet.inline.hpp | 12 +- .../share/gc/shenandoah/shenandoahRuntime.cpp | 12 +- .../share/gc/shenandoah/shenandoahRuntime.hpp | 6 +- 16 files changed, 303 insertions(+), 283 deletions(-) diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index db9c7577e6081..c05b5f9952477 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -109,7 +109,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt __ xchg(access.resolved_addr(), value_opr, result, tmp); if (access.is_oop()) { - result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), ShenandoahBarrierSet::AccessKind::NORMAL); + result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), access.decorators()); LIR_Opr tmp = gen->new_register(type); __ move(result, tmp); result = tmp; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 840464b251f4f..151fa2a1b5ee8 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -225,11 +225,17 @@ void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssemb } } -void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, ShenandoahBarrierSet::AccessKind kind) { +void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); assert(dst != rscratch2, "need rscratch2"); assert_different_registers(load_addr.base(), load_addr.index(), rscratch1, rscratch2); + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + bool is_narrow = UseCompressedOops && !is_native; + Label heap_stable, not_cset; __ enter(); Address gc_state(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset())); @@ -252,7 +258,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ mov(r0, dst); // Test for in-cset - if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (is_strong) { __ mov(rscratch2, ShenandoahHeap::in_cset_fast_test_addr()); __ lsr(rscratch1, r0, ShenandoahHeapRegion::region_size_bytes_shift_jint()); __ ldrb(rscratch2, Address(rscratch2, rscratch1)); @@ -260,26 +266,22 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, } __ push_call_clobbered_registers(); - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); - } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); - } - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); - } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); - } - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: + if (is_strong) { + if (is_narrow) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow)); + } else { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong)); + } + } else if (is_weak) { + if (is_narrow) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + } else { __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); - break; - default: - ShouldNotReachHere(); + } + } else { + assert(is_phantom, "only remaining strength"); + assert(!is_narrow, "phantom access cannot be narrow"); + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); } __ blr(lr); __ mov(rscratch1, r0); @@ -338,8 +340,7 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread); - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); - load_reference_barrier(masm, dst, src, kind); + load_reference_barrier(masm, dst, src, decorators); if (dst != result_dst) { __ mov(result_dst, dst); @@ -617,6 +618,12 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); __ bind(*stub->entry()); + DecoratorSet decorators = stub->decorators(); + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + Register obj = stub->obj()->as_register(); Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); @@ -629,42 +636,27 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble __ mov(res, obj); } - // Check for null. - __ cbz(res, *stub->continuation()); - - // Check for object in cset. - __ mov(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); - __ lsr(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ ldrb(tmp2, Address(tmp2, tmp1)); - __ cbz(tmp2, *stub->continuation()); - - // Check if object is already forwarded. - Label slow_path; - __ ldr(tmp1, Address(res, oopDesc::mark_offset_in_bytes())); - __ eon(tmp1, tmp1, zr); - __ ands(zr, tmp1, markWord::lock_mask_in_place); - __ br(Assembler::NE, slow_path); - - // Decode forwarded object. - __ orr(tmp1, tmp1, markWord::marked_value); - __ eon(res, tmp1, zr); - __ b(*stub->continuation()); + if (is_strong) { + // Check for object in cset. + __ mov(tmp2, ShenandoahHeap::in_cset_fast_test_addr()); + __ lsr(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + __ ldrb(tmp2, Address(tmp2, tmp1)); + __ cbz(tmp2, *stub->continuation()); + } - __ bind(slow_path); ce->store_parameter(res, 0); ce->store_parameter(addr, 1); - switch (stub->kind()) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - __ far_call(RuntimeAddress(bs->load_reference_barrier_normal_rt_code_blob()->code_begin())); - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: - __ far_call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); - break; - default: - ShouldNotReachHere(); + if (is_strong) { + if (is_native) { + __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); + } else { + __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); + } + } else if (is_weak) { + __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); + } else { + assert(is_phantom, "only remaining strength"); + __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); } __ b(*stub->continuation()); @@ -720,33 +712,39 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind) { +void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); // arg0 : object to be resolved __ push_call_clobbered_registers(); __ load_parameter(0, r0); __ load_parameter(1, r1); - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)); - } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)); - } - break; - case ShenandoahBarrierSet::AccessKind::WEAK: + + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + if (is_strong) { + if (is_native) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong)); + } else { if (UseCompressedOops) { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow)); } else { - __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong)); } - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: + } + } else if (is_weak) { + assert(!is_native, "weak must not be called off-heap"); + if (UseCompressedOops) { + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + } else { __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)); - break; - default: - ShouldNotReachHere(); + } + } else { + assert(is_phantom, "only remaining strength"); + assert(is_native, "phantom must only be called off-heap"); + __ mov(lr, CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom)); } __ blr(lr); __ mov(rscratch1, r0); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index 60303725fd8a1..bbf3fe131f8a9 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -56,7 +56,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp = noreg); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp = noreg); - void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, ShenandoahBarrierSet::AccessKind kind); + void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators); public: @@ -66,7 +66,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind); + void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif virtual void arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop, diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 2aac06082079f..65599ccddf641 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -111,8 +111,7 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt __ xchg(access.resolved_addr(), result, result, LIR_OprFact::illegalOpr); if (access.is_oop()) { - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); - result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), kind); + result = load_reference_barrier(access.gen(), result, LIR_OprFact::addressConst(0), access.decorators()); LIR_Opr tmp = gen->new_register(type); __ move(result, tmp); result = tmp; diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 40f16ef273103..b9abe5be130ac 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -268,9 +268,15 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm, __ bind(done); } -void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address src, ShenandoahBarrierSet::AccessKind kind) { +void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address src, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + bool is_narrow = UseCompressedOops && !is_native; + Label heap_stable, not_cset; __ block_comment("load_reference_barrier { "); @@ -292,7 +298,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ jcc(Assembler::zero, heap_stable); Register tmp1 = noreg, tmp2 = noreg; - if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (is_strong) { // Test for object in cset // Allocate temporary registers for (int i = 0; i < 8; i++) { @@ -357,26 +363,22 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, } save_xmm_registers(masm); - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), arg0, arg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), arg0, arg1); - } - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - if (UseCompressedOops) { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), arg0, arg1); - } else { - __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); - } - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); - break; - default: - ShouldNotReachHere(); + if (is_strong) { + if (is_narrow) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), arg0, arg1); + } else { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), arg0, arg1); + } + } else if (is_weak) { + if (is_narrow) { + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), arg0, arg1); + } else { + __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), arg0, arg1); + } + } else { + assert(is_phantom, "only remaining strength"); + assert(!is_narrow, "phantom access cannot be narrow"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), arg0, arg1); } restore_xmm_registers(masm); @@ -401,7 +403,7 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ bind(not_cset); - if (kind == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (is_strong) { __ pop(tmp2); __ pop(tmp1); } @@ -499,8 +501,7 @@ void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet d BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread); - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); - load_reference_barrier(masm, dst, src, kind); + load_reference_barrier(masm, dst, src, decorators); // Move loaded oop to final destination if (dst != result_dst) { @@ -813,6 +814,12 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1(); __ bind(*stub->entry()); + DecoratorSet decorators = stub->decorators(); + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + Register obj = stub->obj()->as_register(); Register res = stub->result()->as_register(); Register addr = stub->addr()->as_pointer_register(); @@ -828,40 +835,37 @@ void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assemble __ mov(res, obj); } - // Check for null. - __ testptr(res, res); - __ jcc(Assembler::zero, *stub->continuation()); - - // Check for object being in the collection set. - __ mov(tmp1, res); - __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); - __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); + if (is_strong) { + // Check for object being in the collection set. + __ mov(tmp1, res); + __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr()); #ifdef _LP64 - __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testbool(tmp2); + __ movbool(tmp2, Address(tmp2, tmp1, Address::times_1)); + __ testbool(tmp2); #else - // On x86_32, C1 register allocator can give us the register without 8-bit support. - // Do the full-register access and test to avoid compilation failures. - __ movptr(tmp2, Address(tmp2, tmp1, Address::times_1)); - __ testptr(tmp2, 0xFF); + // On x86_32, C1 register allocator can give us the register without 8-bit support. + // Do the full-register access and test to avoid compilation failures. + __ movptr(tmp2, Address(tmp2, tmp1, Address::times_1)); + __ testptr(tmp2, 0xFF); #endif - __ jcc(Assembler::zero, *stub->continuation()); + __ jcc(Assembler::zero, *stub->continuation()); + } __ bind(slow_path); ce->store_parameter(res, 0); ce->store_parameter(addr, 1); - switch (stub->kind()) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - __ call(RuntimeAddress(bs->load_reference_barrier_normal_rt_code_blob()->code_begin())); - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: - __ call(RuntimeAddress(bs->load_reference_barrier_native_rt_code_blob()->code_begin())); - break; - default: - ShouldNotReachHere(); + if (is_strong) { + if (is_native) { + __ call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin())); + } else { + __ call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin())); + } + } else if (is_weak) { + __ call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin())); + } else { + assert(is_phantom, "only remaining strength"); + __ call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin())); } __ jmp(*stub->continuation()); } @@ -926,49 +930,52 @@ void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAss __ epilogue(); } -void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind) { +void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) { __ prologue("shenandoah_load_reference_barrier", false); // arg0 : object to be resolved __ save_live_registers_no_oop_map(true); + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + #ifdef _LP64 __ load_parameter(0, c_rarg0); __ load_parameter(1, c_rarg1); - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow), c_rarg0, c_rarg1); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), c_rarg0, c_rarg1); - } - break; - case ShenandoahBarrierSet::AccessKind::WEAK: + if (is_strong) { + if (is_native) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); + } else { if (UseCompressedOops) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow), c_rarg0, c_rarg1); } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), c_rarg0, c_rarg1); } - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: + } + } else if (is_weak) { + assert(!is_native, "weak must not be called off-heap"); + if (UseCompressedOops) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow), c_rarg0, c_rarg1); + } else { __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), c_rarg0, c_rarg1); - break; - default: - ShouldNotReachHere(); + } + } else { + assert(is_phantom, "only remaining strength"); + assert(is_native, "phantom must only be called off-heap"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), c_rarg0, c_rarg1); } #else __ load_parameter(0, rax); __ load_parameter(1, rbx); - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier), rax, rbx); - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - case ShenandoahBarrierSet::AccessKind::NATIVE: - __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), rax, rbx); - break; - default: - ShouldNotReachHere(); + if (is_strong) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong), rax, rbx); + } else if (is_weak) { + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak), rax, rbx); + } else { + assert(is_phantom, "only remaining strength"); + __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom), rax, rbx); } #endif diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 108b5670206c0..686389aae6672 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -64,10 +64,10 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); void generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm); - void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, ShenandoahBarrierSet::AccessKind kind); + void generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm, DecoratorSet decorators); #endif - void load_reference_barrier(MacroAssembler* masm, Register dst, Address src, ShenandoahBarrierSet::AccessKind kind); + void load_reference_barrier(MacroAssembler* masm, Register dst, Address src, DecoratorSet decorators); void cmpxchg_oop(MacroAssembler* masm, Register res, Address addr, Register oldval, Register newval, diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index f0c034d14013c..7c63902efbd54 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -51,9 +51,10 @@ void ShenandoahLoadReferenceBarrierStub::emit_code(LIR_Assembler* ce) { ShenandoahBarrierSetC1::ShenandoahBarrierSetC1() : _pre_barrier_c1_runtime_code_blob(NULL), - _load_reference_barrier_normal_rt_code_blob(NULL), - _load_reference_barrier_native_rt_code_blob(NULL), - _load_reference_barrier_weak_rt_code_blob(NULL) {} + _load_reference_barrier_strong_rt_code_blob(NULL), + _load_reference_barrier_strong_native_rt_code_blob(NULL), + _load_reference_barrier_weak_rt_code_blob(NULL), + _load_reference_barrier_phantom_rt_code_blob(NULL) {} void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val) { // First we test whether marking is in progress. @@ -109,15 +110,15 @@ void ShenandoahBarrierSetC1::pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, __ branch_destination(slow->continuation()); } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind) { +LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { if (ShenandoahLoadRefBarrier) { - return load_reference_barrier_impl(gen, obj, addr, kind); + return load_reference_barrier_impl(gen, obj, addr, decorators); } else { return obj; } } -LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind) { +LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators) { assert(ShenandoahLoadRefBarrier, "Should be enabled"); obj = ensure_in_register(gen, obj, T_OBJECT); @@ -150,7 +151,7 @@ LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier_impl(LIRGenerator* gen, L } __ cmp(lir_cond_notEqual, flag_val, LIR_OprFact::intConst(0)); - CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, kind); + CodeStub* slow = new ShenandoahLoadReferenceBarrierStub(obj, addr, result, tmp1, tmp2, decorators); __ branch(lir_cond_notEqual, slow); __ branch_destination(slow->continuation()); @@ -213,8 +214,7 @@ void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { LIR_Opr tmp = gen->new_register(T_OBJECT); BarrierSetC1::load_at_resolved(access, tmp); - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); - tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), kind); + tmp = load_reference_barrier(gen, tmp, access.resolved_addr(), decorators); __ move(tmp, result); } else { BarrierSetC1::load_at_resolved(access, result); @@ -253,14 +253,14 @@ class C1ShenandoahPreBarrierCodeGenClosure : public StubAssemblerCodeGenClosure class C1ShenandoahLoadReferenceBarrierCodeGenClosure : public StubAssemblerCodeGenClosure { private: - const ShenandoahBarrierSet::AccessKind _kind; + const DecoratorSet _decorators; public: - C1ShenandoahLoadReferenceBarrierCodeGenClosure(ShenandoahBarrierSet::AccessKind kind) : _kind(kind) {} + C1ShenandoahLoadReferenceBarrierCodeGenClosure(DecoratorSet decorators) : _decorators(decorators) {} virtual OopMapSet* generate_code(StubAssembler* sasm) { ShenandoahBarrierSetAssembler* bs = (ShenandoahBarrierSetAssembler*)BarrierSet::barrier_set()->barrier_set_assembler(); - bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _kind); + bs->generate_c1_load_reference_barrier_runtime_stub(sasm, _decorators); return NULL; } }; @@ -271,19 +271,24 @@ void ShenandoahBarrierSetC1::generate_c1_runtime_stubs(BufferBlob* buffer_blob) "shenandoah_pre_barrier_slow", false, &pre_code_gen_cl); if (ShenandoahLoadRefBarrier) { - C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_code_gen_cl(ShenandoahBarrierSet::AccessKind::NORMAL); - _load_reference_barrier_normal_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, - "shenandoah_load_reference_barrier_slow", - false, &lrb_code_gen_cl); + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_strong_code_gen_cl(ON_STRONG_OOP_REF); + _load_reference_barrier_strong_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, + "shenandoah_load_reference_barrier_strong_slow", + false, &lrb_strong_code_gen_cl); - C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_native_code_gen_cl(ShenandoahBarrierSet::AccessKind::NATIVE); - _load_reference_barrier_native_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, - "shenandoah_load_reference_barrier_native_slow", - false, &lrb_native_code_gen_cl); + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_strong_native_code_gen_cl(ON_STRONG_OOP_REF | IN_NATIVE); + _load_reference_barrier_strong_native_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, + "shenandoah_load_reference_barrier_strong_native_slow", + false, &lrb_strong_native_code_gen_cl); - C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_weak_code_gen_cl(ShenandoahBarrierSet::AccessKind::WEAK); + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_weak_code_gen_cl(ON_WEAK_OOP_REF); _load_reference_barrier_weak_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, - "shenandoah_load_reference_barrier_weak_slow", - false, &lrb_weak_code_gen_cl); + "shenandoah_load_reference_barrier_weak_slow", + false, &lrb_weak_code_gen_cl); + + C1ShenandoahLoadReferenceBarrierCodeGenClosure lrb_phantom_code_gen_cl(ON_PHANTOM_OOP_REF | IN_NATIVE); + _load_reference_barrier_phantom_rt_code_blob = Runtime1::generate_blob(buffer_blob, -1, + "shenandoah_load_reference_barrier_phantom_slow", + false, &lrb_phantom_code_gen_cl); } } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index eb65475e60915..2047ab91ead6a 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -94,10 +94,10 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { LIR_Opr _result; LIR_Opr _tmp1; LIR_Opr _tmp2; - ShenandoahBarrierSet::AccessKind _kind; + DecoratorSet _decorators; public: - ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, ShenandoahBarrierSet::AccessKind kind) : - _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _kind(kind) + ShenandoahLoadReferenceBarrierStub(LIR_Opr obj, LIR_Opr addr, LIR_Opr result, LIR_Opr tmp1, LIR_Opr tmp2, DecoratorSet decorators) : + _obj(obj), _addr(addr), _result(result), _tmp1(tmp1), _tmp2(tmp2), _decorators(decorators) { assert(_obj->is_register(), "should be register"); assert(_addr->is_register(), "should be register"); @@ -111,7 +111,7 @@ class ShenandoahLoadReferenceBarrierStub: public CodeStub { LIR_Opr result() const { return _result; } LIR_Opr tmp1() const { return _tmp1; } LIR_Opr tmp2() const { return _tmp2; } - ShenandoahBarrierSet::AccessKind kind() const { return _kind; } + DecoratorSet decorators() const { return _decorators; } virtual void emit_code(LIR_Assembler* e); virtual void visit(LIR_OpVisitState* visitor) { @@ -190,16 +190,17 @@ class LIR_OpShenandoahCompareAndSwap : public LIR_Op { class ShenandoahBarrierSetC1 : public BarrierSetC1 { private: CodeBlob* _pre_barrier_c1_runtime_code_blob; - CodeBlob* _load_reference_barrier_normal_rt_code_blob; - CodeBlob* _load_reference_barrier_native_rt_code_blob; + CodeBlob* _load_reference_barrier_strong_rt_code_blob; + CodeBlob* _load_reference_barrier_strong_native_rt_code_blob; CodeBlob* _load_reference_barrier_weak_rt_code_blob; + CodeBlob* _load_reference_barrier_phantom_rt_code_blob; void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); - LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind); + LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); LIR_Opr storeval_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators); - LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, ShenandoahBarrierSet::AccessKind kind); + LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); LIR_Opr ensure_in_register(LIRGenerator* gen, LIR_Opr obj, BasicType type); @@ -211,20 +212,26 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 { return _pre_barrier_c1_runtime_code_blob; } - CodeBlob* load_reference_barrier_normal_rt_code_blob() { - assert(_load_reference_barrier_normal_rt_code_blob != NULL, ""); - return _load_reference_barrier_normal_rt_code_blob; + CodeBlob* load_reference_barrier_strong_rt_code_blob() { + assert(_load_reference_barrier_strong_rt_code_blob != NULL, ""); + return _load_reference_barrier_strong_rt_code_blob; } - CodeBlob* load_reference_barrier_native_rt_code_blob() { - assert(_load_reference_barrier_native_rt_code_blob != NULL, ""); - return _load_reference_barrier_native_rt_code_blob; + CodeBlob* load_reference_barrier_strong_native_rt_code_blob() { + assert(_load_reference_barrier_strong_native_rt_code_blob != NULL, ""); + return _load_reference_barrier_strong_native_rt_code_blob; } CodeBlob* load_reference_barrier_weak_rt_code_blob() { assert(_load_reference_barrier_weak_rt_code_blob != NULL, ""); return _load_reference_barrier_weak_rt_code_blob; } + + CodeBlob* load_reference_barrier_phantom_rt_code_blob() { + assert(_load_reference_barrier_phantom_rt_code_blob != NULL, ""); + return _load_reference_barrier_phantom_rt_code_blob; + } + protected: virtual void store_at_resolved(LIRAccess& access, LIR_Opr value); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index e13065e56c2be..f901767aeb834 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -303,10 +303,11 @@ bool ShenandoahBarrierSetC2::is_shenandoah_lrb_call(Node* call) { } address entry_point = call->as_CallLeaf()->entry_point(); - return (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier)) || - (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow)) || + return (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong)) || + (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow)) || (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak)) || - (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)); + (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow)) || + (entry_point == CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom)); } bool ShenandoahBarrierSetC2::is_shenandoah_marking_if(PhaseTransform *phase, Node* n) { @@ -546,8 +547,7 @@ Node* ShenandoahBarrierSetC2::load_at_resolved(C2Access& access, const Type* val // 2: apply LRB if needed if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) { - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(decorators, type); - load = new ShenandoahLoadReferenceBarrierNode(NULL, load, kind); + load = new ShenandoahLoadReferenceBarrierNode(NULL, load, decorators); if (access.is_parse_access()) { load = static_cast(access).kit()->gvn().transform(load); } else { @@ -644,8 +644,7 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess load_store = kit->gvn().transform(new DecodeNNode(load_store, load_store->get_ptr_type())); } #endif - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); - load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, kind)); + load_store = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, load_store, access.decorators())); return load_store; } return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, value_type); @@ -713,8 +712,7 @@ Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& acces } Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type); if (access.is_oop()) { - ShenandoahBarrierSet::AccessKind kind = ShenandoahBarrierSet::access_kind(access.decorators(), access.type()); - result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, kind)); + result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(NULL, result, access.decorators())); shenandoah_write_barrier_pre(kit, false /* do_load */, NULL, NULL, max_juint, NULL, NULL, result /* pre_val */, T_OBJECT); @@ -1062,15 +1060,15 @@ Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_resh Node* in1 = n->in(1); Node* in2 = n->in(2); - // If one input is NULL, then step over the barriers normal LRB barriers on the other input + // If one input is NULL, then step over the strong LRB barriers on the other input if (in1->bottom_type() == TypePtr::NULL_PTR && !((in2->Opcode() == Op_ShenandoahLoadReferenceBarrier) && - ((ShenandoahLoadReferenceBarrierNode*)in2)->kind() != ShenandoahBarrierSet::AccessKind::NORMAL)) { + !ShenandoahBarrierSet::is_strong_access(((ShenandoahLoadReferenceBarrierNode*)in2)->decorators()))) { in2 = step_over_gc_barrier(in2); } if (in2->bottom_type() == TypePtr::NULL_PTR && !((in1->Opcode() == Op_ShenandoahLoadReferenceBarrier) && - ((ShenandoahLoadReferenceBarrierNode*)in1)->kind() != ShenandoahBarrierSet::AccessKind::NORMAL)) { + !ShenandoahBarrierSet::is_strong_access(((ShenandoahLoadReferenceBarrierNode*)in1)->decorators()))) { in1 = step_over_gc_barrier(in1); } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index bd7f450443f75..01c81bd1b6347 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -960,7 +960,7 @@ void ShenandoahBarrierC2Support::test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, } void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, - ShenandoahBarrierSet::AccessKind kind, PhaseIdealLoop* phase) { + DecoratorSet decorators, PhaseIdealLoop* phase) { IdealLoopTree*loop = phase->get_loop(ctrl); const TypePtr* obj_type = phase->igvn().type(val)->is_oopptr(); @@ -973,25 +973,32 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo address calladdr = NULL; const char* name = NULL; - switch (kind) { - case ShenandoahBarrierSet::AccessKind::NATIVE: + bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators); + bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators); + bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators); + bool is_native = ShenandoahBarrierSet::is_native_access(decorators); + bool is_narrow = UseCompressedOops && !is_native; + if (is_strong) { + if (is_narrow) { + calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow); + name = "load_reference_barrier_strong_narrow"; + } else { + calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong); + name = "load_reference_barrier_strong"; + } + } else if (is_weak) { + if (is_narrow) { + calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow); + name = "load_reference_barrier_weak_narrow"; + } else { calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); - name = "load_reference_barrier_native"; - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - calladdr = LP64_ONLY(UseCompressedOops) NOT_LP64(false) ? - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow) : - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak); name = "load_reference_barrier_weak"; - break; - case ShenandoahBarrierSet::AccessKind::NORMAL: - calladdr = LP64_ONLY(UseCompressedOops) NOT_LP64(false) ? - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_narrow) : - CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier); - name = "load_reference_barrier"; - break; - default: - ShouldNotReachHere(); + } + } else { + assert(is_phantom, "only remaining strength"); + assert(!is_narrow, "phantom access cannot be narrow"); + calladdr = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom); + name = "load_reference_barrier_phantom"; } Node* call = new CallLeafNode(ShenandoahBarrierSetC2::shenandoah_load_reference_barrier_Type(), calladdr, name, TypeRawPtr::BOTTOM); @@ -1357,7 +1364,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { // even for non-cset objects to prevent ressurrection of such objects. // Wires !in_cset(obj) to slot 2 of region and phis Node* not_cset_ctrl = NULL; - if (lrb->kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (ShenandoahBarrierSet::is_strong_access(lrb->decorators())) { test_in_cset(ctrl, not_cset_ctrl, val, raw_mem, phase); } if (not_cset_ctrl != NULL) { @@ -1408,7 +1415,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { } } } - call_lrb_stub(ctrl, val, addr, result_mem, raw_mem, lrb->kind(), phase); + call_lrb_stub(ctrl, val, addr, result_mem, raw_mem, lrb->decorators(), phase); region->init_req(_evac_path, ctrl); val_phi->init_req(_evac_path, val); raw_mem_phi->init_req(_evac_path, result_mem); @@ -2904,40 +2911,32 @@ void MemoryGraphFixer::fix_memory_uses(Node* mem, Node* replacement, Node* rep_p } } -ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj, ShenandoahBarrierSet::AccessKind kind) -: Node(ctrl, obj), _kind(kind) { +ShenandoahLoadReferenceBarrierNode::ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* obj, DecoratorSet decorators) +: Node(ctrl, obj), _decorators(decorators) { ShenandoahBarrierSetC2::bsc2()->state()->add_load_reference_barrier(this); } -ShenandoahBarrierSet::AccessKind ShenandoahLoadReferenceBarrierNode::kind() const { - return _kind; +DecoratorSet ShenandoahLoadReferenceBarrierNode::decorators() const { + return _decorators; } uint ShenandoahLoadReferenceBarrierNode::size_of() const { return sizeof(*this); } +static DecoratorSet mask_decorators(DecoratorSet decorators) { + return decorators & (ON_STRONG_OOP_REF | ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF | ON_UNKNOWN_OOP_REF | IN_NATIVE); +} + uint ShenandoahLoadReferenceBarrierNode::hash() const { uint hash = Node::hash(); - switch (_kind) { - case ShenandoahBarrierSet::AccessKind::NORMAL: - hash += 0; - break; - case ShenandoahBarrierSet::AccessKind::WEAK: - hash += 1; - break; - case ShenandoahBarrierSet::AccessKind::NATIVE: - hash += 2; - break; - default: - ShouldNotReachHere(); - } + hash += mask_decorators(_decorators); return hash; } bool ShenandoahLoadReferenceBarrierNode::cmp( const Node &n ) const { return Node::cmp(n) && n.Opcode() == Op_ShenandoahLoadReferenceBarrier && - _kind == ((const ShenandoahLoadReferenceBarrierNode&)n)._kind; + mask_decorators(_decorators) == mask_decorators(((const ShenandoahLoadReferenceBarrierNode&)n)._decorators); } const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const { @@ -2949,7 +2948,7 @@ const Type* ShenandoahLoadReferenceBarrierNode::bottom_type() const { return t; } - if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (ShenandoahBarrierSet::is_strong_access(decorators())) { return t; } @@ -2965,7 +2964,7 @@ const Type* ShenandoahLoadReferenceBarrierNode::Value(PhaseGVN* phase) const { return t2; } - if (kind() == ShenandoahBarrierSet::AccessKind::NORMAL) { + if (ShenandoahBarrierSet::is_strong_access(decorators())) { return t2; } diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp index c07b4a1ed72a9..f6a5e3243617c 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp @@ -62,7 +62,7 @@ class ShenandoahBarrierC2Support : public AllStatic { static void test_gc_state(Node*& ctrl, Node* raw_mem, Node*& heap_stable_ctrl, PhaseIdealLoop* phase, int flags); static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr, Node*& result_mem, Node* raw_mem, - ShenandoahBarrierSet::AccessKind kind, PhaseIdealLoop* phase); + DecoratorSet decorators, PhaseIdealLoop* phase); static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase); static void move_gc_state_test_out_of_loop(IfNode* iff, PhaseIdealLoop* phase); static void merge_back_to_back_tests(Node* n, PhaseIdealLoop* phase); @@ -231,12 +231,12 @@ class ShenandoahLoadReferenceBarrierNode : public Node { }; private: - ShenandoahBarrierSet::AccessKind _kind; + DecoratorSet _decorators; public: - ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val, ShenandoahBarrierSet::AccessKind kind); + ShenandoahLoadReferenceBarrierNode(Node* ctrl, Node* val, DecoratorSet decorators); - ShenandoahBarrierSet::AccessKind kind() const; + DecoratorSet decorators() const; virtual int Opcode() const; virtual const Type* bottom_type() const; virtual const Type* Value(PhaseGVN* phase) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index adf349d3b24c8..d731a376652bf 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -98,16 +98,6 @@ bool ShenandoahBarrierSet::need_keep_alive_barrier(DecoratorSet decorators,Basic return (on_weak_ref || unknown) && keep_alive; } -ShenandoahBarrierSet::AccessKind ShenandoahBarrierSet::access_kind(DecoratorSet decorators, BasicType type) { - if ((decorators & IN_NATIVE) != 0) { - return AccessKind::NATIVE; - } else if ((decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF | ON_UNKNOWN_OOP_REF)) != 0) { - return AccessKind::WEAK; - } else { - return AccessKind::NORMAL; - } -} - void ShenandoahBarrierSet::on_thread_create(Thread* thread) { // Create thread local data ShenandoahThreadLocalData::create(thread); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index eee66c0c05dc4..0deda387f15c7 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -33,18 +33,6 @@ class ShenandoahBarrierSetAssembler; class ShenandoahBarrierSet: public BarrierSet { -public: - enum class AccessKind { - // Regular in-heap access on reference fields - NORMAL, - - // Off-heap reference access - NATIVE, - - // In-heap reference access on referent fields of j.l.r.Reference objects - WEAK - }; - private: ShenandoahHeap* _heap; BufferNode::Allocator _satb_mark_queue_buffer_allocator; @@ -65,7 +53,22 @@ class ShenandoahBarrierSet: public BarrierSet { static bool need_load_reference_barrier(DecoratorSet decorators, BasicType type); static bool need_keep_alive_barrier(DecoratorSet decorators, BasicType type); - static AccessKind access_kind(DecoratorSet decorators, BasicType type); + + static bool is_strong_access(DecoratorSet decorators) { + return (decorators & (ON_WEAK_OOP_REF | ON_PHANTOM_OOP_REF | ON_UNKNOWN_OOP_REF)) == 0; + } + + static bool is_weak_access(DecoratorSet decorators) { + return (decorators & (ON_WEAK_OOP_REF | ON_UNKNOWN_OOP_REF)) != 0; + } + + static bool is_phantom_access(DecoratorSet decorators) { + return (decorators & ON_PHANTOM_OOP_REF) != 0; + } + + static bool is_native_access(DecoratorSet decorators) { + return (decorators & IN_NATIVE) != 0; + } void print_on(outputStream* st) const; diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index 1388465328dab..20370d1828fb6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -102,13 +102,21 @@ inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj) { template inline oop ShenandoahBarrierSet::load_reference_barrier(oop obj, T* load_addr) { - // Prevent resurrection of unreachable non-strorg references. - if (!HasDecorator::value && obj != NULL && + // Prevent resurrection of unreachable phantom (i.e. weak-native) references. + if (HasDecorator::value && obj != NULL && _heap->is_concurrent_weak_root_in_progress() && !_heap->marking_context()->is_marked(obj)) { return NULL; } + // Prevent resurrection of unreachable weak references. + if ((HasDecorator::value || HasDecorator::value) && + obj != NULL && _heap->is_concurrent_weak_root_in_progress() && + !_heap->marking_context()->is_marked_strong(obj)) { + assert(Thread::current()->is_Java_thread(), "only Java threads get here"); + return NULL; + } + // Prevent resurrection of unreachable objects that are visited during // concurrent class-unloading. if (HasDecorator::value && obj != NULL && diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp index 5fb69eb9b0f65..97bfecd7161f2 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.cpp @@ -50,11 +50,11 @@ JRT_LEAF(void, ShenandoahRuntime::write_ref_field_pre_entry(oopDesc* orig, JavaT ShenandoahThreadLocalData::satb_mark_queue(thread).enqueue_known_active(orig); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier(oopDesc* src, oop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong(oopDesc* src, oop* load_addr)) return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr); JRT_END -JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_narrow(oopDesc* src, narrowOop* load_addr)) +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_strong_narrow(oopDesc* src, narrowOop* load_addr)) return ShenandoahBarrierSet::barrier_set()->load_reference_barrier_mutator(src, load_addr); JRT_END @@ -67,9 +67,13 @@ JRT_LEAF(void, ShenandoahRuntime::shenandoah_clone_barrier(oopDesc* src)) JRT_END JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak(oopDesc * src, oop* load_addr)) - return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); + return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); JRT_END JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_weak_narrow(oopDesc * src, narrowOop* load_addr)) - return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); + return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); +JRT_END + +JRT_LEAF(oopDesc*, ShenandoahRuntime::load_reference_barrier_phantom(oopDesc * src, oop* load_addr)) + return (oopDesc*) ShenandoahBarrierSet::barrier_set()->load_reference_barrier(oop(src), load_addr); JRT_END diff --git a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp index c0446d3216853..9a73493269a91 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahRuntime.hpp @@ -38,12 +38,14 @@ class ShenandoahRuntime : public AllStatic { static void write_ref_field_pre_entry(oopDesc* orig, JavaThread* thread); - static oopDesc* load_reference_barrier(oopDesc* src, oop* load_addr); - static oopDesc* load_reference_barrier_narrow(oopDesc* src, narrowOop* load_addr); + static oopDesc* load_reference_barrier_strong(oopDesc* src, oop* load_addr); + static oopDesc* load_reference_barrier_strong_narrow(oopDesc* src, narrowOop* load_addr); static oopDesc* load_reference_barrier_weak(oopDesc* src, oop* load_addr); static oopDesc* load_reference_barrier_weak_narrow(oopDesc* src, narrowOop* load_addr); + static oopDesc* load_reference_barrier_phantom(oopDesc* src, oop* load_addr); + static void shenandoah_clone_barrier(oopDesc* src); }; From 8c31bd293906bbd490ea8d1a579f9975352f55b6 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 13 Nov 2020 11:07:53 +0000 Subject: [PATCH 103/124] 8256275: Optimized build is broken Reviewed-by: redestad, coleenp --- src/hotspot/share/classfile/vmIntrinsics.cpp | 4 ++-- src/hotspot/share/classfile/vmIntrinsics.hpp | 2 +- src/hotspot/share/gc/g1/g1RemSet.hpp | 2 +- src/hotspot/share/runtime/stackWatermark.hpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/classfile/vmIntrinsics.cpp b/src/hotspot/share/classfile/vmIntrinsics.cpp index ce3001b0fd99a..03e739491d4ba 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.cpp +++ b/src/hotspot/share/classfile/vmIntrinsics.cpp @@ -705,7 +705,7 @@ const char* vmIntrinsics::short_name_as_C_string(vmIntrinsics::ID id, char* buf, #define ID4(x, y, z, f) ((ID3(x, y, z) << vmIntrinsics::log2_FLAG_LIMIT) | (jlong) (f)) -#ifdef ASSERT +#ifndef PRODUCT static const jlong intrinsic_info_array[vmIntrinsics::ID_LIMIT+1] = { #define VM_INTRINSIC_INFO(ignore_id, klass, name, sig, fcode) \ ID4(SID_ENUM(klass), SID_ENUM(name), SID_ENUM(sig), vmIntrinsics::fcode), @@ -747,4 +747,4 @@ vmIntrinsics::Flags vmIntrinsics::flags_for(vmIntrinsics::ID id) { assert(((ID4(1021,1022,1023,15) >> shift) & mask) == 15, ""); return Flags( (info >> shift) & mask ); } -#endif // ASSERT +#endif // !PRODUCT diff --git a/src/hotspot/share/classfile/vmIntrinsics.hpp b/src/hotspot/share/classfile/vmIntrinsics.hpp index f165adf39fcf4..69b67aeaa9e65 100644 --- a/src/hotspot/share/classfile/vmIntrinsics.hpp +++ b/src/hotspot/share/classfile/vmIntrinsics.hpp @@ -1092,7 +1092,7 @@ class vmIntrinsics : AllStatic { return id; } -#ifdef ASSERT +#ifndef PRODUCT // Find out the symbols behind an intrinsic: static vmSymbolID class_for(ID id); static vmSymbolID name_for(ID id); diff --git a/src/hotspot/share/gc/g1/g1RemSet.hpp b/src/hotspot/share/gc/g1/g1RemSet.hpp index f1a090d9b5b78..0dc6264288a04 100644 --- a/src/hotspot/share/gc/g1/g1RemSet.hpp +++ b/src/hotspot/share/gc/g1/g1RemSet.hpp @@ -68,7 +68,7 @@ class G1RemSet: public CHeapObj { void print_merge_heap_roots_stats(); - void assert_scan_top_is_null(uint hrm_index) PRODUCT_RETURN; + void assert_scan_top_is_null(uint hrm_index) NOT_DEBUG_RETURN; public: typedef CardTable::CardValue CardValue; diff --git a/src/hotspot/share/runtime/stackWatermark.hpp b/src/hotspot/share/runtime/stackWatermark.hpp index 342bbd156131c..99bd9b9f4055d 100644 --- a/src/hotspot/share/runtime/stackWatermark.hpp +++ b/src/hotspot/share/runtime/stackWatermark.hpp @@ -101,7 +101,7 @@ class StackWatermark : public CHeapObj { void yield_processing(); static bool has_barrier(const frame& f); void ensure_safe(const frame& f); - void assert_is_frame_safe(const frame& f) PRODUCT_RETURN; + void assert_is_frame_safe(const frame& f) NOT_DEBUG_RETURN; bool is_frame_safe(const frame& f); // API for consumers of the stack watermark barrier. From 5973e91cc3db8cbba20ce8028a3f6f9373f12a33 Mon Sep 17 00:00:00 2001 From: Patrick Concannon Date: Fri, 13 Nov 2020 11:31:25 +0000 Subject: [PATCH 104/124] 8253005: Add `@throws IOException` in javadoc for `HttpEchange.sendResponseHeaders` Reviewed-by: dfuchs --- .../com/sun/net/httpserver/HttpExchange.java | 7 +- .../httpclient/SendResponseHeadersTest.java | 122 ++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 test/jdk/java/net/httpclient/SendResponseHeadersTest.java diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java index 32e664cd2ee7b..1508dcc06fe1a 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpExchange.java @@ -30,6 +30,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.URI; +import java.util.Map; /** * This class encapsulates a HTTP request received and a @@ -163,7 +164,7 @@ protected HttpExchange() { *

    If the call to {@link #sendResponseHeaders(int, long)} specified a * fixed response body length, then the exact number of bytes specified in * that call must be written to this stream. If too many bytes are written, - * then {@link OutputStream#write()} will throw an {@code IOException}. + * then the write method of {@link OutputStream} will throw an {@code IOException}. * If too few bytes are written then the stream * {@link OutputStream#close()} will throw an {@code IOException}. * In both cases, the exchange is aborted and the underlying TCP connection @@ -207,7 +208,7 @@ protected HttpExchange() { * and an arbitrary number of bytes may be written. * If {@literal <= -1}, then no response body length is * specified and no response body may be written. - * + * @throws IOException if the response headers have already been sent or an I/O error occurs * @see HttpExchange#getResponseBody() */ public abstract void sendResponseHeaders(int rCode, long responseLength) throws IOException; @@ -243,7 +244,7 @@ protected HttpExchange() { public abstract String getProtocol(); /** - * {@Link Filter} modules may store arbitrary objects with {@code HttpExchange} + * {@link Filter} modules may store arbitrary objects with {@code HttpExchange} * instances as an out-of-band communication mechanism. Other filters * or the exchange handler may then access these objects. * diff --git a/test/jdk/java/net/httpclient/SendResponseHeadersTest.java b/test/jdk/java/net/httpclient/SendResponseHeadersTest.java new file mode 100644 index 0000000000000..df8bc3b084494 --- /dev/null +++ b/test/jdk/java/net/httpclient/SendResponseHeadersTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8253005 + * @library /test/lib + * @summary Check that sendResponseHeaders throws an IOException when headers + * have already been sent + * @run testng/othervm SendResponseHeadersTest + */ + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import jdk.test.lib.net.URIBuilder; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static java.net.http.HttpClient.Builder.NO_PROXY; +import static org.testng.Assert.expectThrows; +import static org.testng.Assert.fail; + +public class SendResponseHeadersTest { + URI uri; + HttpServer server; + ExecutorService executor; + + @BeforeTest + public void setUp() throws IOException, URISyntaxException { + var loopback = InetAddress.getLoopbackAddress(); + var addr = new InetSocketAddress(loopback, 0); + server = HttpServer.create(addr, 0); + server.createContext("/test", new TestHandler()); + executor = Executors.newCachedThreadPool(); + server.setExecutor(executor); + server.start(); + + uri = URIBuilder.newBuilder() + .scheme("http") + .host(loopback) + .port(server.getAddress().getPort()) + .path("/test/foo.html") + .build(); + } + + @Test + public void testSend() throws Exception { + HttpClient client = HttpClient.newBuilder() + .proxy(NO_PROXY) + .build(); + HttpRequest request = HttpRequest.newBuilder(uri) + .GET() + .build(); + HttpResponse response = client.send(request, BodyHandlers.ofString()); + // verify empty response received, otherwise an error has occurred + if (!response.body().isEmpty()) + fail(response.body()); + } + + @AfterTest + public void tearDown() { + server.stop(0); + executor.shutdown(); + } + + static class TestHandler implements HttpHandler { + public void handle(HttpExchange exchange) throws IOException { + try (InputStream is = exchange.getRequestBody(); + OutputStream os = exchange.getResponseBody()) { + + is.readAllBytes(); + exchange.sendResponseHeaders(200, 0); + try { + IOException io = expectThrows(IOException.class, + () -> exchange.sendResponseHeaders(200, 0)); + System.out.println("Got expected exception: " + io); + } catch (Throwable t) { + // expectThrows triggered an assertion, return error message to the client + t.printStackTrace(); + os.write(("Unexpected error: " + t).getBytes(StandardCharsets.UTF_8)); + } + } + } + } +} From c8dd0b53ee37e103e02d256268521ea24510f8bb Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Fri, 13 Nov 2020 12:23:08 +0000 Subject: [PATCH 105/124] 8256320: ZGC: Update zDebug to support UseCompressedClassPointers Reviewed-by: pliden --- src/hotspot/share/gc/z/zDebug.gdb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/gc/z/zDebug.gdb b/src/hotspot/share/gc/z/zDebug.gdb index 66a55ea4908cd..210063c3ac3f4 100644 --- a/src/hotspot/share/gc/z/zDebug.gdb +++ b/src/hotspot/share/gc/z/zDebug.gdb @@ -50,7 +50,12 @@ define zpo end printf "\t Page: %llu\n", ((uintptr_t)$obj & ZAddressOffsetMask) >> ZGranuleSizeShift x/16gx $obj - printf "Mark: 0x%016llx\tKlass: %s\n", (uintptr_t)$obj->_mark, (char*)$obj->_metadata->_klass->_name->_body + if (UseCompressedClassPointers) + set $klass = (Klass*)(void*)((uintptr_t)CompressedKlassPointers::_narrow_klass._base +((uintptr_t)$obj->_metadata->_compressed_klass << CompressedKlassPointers::_narrow_klass._shift)) + else + set $klass = $obj->_metadata->_klass + end + printf "Mark: 0x%016llx\tKlass: %s\n", (uintptr_t)$obj->_mark, (char*)$klass->_name->_body end # Print heap page by page table index From e9956fec9aa299eedc2a1749f578dff62f6a3d39 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 13 Nov 2020 12:36:51 +0000 Subject: [PATCH 106/124] 8256127: Add cross-compiled foreign architectures builds to submit workflow Reviewed-by: ihse, rwestberg --- .github/workflows/submit.yml | 558 ++++++++++++++++++++++++++++++++++- 1 file changed, 557 insertions(+), 1 deletion(-) diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index ebe723817382b..f488a9b2b5a9d 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -9,7 +9,7 @@ on: platforms: description: "Platform(s) to execute on" required: true - default: "Linux x64, Linux x86, Windows x64, macOS x64" + default: "Linux aarch64, Linux arm, Linux ppc64le, Linux s390x, Linux x64, Linux x86, Windows x64, macOS x64" jobs: prerequisites: @@ -18,6 +18,10 @@ jobs: outputs: should_run: ${{ steps.check_submit.outputs.should_run }} bundle_id: ${{ steps.check_bundle_id.outputs.bundle_id }} + platform_linux_aarch64: ${{ steps.check_platforms.outputs.platform_linux_aarch64 }} + platform_linux_arm: ${{ steps.check_platforms.outputs.platform_linux_arm }} + platform_linux_ppc64le: ${{ steps.check_platforms.outputs.platform_linux_ppc64le }} + platform_linux_s390x: ${{ steps.check_platforms.outputs.platform_linux_s390x }} platform_linux_x86: ${{ steps.check_platforms.outputs.platform_linux_x86 }} platform_linux_x64: ${{ steps.check_platforms.outputs.platform_linux_x64 }} platform_windows_x64: ${{ steps.check_platforms.outputs.platform_windows_x64 }} @@ -32,6 +36,10 @@ jobs: - name: Check which platforms should be included id: check_platforms run: | + echo "::set-output name=platform_linux_aarch64::${{ contains(github.event.inputs.platforms, 'linux aarch64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux aarch64'))) }}" + echo "::set-output name=platform_linux_arm::${{ contains(github.event.inputs.platforms, 'linux arm') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux arm'))) }}" + echo "::set-output name=platform_linux_ppc64le::${{ contains(github.event.inputs.platforms, 'linux ppc64le') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux ppc64le'))) }}" + echo "::set-output name=platform_linux_s390x::${{ contains(github.event.inputs.platforms, 'linux s390x') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux s390x'))) }}" echo "::set-output name=platform_linux_x64::${{ contains(github.event.inputs.platforms, 'linux x64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux x64'))) }}" echo "::set-output name=platform_linux_x86::${{ contains(github.event.inputs.platforms, 'linux x86') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'linux x86'))) }}" echo "::set-output name=platform_windows_x64::${{ contains(github.event.inputs.platforms, 'windows x64') || (github.event.inputs.platforms == '' && (secrets.JDK_SUBMIT_PLATFORMS == '' || contains(secrets.JDK_SUBMIT_PLATFORMS, 'windows x64'))) }}" @@ -386,6 +394,550 @@ jobs: path: ~/linux-x64${{ matrix.artifact }}_testsupport_${{ env.logsuffix }}.zip continue-on-error: true + linux_aarch64_build: + name: Linux aarch64 + runs-on: "ubuntu-latest" + needs: + - prerequisites + - linux_x64_build + if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_linux_aarch64 != 'false' + + strategy: + fail-fast: false + matrix: + flavor: + - build hotspot no-pch + include: + - flavor: build hotspot no-pch + flags: --enable-debug --disable-precompiled-headers + artifact: -debug + build-target: hotspot + + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + with: + path: jdk + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore build JDK + id: build_restore + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x64_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x64 + continue-on-error: true + + - name: Unpack jdk + run: | + mkdir -p "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + tar -xf "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin.tar.gz" -C "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + + - name: Find root of build JDK image dir + run: | + build_jdk_root=`find ${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin -name release -type f` + echo "build_jdk_root=`dirname ${build_jdk_root}`" >> $GITHUB_ENV + + - name: Install cross-compilation host dependencies + run: | + sudo apt-get update + sudo apt-get install g++-aarch64-linux-gnu gcc-aarch64-linux-gnu + + - name: Cache sysroot + id: cache-sysroot + uses: actions/cache@v2 + with: + path: ~/sysroot-arm64/ + key: sysroot-arm64-${{ hashFiles('jdk/.github/workflows/submit.yml') }} + + - name: Install sysroot host dependencies + run: | + sudo apt-get install debootstrap qemu-user-static + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Create sysroot + run: > + sudo qemu-debootstrap + --arch=arm64 + --verbose + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev + --resolve-deps + buster + ~/sysroot-arm64 + http://httpredir.debian.org/debian/ + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Relativize symlinks in sysroot + run: > + sudo chroot ~/sysroot-arm64 symlinks -cr . + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Fix permissions in sysroot + run: > + sudo chown ${USER} -R ~/sysroot-arm64 + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Remove special directories in sysroot + run: > + rm -rf ~/sysroot-arm64/{dev,proc,run,sys} + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Configure + env: + CC: aarch64-linux-gnu-gcc + CXX: aarch64-linux-gnu-g++ + run: > + bash configure + ${{ matrix.flags }} + --openjdk-target=aarch64-linux-gnu + --with-sysroot=${HOME}/sysroot-arm64/ + --with-toolchain-path=${HOME}/sysroot-arm64/ + --with-freetype-lib=${HOME}/sysroot-arm64/usr/lib/aarch64-linux-gnu/ + --with-freetype-include=${HOME}/sysroot-arm64/usr/include/freetype2/ + --x-libraries=${HOME}/sysroot-arm64/usr/lib/aarch64-linux-gnu/ + --with-build-jdk=${{ env.build_jdk_root }} + --with-conf-name=linux-aarch64 + --with-version-opt=${GITHUB_ACTOR}-${GITHUB_SHA} + --with-version-build=0 + --with-boot-jdk=${HOME}/bootjdk/${BOOT_JDK_VERSION} + --with-default-make-target="product-bundles test-bundles" + --with-zlib=system + working-directory: jdk + + - name: Build + run: make CONF_NAME=linux-aarch64 ${{ matrix.build-target }} + working-directory: jdk + + linux_arm_build: + name: Linux arm + runs-on: "ubuntu-latest" + needs: + - prerequisites + - linux_x64_build + if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_linux_arm != 'false' + + strategy: + fail-fast: false + matrix: + flavor: + - build hotspot no-pch + include: + - flavor: build hotspot no-pch + flags: --enable-debug --disable-precompiled-headers + artifact: -debug + build-target: hotspot + + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + with: + path: jdk + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore build JDK + id: build_restore + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x64_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x64 + continue-on-error: true + + - name: Unpack jdk + run: | + mkdir -p "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + tar -xf "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin.tar.gz" -C "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + + - name: Find root of build JDK image dir + run: | + build_jdk_root=`find ${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin -name release -type f` + echo "build_jdk_root=`dirname ${build_jdk_root}`" >> $GITHUB_ENV + + - name: Install cross-compilation host dependencies + run: | + sudo apt-get update + sudo apt-get install g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf + + - name: Cache sysroot + id: cache-sysroot + uses: actions/cache@v2 + with: + path: ~/sysroot-armhf/ + key: sysroot-armhf-${{ hashFiles('jdk/.github/workflows/submit.yml') }} + + - name: Install sysroot host dependencies + run: | + sudo apt-get install debootstrap qemu-user-static + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Create sysroot + run: > + sudo qemu-debootstrap + --arch=armhf + --verbose + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev + --resolve-deps + buster + ~/sysroot-armhf + http://httpredir.debian.org/debian/ + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Relativize symlinks in sysroot + run: > + sudo chroot ~/sysroot-armhf symlinks -cr . + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Fix permissions in sysroot + run: > + sudo chown ${USER} -R ~/sysroot-armhf + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Remove special directories in sysroot + run: > + rm -rf ~/sysroot-armhf/{dev,proc,run,sys} + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Configure + env: + CC: arm-linux-gnueabihf-gcc + CXX: arm-linux-gnueabihf-g++ + run: > + bash configure + ${{ matrix.flags }} + --openjdk-target=arm-linux-gnueabihf + --with-sysroot=${HOME}/sysroot-armhf/ + --with-toolchain-path=${HOME}/sysroot-armhf/ + --with-freetype-lib=${HOME}/sysroot-armhf/usr/lib/arm-linux-gnueabihf/ + --with-freetype-include=${HOME}/sysroot-armhf/usr/include/freetype2/ + --x-libraries=${HOME}/sysroot-armhf/usr/lib/arm-linux-gnueabihf/ + --with-build-jdk=${{ env.build_jdk_root }} + --with-conf-name=linux-arm + --with-version-opt=${GITHUB_ACTOR}-${GITHUB_SHA} + --with-version-build=0 + --with-boot-jdk=${HOME}/bootjdk/${BOOT_JDK_VERSION} + --with-default-make-target="product-bundles test-bundles" + --with-zlib=system + working-directory: jdk + + - name: Build + run: make CONF_NAME=linux-arm ${{ matrix.build-target }} + working-directory: jdk + + linux_s390x_build: + name: Linux s390x + runs-on: "ubuntu-latest" + needs: + - prerequisites + - linux_x64_build + if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_linux_s390x != 'false' + + strategy: + fail-fast: false + matrix: + flavor: + - build hotspot no-pch + include: + - flavor: build hotspot no-pch + flags: --enable-debug --disable-precompiled-headers + artifact: -debug + build-target: hotspot + + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + with: + path: jdk + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore build JDK + id: build_restore + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x64_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x64 + continue-on-error: true + + - name: Unpack jdk + run: | + mkdir -p "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + tar -xf "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin.tar.gz" -C "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + + - name: Find root of build JDK image dir + run: | + build_jdk_root=`find ${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin -name release -type f` + echo "build_jdk_root=`dirname ${build_jdk_root}`" >> $GITHUB_ENV + + - name: Install cross-compilation host dependencies + run: | + sudo apt-get update + sudo apt-get install g++-s390x-linux-gnu gcc-s390x-linux-gnu + + - name: Cache sysroot + id: cache-sysroot + uses: actions/cache@v2 + with: + path: ~/sysroot-s390x/ + key: sysroot-s390x-${{ hashFiles('jdk/.github/workflows/submit.yml') }} + + - name: Install sysroot host dependencies + run: | + sudo apt-get install debootstrap qemu-user-static + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Create sysroot + run: > + sudo qemu-debootstrap + --arch=s390x + --verbose + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev + --resolve-deps + buster + ~/sysroot-s390x + http://httpredir.debian.org/debian/ + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Relativize symlinks in sysroot + run: > + sudo chroot ~/sysroot-s390x symlinks -cr . + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Fix permissions in sysroot + run: > + sudo chown ${USER} -R ~/sysroot-s390x + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Remove special directories in sysroot + run: > + rm -rf ~/sysroot-s390x/{dev,proc,run,sys} + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Configure + env: + CC: s390x-linux-gnu-gcc + CXX: s390x-linux-gnu-g++ + run: > + bash configure + ${{ matrix.flags }} + --openjdk-target=s390x-linux-gnu + --with-sysroot=${HOME}/sysroot-s390x/ + --with-toolchain-path=${HOME}/sysroot-s390x/ + --with-freetype-lib=${HOME}/sysroot-s390x/usr/lib/s390x-linux-gnu/ + --with-freetype-include=${HOME}/sysroot-s390x/usr/include/freetype2/ + --x-libraries=${HOME}/sysroot-s390x/usr/lib/s390x-linux-gnu/ + --with-build-jdk=${{ env.build_jdk_root }} + --with-conf-name=linux-s390x + --with-version-opt=${GITHUB_ACTOR}-${GITHUB_SHA} + --with-version-build=0 + --with-boot-jdk=${HOME}/bootjdk/${BOOT_JDK_VERSION} + --with-default-make-target="product-bundles test-bundles" + --with-zlib=system + working-directory: jdk + + - name: Build + run: make CONF_NAME=linux-s390x ${{ matrix.build-target }} + working-directory: jdk + + linux_ppc64le_build: + name: Linux ppc64le + runs-on: "ubuntu-latest" + needs: + - prerequisites + - linux_x64_build + if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_linux_ppc64le != 'false' + + strategy: + fail-fast: false + matrix: + flavor: + - build hotspot no-pch + include: + - flavor: build hotspot no-pch + flags: --enable-debug --disable-precompiled-headers + artifact: -debug + build-target: hotspot + + env: + JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" + BOOT_JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).BOOT_JDK_VERSION }}" + BOOT_JDK_FILENAME: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_FILENAME }}" + BOOT_JDK_URL: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_URL }}" + BOOT_JDK_SHA256: "${{ fromJson(needs.prerequisites.outputs.dependencies).LINUX_X64_BOOT_JDK_SHA256 }}" + + steps: + - name: Checkout the source + uses: actions/checkout@v2 + with: + path: jdk + + - name: Restore boot JDK from cache + id: bootjdk + uses: actions/cache@v2 + with: + path: ~/bootjdk/${{ env.BOOT_JDK_VERSION }} + key: bootjdk-${{ runner.os }}-${{ env.BOOT_JDK_VERSION }}-${{ env.BOOT_JDK_SHA256 }}-v1 + + - name: Download boot JDK + run: | + mkdir -p "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + wget -O "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" "${BOOT_JDK_URL}" + echo "${BOOT_JDK_SHA256} ${HOME}/bootjdk/${BOOT_JDK_FILENAME}" | sha256sum -c >/dev/null - + tar -xf "${HOME}/bootjdk/${BOOT_JDK_FILENAME}" -C "${HOME}/bootjdk/${BOOT_JDK_VERSION}" + mv "${HOME}/bootjdk/${BOOT_JDK_VERSION}/"*/* "${HOME}/bootjdk/${BOOT_JDK_VERSION}/" + if: steps.bootjdk.outputs.cache-hit != 'true' + + - name: Restore build JDK + id: build_restore + uses: actions/download-artifact@v2 + with: + name: transient_jdk-linux-x64_${{ needs.prerequisites.outputs.bundle_id }} + path: ~/jdk-linux-x64 + continue-on-error: true + + - name: Unpack jdk + run: | + mkdir -p "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + tar -xf "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin.tar.gz" -C "${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin" + + - name: Find root of build JDK image dir + run: | + build_jdk_root=`find ${HOME}/jdk-linux-x64/jdk-${{ env.JDK_VERSION }}-internal+0_linux-x64_bin -name release -type f` + echo "build_jdk_root=`dirname ${build_jdk_root}`" >> $GITHUB_ENV + + - name: Install cross-compilation host dependencies + run: | + sudo apt-get update + sudo apt-get install g++-powerpc64le-linux-gnu gcc-powerpc64le-linux-gnu: + + - name: Cache sysroot + id: cache-sysroot + uses: actions/cache@v2 + with: + path: ~/sysroot-ppc64el/ + key: sysroot-ppc64el-${{ hashFiles('jdk/.github/workflows/submit.yml') }} + + - name: Install sysroot host dependencies + run: | + sudo apt-get install debootstrap qemu-user-static + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Create sysroot + run: > + sudo qemu-debootstrap + --arch=ppc64el + --verbose + --include=fakeroot,symlinks,build-essential,libx11-dev,libxext-dev,libxrender-dev,libxrandr-dev,libxtst-dev,libxt-dev,libcups2-dev,libfontconfig1-dev,libasound2-dev,libfreetype6-dev,libpng-dev + --resolve-deps + buster + ~/sysroot-ppc64el + http://httpredir.debian.org/debian/ + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Relativize symlinks in sysroot + run: > + sudo chroot ~/sysroot-ppc64el symlinks -cr . + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Fix permissions in sysroot + run: > + sudo chown ${USER} -R ~/sysroot-ppc64el + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Remove special directories in sysroot + run: > + rm -rf ~/sysroot-ppc64el/{dev,proc,run,sys} + if: steps.cache-sysroot.outputs.cache-hit != 'true' + + - name: Configure + env: + CC: powerpc64le-linux-gnu-gcc + CXX: powerpc64le-linux-gnu-g++ + run: > + bash configure + ${{ matrix.flags }} + --openjdk-target=powerpc64le-linux-gnu + --with-sysroot=${HOME}/sysroot-ppc64el/ + --with-toolchain-path=${HOME}/sysroot-ppc64el/ + --with-freetype-lib=${HOME}/sysroot-ppc64el/usr/lib/powerpc64le-linux-gnu/ + --with-freetype-include=${HOME}/sysroot-ppc64el/usr/include/freetype2/ + --x-libraries=${HOME}/sysroot-ppc64el/usr/lib/powerpc64le-linux-gnu/ + --with-build-jdk=${{ env.build_jdk_root }} + --with-conf-name=linux-ppc64le + --with-version-opt=${GITHUB_ACTOR}-${GITHUB_SHA} + --with-version-build=0 + --with-boot-jdk=${HOME}/bootjdk/${BOOT_JDK_VERSION} + --with-default-make-target="product-bundles test-bundles" + --with-zlib=system + working-directory: jdk + + - name: Build + run: make CONF_NAME=linux-ppc64le ${{ matrix.build-target }} + working-directory: jdk + linux_x86_build: name: Linux x86 runs-on: "ubuntu-latest" @@ -1278,6 +1830,10 @@ jobs: continue-on-error: true needs: - prerequisites + - linux_aarch64_build + - linux_arm_build + - linux_ppc64le_build + - linux_s390x_build - linux_x64_test - linux_x86_test - windows_x64_test From 56ea7864d435fcdafbe884c6b23c079508f6c880 Mon Sep 17 00:00:00 2001 From: Harold Seigel Date: Fri, 13 Nov 2020 13:13:43 +0000 Subject: [PATCH 107/124] 8245215: Obsolete InitialBootClassLoaderMetaspaceSize and UseLargePagesInMetaspace Reviewed-by: lfoltan, ccheung, stuefe, coleenp --- src/hotspot/share/runtime/arguments.cpp | 4 ++-- src/hotspot/share/runtime/globals.hpp | 9 --------- .../CompressedClassPointers.java | 18 ------------------ 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 549370cb8fa0c..b7bc61fd725be 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -529,8 +529,6 @@ static SpecialFlag const special_jvm_flags[] = { { "BiasedLockingDecayTime", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "UseOptoBiasInlining", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "PrintPreciseBiasedLockingStatistics", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, - { "InitialBootClassLoaderMetaspaceSize", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, - { "UseLargePagesInMetaspace", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "CriticalJNINatives", JDK_Version::jdk(16), JDK_Version::jdk(17), JDK_Version::jdk(18) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: @@ -553,6 +551,8 @@ static SpecialFlag const special_jvm_flags[] = { { "UseNewFieldLayout", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "UseSemaphoreGCThreadsSynchronization", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "ForceNUMA", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, + { "InitialBootClassLoaderMetaspaceSize", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, + { "UseLargePagesInMetaspace", JDK_Version::jdk(15), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "InsertMemBarAfterArraycopy", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "Debugging", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, { "UseRDPCForConstantTableBase", JDK_Version::undefined(), JDK_Version::jdk(16), JDK_Version::jdk(17) }, diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index b0310915ae6f6..dc9a85772e839 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -197,10 +197,6 @@ const intx ObjectAlignmentInBytes = 8; develop(bool, LargePagesIndividualAllocationInjectError, false, \ "Fail large pages individual allocation") \ \ - product(bool, UseLargePagesInMetaspace, false, \ - "(Deprecated) Use large page memory in metaspace. " \ - "Only used if UseLargePages is enabled.") \ - \ product(bool, UseNUMA, false, \ "Use NUMA if available") \ \ @@ -946,11 +942,6 @@ const intx ObjectAlignmentInBytes = 8; product(bool, IgnoreEmptyClassPaths, false, \ "Ignore empty path elements in -classpath") \ \ - product(size_t, InitialBootClassLoaderMetaspaceSize, \ - NOT_LP64(2200*K) LP64_ONLY(4*M), \ - "(Deprecated) Initial size of the boot class loader data metaspace") \ - range(30*K, max_uintx/BytesPerWord) \ - \ product(bool, PrintHeapAtSIGBREAK, true, \ "Print heap layout in response to SIGBREAK") \ \ diff --git a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java index 8fd125e2aab26..ec46c128d7291 100644 --- a/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java +++ b/test/hotspot/jtreg/runtime/CompressedOops/CompressedClassPointers.java @@ -120,23 +120,6 @@ public static void largePagesForHeapTest() throws Exception { output.shouldHaveExitValue(0); } - // Using large pages for heap and metaspace. - // Note that this is still unexciting since the compressed class space always uses small pages; - // UseLargePagesInMetaspace only affects non-class metaspace. - public static void largePagesForHeapAndMetaspaceTest() throws Exception { - ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( - "-XX:+UnlockDiagnosticVMOptions", - "-Xmx128m", - "-XX:+UseLargePages", "-XX:+UseLargePagesInMetaspace", - logging_option, - "-XX:+VerifyBeforeGC", "-version"); - OutputAnalyzer output = new OutputAnalyzer(pb.start()); - if (testNarrowKlassBase()) { - output.shouldContain("Narrow klass base:"); - } - output.shouldHaveExitValue(0); - } - public static void heapBaseMinAddressTest() throws Exception { ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( "-XX:HeapBaseMinAddress=1m", @@ -311,7 +294,6 @@ public static void main(String[] args) throws Exception { smallHeapTestWith1G(); largeHeapTest(); largePagesForHeapTest(); - largePagesForHeapAndMetaspaceTest(); heapBaseMinAddressTest(); sharingTest(); From 1c47244b01dd9e7e176dd91215baef2afbc2626f Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Fri, 13 Nov 2020 15:10:41 +0000 Subject: [PATCH 108/124] 8255244: HttpClient: Response headers contain incorrectly encoded Unicode characters Reviewed-by: chegar, michaelm --- .../internal/net/http/Http1HeaderParser.java | 63 ++- .../internal/net/http/Http2Connection.java | 1 + .../jdk/internal/net/http/common/Utils.java | 2 +- .../net/httpclient/HttpServerAdapters.java | 4 +- .../java/net/httpclient/ISO_8859_1_Test.java | 442 ++++++++++++++++++ .../net/http/Http1HeaderParserTest.java | 75 ++- 6 files changed, 561 insertions(+), 26 deletions(-) create mode 100644 test/jdk/java/net/httpclient/ISO_8859_1_Test.java diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java index 4e4032537bb42..6de2bbfcf9fe8 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http1HeaderParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package jdk.internal.net.http; import java.net.ProtocolException; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; @@ -33,6 +34,9 @@ import java.util.Locale; import java.util.Map; import java.net.http.HttpHeaders; + +import jdk.internal.net.http.common.Utils; + import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static jdk.internal.net.http.common.Utils.ACCEPT_ALL; @@ -166,9 +170,30 @@ private boolean canContinueParsing(ByteBuffer buffer) { } } + /** + * Returns a character (char) corresponding to the next byte in the + * input, interpreted as an ISO-8859-1 encoded character. + *

    + * The ISO-8859-1 encoding is a 8-bit character coding that + * corresponds to the first 256 Unicode characters - from U+0000 to + * U+00FF. UTF-16 is backward compatible with ISO-8859-1 - which + * means each byte in the input should be interpreted as an unsigned + * value from [0, 255] representing the character code. + * + * @param input a {@code ByteBuffer} containing a partial input + * @return the next byte in the input, interpreted as an ISO-8859-1 + * encoded char + * @throws BufferUnderflowException + * if the input buffer's current position is not smaller + * than its limit + */ + private char get(ByteBuffer input) { + return (char)(input.get() & 0xFF); + } + private void readResumeStatusLine(ByteBuffer input) { char c = 0; - while (input.hasRemaining() && (c =(char)input.get()) != CR) { + while (input.hasRemaining() && (c = get(input)) != CR) { if (c == LF) break; sb.append(c); } @@ -180,7 +205,7 @@ private void readResumeStatusLine(ByteBuffer input) { } private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { - char c = state == State.STATUS_LINE_FOUND_LF ? LF : (char)input.get(); + char c = state == State.STATUS_LINE_FOUND_LF ? LF : get(input); if (c != LF) { throw protocolException("Bad trailing char, \"%s\", when parsing status line, \"%s\"", c, sb.toString()); @@ -210,7 +235,7 @@ private void readStatusLineFeed(ByteBuffer input) throws ProtocolException { private void maybeStartHeaders(ByteBuffer input) { assert state == State.STATUS_LINE_END; assert sb.length() == 0; - char c = (char)input.get(); + char c = get(input); if (c == CR) { state = State.STATUS_LINE_END_CR; } else if (c == LF) { @@ -224,7 +249,7 @@ private void maybeStartHeaders(ByteBuffer input) { private void maybeEndHeaders(ByteBuffer input) throws ProtocolException { assert state == State.STATUS_LINE_END_CR || state == State.STATUS_LINE_END_LF; assert sb.length() == 0; - char c = state == State.STATUS_LINE_END_LF ? LF : (char)input.get(); + char c = state == State.STATUS_LINE_END_LF ? LF : get(input); if (c == LF) { headers = HttpHeaders.of(privateMap, ACCEPT_ALL); privateMap = null; @@ -238,7 +263,7 @@ private void readResumeHeader(ByteBuffer input) { assert state == State.HEADER; assert input.hasRemaining(); while (input.hasRemaining()) { - char c = (char)input.get(); + char c = get(input); if (c == CR) { state = State.HEADER_FOUND_CR; break; @@ -253,15 +278,23 @@ private void readResumeHeader(ByteBuffer input) { } } - private void addHeaderFromString(String headerString) { + private void addHeaderFromString(String headerString) throws ProtocolException { assert sb.length() == 0; int idx = headerString.indexOf(':'); if (idx == -1) return; - String name = headerString.substring(0, idx).trim(); - if (name.isEmpty()) - return; - String value = headerString.substring(idx + 1, headerString.length()).trim(); + String name = headerString.substring(0, idx); + + // compatibility with HttpURLConnection; + if (name.isEmpty()) return; + + if (!Utils.isValidName(name)) { + throw protocolException("Invalid header name \"%s\"", name); + } + String value = headerString.substring(idx + 1).trim(); + if (!Utils.isValidValue(value)) { + throw protocolException("Invalid header value \"%s: %s\"", name, value); + } privateMap.computeIfAbsent(name.toLowerCase(Locale.US), k -> new ArrayList<>()).add(value); @@ -269,7 +302,7 @@ private void addHeaderFromString(String headerString) { private void resumeOrLF(ByteBuffer input) { assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF; - char c = state == State.HEADER_FOUND_LF ? LF : (char)input.get(); + char c = state == State.HEADER_FOUND_LF ? LF : get(input); if (c == LF) { // header value will be flushed by // resumeOrSecondCR if next line does not @@ -285,9 +318,9 @@ private void resumeOrLF(ByteBuffer input) { } } - private void resumeOrSecondCR(ByteBuffer input) { + private void resumeOrSecondCR(ByteBuffer input) throws ProtocolException { assert state == State.HEADER_FOUND_CR_LF; - char c = (char)input.get(); + char c = get(input); if (c == CR || c == LF) { if (sb.length() > 0) { // no continuation line - flush @@ -322,7 +355,7 @@ private void resumeOrSecondCR(ByteBuffer input) { private void resumeOrEndHeaders(ByteBuffer input) throws ProtocolException { assert state == State.HEADER_FOUND_CR_LF_CR; - char c = (char)input.get(); + char c = get(input); if (c == LF) { state = State.FINISHED; headers = HttpHeaders.of(privateMap, ACCEPT_ALL); diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java index 554e35449b92f..9cfc3d3f95ab1 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/Http2Connection.java @@ -801,6 +801,7 @@ void processFrame(Http2Frame frame) throws IOException { try { decodeHeaders((HeaderFrame) frame, stream.rspHeadersConsumer()); } catch (UncheckedIOException e) { + debug.log("Error decoding headers: " + e.getMessage(), e); protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage()); return; } diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java index 3236517de4058..fb16496edfeb1 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/common/Utils.java @@ -400,7 +400,7 @@ public static URLPermission permissionForServer(URI uri, for (char c : allowedTokenChars) { tchar[c] = true; } - for (char c = 0x21; c < 0xFF; c++) { + for (char c = 0x21; c <= 0xFF; c++) { fieldvchar[c] = true; } fieldvchar[0x7F] = false; // a little hole (DEL) in the range diff --git a/test/jdk/java/net/httpclient/HttpServerAdapters.java b/test/jdk/java/net/httpclient/HttpServerAdapters.java index 5147afa4f3278..07a836b6ec259 100644 --- a/test/jdk/java/net/httpclient/HttpServerAdapters.java +++ b/test/jdk/java/net/httpclient/HttpServerAdapters.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -196,7 +196,7 @@ public void addHeader(String name, String value) { /** * A version agnostic adapter class for HTTP Server Exchange. */ - public static abstract class HttpTestExchange { + public static abstract class HttpTestExchange implements AutoCloseable { public abstract Version getServerVersion(); public abstract Version getExchangeVersion(); public abstract InputStream getRequestBody(); diff --git a/test/jdk/java/net/httpclient/ISO_8859_1_Test.java b/test/jdk/java/net/httpclient/ISO_8859_1_Test.java new file mode 100644 index 0000000000000..256e42ac30f47 --- /dev/null +++ b/test/jdk/java/net/httpclient/ISO_8859_1_Test.java @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8252374 + * @library /test/lib http2/server + * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters + * ReferenceTracker AggregateRequestBodyTest + * @modules java.base/sun.net.www.http + * java.net.http/jdk.internal.net.http.common + * java.net.http/jdk.internal.net.http.frame + * java.net.http/jdk.internal.net.http.hpack + * @run testng/othervm -Djdk.internal.httpclient.debug=true + * -Djdk.httpclient.HttpClient.log=requests,responses,errors + * ISO_8859_1_Test + * @summary Tests that a client is able to receive ISO-8859-1 encoded header values. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URL; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.Flow.Subscription; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import javax.net.ssl.SSLContext; + +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; +import jdk.test.lib.net.SimpleSSLContext; +import org.testng.Assert; +import org.testng.ITestContext; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterTest; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static java.lang.System.out; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.expectThrows; + +public class ISO_8859_1_Test implements HttpServerAdapters { + + SSLContext sslContext; + DummyServer http1DummyServer; + HttpServerAdapters.HttpTestServer http1TestServer; // HTTP/1.1 ( http ) + HttpServerAdapters.HttpTestServer https1TestServer; // HTTPS/1.1 ( https ) + HttpServerAdapters.HttpTestServer http2TestServer; // HTTP/2 ( h2c ) + HttpServerAdapters.HttpTestServer https2TestServer; // HTTP/2 ( h2 ) + String http1Dummy; + String http1URI; + String https1URI; + String http2URI; + String https2URI; + + static final int RESPONSE_CODE = 200; + static final int ITERATION_COUNT = 4; + static final Class IAE = IllegalArgumentException.class; + static final Class CE = CompletionException.class; + // a shared executor helps reduce the amount of threads created by the test + static final Executor executor = new TestExecutor(Executors.newCachedThreadPool()); + static final ConcurrentMap FAILURES = new ConcurrentHashMap<>(); + static volatile boolean tasksFailed; + static final AtomicLong serverCount = new AtomicLong(); + static final AtomicLong clientCount = new AtomicLong(); + static final long start = System.nanoTime(); + public static String now() { + long now = System.nanoTime() - start; + long secs = now / 1000_000_000; + long mill = (now % 1000_000_000) / 1000_000; + long nan = now % 1000_000; + return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); + } + + final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE; + private volatile HttpClient sharedClient; + + static class TestExecutor implements Executor { + final AtomicLong tasks = new AtomicLong(); + Executor executor; + TestExecutor(Executor executor) { + this.executor = executor; + } + + @Override + public void execute(Runnable command) { + long id = tasks.incrementAndGet(); + executor.execute(() -> { + try { + command.run(); + } catch (Throwable t) { + tasksFailed = true; + System.out.printf(now() + "Task %s failed: %s%n", id, t); + System.err.printf(now() + "Task %s failed: %s%n", id, t); + FAILURES.putIfAbsent("Task " + id, t); + throw t; + } + }); + } + } + + protected boolean stopAfterFirstFailure() { + return Boolean.getBoolean("jdk.internal.httpclient.debug"); + } + + @BeforeMethod + void beforeMethod(ITestContext context) { + if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { + throw new RuntimeException("some tests failed"); + } + } + + @AfterClass + static final void printFailedTests() { + out.println("\n========================="); + try { + out.printf("%n%sCreated %d servers and %d clients%n", + now(), serverCount.get(), clientCount.get()); + if (FAILURES.isEmpty()) return; + out.println("Failed tests: "); + FAILURES.entrySet().forEach((e) -> { + out.printf("\t%s: %s%n", e.getKey(), e.getValue()); + e.getValue().printStackTrace(out); + e.getValue().printStackTrace(); + }); + if (tasksFailed) { + System.out.println("WARNING: Some tasks failed"); + } + } finally { + out.println("\n=========================\n"); + } + } + + private String[] uris() { + return new String[] { + http1Dummy, + http1URI, + https1URI, + http2URI, + https2URI, + }; + } + + static AtomicLong URICOUNT = new AtomicLong(); + + @DataProvider(name = "variants") + public Object[][] variants(ITestContext context) { + if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) { + return new Object[0][]; + } + String[] uris = uris(); + Object[][] result = new Object[uris.length * 2][]; + int i = 0; + for (boolean sameClient : List.of(false, true)) { + for (String uri : uris()) { + result[i++] = new Object[]{uri, sameClient}; + } + } + assert i == uris.length * 2; + return result; + } + + private HttpClient makeNewClient() { + clientCount.incrementAndGet(); + HttpClient client = HttpClient.newBuilder() + .proxy(HttpClient.Builder.NO_PROXY) + .executor(executor) + .sslContext(sslContext) + .build(); + return TRACKER.track(client); + } + + HttpClient newHttpClient(boolean share) { + if (!share) return makeNewClient(); + HttpClient shared = sharedClient; + if (shared != null) return shared; + synchronized (this) { + shared = sharedClient; + if (shared == null) { + shared = sharedClient = makeNewClient(); + } + return shared; + } + } + + private static final Exception completionCause(CompletionException x) { + Throwable c = x; + while (c instanceof CompletionException + || c instanceof ExecutionException) { + if (c.getCause() == null) break; + c = c.getCause(); + } + if (c instanceof Error) throw (Error)c; + return (Exception)c; + } + + @Test(dataProvider = "variants") + public void test(String uri, boolean sameClient) throws Exception { + System.out.println("Request to " + uri); + + HttpClient client = newHttpClient(sameClient); + + List>> cfs = new ArrayList<>(); + for (int i = 0; i < ITERATION_COUNT; i++) { + HttpRequest request = HttpRequest.newBuilder(URI.create(uri + "/" + i)) + .build(); + cfs.add(client.sendAsync(request, BodyHandlers.ofString())); + } + try { + CompletableFuture.allOf(cfs.toArray(CompletableFuture[]::new)).join(); + } catch (CompletionException x) { + throw completionCause(x); + } + for (CompletableFuture> cf : cfs) { + var response = cf.get(); + System.out.println("Got: " + response); + var value = response.headers().firstValue("Header8859").orElse(null); + assertEquals(value, "U\u00ffU"); + } + System.out.println("HttpClient: PASSED"); + if (uri.contains("http1")) { + System.out.println("Testing with URLConnection"); + var url = URI.create(uri).toURL(); + var conn = url.openConnection(); + conn.connect(); + conn.getInputStream().readAllBytes(); + var value = conn.getHeaderField("Header8859"); + assertEquals(value, "U\u00ffU", "legacy stack failed"); + System.out.println("URLConnection: PASSED"); + } + System.out.println("test: DONE"); + } + + static final class DummyServer extends Thread implements AutoCloseable { + String RESP = """ + HTTP/1.1 200 OK\r + Content-length: 0\r + Header8859: U\u00ffU\r + Connection: close\r + \r + """; + + static final InetSocketAddress LOOPBACK = + new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + final ServerSocket socket; + final CopyOnWriteArrayList accepted = new CopyOnWriteArrayList(); + final CompletableFuture done = new CompletableFuture(); + volatile boolean closed; + DummyServer() throws IOException { + socket = new ServerSocket(); + socket.bind(LOOPBACK); + } + + public String serverAuthority() { + String address = socket.getInetAddress().getHostAddress(); + if (address.indexOf(':') >= 0) { + address = "[" + address + "]"; + } + return address + ":" + socket.getLocalPort(); + } + + public void run() { + try { + while (!socket.isClosed()) { + try (Socket client = socket.accept()) { + accepted.add(client); + try { + System.out.println("Accepted: " + client); + String req = ""; + BufferedReader reader = new BufferedReader( + new InputStreamReader(client.getInputStream(), + StandardCharsets.ISO_8859_1)); + String line = null; + while (!(line = reader.readLine()).isEmpty()) { + System.out.println("Got line: " + line); + req = req + line + "\r\n"; + } + System.out.println(req); + System.out.println("Sending back " + RESP); + client.getOutputStream().write(RESP.getBytes(StandardCharsets.ISO_8859_1)); + client.getOutputStream().flush(); + } finally { + accepted.remove(client); + } + } + } + } catch (Throwable t) { + if (closed) { + done.complete(null); + } else { + done.completeExceptionally(t); + } + } finally { + done.complete(null); + } + } + + final void close(AutoCloseable toclose) { + try { toclose.close(); } catch (Exception x) {}; + } + + final public void close() { + closed = true; + close(socket); + accepted.forEach(this::close); + } + } + + final static class ISO88591Handler implements HttpServerAdapters.HttpTestHandler { + @Override + public void handle(HttpTestExchange t) throws IOException { + try (HttpTestExchange e = t) { + t.getRequestBody().readAllBytes(); + t.getResponseHeaders().addHeader("Header8859", "U\u00ffU"); + t.sendResponseHeaders(200, 0); + } + + } + } + + @BeforeTest + public void setup() throws Exception { + sslContext = new SimpleSSLContext().get(); + if (sslContext == null) + throw new AssertionError("Unexpected null sslContext"); + + HttpServerAdapters.HttpTestHandler handler = new ISO88591Handler(); + InetSocketAddress loopback = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + + http1DummyServer = new DummyServer(); + http1Dummy = "http://" + http1DummyServer.serverAuthority() +"/http1/dummy/x"; + + HttpServer http1 = HttpServer.create(loopback, 0); + http1TestServer = HttpServerAdapters.HttpTestServer.of(http1); + http1TestServer.addHandler(handler, "/http1/server/"); + http1URI = "http://" + http1TestServer.serverAuthority() + "/http1/server/x"; + + HttpsServer https1 = HttpsServer.create(loopback, 0); + https1.setHttpsConfigurator(new HttpsConfigurator(sslContext)); + https1TestServer = HttpServerAdapters.HttpTestServer.of(https1); + https1TestServer.addHandler(handler, "/https1/server/"); + https1URI = "https://" + https1TestServer.serverAuthority() + "/https1/server/x"; + + // HTTP/2 + http2TestServer = HttpServerAdapters.HttpTestServer.of(new Http2TestServer("localhost", false, 0)); + http2TestServer.addHandler(handler, "/http2/server/"); + http2URI = "http://" + http2TestServer.serverAuthority() + "/http2/server/x"; + + https2TestServer = HttpServerAdapters.HttpTestServer.of(new Http2TestServer("localhost", true, sslContext)); + https2TestServer.addHandler(handler, "/https2/server/"); + https2URI = "https://" + https2TestServer.serverAuthority() + "/https2/server/x"; + + serverCount.addAndGet(5); + http1TestServer.start(); + https1TestServer.start(); + http2TestServer.start(); + https2TestServer.start(); + http1DummyServer.start(); + } + + @AfterTest + public void teardown() throws Exception { + String sharedClientName = + sharedClient == null ? null : sharedClient.toString(); + sharedClient = null; + Thread.sleep(100); + AssertionError fail = TRACKER.check(500); + try { + http1TestServer.stop(); + https1TestServer.stop(); + http2TestServer.stop(); + https2TestServer.stop(); + http1DummyServer.close(); + } finally { + if (fail != null) { + if (sharedClientName != null) { + System.err.println("Shared client name is: " + sharedClientName); + } + throw fail; + } + } + } +} diff --git a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java index d78c720573e78..9bde910909574 100644 --- a/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java +++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/Http1HeaderParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,7 @@ import org.testng.annotations.DataProvider; import static java.lang.System.out; import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.US_ASCII; import static java.util.stream.Collectors.toList; import static org.testng.Assert.*; @@ -69,6 +70,12 @@ public Object[][] responses() { "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "XXXXX", + "HTTP/1.1 200 OK\r\n" + + "X-Header: U\u00ffU\r\n" + // value with U+00FF - Extended Latin-1 + "Content-Length: 9\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + "HTTP/1.1 200 OK\r\n" + "Content-Length: 9\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + // more than one SP after ':' @@ -222,13 +229,19 @@ public Object[][] responses() { "XXXXX", "HTTP/1.1 200 OK\r\n" + - ": no header\r\n\r\n" + // no/empty header-name, followed by header + "X-foo: bar\r\n" + + " : no header\r\n" + // fold, not a blank header-name + "Content-Length: 65\r\n\r\n" + "XXXXX", "HTTP/1.1 200 OK\r\n" + - "Conte\r" + - " nt-Length: 9\r\n" + // fold/bad header name ??? - "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "X-foo: bar\r\n" + + " \t : no header\r\n" + // fold, not a blank header-name + "Content-Length: 65\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + ": no header\r\n\r\n" + // no/empty header-name, followed by header "XXXXX", "HTTP/1.1 200 OK\r\n" + @@ -310,7 +323,7 @@ public void verifyHeaders(String respString) throws Exception { .replace("\r", "") .replace("\n","") .replace("LF>", "LF>\n\t")); - byte[] bytes = respString.getBytes(US_ASCII); + byte[] bytes = respString.getBytes(ISO_8859_1); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); MessageHeader m = new MessageHeader(bais); Map> messageHeaderMap = m.getHeaders(); @@ -326,7 +339,7 @@ public void verifyHeaders(String respString) throws Exception { String statusLine1 = messageHeaderMap.get(null).get(0); String statusLine2 = decoder.statusLine(); if (statusLine1.startsWith("HTTP")) {// skip the case where MH's messes up the status-line - assertEquals(statusLine1, statusLine2, "Status-line not equal"); + assertEquals(statusLine2, statusLine1, "Status-line not equal"); } else { assertTrue(statusLine2.startsWith("HTTP/1."), "Status-line not HTTP/1."); } @@ -384,7 +397,53 @@ public Object[][] errors() { "HTTP/1.1 -22\r\n", - "HTTP/1.1 -20 \r\n" + "HTTP/1.1 -20 \r\n", + + "HTTP/1.1 200 OK\r\n" + + "X-fo\u00ffo: foo\r\n" + // invalid char in name + "Content-Length: 5\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "HTTP/1.1 200 OK\r\n" + + "X-foo : bar\r\n" + // trim space after name + "Content-Length: 5\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + " X-foo: bar\r\n" + // trim space before name + "Content-Length: 5\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "X foo: bar\r\n" + // invalid space in name + "Content-Length: 5\r\n" + + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Content-Length: 5\r\n" + + "Content Type: text/html; charset=UTF-8\r\n\r\n" + // invalid space in name + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + "Conte\r" + + " nt-Length: 9\r\n" + // fold results in space in header name + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + " : no header\r\n" + // all blank header-name (not fold) + "Content-Length: 65\r\n\r\n" + + "XXXXX", + + "HTTP/1.1 200 OK\r\n" + + " \t : no header\r\n" + // all blank header-name (not fold) + "Content-Length: 65\r\n\r\n" + + "XXXXX", }; Arrays.stream(bad).forEach(responses::add); From 1e9a432d59fa3fb3d038c83c88fd7eeb3052960c Mon Sep 17 00:00:00 2001 From: Christoph Langer Date: Fri, 13 Nov 2020 17:28:05 +0000 Subject: [PATCH 109/124] 8256202: Some tweaks for jarsigner tests PosixPermissionsTest and SymLinkTest Reviewed-by: mbaesken --- .../tools/jarsigner/PosixPermissionsTest.java | 38 ++++--- .../security/tools/jarsigner/SymLinkTest.java | 103 +++++++++++------- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/test/jdk/sun/security/tools/jarsigner/PosixPermissionsTest.java b/test/jdk/sun/security/tools/jarsigner/PosixPermissionsTest.java index 54cc0600ba94c..6b698a3424764 100644 --- a/test/jdk/sun/security/tools/jarsigner/PosixPermissionsTest.java +++ b/test/jdk/sun/security/tools/jarsigner/PosixPermissionsTest.java @@ -32,15 +32,21 @@ */ import java.net.URI; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import jdk.test.lib.SecurityTools; public class PosixPermissionsTest { - private static List perms = List.of( "---------", "r--------", @@ -77,16 +83,13 @@ public class PosixPermissionsTest { "protected by the signature."; public static void main(String[] args) throws Exception { - if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) { - System.out.println("No posix support. Skipping"); - return; - } - createFiles(); + // check permissions before signing verifyFilePermissions(ZIPURI, true); verifyFilePermissions(JARURI, false); + // generate key for signing SecurityTools.keytool( "-genkey", "-keyalg", "RSA", @@ -98,6 +101,7 @@ public static void main(String[] args) throws Exception { "-validity", "365") .shouldHaveExitValue(0); + // sign zip file - expect warning SecurityTools.jarsigner( "-keystore", "examplekeystore", "-verbose", ZIPFILENAME, @@ -107,11 +111,12 @@ public static void main(String[] args) throws Exception { .shouldHaveExitValue(0) .shouldContain(WARNING_MSG); - // zip file now signed. Recheck file permissions + // recheck permissions after signing verifyFilePermissions(ZIPURI, true); - // sign jar file - no posix warning message expected - SecurityTools.jarsigner("-keystore", "examplekeystore", + // sign jar file - expect no warning + SecurityTools.jarsigner( + "-keystore", "examplekeystore", "-verbose", JARFILENAME, "-storepass", "password", "-keypass", "password", @@ -119,10 +124,12 @@ public static void main(String[] args) throws Exception { .shouldHaveExitValue(0) .shouldNotContain(WARNING_MSG); - // default attributes expected + // recheck permissions after signing verifyFilePermissions(JARURI, false); - SecurityTools.jarsigner("-keystore", "examplekeystore", + // verify zip file - expect warning + SecurityTools.jarsigner( + "-keystore", "examplekeystore", "-storepass", "password", "-keypass", "password", "-verbose", @@ -130,8 +137,9 @@ public static void main(String[] args) throws Exception { .shouldHaveExitValue(0) .shouldContain(WARNING_MSG); - // no warning expected for regular jar file - SecurityTools.jarsigner("-keystore", "examplekeystore", + // verify jar file - expect no warning + SecurityTools.jarsigner( + "-keystore", "examplekeystore", "-storepass", "password", "-keypass", "password", "-verbose", diff --git a/test/jdk/sun/security/tools/jarsigner/SymLinkTest.java b/test/jdk/sun/security/tools/jarsigner/SymLinkTest.java index 8ed86dcf151d5..c96eeeeb74d0d 100644 --- a/test/jdk/sun/security/tools/jarsigner/SymLinkTest.java +++ b/test/jdk/sun/security/tools/jarsigner/SymLinkTest.java @@ -31,28 +31,35 @@ * @run main/othervm SymLinkTest */ -import java.io.*; -import java.net.URI; -import java.nio.file.*; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Formatter; import jdk.test.lib.SecurityTools; public class SymLinkTest { + private final static int BYTES_PER_ROW = 8; private final static String ZIPFILENAME = "8250968-test.zip"; private static final String WARNING_MSG = "POSIX file permission and/or symlink " + "attributes detected. These attributes are ignored when signing and are not " + "protected by the signature."; public static void main(String[] args) throws Exception { - Files.deleteIfExists(Paths.get(ZIPFILENAME)); - try (FileOutputStream fos = new FileOutputStream(ZIPFILENAME)) { - fos.write(ZIPBYTES); + // call main with an argument to print the prepared zipfile as byte array declaration + if (args.length > 0) { + System.out.println("Bytes of " + ZIPFILENAME + ":"); + System.out.println(createByteArray(Files.readAllBytes(Path.of(ZIPFILENAME)), "ZIPBYTES")); + System.exit(0); } - // check permissions before signing + Files.write(Path.of(ZIPFILENAME), ZIPBYTES); + + // check attributes before signing verifyExtraAttrs(ZIPFILENAME); + // generate key for signing SecurityTools.keytool( "-genkey", "-keyalg", "RSA", @@ -64,6 +71,7 @@ public static void main(String[] args) throws Exception { "-validity", "365") .shouldHaveExitValue(0); + // sign zip file - expect warning SecurityTools.jarsigner( "-keystore", "examplekeystore", "-verbose", ZIPFILENAME, @@ -73,10 +81,12 @@ public static void main(String[] args) throws Exception { .shouldHaveExitValue(0) .shouldContain(WARNING_MSG); - // zip file now signed. Recheck attributes + // recheck attributes after signing verifyExtraAttrs(ZIPFILENAME); - SecurityTools.jarsigner("-keystore", "examplekeystore", + // verify zip file - expect warning + SecurityTools.jarsigner( + "-keystore", "examplekeystore", "-storepass", "password", "-keypass", "password", "-verbose", @@ -114,48 +124,57 @@ private static void verifyExtraAttrs(String zipFileName) throws IOException { * @param name Name to be used in the byte array declaration * @return The formatted byte array declaration */ - public static String createByteArray(byte[] bytes, String name) { - StringBuilder sb = new StringBuilder(bytes.length * 5); - Formatter fmt = new Formatter(sb); - fmt.format(" public static byte[] %s = {", name); - final int linelen = 8; - for (int i = 0; i < bytes.length; i++) { - if (i % linelen == 0) { - fmt.format("%n "); + private static String createByteArray(byte[] bytes, String name) { + StringBuilder sb = new StringBuilder(); + try (Formatter fmt = new Formatter(sb)) { + fmt.format(" public final static byte[] %s = {", name); + for (int i = 0; i < bytes.length; i++) { + int mod = i % BYTES_PER_ROW; + if (mod == 0) { + fmt.format("%n "); + } else { + fmt.format(" "); + } + fmt.format("(byte)0x%02x", bytes[i]); + if (i != bytes.length - 1) { + fmt.format(","); + } } - fmt.format(" (byte) 0x%x,", bytes[i] & 0xff); + fmt.format("%n };%n"); } - fmt.format("%n };%n"); return sb.toString(); } /* - * Created using the createByteArray utility method. - * The zipfile itself was created via this example: + * The zipfile itself was created like this: + * $ ln -s ../z z * $ ls -l z * lrwxrwxrwx 1 test test 4 Aug 27 18:33 z -> ../z - * $ zip -ry test.zip z + * $ zip -ry 8250968-test.zip z + * + * The byte array representation was generated using the createByteArray utility method: + * $ java SymLinkTest generate */ public final static byte[] ZIPBYTES = { - (byte) 0x50, (byte) 0x4b, (byte) 0x3, (byte) 0x4, (byte) 0xa, (byte) 0x0, (byte) 0x0, (byte) 0x0, - (byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x94, (byte) 0x1b, (byte) 0x51, (byte) 0xb4, (byte) 0xcc, - (byte) 0xb6, (byte) 0xf1, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0x0, - (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x1c, (byte) 0x0, (byte) 0x7a, (byte) 0x55, - (byte) 0x54, (byte) 0x9, (byte) 0x0, (byte) 0x3, (byte) 0x77, (byte) 0xfc, (byte) 0x47, (byte) 0x5f, - (byte) 0x78, (byte) 0xfc, (byte) 0x47, (byte) 0x5f, (byte) 0x75, (byte) 0x78, (byte) 0xb, (byte) 0x0, - (byte) 0x1, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x4, (byte) 0xec, - (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x2e, (byte) 0x2f, (byte) 0x7a, (byte) 0x50, - (byte) 0x4b, (byte) 0x1, (byte) 0x2, (byte) 0x1e, (byte) 0x3, (byte) 0xa, (byte) 0x0, (byte) 0x0, - (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x2e, (byte) 0x94, (byte) 0x1b, (byte) 0x51, (byte) 0xb4, - (byte) 0xcc, (byte) 0xb6, (byte) 0xf1, (byte) 0x4, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x4, - (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, (byte) 0x18, (byte) 0x0, (byte) 0x0, - (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff, - (byte) 0xa1, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x7a, (byte) 0x55, (byte) 0x54, - (byte) 0x5, (byte) 0x0, (byte) 0x3, (byte) 0x77, (byte) 0xfc, (byte) 0x47, (byte) 0x5f, (byte) 0x75, - (byte) 0x78, (byte) 0xb, (byte) 0x0, (byte) 0x1, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0, - (byte) 0x0, (byte) 0x4, (byte) 0xec, (byte) 0x3, (byte) 0x0, (byte) 0x0, (byte) 0x50, (byte) 0x4b, - (byte) 0x5, (byte) 0x6, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x1, (byte) 0x0, - (byte) 0x1, (byte) 0x0, (byte) 0x47, (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x3f, (byte) 0x0, - (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte)0x50, (byte)0x4b, (byte)0x03, (byte)0x04, (byte)0x0a, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x2e, (byte)0x94, (byte)0x1b, (byte)0x51, (byte)0xb4, (byte)0xcc, + (byte)0xb6, (byte)0xf1, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1c, (byte)0x00, (byte)0x7a, (byte)0x55, + (byte)0x54, (byte)0x09, (byte)0x00, (byte)0x03, (byte)0x77, (byte)0xfc, (byte)0x47, (byte)0x5f, + (byte)0x78, (byte)0xfc, (byte)0x47, (byte)0x5f, (byte)0x75, (byte)0x78, (byte)0x0b, (byte)0x00, + (byte)0x01, (byte)0x04, (byte)0xec, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xec, + (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x2e, (byte)0x2e, (byte)0x2f, (byte)0x7a, (byte)0x50, + (byte)0x4b, (byte)0x01, (byte)0x02, (byte)0x1e, (byte)0x03, (byte)0x0a, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x2e, (byte)0x94, (byte)0x1b, (byte)0x51, (byte)0xb4, + (byte)0xcc, (byte)0xb6, (byte)0xf1, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x18, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xff, + (byte)0xa1, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x7a, (byte)0x55, (byte)0x54, + (byte)0x05, (byte)0x00, (byte)0x03, (byte)0x77, (byte)0xfc, (byte)0x47, (byte)0x5f, (byte)0x75, + (byte)0x78, (byte)0x0b, (byte)0x00, (byte)0x01, (byte)0x04, (byte)0xec, (byte)0x03, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0xec, (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x50, (byte)0x4b, + (byte)0x05, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, + (byte)0x01, (byte)0x00, (byte)0x47, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x3f, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 }; } From 298bce1d925b3ce50bb5de74fa767153fcc2f05c Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 16 Nov 2020 07:15:43 +0000 Subject: [PATCH 110/124] 8256367: [windows] Better logging for some system calls Reviewed-by: iklam --- src/hotspot/os/windows/os_windows.cpp | 128 +++++++++++++++++++++----- 1 file changed, 107 insertions(+), 21 deletions(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index 70889368283fc..b8b5946b9d07d 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -178,6 +178,91 @@ static inline double fileTimeAsDouble(FILETIME* time) { // Implementation of os +#define RANGE_FORMAT "[" PTR_FORMAT "-" PTR_FORMAT ")" +#define RANGE_FORMAT_ARGS(p, len) p2i(p), p2i((address)p + len) + +// A number of wrappers for more frequently used system calls, to add standard logging. + +struct PreserveLastError { + const DWORD v; + PreserveLastError() : v(::GetLastError()) {} + ~PreserveLastError() { ::SetLastError(v); } +}; + +// Logging wrapper for VirtualAlloc +static LPVOID virtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { + LPVOID result = ::VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); + if (result != NULL) { + log_trace(os)("VirtualAlloc(" PTR_FORMAT ", " SIZE_FORMAT ", %x, %x) returned " PTR_FORMAT "%s.", + p2i(lpAddress), dwSize, flAllocationType, flProtect, p2i(result), + ((lpAddress != NULL && result != lpAddress) ? " " : "")); + } else { + PreserveLastError ple; + log_info(os)("VirtualAlloc(" PTR_FORMAT ", " SIZE_FORMAT ", %x, %x) failed (%u).", + p2i(lpAddress), dwSize, flAllocationType, flProtect, ple.v); + } + return result; +} + +// Logging wrapper for VirtualFree +static BOOL virtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) { + BOOL result = ::VirtualFree(lpAddress, dwSize, dwFreeType); + if (result != FALSE) { + log_trace(os)("VirtualFree(" PTR_FORMAT ", " SIZE_FORMAT ", %x) succeeded", + p2i(lpAddress), dwSize, dwFreeType); + } else { + PreserveLastError ple; + log_info(os)("VirtualFree(" PTR_FORMAT ", " SIZE_FORMAT ", %x) failed (%u).", + p2i(lpAddress), dwSize, dwFreeType, ple.v); + } + return result; +} + +// Logging wrapper for VirtualAllocExNuma +static LPVOID virtualAllocExNuma(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, + DWORD flProtect, DWORD nndPreferred) { + LPVOID result = ::VirtualAllocExNuma(hProcess, lpAddress, dwSize, flAllocationType, flProtect, nndPreferred); + if (result != NULL) { + log_trace(os)("VirtualAllocExNuma(" PTR_FORMAT ", " SIZE_FORMAT ", %x, %x, %x) returned " PTR_FORMAT "%s.", + p2i(lpAddress), dwSize, flAllocationType, flProtect, nndPreferred, p2i(result), + ((lpAddress != NULL && result != lpAddress) ? " " : "")); + } else { + PreserveLastError ple; + log_info(os)("VirtualAllocExNuma(" PTR_FORMAT ", " SIZE_FORMAT ", %x, %x, %x) failed (%u).", + p2i(lpAddress), dwSize, flAllocationType, flProtect, nndPreferred, ple.v); + } + return result; +} + +// Logging wrapper for MapViewOfFileEx +static LPVOID mapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap, LPVOID lpBaseAddress) { + LPVOID result = ::MapViewOfFileEx(hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, + dwFileOffsetLow, dwNumberOfBytesToMap, lpBaseAddress); + if (result != NULL) { + log_trace(os)("MapViewOfFileEx(" PTR_FORMAT ", " SIZE_FORMAT ") returned " PTR_FORMAT "%s.", + p2i(lpBaseAddress), dwNumberOfBytesToMap, p2i(result), + ((lpBaseAddress != NULL && result != lpBaseAddress) ? " " : "")); + } else { + PreserveLastError ple; + log_info(os)("MapViewOfFileEx(" PTR_FORMAT ", " SIZE_FORMAT ") failed (%u).", + p2i(lpBaseAddress), dwNumberOfBytesToMap, ple.v); + } + return result; +} + +// Logging wrapper for UnmapViewOfFile +static BOOL unmapViewOfFile(LPCVOID lpBaseAddress) { + BOOL result = ::UnmapViewOfFile(lpBaseAddress); + if (result != FALSE) { + log_trace(os)("UnmapViewOfFile(" PTR_FORMAT ") succeeded", p2i(lpBaseAddress)); + } else { + PreserveLastError ple; + log_info(os)("UnmapViewOfFile(" PTR_FORMAT ") failed (%u).", p2i(lpBaseAddress), ple.v); + } + return result; +} + bool os::unsetenv(const char* name) { assert(name != NULL, "Null pointer"); return (SetEnvironmentVariable(name, NULL) == TRUE); @@ -340,7 +425,6 @@ int os::get_native_stack(address* stack, int frames, int toSkip) { return captured; } - // os::current_stack_base() // // Returns the base of the stack, which is the stack's @@ -2900,7 +2984,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, // Overflowed. return NULL; } - p_buf = (char *) VirtualAlloc(addr, + p_buf = (char *) virtualAlloc(addr, size_of_reserve, // size of Reserve MEM_RESERVE, PAGE_READWRITE); @@ -2946,7 +3030,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, p_new = NULL; } else { if (!UseNUMAInterleaving) { - p_new = (char *) VirtualAlloc(next_alloc_addr, + p_new = (char *) virtualAlloc(next_alloc_addr, bytes_to_rq, flags, prot); @@ -2954,7 +3038,7 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags, // get the next node to use from the used_node_list assert(numa_node_list_holder.get_count() > 0, "Multiple NUMA nodes expected"); DWORD node = numa_node_list_holder.get_node_list_entry(count % numa_node_list_holder.get_count()); - p_new = (char *)VirtualAllocExNuma(hProc, next_alloc_addr, bytes_to_rq, flags, prot, node); + p_new = (char *)virtualAllocExNuma(hProc, next_alloc_addr, bytes_to_rq, flags, prot, node); } } @@ -3100,7 +3184,7 @@ char* os::map_memory_to_file(char* base, size_t size, int fd) { return NULL; } - LPVOID addr = MapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base); + LPVOID addr = mapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base); CloseHandle(fileMapping); @@ -3127,14 +3211,19 @@ void os::split_reserved_memory(char *base, size_t size, size_t split) { assert(is_aligned(base, os::vm_allocation_granularity()), "Sanity"); assert(is_aligned(split_address, os::vm_allocation_granularity()), "Sanity"); - release_memory(base, size); - attempt_reserve_memory_at(base, split); - attempt_reserve_memory_at(split_address, size - split); + const bool rc = release_memory(base, size) && + (attempt_reserve_memory_at(base, split) != NULL) && + (attempt_reserve_memory_at(split_address, size - split) != NULL); + if (!rc) { + log_warning(os)("os::split_reserved_memory failed for [" RANGE_FORMAT ")", + RANGE_FORMAT_ARGS(base, size)); + assert(false, "os::split_reserved_memory failed for [" RANGE_FORMAT ")", + RANGE_FORMAT_ARGS(base, size)); + } // NMT: nothing to do here. Since Windows implements the split by // releasing and re-reserving memory, the parts are already registered // as individual mappings with NMT. - } // Multiple threads can race in this code but it's not possible to unmap small sections of @@ -3198,7 +3287,7 @@ char* os::pd_attempt_reserve_memory_at(char* addr, size_t bytes) { // will go thru reserve_memory_special rather than thru here. bool use_individual = (UseNUMAInterleaving && !UseLargePages); if (!use_individual) { - res = (char*)VirtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); + res = (char*)virtualAlloc(addr, bytes, MEM_RESERVE, PAGE_READWRITE); } else { elapsedTimer reserveTimer; if (Verbose && PrintMiscellaneous) reserveTimer.start(); @@ -3277,7 +3366,7 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, char* addr, // normal policy just allocate it all at once DWORD flag = MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES; - char * res = (char *)VirtualAlloc(addr, bytes, flag, prot); + char * res = (char *)virtualAlloc(addr, bytes, flag, prot); return res; } @@ -3314,7 +3403,7 @@ bool os::pd_commit_memory(char* addr, size_t bytes, bool exec) { // is always within a reserve covered by a single VirtualAlloc // in that case we can just do a single commit for the requested size if (!UseNUMAInterleaving) { - if (VirtualAlloc(addr, bytes, MEM_COMMIT, PAGE_READWRITE) == NULL) { + if (virtualAlloc(addr, bytes, MEM_COMMIT, PAGE_READWRITE) == NULL) { NOT_PRODUCT(warn_fail_commit_memory(addr, bytes, exec);) return false; } @@ -3339,7 +3428,7 @@ bool os::pd_commit_memory(char* addr, size_t bytes, bool exec) { MEMORY_BASIC_INFORMATION alloc_info; VirtualQuery(next_alloc_addr, &alloc_info, sizeof(alloc_info)); size_t bytes_to_rq = MIN2(bytes_remaining, (size_t)alloc_info.RegionSize); - if (VirtualAlloc(next_alloc_addr, bytes_to_rq, MEM_COMMIT, + if (virtualAlloc(next_alloc_addr, bytes_to_rq, MEM_COMMIT, PAGE_READWRITE) == NULL) { NOT_PRODUCT(warn_fail_commit_memory(next_alloc_addr, bytes_to_rq, exec);) @@ -3391,11 +3480,11 @@ bool os::pd_uncommit_memory(char* addr, size_t bytes) { } assert((size_t) addr % os::vm_page_size() == 0, "uncommit on page boundaries"); assert(bytes % os::vm_page_size() == 0, "uncommit in page-sized chunks"); - return (VirtualFree(addr, bytes, MEM_DECOMMIT) != 0); + return (virtualFree(addr, bytes, MEM_DECOMMIT) == TRUE); } bool os::pd_release_memory(char* addr, size_t bytes) { - return VirtualFree(addr, 0, MEM_RELEASE) != 0; + return virtualFree(addr, 0, MEM_RELEASE) != 0; } bool os::pd_create_stack_guard_pages(char* addr, size_t size) { @@ -4941,10 +5030,9 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, // we might consider DLLizing the shared archive with a proper PE // header so that mapping executable + sharing is possible. - base = (char*) VirtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE, + base = (char*) virtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (base == NULL) { - log_info(os)("VirtualAlloc() failed: GetLastError->%ld.", GetLastError()); CloseHandle(hFile); return NULL; } @@ -4976,10 +5064,9 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, } DWORD access = read_only ? FILE_MAP_READ : FILE_MAP_COPY; - base = (char*)MapViewOfFileEx(hMap, access, 0, (DWORD)file_offset, + base = (char*)mapViewOfFileEx(hMap, access, 0, (DWORD)file_offset, (DWORD)bytes, addr); if (base == NULL) { - log_info(os)("MapViewOfFileEx() failed: GetLastError->%ld.", GetLastError()); CloseHandle(hMap); CloseHandle(hFile); return NULL; @@ -5051,9 +5138,8 @@ bool os::pd_unmap_memory(char* addr, size_t bytes) { return pd_release_memory(addr, bytes); } - BOOL result = UnmapViewOfFile(addr); + BOOL result = unmapViewOfFile(addr); if (result == 0) { - log_info(os)("UnmapViewOfFile() failed: GetLastError->%ld.", GetLastError()); return false; } return true; From 6a69e304ddeb9b2d66f612644e8242d5724936ea Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Mon, 16 Nov 2020 07:58:40 +0000 Subject: [PATCH 111/124] 8256337: ap01t001.cpp, 67: Received unexpected number of ObjectFree events: 7 Reviewed-by: coleenp, sspitsyn --- .../nsk/jvmti/scenarios/allocation/AP01/ap01t001.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/allocation/AP01/ap01t001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/allocation/AP01/ap01t001.java index 43891ff55915a..1c4bb0854033e 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/allocation/AP01/ap01t001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/scenarios/allocation/AP01/ap01t001.java @@ -32,6 +32,8 @@ public class ap01t001 extends DebugeeClass implements Cloneable { /* number of interations to provoke garbage collecting */ final static int GC_TRYS = 4; + // Prevent test run instance from being freed too early + static ap01t001 keepAlive; public static void main(String[] argv) { argv = nsk.share.jvmti.JVMTITest.commonInit(argv); @@ -41,7 +43,7 @@ public static void main(String[] argv) { } public static int run(String argv[], PrintStream out) { - return new ap01t001().runThis(argv, out); + return (keepAlive = new ap01t001()).runThis(argv, out); } /* private native void setTag(); From 588caab074c495c685136ea66f002abc649ecb67 Mon Sep 17 00:00:00 2001 From: Robin Westberg Date: Mon, 16 Nov 2020 08:00:21 +0000 Subject: [PATCH 112/124] 8256277: Github Action build on macOS should define OS and Xcode versions Reviewed-by: shade, ehelin, erikj --- .github/workflows/submit.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index f488a9b2b5a9d..b5cef57e4a653 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -1543,7 +1543,7 @@ jobs: macos_x64_build: name: macOS x64 - runs-on: "macos-latest" + runs-on: "macos-10.15" needs: prerequisites if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_macos_x64 != 'false' @@ -1613,6 +1613,9 @@ jobs: - name: Install dependencies run: brew install make + - name: Select Xcode version + run: sudo xcode-select --switch /Applications/Xcode_11.3.1.app/Contents/Developer + - name: Configure run: > bash configure @@ -1643,7 +1646,7 @@ jobs: macos_x64_test: name: macOS x64 - runs-on: "macos-latest" + runs-on: "macos-10.15" needs: - prerequisites - macos_x64_build @@ -1756,6 +1759,9 @@ jobs: - name: Install dependencies run: brew install make + - name: Select Xcode version + run: sudo xcode-select --switch /Applications/Xcode_11.3.1.app/Contents/Developer + - name: Find root of jdk image dir run: | imageroot=`find ${HOME}/jdk-macos-x64${{ matrix.artifact }}/jdk-${{ env.JDK_VERSION }}-internal+0_osx-x64_bin${{ matrix.artifact }} -name release -type f` From 1103e3374c585d4b5f6e8b3762658a392f673426 Mon Sep 17 00:00:00 2001 From: Robin Westberg Date: Mon, 16 Nov 2020 08:02:11 +0000 Subject: [PATCH 113/124] 8256354: Github Action build on Windows should define OS and MSVC versions Reviewed-by: erikj, shade --- .github/workflows/submit.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index b5cef57e4a653..26b5cc84bf873 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -1227,7 +1227,7 @@ jobs: windows_x64_build: name: Windows x64 - runs-on: "windows-latest" + runs-on: "windows-2019" needs: prerequisites if: needs.prerequisites.outputs.should_run != 'false' && needs.prerequisites.outputs.platform_windows_x64 != 'false' @@ -1307,12 +1307,19 @@ jobs: path: ~/jtreg/ if: steps.jtreg_restore.outcome == 'failure' + - name: Ensure a specific version of MSVC is installed + run: > + Start-Process -FilePath 'C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe' -Wait -NoNewWindow -ArgumentList + 'modify --installPath "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise" --quiet + --add Microsoft.VisualStudio.Component.VC.14.27.x86.x64' + - name: Configure run: > $env:Path = "$HOME\cygwin\cygwin64\bin;$HOME\cygwin\cygwin64\bin;$env:Path" ; $env:Path = $env:Path -split ";" -match "C:\\Windows|PowerShell|cygwin" -join ";" ; & bash configure --with-conf-name=windows-x64 + --with-msvc-toolset-version=14.27 ${{ matrix.flags }} --with-version-opt="$env:GITHUB_ACTOR-$env:GITHUB_SHA" --with-version-build=0 @@ -1342,7 +1349,7 @@ jobs: windows_x64_test: name: Windows x64 - runs-on: "windows-latest" + runs-on: "windows-2019" needs: - prerequisites - windows_x64_build From ac3948930ef2b188401d67f20f730fefe7d15e1e Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Mon, 16 Nov 2020 10:11:33 +0000 Subject: [PATCH 114/124] 8256323: Remove HeapRegionManager::update_committed_space() Reviewed-by: shade, sjohanss --- src/hotspot/share/gc/g1/heapRegionManager.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/hotspot/share/gc/g1/heapRegionManager.hpp b/src/hotspot/share/gc/g1/heapRegionManager.hpp index be7bd39434cf9..ac35e6bd31376 100644 --- a/src/hotspot/share/gc/g1/heapRegionManager.hpp +++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp @@ -107,9 +107,6 @@ class HeapRegionManager: public CHeapObj { // Pass down commit calls to the VirtualSpace. void commit_regions(uint index, size_t num_regions = 1, WorkGang* pretouch_gang = NULL); - // Notify other data structures about change in the heap layout. - void update_committed_space(HeapWord* old_end, HeapWord* new_end); - // Find a contiguous set of empty or uncommitted regions of length num_regions and return // the index of the first region or G1_NO_HRM_INDEX if the search was unsuccessful. // Start and end defines the range to seek in, policy is first-fit. From 8eeb36f14a9121b6cb1ed3228f78021d5da9e81b Mon Sep 17 00:00:00 2001 From: Hui Shi Date: Mon, 16 Nov 2020 11:34:47 +0000 Subject: [PATCH 115/124] 8255883: Avoid duplicated GeneratedMethodAccessor when reflect method invoked from different threads Reviewed-by: shade, alanb --- .../NativeConstructorAccessorImpl.java | 30 +++++++++++----- .../reflect/NativeMethodAccessorImpl.java | 34 +++++++++++++------ 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java index b50da8542f885..c7d8885373cc8 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/NativeConstructorAccessorImpl.java @@ -28,14 +28,20 @@ import sun.reflect.misc.ReflectUtil; import java.lang.reflect.*; +import jdk.internal.misc.Unsafe; /** Used only for the first few invocations of a Constructor; afterward, switches to bytecode-based implementation */ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl { + private static final Unsafe U = Unsafe.getUnsafe(); + private static final long GENERATED_OFFSET + = U.objectFieldOffset(NativeConstructorAccessorImpl.class, "generated"); + private final Constructor c; private DelegatingConstructorAccessorImpl parent; private int numInvocations; + private volatile int generated; NativeConstructorAccessorImpl(Constructor c) { this.c = c; @@ -51,14 +57,22 @@ public Object newInstance(Object[] args) // be found from the generated bytecode. if (++numInvocations > ReflectionFactory.inflationThreshold() && !c.getDeclaringClass().isHidden() - && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) { - ConstructorAccessorImpl acc = (ConstructorAccessorImpl) - new MethodAccessorGenerator(). - generateConstructor(c.getDeclaringClass(), - c.getParameterTypes(), - c.getExceptionTypes(), - c.getModifiers()); - parent.setDelegate(acc); + && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass()) + && generated == 0 + && U.compareAndSetInt(this, GENERATED_OFFSET, 0, 1)) { + try { + ConstructorAccessorImpl acc = (ConstructorAccessorImpl) + new MethodAccessorGenerator(). + generateConstructor(c.getDeclaringClass(), + c.getParameterTypes(), + c.getExceptionTypes(), + c.getModifiers()); + parent.setDelegate(acc); + } catch (Throwable t) { + // Throwable happens in generateConstructor, restore generated to 0 + generated = 0; + throw t; + } } return newInstance0(c, args); diff --git a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java index 4d41eb3c99067..dc8c577bc8a1f 100644 --- a/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java +++ b/src/java.base/share/classes/jdk/internal/reflect/NativeMethodAccessorImpl.java @@ -28,14 +28,20 @@ import sun.reflect.misc.ReflectUtil; import java.lang.reflect.*; +import jdk.internal.misc.Unsafe; /** Used only for the first few invocations of a Method; afterward, switches to bytecode-based implementation */ class NativeMethodAccessorImpl extends MethodAccessorImpl { + private static final Unsafe U = Unsafe.getUnsafe(); + private static final long GENERATED_OFFSET + = U.objectFieldOffset(NativeMethodAccessorImpl.class, "generated"); + private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; + private volatile int generated; NativeMethodAccessorImpl(Method method) { this.method = method; @@ -49,16 +55,24 @@ public Object invoke(Object obj, Object[] args) // found from the generated bytecode. if (++numInvocations > ReflectionFactory.inflationThreshold() && !method.getDeclaringClass().isHidden() - && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { - MethodAccessorImpl acc = (MethodAccessorImpl) - new MethodAccessorGenerator(). - generateMethod(method.getDeclaringClass(), - method.getName(), - method.getParameterTypes(), - method.getReturnType(), - method.getExceptionTypes(), - method.getModifiers()); - parent.setDelegate(acc); + && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass()) + && generated == 0 + && U.compareAndSetInt(this, GENERATED_OFFSET, 0, 1)) { + try { + MethodAccessorImpl acc = (MethodAccessorImpl) + new MethodAccessorGenerator(). + generateMethod(method.getDeclaringClass(), + method.getName(), + method.getParameterTypes(), + method.getReturnType(), + method.getExceptionTypes(), + method.getModifiers()); + parent.setDelegate(acc); + } catch (Throwable t) { + // Throwable happens in generateMethod, restore generated to 0 + generated = 0; + throw t; + } } return invoke0(method, obj, args); From c5fe2c1fcb5b5cec0a3f53716c24b2778467900b Mon Sep 17 00:00:00 2001 From: Robbin Ehn Date: Mon, 16 Nov 2020 13:06:45 +0000 Subject: [PATCH 116/124] 8244679: JVM/TI GetCurrentContendedMonitor/contmon001 failed due to "(IsSameObject#3) unexpected monitor object: 0x000000562336DBA8" Reviewed-by: pchilanomate, dcubed, dholmes, sspitsyn --- .../contmon001.java | 27 ++++++++++++------- .../contmon002.java | 18 +++++++++---- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon001.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon001.java index 320190d05c8bd..ff64525040c59 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon001.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon001.java @@ -23,7 +23,6 @@ package nsk.jvmti.GetCurrentContendedMonitor; -import nsk.share.Wicket; import java.io.PrintStream; public class contmon001 { @@ -42,8 +41,8 @@ public class contmon001 { } } - public static Wicket startingBarrier; - public static Wicket waitingBarrier; + public static volatile boolean startingBarrier = true; + public static volatile boolean waitingBarrier = true; static Object lockFld = new Object(); static boolean DEBUG_MODE = false; @@ -55,6 +54,14 @@ public static void main(String[] args) { System.exit(run(args, System.out) + 95/*STATUS_TEMP*/); } + public static void doSleep() { + try { + Thread.sleep(10); + } catch (Exception e) { + throw new Error("Unexpected " + e); + } + } + public static int run(String argv[], PrintStream ref) { out = ref; for (int i = 0; i < argv.length; i++) { @@ -75,13 +82,13 @@ public static int run(String argv[], PrintStream ref) { out.println("Check #1 done"); contmon001a thr = new contmon001a(); - startingBarrier = new Wicket(); - waitingBarrier = new Wicket(); thr.start(); if (DEBUG_MODE) out.println("\nWaiting for auxiliary thread ..."); - startingBarrier.waitFor(); + while (startingBarrier) { + doSleep(); + } if (DEBUG_MODE) out.println("Auxiliary thread is ready"); @@ -93,7 +100,9 @@ public static int run(String argv[], PrintStream ref) { thr.letItGo(); - waitingBarrier.waitFor(); + while (waitingBarrier) { + doSleep(); + } synchronized (lockFld) { if (DEBUG_MODE) out.println("\nMain thread entered lockFld's monitor" @@ -138,7 +147,7 @@ public void run() { if (contmon001.DEBUG_MODE) contmon001.out.println("notifying main thread"); - contmon001.startingBarrier.unlock(); + contmon001.startingBarrier = false; if (contmon001.DEBUG_MODE) contmon001.out.println("thread is going to loop while is true ..."); @@ -158,7 +167,7 @@ public void run() { contmon001.out.println("looping is done: is false"); synchronized (contmon001.lockFld) { - contmon001.waitingBarrier.unlock(); + contmon001.waitingBarrier = false; if (contmon001.DEBUG_MODE) contmon001.out.println("\nthread entered lockFld's monitor" + "\n\tand releasing it through the lockFld.wait() call"); diff --git a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon002.java b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon002.java index 3b9296d70835c..aac334b1a5877 100644 --- a/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon002.java +++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/GetCurrentContendedMonitor/contmon002.java @@ -23,7 +23,6 @@ package nsk.jvmti.GetCurrentContendedMonitor; -import nsk.share.Wicket; import java.io.PrintStream; public class contmon002 { @@ -42,7 +41,7 @@ public class contmon002 { } } - public static Wicket startingBarrier; + public static boolean startingBarrier = true; public static void main(String[] args) { args = nsk.share.jvmti.JVMTITest.commonInit(args); @@ -50,13 +49,22 @@ public static void main(String[] args) { System.exit(run(args, System.out) + 95/*STATUS_TEMP*/); } + public static void doSleep() { + try { + Thread.sleep(10); + } catch (Exception e) { + throw new Error("Unexpected " + e); + } + } + public static int run(String argv[], PrintStream ref) { checkMon(1, Thread.currentThread()); contmon002a thr = new contmon002a(); - startingBarrier = new Wicket(); thr.start(); - startingBarrier.waitFor(); + while (startingBarrier) { + doSleep(); + } checkMon(2, thr); thr.letItGo(); try { @@ -73,7 +81,7 @@ class contmon002a extends Thread { private volatile boolean flag = true; private synchronized void meth() { - contmon002.startingBarrier.unlock(); + contmon002.startingBarrier = false; int i = 0; int n = 1000; while (flag) { From c85c9ad1f1b1b7aa358c587b654a2030927a65a8 Mon Sep 17 00:00:00 2001 From: Jie Kang Date: Mon, 16 Nov 2020 13:25:40 +0000 Subject: [PATCH 117/124] 8255992: JFR EventWriter does not use first string from StringPool with id 0 Reviewed-by: egahlin --- src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java index b3f5f2399428b..554359d2f9a5e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/StringPool.java @@ -49,7 +49,7 @@ private static boolean getCurrentEpoch() { } private static class SimpleStringIdPool { /* string id index */ - private final AtomicLong sidIdx = new AtomicLong(); + private final AtomicLong sidIdx = new AtomicLong(1); /* epoch of cached strings */ private boolean poolEpoch; /* the cache */ From 1d7ed03d5cf964a6868c9e7a4325d0ee433f5fbc Mon Sep 17 00:00:00 2001 From: "Daniel D. Daugherty" Date: Mon, 16 Nov 2020 14:10:08 +0000 Subject: [PATCH 118/124] 8244376: possibly stale comment above "struct SharedGlobals" in synchronizer.cpp Reviewed-by: hseigel, dholmes --- src/hotspot/share/runtime/synchronizer.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index f5055af4c7c63..6132409972c1e 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -720,24 +720,6 @@ void ObjectSynchronizer::notifyall(Handle obj, TRAPS) { // ----------------------------------------------------------------------------- // Hash Code handling -// -// Performance concern: -// OrderAccess::storestore() calls release() which at one time stored 0 -// into the global volatile OrderAccess::dummy variable. This store was -// unnecessary for correctness. Many threads storing into a common location -// causes considerable cache migration or "sloshing" on large SMP systems. -// As such, I avoided using OrderAccess::storestore(). In some cases -// OrderAccess::fence() -- which incurs local latency on the executing -// processor -- is a better choice as it scales on SMP systems. -// -// See http://blogs.oracle.com/dave/entry/biased_locking_in_hotspot for -// a discussion of coherency costs. Note that all our current reference -// platforms provide strong ST-ST order, so the issue is moot on IA32, -// x64, and SPARC. -// -// As a general policy we use "volatile" to control compiler-based reordering -// and explicit fences (barriers) to control for architectural reordering -// performed by the CPU(s) or platform. struct SharedGlobals { char _pad_prefix[OM_CACHE_LINE_SIZE]; From b8de2391d67f97dc3b6026081d2dc3494451045f Mon Sep 17 00:00:00 2001 From: Jorn Vernee Date: Mon, 16 Nov 2020 14:25:51 +0000 Subject: [PATCH 119/124] 8256380: JDK-8254162 broke 32bit windows build Reviewed-by: shade --- src/hotspot/share/prims/scopedMemoryAccess.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/prims/scopedMemoryAccess.hpp b/src/hotspot/share/prims/scopedMemoryAccess.hpp index 050e729f84481..0b99f3a51ab43 100644 --- a/src/hotspot/share/prims/scopedMemoryAccess.hpp +++ b/src/hotspot/share/prims/scopedMemoryAccess.hpp @@ -29,7 +29,7 @@ #include "jni.h" extern "C" { - void JNICALL JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jobject rec, jobject scope, jthrowable exception); + void JNICALL JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass); } #endif // SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP From f611fdfee854ea7c4f6ade985685c66c81e67ca5 Mon Sep 17 00:00:00 2001 From: Boris Ulasevich Date: Mon, 16 Nov 2020 14:58:14 +0000 Subject: [PATCH 120/124] 8254016: Test8237524 fails with -XX:-CompactStrings option Reviewed-by: shade --- test/hotspot/jtreg/compiler/intrinsics/Test8237524.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/Test8237524.java b/test/hotspot/jtreg/compiler/intrinsics/Test8237524.java index a5a2d298a8bdd..b2ebd1384b804 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/Test8237524.java +++ b/test/hotspot/jtreg/compiler/intrinsics/Test8237524.java @@ -29,7 +29,7 @@ * * @modules java.base/java.lang:open * - * @run main/othervm compiler.intrinsics.Test8237524 + * @run main/othervm -XX:+CompactStrings compiler.intrinsics.Test8237524 */ package compiler.intrinsics; From 3675653c20a6b482d5a18ef3d163054af716e1f9 Mon Sep 17 00:00:00 2001 From: Patricio Chilano Mateo Date: Mon, 16 Nov 2020 17:21:13 +0000 Subject: [PATCH 121/124] 8255384: Remove special_runtime_exit_condition() check from SS::block() Reviewed-by: dholmes, rrich, dcubed --- .../interpreter/zero/bytecodeInterpreter.cpp | 13 +++--- src/hotspot/share/runtime/safepoint.cpp | 43 ++++--------------- .../share/runtime/safepointMechanism.cpp | 2 +- .../share/runtime/safepointMechanism.hpp | 1 + .../runtime/safepointMechanism.inline.hpp | 7 +++ src/hotspot/share/runtime/thread.cpp | 21 +++------ 6 files changed, 30 insertions(+), 57 deletions(-) diff --git a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp index 06f66a1346486..137fd3166927c 100644 --- a/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp +++ b/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp @@ -102,11 +102,14 @@ There really shouldn't be any handles remaining to trash but this is cheap in relation to a safepoint. */ -#define SAFEPOINT \ - { \ - /* zap freed handles rather than GC'ing them */ \ - HandleMarkCleaner __hmc(THREAD); \ - CALL_VM(SafepointMechanism::process_if_requested(THREAD), handle_exception); \ +#define SAFEPOINT \ + { \ + /* zap freed handles rather than GC'ing them */ \ + HandleMarkCleaner __hmc(THREAD); \ + if (SafepointMechanism::should_process(THREAD)) { \ + CALL_VM(SafepointMechanism::process_if_requested_with_exit_check(THREAD, true /* check asyncs */), \ + handle_exception); \ + } \ } /* diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp index ea3a174dae091..e1997cfa212f5 100644 --- a/src/hotspot/share/runtime/safepoint.cpp +++ b/src/hotspot/share/runtime/safepoint.cpp @@ -737,29 +737,6 @@ void SafepointSynchronize::block(JavaThread *thread) { guarantee(thread->safepoint_state()->get_safepoint_id() == InactiveSafepointCounter, "The safepoint id should be set only in block path"); - // Check for pending. async. exceptions or suspends - except if the - // thread was blocked inside the VM. has_special_runtime_exit_condition() - // is called last since it grabs a lock and we only want to do that when - // we must. - // - // Note: we never deliver an async exception at a polling point as the - // compiler may not have an exception handler for it. The polling - // code will notice the async and deoptimize and the exception will - // be delivered. (Polling at a return point is ok though). Sure is - // a lot of bother for a deprecated feature... - // - // We don't deliver an async exception if the thread state is - // _thread_in_native_trans so JNI functions won't be called with - // a surprising pending exception. If the thread state is going back to java, - // async exception is checked in check_special_condition_for_native_trans(). - - if (state != _thread_blocked_trans && - state != _thread_in_vm_trans && - thread->has_special_runtime_exit_condition()) { - thread->handle_special_runtime_exit_condition( - !thread->is_at_poll_safepoint() && (state != _thread_in_native_trans)); - } - // cross_modify_fence is done by SafepointMechanism::process_if_requested // which is the only caller here. } @@ -955,12 +932,7 @@ void ThreadSafepointState::handle_polling_page_exception() { StackWatermarkSet::after_unwind(self); // Process pending operation - SafepointMechanism::process_if_requested(self); - // We have to wait if we are here because of a handshake for object deoptimization. - if (self->is_obj_deopt_suspend()) { - self->wait_for_object_deoptimization(); - } - self->check_and_handle_async_exceptions(); + SafepointMechanism::process_if_requested_with_exit_check(self, true /* check asyncs */); // restore oop result, if any if (return_oop) { @@ -970,17 +942,18 @@ void ThreadSafepointState::handle_polling_page_exception() { // This is a safepoint poll. Verify the return address and block. else { - set_at_poll_safepoint(true); // verify the blob built the "return address" correctly assert(real_return_addr == caller_fr.pc(), "must match"); + set_at_poll_safepoint(true); // Process pending operation - SafepointMechanism::process_if_requested(self); - // We have to wait if we are here because of a handshake for object deoptimization. - if (self->is_obj_deopt_suspend()) { - self->wait_for_object_deoptimization(); - } + // We never deliver an async exception at a polling point as the + // compiler may not have an exception handler for it. The polling + // code will notice the pending async exception, deoptimize and + // the exception will be delivered. (Polling at a return point + // is ok though). Sure is a lot of bother for a deprecated feature... + SafepointMechanism::process_if_requested_with_exit_check(self, false /* check asyncs */); set_at_poll_safepoint(false); // If we have a pending async exception deoptimize the frame diff --git a/src/hotspot/share/runtime/safepointMechanism.cpp b/src/hotspot/share/runtime/safepointMechanism.cpp index bb10694b000d8..c99041b2dcb45 100644 --- a/src/hotspot/share/runtime/safepointMechanism.cpp +++ b/src/hotspot/share/runtime/safepointMechanism.cpp @@ -80,7 +80,7 @@ void SafepointMechanism::process(JavaThread *thread) { // Any load in ::block must not pass the global poll load. // Otherwise we might load an old safepoint counter (for example). OrderAccess::loadload(); - SafepointSynchronize::block(thread); // Recursive + SafepointSynchronize::block(thread); } // The call to on_safepoint fixes the thread's oops and the first few frames. diff --git a/src/hotspot/share/runtime/safepointMechanism.hpp b/src/hotspot/share/runtime/safepointMechanism.hpp index b24ceac4606cf..2f4daa64c4e83 100644 --- a/src/hotspot/share/runtime/safepointMechanism.hpp +++ b/src/hotspot/share/runtime/safepointMechanism.hpp @@ -83,6 +83,7 @@ class SafepointMechanism : public AllStatic { // Processes a pending requested operation. static inline void process_if_requested(JavaThread* thread); + static inline void process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs); // Compute what the poll values should be and install them. static void update_poll_values(JavaThread* thread); diff --git a/src/hotspot/share/runtime/safepointMechanism.inline.hpp b/src/hotspot/share/runtime/safepointMechanism.inline.hpp index 98427fc001937..1279401049f96 100644 --- a/src/hotspot/share/runtime/safepointMechanism.inline.hpp +++ b/src/hotspot/share/runtime/safepointMechanism.inline.hpp @@ -80,6 +80,13 @@ void SafepointMechanism::process_if_requested(JavaThread *thread) { process_if_requested_slow(thread); } +void SafepointMechanism::process_if_requested_with_exit_check(JavaThread* thread, bool check_asyncs) { + process_if_requested(thread); + if (thread->has_special_runtime_exit_condition()) { + thread->handle_special_runtime_exit_condition(check_asyncs); + } +} + void SafepointMechanism::arm_local_poll(JavaThread* thread) { thread->poll_data()->set_polling_word(_poll_word_armed_value); thread->poll_data()->set_polling_page(_poll_page_armed_value); diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp index bace144baa13d..bdc17b380b2d8 100644 --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -2409,11 +2409,11 @@ void JavaThread::java_suspend_self_with_safepoint_check() { // might return to _thread_in_Java and execute bytecodes for an arbitrary // long time. set_thread_state_fence(state); - } while (is_external_suspend()); - if (state != _thread_in_native) { - SafepointMechanism::process_if_requested(this); - } + if (state != _thread_in_native) { + SafepointMechanism::process_if_requested(this); + } + } while (is_external_suspend()); } // Wait for another thread to perform object reallocation and relocking on behalf of @@ -2493,20 +2493,9 @@ void JavaThread::verify_not_published() { // directly and when thread state is _thread_in_native_trans void JavaThread::check_safepoint_and_suspend_for_native_trans(JavaThread *thread) { assert(thread->thread_state() == _thread_in_native_trans, "wrong state"); - assert(!thread->has_last_Java_frame() || thread->frame_anchor()->walkable(), "Unwalkable stack in native->vm transition"); - if (thread->is_external_suspend()) { - thread->java_suspend_self_with_safepoint_check(); - } else { - SafepointMechanism::process_if_requested(thread); - } - - if (thread->is_obj_deopt_suspend()) { - thread->wait_for_object_deoptimization(); - } - - JFR_ONLY(SUSPEND_THREAD_CONDITIONAL(thread);) + SafepointMechanism::process_if_requested_with_exit_check(thread, false /* check asyncs */); } // Slow path when the native==>VM/Java barriers detect a safepoint is in From 68fd71d2ad3b55297dc3703443cef99673090334 Mon Sep 17 00:00:00 2001 From: Igor Ignatyev Date: Mon, 16 Nov 2020 19:30:43 +0000 Subject: [PATCH 122/124] 8256414: add optimized build to submit workflow add linux-x64-optimized to submit workflow Reviewed-by: vlivanov, shade, kvn --- .github/workflows/submit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/submit.yml b/.github/workflows/submit.yml index 26b5cc84bf873..d0817aa724108 100644 --- a/.github/workflows/submit.yml +++ b/.github/workflows/submit.yml @@ -119,6 +119,7 @@ jobs: - build hotspot no-pch - build hotspot zero - build hotspot minimal + - build hotspot optimized include: - flavor: build debug flags: --enable-debug @@ -132,6 +133,9 @@ jobs: - flavor: build hotspot minimal flags: --enable-debug --disable-precompiled-headers --with-jvm-variants=minimal build-target: hotspot + - flavor: build hotspot optimized + flags: --with-debug-level=optimized --disable-precompiled-headers + build-target: hotspot env: JDK_VERSION: "${{ fromJson(needs.prerequisites.outputs.dependencies).DEFAULT_VERSION_FEATURE }}" From 6e35bcbf038cec0210c38428a8e1c233e102911a Mon Sep 17 00:00:00 2001 From: Claes Redestad Date: Mon, 16 Nov 2020 19:39:36 +0000 Subject: [PATCH 123/124] 8256205: Simplify compiler calling convention handling Reviewed-by: kvn, neliasso --- src/hotspot/cpu/aarch64/aarch64.ad | 30 +---------- .../cpu/aarch64/c1_CodeStubs_aarch64.cpp | 2 +- .../cpu/aarch64/c1_FrameMap_aarch64.cpp | 2 +- .../cpu/aarch64/sharedRuntime_aarch64.cpp | 12 ++++- src/hotspot/cpu/arm/arm.ad | 22 -------- src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp | 2 +- src/hotspot/cpu/arm/sharedRuntime_arm.cpp | 13 +++-- src/hotspot/cpu/ppc/ppc.ad | 36 ------------- src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp | 7 ++- src/hotspot/cpu/s390/s390.ad | 34 ------------- src/hotspot/cpu/s390/sharedRuntime_s390.cpp | 7 ++- src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp | 2 +- src/hotspot/cpu/x86/c1_FrameMap_x86.cpp | 2 +- src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp | 11 +++- src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp | 12 ++++- src/hotspot/cpu/x86/x86_32.ad | 29 ----------- src/hotspot/cpu/x86/x86_64.ad | 25 ---------- src/hotspot/cpu/zero/sharedRuntime_zero.cpp | 3 +- src/hotspot/share/adlc/adlparse.cpp | 50 +++---------------- src/hotspot/share/adlc/adlparse.hpp | 2 - src/hotspot/share/adlc/formsopt.cpp | 3 -- src/hotspot/share/adlc/formsopt.hpp | 3 -- src/hotspot/share/adlc/output_c.cpp | 18 +------ src/hotspot/share/c1/c1_FrameMap.cpp | 2 +- src/hotspot/share/code/nmethod.cpp | 2 +- src/hotspot/share/opto/callnode.cpp | 17 ++++--- src/hotspot/share/opto/compile.hpp | 11 ++-- src/hotspot/share/opto/matcher.cpp | 8 +-- src/hotspot/share/opto/matcher.hpp | 15 ++---- src/hotspot/share/runtime/sharedRuntime.cpp | 16 +++--- src/hotspot/share/runtime/sharedRuntime.hpp | 11 ++-- 31 files changed, 104 insertions(+), 305 deletions(-) diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index a9df812c634bc..2ec3d26ac8138 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -4099,13 +4099,6 @@ frame %{ // Stack alignment requirement stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes) - // Number of stack slots between incoming argument block and the start of - // a new frame. The PROLOG must add this many slots to the stack. The - // EPILOG must remove this many slots. aarch64 needs two slots for - // return address and fp. - // TODO think this is correct but check - in_preserve_stack_slots(4); - // Number of outgoing stack slots killed above the out_preserve_stack_slots // for calls to C. Supports the var-args backing area for register parms. varargs_C_out_slots_killed(frame::arg_reg_save_area_bytes/BytesPerInt); @@ -4124,25 +4117,6 @@ frame %{ Compile::current()->fixed_slots()), stack_alignment_in_slots())); - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - - calling_convention - %{ - // No difference between ingoing/outgoing just pass false - SharedRuntime::java_calling_convention(sig_bt, regs, length, false); - %} - - c_calling_convention - %{ - // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, NULL, length); - %} - // Location of compiled Java return values. Same as C for now. return_value %{ @@ -12401,7 +12375,7 @@ instruct SubL_reg_LShift_reg(iRegLNoSp dst, ins_pipe(ialu_reg_reg_shift); %} - + // This pattern is automatically generated from aarch64_ad.m4 // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE @@ -12708,7 +12682,7 @@ instruct ubfizIConvI2LAndI(iRegLNoSp dst, iRegI src, immI_bitmask msk) %} -// Rotations +// Rotations // This pattern is automatically generated from aarch64_ad.m4 // DO NOT EDIT ANYTHING IN THIS SECTION OF THE FILE instruct extrOrL(iRegLNoSp dst, iRegL src1, iRegL src2, immI lshift, immI rshift, rFlagsReg cr) diff --git a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp index 119bc979e0a99..63f03db2dc5db 100644 --- a/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_CodeStubs_aarch64.cpp @@ -323,7 +323,7 @@ void ArrayCopyStub::emit_code(LIR_Assembler* ce) { // VMRegPair args[5]; BasicType signature[5] = { T_OBJECT, T_INT, T_OBJECT, T_INT, T_INT}; - SharedRuntime::java_calling_convention(signature, args, 5, true); + SharedRuntime::java_calling_convention(signature, args, 5); // push parameters // (src, src_pos, dest, destPos, length) diff --git a/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp index 42ff62a986cba..0da37ae08a643 100644 --- a/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/c1_FrameMap_aarch64.cpp @@ -290,7 +290,7 @@ void FrameMap::initialize() { VMRegPair regs; BasicType sig_bt = T_OBJECT; - SharedRuntime::java_calling_convention(&sig_bt, ®s, 1, true); + SharedRuntime::java_calling_convention(&sig_bt, ®s, 1); receiver_opr = as_oop_opr(regs.first()->as_Register()); for (int i = 0; i < nof_caller_save_fpu_regs; i++) { diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 92a07a84d2a94..e13ef62593b95 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -276,8 +276,7 @@ static int reg2offset_out(VMReg r) { int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { // Create the mapping between argument positions and // registers. @@ -2522,6 +2521,15 @@ void SharedRuntime::generate_deopt_blob() { #endif } +// Number of stack slots between incoming argument block and the start of +// a new frame. The PROLOG must add this many slots to the stack. The +// EPILOG must remove this many slots. aarch64 needs two slots for +// return address and fp. +// TODO think this is correct but check +uint SharedRuntime::in_preserve_stack_slots() { + return 4; +} + uint SharedRuntime::out_preserve_stack_slots() { return 0; } diff --git a/src/hotspot/cpu/arm/arm.ad b/src/hotspot/cpu/arm/arm.ad index 7ea64d1d1ac9e..1c23d72e0af01 100644 --- a/src/hotspot/cpu/arm/arm.ad +++ b/src/hotspot/cpu/arm/arm.ad @@ -1673,12 +1673,6 @@ frame %{ // LP64: Alignment size in bytes (128-bit -> 16 bytes) // !LP64: Alignment size in bytes (64-bit -> 8 bytes) - // Number of stack slots between incoming argument block and the start of - // a new frame. The PROLOG must add this many slots to the stack. The - // EPILOG must remove this many slots. - // FP + LR - in_preserve_stack_slots(2 * VMRegImpl::slots_per_word); - // Number of outgoing stack slots killed above the out_preserve_stack_slots // for calls to C. Supports the var-args backing area for register parms. // ADLC doesn't support parsing expressions, so I folded the math by hand. @@ -1695,22 +1689,6 @@ frame %{ Compile::current()->fixed_slots()), stack_alignment_in_slots())); - // Body of function which returns an OptoRegs array locating - // arguments either in registers or in stack slots for calling - // java - calling_convention %{ - (void) SharedRuntime::java_calling_convention(sig_bt, regs, length, is_outgoing); - - %} - - // Body of function which returns an OptoRegs array locating - // arguments either in registers or in stack slots for callin - // C. - c_calling_convention %{ - // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); - %} - // Location of compiled Java return values. Same as C return_value %{ return c2::return_value(ideal_reg); diff --git a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp index 6b390c1cda34a..b0ace8d21f9d3 100644 --- a/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp +++ b/src/hotspot/cpu/arm/c1_CodeStubs_arm.cpp @@ -421,7 +421,7 @@ void ArrayCopyStub::emit_code(LIR_Assembler* ce) { VMRegPair args[5]; BasicType signature[5] = { T_OBJECT, T_INT, T_OBJECT, T_INT, T_INT }; - SharedRuntime::java_calling_convention(signature, args, 5, true); + SharedRuntime::java_calling_convention(signature, args, 5); Register r[5]; r[0] = src()->as_pointer_register(); diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index a4216785e4e7d..cb1399776842d 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -364,13 +364,11 @@ int SharedRuntime::c_calling_convention(const BasicType *sig_bt, int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { #ifdef __SOFTFP__ // soft float is the same as the C calling convention. return c_calling_convention(sig_bt, regs, NULL, total_args_passed); #endif // __SOFTFP__ - (void) is_outgoing; int slot = 0; int ireg = 0; int freg = 0; @@ -1371,11 +1369,18 @@ int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) } +// Number of stack slots between incoming argument block and the start of +// a new frame. The PROLOG must add this many slots to the stack. The +// EPILOG must remove this many slots. +// FP + LR +uint SharedRuntime::in_preserve_stack_slots() { + return 2 * VMRegImpl::slots_per_word; +} + uint SharedRuntime::out_preserve_stack_slots() { return 0; } - //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { ResourceMark rm; diff --git a/src/hotspot/cpu/ppc/ppc.ad b/src/hotspot/cpu/ppc/ppc.ad index 27dad6cff948c..8bbbc3d4aa374 100644 --- a/src/hotspot/cpu/ppc/ppc.ad +++ b/src/hotspot/cpu/ppc/ppc.ad @@ -3848,8 +3848,6 @@ frame %{ stack_alignment(frame::alignment_in_bytes); - in_preserve_stack_slots((frame::jit_in_preserve_size / VMRegImpl::stack_slot_size)); - // Number of outgoing stack slots killed above the // out_preserve_stack_slots for calls to C. Supports the var-args // backing area for register parms. @@ -3874,40 +3872,6 @@ frame %{ // 4 what apparently works and saves us some spills. return_addr(STACK 4); - // This is the body of the function - // - // void Matcher::calling_convention(OptoRegPair* sig, // array of ideal regs - // uint length, // length of array - // bool is_outgoing) - // - // The `sig' array is to be updated. sig[j] represents the location - // of the j-th argument, either a register or a stack slot. - - // Comment taken from x86_32.ad: - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - calling_convention %{ - // No difference between ingoing/outgoing. Just pass false. - SharedRuntime::java_calling_convention(sig_bt, regs, length, false); - %} - - // Comment taken from x86_32.ad: - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - c_calling_convention %{ - // This is obviously always outgoing. - // C argument in register AND stack slot. - (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); - %} - // Location of native (C/C++) and interpreter return values. This // is specified to be the same as Java. In the 32-bit VM, long // values are actually returned from native calls in O0:O1 and diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index e8498ba0ed34f..59c249151beab 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -633,8 +633,7 @@ const int num_java_farg_registers = sizeof(java_farg_reg) / sizeof(java_farg_reg int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { // C2c calling conventions for compiled-compiled calls. // Put 8 ints/longs into registers _AND_ 13 float/doubles into // registers _AND_ put the rest on the stack. @@ -2511,6 +2510,10 @@ int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) return align_up((callee_locals - callee_parameters) * Interpreter::stackElementWords, frame::alignment_in_bytes); } +uint SharedRuntime::in_preserve_stack_slots() { + return frame::jit_in_preserve_size / VMRegImpl::stack_slot_size; +} + uint SharedRuntime::out_preserve_stack_slots() { #if defined(COMPILER1) || defined(COMPILER2) return frame::jit_out_preserve_size / VMRegImpl::stack_slot_size; diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 98194c73047c1..9a574ac0854e7 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -2449,8 +2449,6 @@ frame %{ // Use alignment_in_bytes instead of log_2_of_alignment_in_bits. stack_alignment(frame::alignment_in_bytes); - in_preserve_stack_slots(frame::jit_in_preserve_size_in_4_byte_units); - // A `slot' is assumed 4 bytes here! // out_preserve_stack_slots(frame::jit_out_preserve_size_in_4_byte_units); @@ -2465,38 +2463,6 @@ frame %{ // stack slot. return_addr(REG Z_R14); - // This is the body of the function - // - // void Matcher::calling_convention(OptoRegPair* sig /* array of ideal regs */, - // uint length /* length of array */, - // bool is_outgoing) - // - // The `sig' array is to be updated. Sig[j] represents the location - // of the j-th argument, either a register or a stack slot. - - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - calling_convention %{ - // No difference between ingoing/outgoing just pass false. - SharedRuntime::java_calling_convention(sig_bt, regs, length, false); - %} - - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - c_calling_convention %{ - // This is obviously always outgoing. - // C argument must be in register AND stack slot. - (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); - %} - // Location of native (C/C++) and interpreter return values. This // is specified to be the same as Java. In the 32-bit VM, long // values are actually returned from native calls in O0:O1 and diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index a0c46b182ff28..c18d531925765 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -649,8 +649,7 @@ void SharedRuntime::restore_native_result(MacroAssembler *masm, // advantage out of it. int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { // c2c calling conventions for compiled-compiled calls. // An int/float occupies 1 slot here. @@ -2579,6 +2578,10 @@ int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals) frame::z_parent_ijava_frame_abi_size / BytesPerWord; } +uint SharedRuntime::in_preserve_stack_slots() { + return frame::jit_in_preserve_size_in_4_byte_units; +} + uint SharedRuntime::out_preserve_stack_slots() { return frame::z_jit_out_preserve_size/VMRegImpl::stack_slot_size; } diff --git a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp index 6853953f0ebe7..1ba5061a57481 100644 --- a/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp +++ b/src/hotspot/cpu/x86/c1_CodeStubs_x86.cpp @@ -509,7 +509,7 @@ void ArrayCopyStub::emit_code(LIR_Assembler* ce) { // VMRegPair args[5]; BasicType signature[5] = { T_OBJECT, T_INT, T_OBJECT, T_INT, T_INT}; - SharedRuntime::java_calling_convention(signature, args, 5, true); + SharedRuntime::java_calling_convention(signature, args, 5); // push parameters // (src, src_pos, dest, destPos, length) diff --git a/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp b/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp index f25cefb8a44dd..a7411412d18f9 100644 --- a/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp +++ b/src/hotspot/cpu/x86/c1_FrameMap_x86.cpp @@ -299,7 +299,7 @@ void FrameMap::initialize() { VMRegPair regs; BasicType sig_bt = T_OBJECT; - SharedRuntime::java_calling_convention(&sig_bt, ®s, 1, true); + SharedRuntime::java_calling_convention(&sig_bt, ®s, 1); receiver_opr = as_oop_opr(regs.first()->as_Register()); } diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index 3e2b3a118c7bb..fd6c7ab5f7008 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -418,8 +418,7 @@ static int reg2offset_out(VMReg r) { // the doubles will grab the registers before the floats will. int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { uint stack = 0; // Starting stack position for args on stack @@ -2199,6 +2198,14 @@ int Deoptimization::last_frame_adjust(int callee_parameters, int callee_locals ) } +// Number of stack slots between incoming argument block and the start of +// a new frame. The PROLOG must add this many slots to the stack. The +// EPILOG must remove this many slots. Intel needs one slot for +// return address and one for rbp, (must save rbp) +uint SharedRuntime::in_preserve_stack_slots() { + return 2+VerifyStackAtCalls; +} + uint SharedRuntime::out_preserve_stack_slots() { return 0; } diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index b238b0e0d352e..635ea4288c9d6 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -451,8 +451,7 @@ static int reg2offset_out(VMReg r) { int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { // Create the mapping between argument positions and // registers. @@ -2592,6 +2591,15 @@ uint SharedRuntime::out_preserve_stack_slots() { return 0; } + +// Number of stack slots between incoming argument block and the start of +// a new frame. The PROLOG must add this many slots to the stack. The +// EPILOG must remove this many slots. amd64 needs two slots for +// return address. +uint SharedRuntime::in_preserve_stack_slots() { + return 4 + 2 * VerifyStackAtCalls; +} + //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { // Allocate space for the code diff --git a/src/hotspot/cpu/x86/x86_32.ad b/src/hotspot/cpu/x86/x86_32.ad index 130cdbed3e1c7..4d815e62240aa 100644 --- a/src/hotspot/cpu/x86/x86_32.ad +++ b/src/hotspot/cpu/x86/x86_32.ad @@ -3159,12 +3159,6 @@ frame %{ // Alignment size in bytes (128-bit -> 16 bytes) stack_alignment(StackAlignmentInBytes); - // Number of stack slots between incoming argument block and the start of - // a new frame. The PROLOG must add this many slots to the stack. The - // EPILOG must remove this many slots. Intel needs one slot for - // return address and one for rbp, (must save rbp) - in_preserve_stack_slots(2+VerifyStackAtCalls); - // Number of outgoing stack slots killed above the out_preserve_stack_slots // for calls to C. Supports the var-args backing area for register parms. varargs_C_out_slots_killed(0); @@ -3180,29 +3174,6 @@ frame %{ Compile::current()->fixed_slots()), stack_alignment_in_slots())); - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - calling_convention %{ - // No difference between ingoing/outgoing just pass false - SharedRuntime::java_calling_convention(sig_bt, regs, length, false); - %} - - - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - c_calling_convention %{ - // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); - %} - // Location of C & interpreter return values c_return_value %{ assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" ); diff --git a/src/hotspot/cpu/x86/x86_64.ad b/src/hotspot/cpu/x86/x86_64.ad index 16cf230d602ba..8ae83fb298b26 100644 --- a/src/hotspot/cpu/x86/x86_64.ad +++ b/src/hotspot/cpu/x86/x86_64.ad @@ -2756,12 +2756,6 @@ frame // Stack alignment requirement stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes) - // Number of stack slots between incoming argument block and the start of - // a new frame. The PROLOG must add this many slots to the stack. The - // EPILOG must remove this many slots. amd64 needs two slots for - // return address. - in_preserve_stack_slots(4 + 2 * VerifyStackAtCalls); - // Number of outgoing stack slots killed above the out_preserve_stack_slots // for calls to C. Supports the var-args backing area for register parms. varargs_C_out_slots_killed(frame::arg_reg_save_area_bytes/BytesPerInt); @@ -2777,25 +2771,6 @@ frame Compile::current()->fixed_slots()), stack_alignment_in_slots())); - // Body of function which returns an integer array locating - // arguments either in registers or in stack slots. Passed an array - // of ideal registers called "sig" and a "length" count. Stack-slot - // offsets are based on outgoing arguments, i.e. a CALLER setting up - // arguments for a CALLEE. Incoming stack arguments are - // automatically biased by the preserve_stack_slots field above. - - calling_convention - %{ - // No difference between ingoing/outgoing just pass false - SharedRuntime::java_calling_convention(sig_bt, regs, length, false); - %} - - c_calling_convention - %{ - // This is obviously always outgoing - (void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length); - %} - // Location of compiled Java return values. Same as C for now. return_value %{ diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index 12b66b74387f7..f4aaeb6ea1e49 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -51,8 +51,7 @@ static address zero_null_code_stub() { int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, - int total_args_passed, - int is_outgoing) { + int total_args_passed) { return 0; } diff --git a/src/hotspot/share/adlc/adlparse.cpp b/src/hotspot/share/adlc/adlparse.cpp index 7d62f861686e7..ecb722c56b232 100644 --- a/src/hotspot/share/adlc/adlparse.cpp +++ b/src/hotspot/share/adlc/adlparse.cpp @@ -1019,7 +1019,8 @@ void ADLParser::frame_parse(void) { return_addr_parse(frame, false); } if (strcmp(token,"in_preserve_stack_slots")==0) { - preserve_stack_parse(frame); + parse_err(WARN, "Using obsolete token, in_preserve_stack_slots"); + skipws(); } if (strcmp(token,"out_preserve_stack_slots")==0) { parse_err(WARN, "Using obsolete token, out_preserve_stack_slots"); @@ -1029,7 +1030,8 @@ void ADLParser::frame_parse(void) { frame->_varargs_C_out_slots_killed = parse_one_arg("varargs C out slots killed"); } if (strcmp(token,"calling_convention")==0) { - frame->_calling_convention = calling_convention_parse(); + parse_err(WARN, "Using obsolete token, calling_convention"); + skipws(); } if (strcmp(token,"return_value")==0) { frame->_return_value = return_value_parse(); @@ -1041,7 +1043,8 @@ void ADLParser::frame_parse(void) { return_addr_parse(frame, true); } if (strcmp(token,"c_calling_convention")==0) { - frame->_c_calling_convention = calling_convention_parse(); + parse_err(WARN, "Using obsolete token, c_calling_convention"); + skipws(); } if (strcmp(token,"c_return_value")==0) { frame->_c_return_value = return_value_parse(); @@ -1072,18 +1075,10 @@ void ADLParser::frame_parse(void) { parse_err(SYNERR, "missing return address location in frame section.\n"); return; } - if(frame->_in_preserve_slots == NULL) { - parse_err(SYNERR, "missing stack slot preservation definition in frame section.\n"); - return; - } if(frame->_varargs_C_out_slots_killed == NULL) { parse_err(SYNERR, "missing varargs C out slots killed definition in frame section.\n"); return; } - if(frame->_calling_convention == NULL) { - parse_err(SYNERR, "missing calling convention definition in frame section.\n"); - return; - } if(frame->_return_value == NULL) { parse_err(SYNERR, "missing return value definition in frame section.\n"); return; @@ -1096,9 +1091,6 @@ void ADLParser::frame_parse(void) { frame->_c_return_addr = frame->_return_addr; frame->_c_return_addr_loc = frame->_return_addr_loc; } - if(frame->_c_calling_convention == NULL) { - frame->_c_calling_convention = frame->_calling_convention; - } if(frame->_c_return_value == NULL) { frame->_c_return_value = frame->_return_value; } @@ -1221,37 +1213,9 @@ void ADLParser::return_addr_parse(FrameForm *frame, bool native) { } } -//------------------------------preserve_stack_parse--------------------------- -void ADLParser::preserve_stack_parse(FrameForm *frame) { - if(_curchar == '(') { - char *token = get_paren_expr("preserve_stack_slots"); - frame->_in_preserve_slots = token; - - if(_curchar != ';') { // check for semi-colon - parse_err(SYNERR, "missing %c in preserve stack slot entry.\n", ';'); - return; - } - next_char(); // skip the semi-colon - } - else { - parse_err(SYNERR, "Missing %c in preserve stack slot entry.\n", '('); - } -} - -//------------------------------calling_convention_parse----------------------- -char *ADLParser::calling_convention_parse() { - char *desc = NULL; // String representation of calling_convention - - skipws(); // Skip leading whitespace - if ( (desc = find_cpp_block("calling convention block")) == NULL ) { - parse_err(SYNERR, "incorrect or missing block for 'calling_convention'.\n"); - } - return desc; -} - //------------------------------return_value_parse----------------------------- char *ADLParser::return_value_parse() { - char *desc = NULL; // String representation of calling_convention + char *desc = NULL; // String representation of return_value skipws(); // Skip leading whitespace if ( (desc = find_cpp_block("return value block")) == NULL ) { diff --git a/src/hotspot/share/adlc/adlparse.hpp b/src/hotspot/share/adlc/adlparse.hpp index f73c3a5c36565..0435144318f7a 100644 --- a/src/hotspot/share/adlc/adlparse.hpp +++ b/src/hotspot/share/adlc/adlparse.hpp @@ -119,8 +119,6 @@ class ADLParser { void cisc_spilling_operand_name_parse(FrameForm *frame, bool native); void stack_alignment_parse(FrameForm *frame); void return_addr_parse(FrameForm *frame, bool native); - void preserve_stack_parse(FrameForm *frame); - char *calling_convention_parse(); char *return_value_parse(); // Parse components of the register section diff --git a/src/hotspot/share/adlc/formsopt.cpp b/src/hotspot/share/adlc/formsopt.cpp index 85aaac495fe5b..351904bbb291a 100644 --- a/src/hotspot/share/adlc/formsopt.cpp +++ b/src/hotspot/share/adlc/formsopt.cpp @@ -441,10 +441,7 @@ FrameForm::FrameForm() { _alignment = NULL; _return_addr = NULL; _c_return_addr = NULL; - _in_preserve_slots = NULL; _varargs_C_out_slots_killed = NULL; - _calling_convention = NULL; - _c_calling_convention = NULL; _return_value = NULL; _c_return_value = NULL; _interpreter_frame_pointer_reg = NULL; diff --git a/src/hotspot/share/adlc/formsopt.hpp b/src/hotspot/share/adlc/formsopt.hpp index 400c2690a9ab4..6e872b5c64766 100644 --- a/src/hotspot/share/adlc/formsopt.hpp +++ b/src/hotspot/share/adlc/formsopt.hpp @@ -345,10 +345,7 @@ class FrameForm : public Form { bool _c_return_addr_loc; char *_return_addr; char *_c_return_addr; - char *_in_preserve_slots; char *_varargs_C_out_slots_killed; - char *_calling_convention; - char *_c_calling_convention; char *_return_value; char *_c_return_value; diff --git a/src/hotspot/share/adlc/output_c.cpp b/src/hotspot/share/adlc/output_c.cpp index c6083533b58b1..a044ceab00b49 100644 --- a/src/hotspot/share/adlc/output_c.cpp +++ b/src/hotspot/share/adlc/output_c.cpp @@ -4158,29 +4158,15 @@ void ArchDesc::buildFrameMethods(FILE *fp_cpp) { fprintf(fp_cpp," return OptoReg::stack2reg(%s); }\n\n", _frame->_return_addr); } - // Java Stack Slot Preservation - fprintf(fp_cpp,"uint Compile::in_preserve_stack_slots() "); - fprintf(fp_cpp,"{ return %s; }\n\n", _frame->_in_preserve_slots); - // Top Of Stack Slot Preservation, for both Java and C - fprintf(fp_cpp,"uint Compile::out_preserve_stack_slots() "); - fprintf(fp_cpp,"{ return SharedRuntime::out_preserve_stack_slots(); }\n\n"); // varargs C out slots killed fprintf(fp_cpp,"uint Compile::varargs_C_out_slots_killed() const "); fprintf(fp_cpp,"{ return %s; }\n\n", _frame->_varargs_C_out_slots_killed); - // Java Argument Position - fprintf(fp_cpp,"void Matcher::calling_convention(BasicType *sig_bt, VMRegPair *regs, uint length, bool is_outgoing) {\n"); - fprintf(fp_cpp,"%s\n", _frame->_calling_convention); - fprintf(fp_cpp,"}\n\n"); - // Native Argument Position - fprintf(fp_cpp,"void Matcher::c_calling_convention(BasicType *sig_bt, VMRegPair *regs, uint length) {\n"); - fprintf(fp_cpp,"%s\n", _frame->_c_calling_convention); - fprintf(fp_cpp,"}\n\n"); // Java Return Value Location - fprintf(fp_cpp,"OptoRegPair Matcher::return_value(uint ideal_reg, bool is_outgoing) {\n"); + fprintf(fp_cpp,"OptoRegPair Matcher::return_value(uint ideal_reg) {\n"); fprintf(fp_cpp,"%s\n", _frame->_return_value); fprintf(fp_cpp,"}\n\n"); // Native Return Value Location - fprintf(fp_cpp,"OptoRegPair Matcher::c_return_value(uint ideal_reg, bool is_outgoing) {\n"); + fprintf(fp_cpp,"OptoRegPair Matcher::c_return_value(uint ideal_reg) {\n"); fprintf(fp_cpp,"%s\n", _frame->_c_return_value); fprintf(fp_cpp,"}\n\n"); diff --git a/src/hotspot/share/c1/c1_FrameMap.cpp b/src/hotspot/share/c1/c1_FrameMap.cpp index 1a517c9f54924..fe934d54146f7 100644 --- a/src/hotspot/share/c1/c1_FrameMap.cpp +++ b/src/hotspot/share/c1/c1_FrameMap.cpp @@ -72,7 +72,7 @@ CallingConvention* FrameMap::java_calling_convention(const BasicTypeArray* signa } } - intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs, outgoing); + intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs); LIR_OprList* args = new LIR_OprList(signature->length()); for (i = 0; i < sizeargs;) { BasicType t = sig_bt[i]; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 4e21b69836bb2..9f03c16ef9644 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3110,7 +3110,7 @@ void nmethod::print_nmethod_labels(outputStream* stream, address block_begin, bo assert(sig_index == sizeargs, ""); } const char* spname = "sp"; // make arch-specific? - intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs, false); + intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs); int stack_slot_offset = this->frame_size() * wordSize; int tab1 = 14, tab2 = 24; int sig_index = 0; diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 78a632bf8aca0..1e1692dc6f12b 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -42,6 +42,7 @@ #include "opto/regmask.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" +#include "runtime/sharedRuntime.hpp" #include "utilities/powerOfTwo.hpp" // Portions of code courtesy of Clifford Click @@ -65,8 +66,8 @@ Node *StartNode::Ideal(PhaseGVN *phase, bool can_reshape){ } //------------------------------calling_convention----------------------------- -void StartNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { - Matcher::calling_convention( sig_bt, parm_regs, argcnt, false ); +void StartNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const { + SharedRuntime::java_calling_convention(sig_bt, parm_regs, argcnt); } //------------------------------Registers-------------------------------------- @@ -696,9 +697,9 @@ const Type* CallNode::Value(PhaseGVN* phase) const { } //------------------------------calling_convention----------------------------- -void CallNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { +void CallNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const { // Use the standard compiler calling convention - Matcher::calling_convention( sig_bt, parm_regs, argcnt, true ); + SharedRuntime::java_calling_convention(sig_bt, parm_regs, argcnt); } @@ -720,8 +721,8 @@ Node *CallNode::match( const ProjNode *proj, const Matcher *match ) { case TypeFunc::Parms: { // Normal returns uint ideal_reg = tf()->range()->field_at(TypeFunc::Parms)->ideal_reg(); OptoRegPair regs = is_CallRuntime() - ? match->c_return_value(ideal_reg,true) // Calls into C runtime - : match-> return_value(ideal_reg,true); // Calls into compiled Java code + ? match->c_return_value(ideal_reg) // Calls into C runtime + : match-> return_value(ideal_reg); // Calls into compiled Java code RegMask rm = RegMask(regs.first()); if( OptoReg::is_valid(regs.second()) ) rm.Insert( regs.second() ); @@ -1122,8 +1123,8 @@ void CallRuntimeNode::dump_spec(outputStream *st) const { #endif //------------------------------calling_convention----------------------------- -void CallRuntimeNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const { - Matcher::c_calling_convention( sig_bt, parm_regs, argcnt ); +void CallRuntimeNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const { + SharedRuntime::c_calling_convention(sig_bt, parm_regs, /*regs2=*/nullptr, argcnt); } //============================================================================= diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 46c4dcfb9c666..72e216d7bc998 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -40,6 +40,7 @@ #include "opto/phase.hpp" #include "opto/regmask.hpp" #include "runtime/deoptimization.hpp" +#include "runtime/sharedRuntime.hpp" #include "runtime/timerTrace.hpp" #include "runtime/vmThread.hpp" #include "utilities/ticks.hpp" @@ -1033,14 +1034,16 @@ class Compile : public Phase { // Stack slots that may be unused by the calling convention but must // otherwise be preserved. On Intel this includes the return address. // On PowerPC it includes the 4 words holding the old TOC & LR glue. - uint in_preserve_stack_slots(); + uint in_preserve_stack_slots() { + return SharedRuntime::in_preserve_stack_slots(); + } // "Top of Stack" slots that may be unused by the calling convention but must // otherwise be preserved. // On Intel these are not necessary and the value can be zero. - // On Sparc this describes the words reserved for storing a register window - // when an interrupt occurs. - static uint out_preserve_stack_slots(); + static uint out_preserve_stack_slots() { + return SharedRuntime::out_preserve_stack_slots(); + } // Number of outgoing stack slots killed above the out_preserve_stack_slots // for calls to C. Supports the var-args backing area for register parms. diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 0846cad3c3f14..a06252cab8ad1 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -202,7 +202,7 @@ void Matcher::match( ) { uint ireg = range->field_at(TypeFunc::Parms)->ideal_reg(); // Get machine return register uint sop = C->start()->Opcode(); - OptoRegPair regs = return_value(ireg, false); + OptoRegPair regs = return_value(ireg); // And mask for same _return_value_mask = RegMask(regs.first()); @@ -748,7 +748,7 @@ void Matcher::Fixup_Save_On_Entry( ) { uint reth_edge_cnt = TypeFunc::Parms+1; RegMask *reth_rms = init_input_masks( reth_edge_cnt + soe_cnt, _return_addr_mask, c_frame_ptr_mask ); // Rethrow takes exception oop only, but in the argument 0 slot. - OptoReg::Name reg = find_receiver(false); + OptoReg::Name reg = find_receiver(); if (reg >= 0) { reth_rms[TypeFunc::Parms] = mreg2regmask[reg]; #ifdef _LP64 @@ -1994,10 +1994,10 @@ void Matcher::ReduceOper( State *s, int rule, Node *&mem, MachNode *mach ) { //------------------------------find_receiver---------------------------------- // For a given signature, return the OptoReg for parameter 0. -OptoReg::Name Matcher::find_receiver( bool is_outgoing ) { +OptoReg::Name Matcher::find_receiver() { VMRegPair regs; BasicType sig_bt = T_OBJECT; - calling_convention(&sig_bt, ®s, 1, is_outgoing); + SharedRuntime::java_calling_convention(&sig_bt, ®s, 1); // Return argument 0 register. In the LP64 build pointers // take 2 registers, but the VM wants only the 'main' name. return OptoReg::as_OptoReg(regs.first()); diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 285f33d8e9303..6616a9e972b82 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -370,20 +370,16 @@ class Matcher : public PhaseTransform { return stack_alignment_in_bytes() / (VMRegImpl::stack_slot_size); } - // Array mapping arguments to registers. Argument 0 is usually the 'this' - // pointer. Registers can include stack-slots and regular registers. - static void calling_convention( BasicType *, VMRegPair *, uint len, bool is_outgoing ); - // Convert a sig into a calling convention register layout // and find interesting things about it. - static OptoReg::Name find_receiver( bool is_outgoing ); + static OptoReg::Name find_receiver(); // Return address register. On Intel it is a stack-slot. On PowerPC // it is the Link register. On Sparc it is r31? virtual OptoReg::Name return_addr() const; RegMask _return_addr_mask; - // Return value register. On Intel it is EAX. On Sparc i0/o0. - static OptoRegPair return_value(uint ideal_reg, bool is_outgoing); - static OptoRegPair c_return_value(uint ideal_reg, bool is_outgoing); + // Return value register. On Intel it is EAX. + static OptoRegPair return_value(uint ideal_reg); + static OptoRegPair c_return_value(uint ideal_reg); RegMask _return_value_mask; // Inline Cache Register static OptoReg::Name inline_cache_reg(); @@ -421,9 +417,6 @@ class Matcher : public PhaseTransform { // Java-Native calling convention // (what you use when intercalling between Java and C++ code) - // Array mapping arguments to registers. Argument 0 is usually the 'this' - // pointer. Registers can include stack-slots and regular registers. - static void c_calling_convention( BasicType*, VMRegPair *, uint ); // Frame pointer. The frame pointer is kept at the base of the stack // and so is probably the stack pointer for most machines. On Intel // it is ESP. On the PowerPC it is R1. On Sparc it is SP. diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index 95369e6272ee1..ef2b688c7f129 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -1871,8 +1871,7 @@ void SharedRuntime::check_member_name_argument_is_last_argument(const methodHand assert(member_arg_pos >= 0 && member_arg_pos < total_args_passed, "oob"); assert(sig_bt[member_arg_pos] == T_OBJECT, "dispatch argument must be an object"); - const bool is_outgoing = method->is_method_handle_intrinsic(); - int comp_args_on_stack = java_calling_convention(sig_bt, regs_without_member_name, total_args_passed - 1, is_outgoing); + int comp_args_on_stack = java_calling_convention(sig_bt, regs_without_member_name, total_args_passed - 1); for (int i = 0; i < member_arg_pos; i++) { VMReg a = regs_with_member_name[i].first(); @@ -2688,7 +2687,7 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter0(const methodHandle& met } // Get a description of the compiled java calling convention and the largest used (VMReg) stack slot usage - int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, false); + int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed); // Make a C heap allocated version of the fingerprint to store in the adapter fingerprint = new AdapterFingerPrint(total_args_passed, sig_bt); @@ -2896,11 +2895,8 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { assert(i == total_args_passed, ""); BasicType ret_type = ss.type(); - // Now get the compiled-Java layout as input (or output) arguments. - // NOTE: Stubs for compiled entry points of method handle intrinsics - // are just trampolines so the argument registers must be outgoing ones. - const bool is_outgoing = method->is_method_handle_intrinsic(); - int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed, is_outgoing); + // Now get the compiled-Java arguments layout. + int comp_args_on_stack = SharedRuntime::java_calling_convention(sig_bt, regs, total_args_passed); // Generate the compiled-to-native wrapper code nm = SharedRuntime::generate_native_wrapper(&_masm, method, compile_id, sig_bt, regs, ret_type, critical_entry); @@ -2944,7 +2940,7 @@ void AdapterHandlerLibrary::create_native_wrapper(const methodHandle& method) { VMReg SharedRuntime::name_for_receiver() { VMRegPair regs; BasicType sig_bt = T_OBJECT; - (void) java_calling_convention(&sig_bt, ®s, 1, true); + (void) java_calling_convention(&sig_bt, ®s, 1); // Return argument 0 register. In the LP64 build pointers // take 2 registers, but the VM wants only the 'main' name. return regs.first(); @@ -2975,7 +2971,7 @@ VMRegPair *SharedRuntime::find_callee_arguments(Symbol* sig, bool has_receiver, assert(cnt < 256, "grow table size"); int comp_args_on_stack; - comp_args_on_stack = java_calling_convention(sig_bt, regs, cnt, true); + comp_args_on_stack = java_calling_convention(sig_bt, regs, cnt); // the calling convention doesn't count out_preserve_stack_slots so // we must add that in to get "true" stack offsets. diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 1dc5294c1d922..4468022d4bc17 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -368,11 +368,9 @@ class SharedRuntime: AllStatic { // registers, those above refer to 4-byte stack slots. All stack slots are // based off of the window top. SharedInfo::stack0 refers to the first usable // slot in the bottom of the frame. SharedInfo::stack0+1 refers to the memory word - // 4-bytes higher. So for sparc because the register window save area is at - // the bottom of the frame the first 16 words will be skipped and SharedInfo::stack0 - // will be just above it. ( + // 4-bytes higher. // return value is the maximum number of VMReg stack slots the convention will use. - static int java_calling_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed, int is_outgoing); + static int java_calling_convention(const BasicType* sig_bt, VMRegPair* regs, int total_args_passed); static void check_member_name_argument_is_last_argument(const methodHandle& method, const BasicType* sig_bt, @@ -461,6 +459,11 @@ class SharedRuntime: AllStatic { // when an interrupt occurs. static uint out_preserve_stack_slots(); + // Stack slots that may be unused by the calling convention but must + // otherwise be preserved. On Intel this includes the return address. + // On PowerPC it includes the 4 words holding the old TOC & LR glue. + static uint in_preserve_stack_slots(); + // Is vector's size (in bytes) bigger than a size saved by default? // For example, on x86 16 bytes XMM registers are saved by default. static bool is_wide_vector(int size); From 0357db358133b38d82ad959261bc3175ea442043 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Mon, 16 Nov 2020 20:08:49 +0000 Subject: [PATCH 124/124] 8256287: [windows] add loop fuse to map_or_reserve_memory_aligned Reviewed-by: luhenry, iklam, minqi --- src/hotspot/os/windows/os_windows.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index b8b5946b9d07d..cb5b487bb5480 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -3238,28 +3238,31 @@ static char* map_or_reserve_memory_aligned(size_t size, size_t alignment, int fi assert(extra_size >= size, "overflow, size is too large to allow alignment"); char* aligned_base = NULL; + static const int max_attempts = 20; - do { - char* extra_base = file_desc != -1 ? - os::map_memory_to_file(extra_size, file_desc) : - os::reserve_memory(extra_size); + for (int attempt = 0; attempt < max_attempts && aligned_base == NULL; attempt ++) { + char* extra_base = file_desc != -1 ? os::map_memory_to_file(extra_size, file_desc) : + os::reserve_memory(extra_size); if (extra_base == NULL) { return NULL; } // Do manual alignment aligned_base = align_up(extra_base, alignment); - if (file_desc != -1) { - os::unmap_memory(extra_base, extra_size); - } else { - os::release_memory(extra_base, extra_size); + bool rc = (file_desc != -1) ? os::unmap_memory(extra_base, extra_size) : + os::release_memory(extra_base, extra_size); + assert(rc, "release failed"); + if (!rc) { + return NULL; } - aligned_base = file_desc != -1 ? - os::attempt_map_memory_to_file_at(aligned_base, size, file_desc) : - os::attempt_reserve_memory_at(aligned_base, size); + // Attempt to map, into the just vacated space, the slightly smaller aligned area. + // Which may fail, hence the loop. + aligned_base = file_desc != -1 ? os::attempt_map_memory_to_file_at(aligned_base, size, file_desc) : + os::attempt_reserve_memory_at(aligned_base, size); + } - } while (aligned_base == NULL); + assert(aligned_base != NULL, "Did not manage to re-map after %d attempts?", max_attempts); return aligned_base; }