Version in base suite: 17.0.2+8-1~deb11u1 Base version: openjdk-17_17.0.2+8-1~deb11u1 Target version: openjdk-17_17.0.3+7-1~deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/o/openjdk-17/openjdk-17_17.0.2+8-1~deb11u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/o/openjdk-17/openjdk-17_17.0.3+7-1~deb11u1.dsc .jcheck/conf | 1 debian/changelog | 31 debian/patches/default-jvm-cfg.diff | 4 debian/patches/hotspot-mips-align.diff | 2 debian/patches/jaw-optional.diff | 2 debian/patches/jdk-getAccessibleValue.diff | 6 debian/patches/jtreg-location.diff | 8 debian/patches/libpcsclite-dlopen.diff | 4 debian/patches/m68k-support.diff | 10 debian/patches/multiple-pkcs11-library-init.diff | 4 debian/patches/reproducible-build-user.diff | 14 debian/patches/reproducible-properties-timestamp.diff | 4 debian/patches/riscv64.diff | 2 debian/patches/series | 1 debian/patches/system-pcsclite.diff | 2 debian/rules | 8 doc/building.html | 2 doc/building.md | 2 doc/testing.html | 4 doc/testing.md | 10 make/Main.gmk | 10 make/RunTests.gmk | 23 make/ToolsJdk.gmk | 2 make/autoconf/basic.m4 | 7 make/autoconf/jdk-options.m4 | 17 make/autoconf/jdk-version.m4 | 11 make/autoconf/spec.gmk.in | 2 make/autoconf/toolchain.m4 | 4 make/autoconf/toolchain_microsoft.m4 | 19 make/common/ZipArchive.gmk | 26 make/conf/version-numbers.conf | 4 make/data/currency/CurrencyData.properties | 4 make/devkit/createJMHBundle.sh | 2 make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java | 231 make/modules/java.base/Gendata.gmk | 6 make/modules/jdk.javadoc/Gendata.gmk | 4 make/test/JtregNativeHotspot.gmk | 4 src/demo/share/jfc/SwingSet2/TableDemo.java | 9 src/hotspot/cpu/aarch64/aarch64.ad | 40 src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp | 2 src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp | 2 src/hotspot/cpu/aarch64/globals_aarch64.hpp | 10 src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp | 38 src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp | 7 src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp | 48 src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp | 206 src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp | 4 src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp | 8 src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp | 5 src/hotspot/cpu/aarch64/vm_version_aarch64.cpp | 35 src/hotspot/cpu/aarch64/vm_version_aarch64.hpp | 7 src/hotspot/cpu/arm/assembler_arm_32.cpp | 2 src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp | 7 src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp | 4 src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp | 2 src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp | 4 src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp | 44 src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp | 10 src/hotspot/cpu/ppc/frame_ppc.cpp | 56 src/hotspot/cpu/ppc/matcher_ppc.hpp | 2 src/hotspot/cpu/ppc/ppc.ad | 40 src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp | 4 src/hotspot/cpu/s390/frame_s390.cpp | 60 src/hotspot/cpu/x86/assembler_x86.cpp | 2 src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp | 8 src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp | 25 src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp | 16 src/hotspot/cpu/x86/nativeInst_x86.cpp | 6 src/hotspot/cpu/x86/nativeInst_x86.hpp | 6 src/hotspot/cpu/x86/vmreg_x86.hpp | 8 src/hotspot/cpu/x86/x86.ad | 15 src/hotspot/cpu/x86/x86_32.ad | 41 src/hotspot/cpu/zero/globals_zero.hpp | 3 src/hotspot/cpu/zero/vm_version_zero.cpp | 4 src/hotspot/cpu/zero/zeroInterpreter_zero.cpp | 8 src/hotspot/os/bsd/decoder_machO.cpp | 6 src/hotspot/os/bsd/os_bsd.cpp | 44 src/hotspot/os/linux/cgroupSubsystem_linux.cpp | 118 src/hotspot/os/linux/cgroupSubsystem_linux.hpp | 14 src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp | 42 src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp | 10 src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp | 58 src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp | 5 src/hotspot/os/linux/osContainer_linux.cpp | 12 src/hotspot/os/linux/osContainer_linux.hpp | 4 src/hotspot/os/linux/os_linux.cpp | 19 src/hotspot/os/posix/signals_posix.cpp | 18 src/hotspot/os/windows/os_windows.cpp | 5 src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp | 15 src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp | 11 src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp | 13 src/hotspot/share/c1/c1_IR.cpp | 9 src/hotspot/share/c1/c1_IR.hpp | 10 src/hotspot/share/c1/c1_Instruction.cpp | 5 src/hotspot/share/c1/c1_LIRAssembler.cpp | 3 src/hotspot/share/c1/c1_LIRGenerator.cpp | 8 src/hotspot/share/c1/c1_MacroAssembler.hpp | 4 src/hotspot/share/cds/dynamicArchive.cpp | 1 src/hotspot/share/cds/metaspaceShared.cpp | 14 src/hotspot/share/classfile/altHashing.cpp | 33 src/hotspot/share/classfile/altHashing.hpp | 4 src/hotspot/share/classfile/classLoaderData.cpp | 5 src/hotspot/share/classfile/symbolTable.cpp | 7 src/hotspot/share/classfile/systemDictionaryShared.cpp | 22 src/hotspot/share/classfile/systemDictionaryShared.hpp | 1 src/hotspot/share/classfile/verifier.cpp | 5 src/hotspot/share/code/codeCache.cpp | 8 src/hotspot/share/compiler/compileBroker.cpp | 1 src/hotspot/share/compiler/compilerDirectives.cpp | 12 src/hotspot/share/gc/g1/g1RemSet.cpp | 4 src/hotspot/share/gc/shared/suspendibleThreadSet.cpp | 26 src/hotspot/share/gc/shared/suspendibleThreadSet.hpp | 8 src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp | 2 src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp | 4 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp | 65 src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp | 1 src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp | 1 src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp | 7 src/hotspot/share/interpreter/bytecodeUtils.cpp | 1 src/hotspot/share/interpreter/bytecodes.cpp | 4 src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp | 73 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp | 3 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp | 6 src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp | 2 src/hotspot/share/jvmci/jvmciCompilerToVM.cpp | 33 src/hotspot/share/jvmci/vmStructs_jvmci.cpp | 4 src/hotspot/share/memory/allocation.hpp | 1 src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp | 3 src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp | 1 src/hotspot/share/memory/metaspace/metaspaceReporter.cpp | 18 src/hotspot/share/memory/metaspace/metaspaceReporter.hpp | 4 src/hotspot/share/oops/instanceKlass.cpp | 48 src/hotspot/share/oops/instanceKlass.hpp | 18 src/hotspot/share/oops/klass.cpp | 2 src/hotspot/share/oops/klass.hpp | 11 src/hotspot/share/oops/method.cpp | 13 src/hotspot/share/oops/methodData.hpp | 8 src/hotspot/share/opto/buildOopMap.cpp | 18 src/hotspot/share/opto/cfgnode.cpp | 28 src/hotspot/share/opto/library_call.cpp | 4 src/hotspot/share/opto/loopPredicate.cpp | 46 src/hotspot/share/opto/loopTransform.cpp | 68 src/hotspot/share/opto/loopnode.cpp | 8 src/hotspot/share/opto/loopnode.hpp | 22 src/hotspot/share/opto/loopopts.cpp | 10 src/hotspot/share/opto/macro.cpp | 14 src/hotspot/share/opto/mulnode.cpp | 13 src/hotspot/share/opto/opaquenode.cpp | 19 src/hotspot/share/opto/opaquenode.hpp | 6 src/hotspot/share/opto/output.cpp | 10 src/hotspot/share/prims/jvmtiEnvBase.cpp | 2 src/hotspot/share/prims/jvmtiRedefineClasses.cpp | 3 src/hotspot/share/prims/vectorSupport.cpp | 1 src/hotspot/share/prims/whitebox.cpp | 2 src/hotspot/share/runtime/deoptimization.cpp | 10 src/hotspot/share/runtime/deoptimization.hpp | 12 src/hotspot/share/runtime/objectMonitor.hpp | 2 src/hotspot/share/runtime/os.cpp | 2 src/hotspot/share/services/attachListener.cpp | 3 src/hotspot/share/utilities/accessFlags.hpp | 5 src/hotspot/share/utilities/resourceHash.hpp | 26 src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java | 12 src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java | 8 src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java | 4 src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java | 27 src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java | 29 src/java.base/macosx/classes/apple/security/KeychainStore.java | 192 src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m | 63 src/java.base/share/classes/java/io/File.java | 10 src/java.base/share/classes/java/io/FileSystem.java | 7 src/java.base/share/classes/java/lang/String.java | 11 src/java.base/share/classes/java/math/BigInteger.java | 4 src/java.base/share/classes/java/util/random/package-info.java | 12 src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java | 6 src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java | 2 src/java.base/share/classes/jdk/internal/platform/Metrics.java | 21 src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java | 2 src/java.base/share/classes/sun/launcher/LauncherHelper.java | 15 src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java | 13 src/java.base/share/classes/sun/security/provider/DSA.java | 5 src/java.base/share/classes/sun/security/provider/certpath/OCSP.java | 18 src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java | 9 src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java | 72 src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java | 23 src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java | 17 src/java.base/share/classes/sun/security/tools/keytool/Main.java | 5 src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java | 17 src/java.base/share/classes/sun/security/util/DerValue.java | 28 src/java.base/share/classes/sun/security/util/ObjectIdentifier.java | 10 src/java.base/share/classes/sun/security/util/SignatureUtil.java | 28 src/java.base/share/classes/sun/security/x509/AlgorithmId.java | 2 src/java.base/share/classes/sun/security/x509/X509CRLImpl.java | 10 src/java.base/share/classes/sun/security/x509/X509CertImpl.java | 8 src/java.base/share/classes/sun/util/resources/CurrencyNames.properties | 4 src/java.base/unix/classes/java/io/UnixFileSystem.java | 7 src/java.base/unix/classes/sun/net/www/content-types.properties | 88 src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c | 4 src/java.base/windows/classes/java/io/WinNTFileSystem.java | 48 src/java.base/windows/classes/sun/net/www/content-types.properties | 86 src/java.base/windows/native/libnet/DefaultProxySelector.c | 18 src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java | 10 src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java | 25 src/java.desktop/share/classes/javax/swing/JLabel.java | 22 src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java | 2 src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java | 71 src/java.desktop/share/native/liblcms/LCMS.c | 12 src/java.desktop/share/native/liblcms/cmsalpha.c | 675 + src/java.desktop/share/native/liblcms/cmscam02.c | 515 src/java.desktop/share/native/liblcms/cmscgats.c | 2815 ++++ src/java.desktop/share/native/liblcms/cmscnvrt.c | 1243 ++ src/java.desktop/share/native/liblcms/cmserr.c | 692 + src/java.desktop/share/native/liblcms/cmsgamma.c | 1517 ++ src/java.desktop/share/native/liblcms/cmsgmt.c | 619 + src/java.desktop/share/native/liblcms/cmshalf.c | 564 src/java.desktop/share/native/liblcms/cmsintrp.c | 1345 ++ src/java.desktop/share/native/liblcms/cmsio0.c | 1979 +++ src/java.desktop/share/native/liblcms/cmsio1.c | 1057 + src/java.desktop/share/native/liblcms/cmslut.c | 1866 +++ src/java.desktop/share/native/liblcms/cmsmd5.c | 342 src/java.desktop/share/native/liblcms/cmsmtrx.c | 205 src/java.desktop/share/native/liblcms/cmsnamed.c | 1009 + src/java.desktop/share/native/liblcms/cmsopt.c | 2005 +++ src/java.desktop/share/native/liblcms/cmspack.c | 3462 ++++++ src/java.desktop/share/native/liblcms/cmspcs.c | 969 + src/java.desktop/share/native/liblcms/cmsplugin.c | 1020 + src/java.desktop/share/native/liblcms/cmsps2.c | 1647 ++ src/java.desktop/share/native/liblcms/cmssamp.c | 576 + src/java.desktop/share/native/liblcms/cmssm.c | 765 + src/java.desktop/share/native/liblcms/cmstypes.c | 5656 ++++++++++ src/java.desktop/share/native/liblcms/cmsvirt.c | 1246 ++ src/java.desktop/share/native/liblcms/cmswtpnt.c | 379 src/java.desktop/share/native/liblcms/cmsxform.c | 1394 ++ src/java.desktop/share/native/liblcms/lcms2.h | 1951 +++ src/java.desktop/share/native/liblcms/lcms2_internal.h | 1151 ++ src/java.desktop/share/native/liblcms/lcms2_plugin.h | 709 + src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c | 20 src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c | 50 src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp | 69 src/java.desktop/windows/native/libawt/windows/awt_Component.cpp | 26 src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java | 16 src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java | 67 src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java | 271 src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java | 109 src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java | 9 src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java | 19 src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java | 279 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java | 10 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java | 6 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java | 181 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java | 12 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java | 3 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/xmlsecurity_en.properties | 3 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/signature/XMLSignatureInput.java | 15 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java | 8 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java | 15 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java | 1 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java | 6 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java | 112 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java | 2 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java | 13 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java | 1 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java | 5 src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java | 45 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java | 3 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java | 25 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java | 2 src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java | 2 src/java.xml.crypto/share/legal/santuario.md | 2 src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java | 62 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java | 480 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java | 15 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java | 47 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java | 12 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java | 14 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java | 25 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java | 15 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java | 9 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java | 10 src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java | 14 src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java | 49 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java | 29 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java | 71 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java | 20 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/LSSerializerImpl.java | 13 src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java | 7 src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java | 14 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java | 89 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java | 161 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java | 76 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java | 319 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java | 17 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java | 20 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java | 6 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java | 8 src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java | 15 src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java | 15 src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java | 14 src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java | 17 src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java | 29 src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java | 48 src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java | 248 src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java | 633 + src/java.xml/share/classes/module-info.java | 97 src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java | 2 src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java | 1 src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java | 5 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java | 48 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java | 75 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java | 12 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java | 67 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java | 11 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java | 42 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java | 153 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java | 210 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java | 81 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java | 557 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java | 13 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java | 150 src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java | 41 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java | 355 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java | 25 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java | 7 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java | 142 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java | 12 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java | 20 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java | 94 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java | 262 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java | 119 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java | 75 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java | 4 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java | 9 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java | 14 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java | 6 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java | 2 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java | 11 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java | 109 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps | 41 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps | 44 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps | 44 src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps | 43 src/jdk.internal.le/share/legal/jline.md | 2 src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java | 27 src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java | 36 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java | 12 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java | 22 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java | 4 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java | 8 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java | 21 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java | 8 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java | 2 src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java | 2 src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java | 23 src/jdk.jartool/share/classes/sun/tools/jar/Main.java | 64 src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties | 10 src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java | 4 src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java | 6 src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java | 6 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java | 1 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java | 3 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java | 27 src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java | 2 src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java | 2 src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java | 6 src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java | 23 src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java | 66 src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties | 7 src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java | 8 src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java | 21 src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java | 71 src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java | 297 src/jdk.random/share/classes/module-info.java | 3 test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp | 8 test/hotspot/gtest/gtestMain.cpp | 61 test/hotspot/gtest/runtime/test_os.cpp | 2 test/hotspot/gtest/unittest.hpp | 11 test/hotspot/gtest/utilities/test_resourceHash.cpp | 24 test/hotspot/jtreg/TEST.groups | 47 test/hotspot/jtreg/compiler/c1/Test8271202.java | 67 test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm | 76 test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java | 40 test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java | 82 test/hotspot/jtreg/compiler/c2/TestSqrt.java | 54 test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java | 58 test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java | 3 test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java | 2 test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java | 22 test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java | 5 test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java | 79 test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java | 102 test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java | 66 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java | 218 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java | 98 test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java | 82 test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java | 5 test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java | 51 test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java | 5 test/hotspot/jtreg/containers/docker/DockerBasicTest.java | 2 test/hotspot/jtreg/containers/docker/TestCPUAwareness.java | 2 test/hotspot/jtreg/containers/docker/TestCPUSets.java | 2 test/hotspot/jtreg/containers/docker/TestJFREvents.java | 2 test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java | 2 test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java | 2 test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java | 2 test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java | 2 test/hotspot/jtreg/containers/docker/TestMisc.java | 6 test/hotspot/jtreg/containers/docker/TestPids.java | 162 test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java | 42 test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java | 2 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java | 5 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java | 18 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java | 7 test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java | 18 test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java | 12 test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java | 5 test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java | 3 test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java | 3 test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java | 3 test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java | 8 test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java | 3 test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java | 5 test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java | 6 test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java | 23 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java | 4 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java | 6 test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java | 8 test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java | 34 test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java | 9 test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java | 18 test/hotspot/jtreg/runtime/Thread/TestSpinPause.java | 87 test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java | 78 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java | 81 test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java | 30 test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java | 34 test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm | 29 test/hotspot/jtreg/runtime/jsig/Testjsig.java | 2 test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java | 2 test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java | 45 test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm | 56 test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java | 2 test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java | 2 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java | 5 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java | 5 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java | 4 test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java | 4 test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java | 9 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java | 8 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties | 1 test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties | 1 test/jdk/ProblemList.txt | 1 test/jdk/TEST.ROOT | 2 test/jdk/TEST.groups | 13 test/jdk/build/AbsPathsInImage.java | 12 test/jdk/com/sun/jdi/TestScaffold.java | 11 test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java | 139 test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java | 10 test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java | 2 test/jdk/java/io/File/GetXSpace.java | 7 test/jdk/java/io/File/LastModifiedTest.java | 60 test/jdk/java/lang/ProcessBuilder/Basic.java | 155 test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c | 54 test/jdk/java/math/BigInteger/BitLengthOverflow.java | 35 test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java | 3 test/jdk/java/nio/file/Files/probeContentType/Basic.java | 76 test/jdk/java/security/testlibrary/CertificateBuilder.java | 42 test/jdk/java/security/testlibrary/SimpleOCSPServer.java | 14 test/jdk/java/text/Format/DateFormat/DateFormatTest.java | 6 test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java | 12 test/jdk/java/util/Currency/ValidateISO4217.java | 6 test/jdk/java/util/Currency/tablea1.txt | 6 test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java | 20 test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java | 39 test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java | 14 test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java | 194 test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java | 252 test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java | 53 test/jdk/jdk/editpad/EditPadTest.java | 1 test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java | 7 test/jdk/jdk/internal/jline/KeyConversionTest.java | 9 test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java | 2 test/jdk/jdk/internal/platform/docker/TestPidsLimit.java | 132 test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java | 2 test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java | 2 test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java | 146 test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java | 143 test/jdk/sun/security/lib/cacerts/VerifyCACerts.java | 4 test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java | 6 test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java | 134 test/jdk/sun/text/resources/LocaleData | 3 test/jdk/sun/text/resources/LocaleDataTest.java | 4 test/jdk/tools/jar/ContentOrder.java | 214 test/jdk/tools/jar/ReproducibleJar.java | 291 test/jdk/tools/jimage/JImageNonAsciiNameTest.java | 98 test/jdk/tools/jmod/JmodTest.java | 79 test/langtools/jdk/jshell/CommandCompletionTest.java | 25 test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java | 50 test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java | 29 test/lib/jdk/test/lib/SecurityTools.java | 61 test/lib/jdk/test/lib/containers/docker/Common.java | 6 test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java | 75 test/lib/sun/hotspot/WhiteBox.java | 2 test/micro/org/openjdk/bench/java/lang/StringEncode.java | 126 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java | 52 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java | 204 test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java | 88 569 files changed, 52486 insertions(+), 3951 deletions(-) diff -Nru openjdk-17-17.0.2+8/.jcheck/conf openjdk-17-17.0.3+7/.jcheck/conf --- openjdk-17-17.0.2+8/.jcheck/conf 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/.jcheck/conf 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,7 @@ [general] project=jdk-updates jbs=JDK +version=17.0.3 [checks] error=author,committer,reviewers,merge,issues,executable,symlink,message,hg-tag,whitespace,problemlists diff -Nru openjdk-17-17.0.2+8/debian/changelog openjdk-17-17.0.3+7/debian/changelog --- openjdk-17-17.0.2+8/debian/changelog 2022-01-21 16:38:47.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/changelog 2022-05-02 22:04:41.000000000 +0000 @@ -1,8 +1,35 @@ -openjdk-17 (17.0.2+8-1~deb11u1) bullseye-security; urgency=medium +openjdk-17 (17.0.3+7-1~deb11u1) bullseye-security; urgency=medium * Rebuild for bullseye - -- Moritz Muehlenhoff Fri, 21 Jan 2022 17:38:47 +0100 + -- Moritz Muehlenhoff Tue, 03 May 2022 00:04:41 +0200 + +openjdk-17 (17.0.3+7-1) unstable; urgency=high + + * OpenJDK 17.0.3+7 (release). + * Security fixes + - JDK-8269938: Enhance XML processing passes redux. + - JDK-8270504, CVE-2022-21426: Better XPath expression handling. + - JDK-8272255: Completely handle MIDI files. + - JDK-8272261: Improve JFR recording file processing. + - JDK-8272588: Enhanced recording parsing. + - JDK-8272594: Better record of recordings. + - JDK-8274221: More definite BER encodings. + - JDK-8275082, JDK-8278008, CVE-2022-21476: Update XML Security for Java + to 2.3.0. + - JDK-8275151, CVE-2022-21443: Improved Object Identification. + - JDK-8277227: Better identification of OIDs. + - JDK-8277233, CVE-2022-21449: Improve ECDSA signature support. + - JDK-8277672, CVE-2022-21434: Better invocation handler handling. + - JDK-8278356: Improve file creation. + - JDK-8278449: Improve keychain support. + - JDK-8278798: Improve supported intrinsic. + - JDK-8278805: Enhance BMP image loading. + - JDK-8278972, CVE-2022-21496: Improve URL supports. + - JDK-8281388: Change wrapping of EncryptedPrivateKeyInfo. + * Refresh patches. + + -- Matthias Klose Mon, 02 May 2022 20:04:05 +0200 openjdk-17 (17.0.2+8-1) unstable; urgency=high diff -Nru openjdk-17-17.0.2+8/debian/patches/default-jvm-cfg.diff openjdk-17-17.0.3+7/debian/patches/default-jvm-cfg.diff --- openjdk-17-17.0.2+8/debian/patches/default-jvm-cfg.diff 2021-04-08 07:39:27.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/default-jvm-cfg.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c -@@ -2077,7 +2077,7 @@ jint +@@ -2079,7 +2079,7 @@ jint ReadKnownVMs(const char *jvmCfgName, jboolean speculative) { FILE *jvmCfg; @@ -9,7 +9,7 @@ int cnt = 0; int lineno = 0; jlong start = 0, end = 0; -@@ -2092,6 +2092,11 @@ ReadKnownVMs(const char *jvmCfgName, jbo +@@ -2094,6 +2094,11 @@ ReadKnownVMs(const char *jvmCfgName, jbo jvmCfg = fopen(jvmCfgName, "r"); if (jvmCfg == NULL) { diff -Nru openjdk-17-17.0.2+8/debian/patches/hotspot-mips-align.diff openjdk-17-17.0.3+7/debian/patches/hotspot-mips-align.diff --- openjdk-17-17.0.2+8/debian/patches/hotspot-mips-align.diff 2021-05-27 09:28:46.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/hotspot-mips-align.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp +++ b/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp -@@ -414,7 +414,7 @@ int ZeroInterpreter::native_entry(Method +@@ -418,7 +418,7 @@ int ZeroInterpreter::native_entry(Method ThreadStateTransition::transition_from_java(thread, _thread_in_native); // Make the call diff -Nru openjdk-17-17.0.2+8/debian/patches/jaw-optional.diff openjdk-17-17.0.3+7/debian/patches/jaw-optional.diff --- openjdk-17-17.0.2+8/debian/patches/jaw-optional.diff 2020-11-07 11:16:22.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jaw-optional.diff 2022-05-02 18:04:05.000000000 +0000 @@ -6,7 +6,7 @@ --- a/src/java.desktop/share/classes/java/awt/Toolkit.java +++ b/src/java.desktop/share/classes/java/awt/Toolkit.java -@@ -600,7 +600,11 @@ public abstract class Toolkit { +@@ -602,7 +602,11 @@ public abstract class Toolkit { toolkit = new HeadlessToolkit(toolkit); } if (!GraphicsEnvironment.isHeadless()) { diff -Nru openjdk-17-17.0.2+8/debian/patches/jdk-getAccessibleValue.diff openjdk-17-17.0.3+7/debian/patches/jdk-getAccessibleValue.diff --- openjdk-17-17.0.2+8/debian/patches/jdk-getAccessibleValue.diff 2021-04-08 07:39:38.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jdk-getAccessibleValue.diff 2022-05-02 18:04:05.000000000 +0000 @@ -193,7 +193,7 @@ --- a/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java +++ b/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java -@@ -6550,7 +6550,7 @@ final public class AccessBridge { +@@ -6555,7 +6555,7 @@ final public class AccessBridge { public AccessibleSelection getAccessibleSelection() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null && isLeaf) { @@ -202,7 +202,7 @@ } else { return this; } -@@ -6565,7 +6565,7 @@ final public class AccessBridge { +@@ -6570,7 +6570,7 @@ final public class AccessBridge { public AccessibleText getAccessibleText() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null) { @@ -211,7 +211,7 @@ } else { return null; } -@@ -6580,7 +6580,7 @@ final public class AccessBridge { +@@ -6585,7 +6585,7 @@ final public class AccessBridge { public AccessibleValue getAccessibleValue() { AccessibleContext ac = getCurrentAccessibleContext(); if (ac != null) { diff -Nru openjdk-17-17.0.2+8/debian/patches/jtreg-location.diff openjdk-17-17.0.3+7/debian/patches/jtreg-location.diff --- openjdk-17-17.0.2+8/debian/patches/jtreg-location.diff 2021-05-27 09:29:01.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/jtreg-location.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/make/autoconf/toolchain.m4 +++ b/make/autoconf/toolchain.m4 -@@ -1022,7 +1022,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1029,7 +1029,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], AC_MSG_ERROR([jtreg home directory from --with-jtreg=$with_jtreg does not exist]) fi @@ -9,7 +9,7 @@ AC_MSG_ERROR([jtreg home directory from --with-jtreg=$with_jtreg is not a valid jtreg home]) fi -@@ -1036,7 +1036,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1043,7 +1043,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], AC_MSG_WARN([Ignoring JT_HOME pointing to invalid directory: $JT_HOME]) JT_HOME= else @@ -18,7 +18,7 @@ AC_MSG_WARN([Ignoring JT_HOME which is not a valid jtreg home: $JT_HOME]) JT_HOME= else -@@ -1052,7 +1052,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], +@@ -1059,7 +1059,7 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JTREG], if test "x$JTREGEXE" != x; then # That's good, now try to derive JT_HOME JT_HOME=`(cd $($DIRNAME $JTREGEXE)/.. && pwd)` @@ -29,7 +29,7 @@ else --- a/make/RunTests.gmk +++ b/make/RunTests.gmk -@@ -837,7 +837,7 @@ define SetupRunJtregTestBody +@@ -846,7 +846,7 @@ define SetupRunJtregTestBody $1_COMMAND_LINE := \ $$(JAVA) $$($1_JTREG_LAUNCHER_OPTIONS) \ diff -Nru openjdk-17-17.0.2+8/debian/patches/libpcsclite-dlopen.diff openjdk-17-17.0.3+7/debian/patches/libpcsclite-dlopen.diff --- openjdk-17-17.0.2+8/debian/patches/libpcsclite-dlopen.diff 2021-02-25 09:49:28.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/libpcsclite-dlopen.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1,6 +1,6 @@ --- a/src/java.smartcardio/unix/classes/sun/security/smartcardio/PlatformPCSC.java +++ b/src/java.smartcardio/unix/classes/sun/security/smartcardio/PlatformPCSC.java -@@ -48,6 +48,7 @@ class PlatformPCSC { +@@ -46,6 +46,7 @@ class PlatformPCSC { private final static String PROP_NAME = "sun.security.smartcardio.library"; @@ -8,7 +8,7 @@ private final static String LIB1 = "/usr/$LIBISA/libpcsclite.so"; private final static String LIB2 = "/usr/local/$LIBISA/libpcsclite.so"; private final static String PCSC_FRAMEWORK = "/System/Library/Frameworks/PCSC.framework/Versions/Current/PCSC"; -@@ -100,40 +101,9 @@ class PlatformPCSC { +@@ -98,40 +99,9 @@ class PlatformPCSC { if (lib.length() != 0) { return lib; } diff -Nru openjdk-17-17.0.2+8/debian/patches/m68k-support.diff openjdk-17-17.0.3+7/debian/patches/m68k-support.diff --- openjdk-17-17.0.2+8/debian/patches/m68k-support.diff 2021-05-27 09:29:19.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/m68k-support.diff 2022-05-02 18:04:05.000000000 +0000 @@ -1042,7 +1042,7 @@ $(eval $(call SetupExecute, gen_x11wrappers, \ --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp -@@ -238,7 +238,7 @@ template class CHeapObj ALL +@@ -239,7 +239,7 @@ template class CHeapObj ALL void operator delete(void* p) { FreeHeap(p); } void operator delete [] (void* p) { FreeHeap(p); } @@ -1051,7 +1051,7 @@ // Base class for objects allocated on the stack only. // Calling new or delete will result in fatal error. -@@ -249,7 +249,7 @@ class StackObj ALLOCATION_SUPER_CLASS_SP +@@ -250,7 +250,7 @@ class StackObj ALLOCATION_SUPER_CLASS_SP void* operator new [](size_t size) throw(); void operator delete(void* p); void operator delete [](void* p); @@ -1060,7 +1060,7 @@ // Base class for objects stored in Metaspace. // Calling delete will result in fatal error. -@@ -356,7 +356,7 @@ class MetaspaceObj { +@@ -372,7 +372,7 @@ class MetaspaceObj { // that should be read-only by default. See symbol.hpp for an example. This function // is used by the templates in metaspaceClosure.hpp static bool is_read_only_by_default() { return false; } @@ -1069,7 +1069,7 @@ // Base class for classes that constitute name spaces. -@@ -441,7 +441,7 @@ protected: +@@ -457,7 +457,7 @@ protected: void operator delete(void* p); void operator delete [](void* p); @@ -1090,7 +1090,7 @@ #endif // SHARE_OOPS_CONSTMETHOD_HPP --- a/src/hotspot/share/oops/oop.hpp +++ b/src/hotspot/share/oops/oop.hpp -@@ -316,6 +316,6 @@ class oopDesc { +@@ -315,6 +315,6 @@ class oopDesc { // Avoid include gc_globals.hpp in oop.inline.hpp DEBUG_ONLY(bool get_UseParallelGC();) DEBUG_ONLY(bool get_UseG1GC();) diff -Nru openjdk-17-17.0.2+8/debian/patches/multiple-pkcs11-library-init.diff openjdk-17-17.0.3+7/debian/patches/multiple-pkcs11-library-init.diff --- openjdk-17-17.0.2+8/debian/patches/multiple-pkcs11-library-init.diff 2021-05-27 09:28:57.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/multiple-pkcs11-library-init.diff 2022-05-02 18:04:05.000000000 +0000 @@ -15,7 +15,7 @@ // same as allowSingleThreadedModules but controlled via a system property // and applied to all providers. if set to false, no SunPKCS11 instances -@@ -986,6 +987,8 @@ final class Config { +@@ -1019,6 +1020,8 @@ final class Config { handleStartupErrors = ERR_IGNORE_LIB; } else if (val.equals("halt")) { handleStartupErrors = ERR_HALT; @@ -26,7 +26,7 @@ } --- a/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java +++ b/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java -@@ -177,26 +177,37 @@ public final class SunPKCS11 extends Aut +@@ -179,26 +179,37 @@ public final class SunPKCS11 extends Aut String nssLibraryDirectory = config.getNssLibraryDirectory(); String nssSecmodDirectory = config.getNssSecmodDirectory(); boolean nssOptimizeSpace = config.getNssOptimizeSpace(); diff -Nru openjdk-17-17.0.2+8/debian/patches/reproducible-build-user.diff openjdk-17-17.0.3+7/debian/patches/reproducible-build-user.diff --- openjdk-17-17.0.2+8/debian/patches/reproducible-build-user.diff 2021-04-08 07:40:21.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/reproducible-build-user.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -Description: Makes the build user invariant to improve the reproducibility (it appears in the interval VM version in libjvm.so) -Author: Emmanuel Bourg -Forwarded: no ---- a/make/autoconf/basic.m4 -+++ b/make/autoconf/basic.m4 -@@ -97,7 +97,7 @@ AC_DEFUN_ONCE([BASIC_SETUP_PATHS], - - # Setup username (for use in adhoc version strings etc) - # Outer [ ] to quote m4. -- [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] -+ [ USERNAME="unknown" ] - AC_SUBST(USERNAME) - ]) - diff -Nru openjdk-17-17.0.2+8/debian/patches/reproducible-properties-timestamp.diff openjdk-17-17.0.3+7/debian/patches/reproducible-properties-timestamp.diff --- openjdk-17-17.0.2+8/debian/patches/reproducible-properties-timestamp.diff 2021-04-08 07:39:56.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/reproducible-properties-timestamp.diff 2022-05-02 18:04:05.000000000 +0000 @@ -3,7 +3,7 @@ Forwarded: no --- a/src/java.base/share/classes/java/util/Properties.java +++ b/src/java.base/share/classes/java/util/Properties.java -@@ -914,7 +914,7 @@ public class Properties extends Hashtabl +@@ -903,7 +903,7 @@ public class Properties extends Hashtabl if (comments != null) { writeComments(bw, comments); } @@ -12,7 +12,7 @@ bw.newLine(); synchronized (this) { for (Map.Entry e : entrySet()) { -@@ -1566,4 +1566,22 @@ public class Properties extends Hashtabl +@@ -1555,4 +1555,22 @@ public class Properties extends Hashtabl } this.map = map; } diff -Nru openjdk-17-17.0.2+8/debian/patches/riscv64.diff openjdk-17-17.0.3+7/debian/patches/riscv64.diff --- openjdk-17-17.0.2+8/debian/patches/riscv64.diff 2021-05-27 09:32:51.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/riscv64.diff 2022-05-02 18:04:05.000000000 +0000 @@ -58,7 +58,7 @@ exit $exitcode --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp -@@ -2506,6 +2506,8 @@ void os::get_summary_cpu_info(char* cpui +@@ -2539,6 +2539,8 @@ void os::get_summary_cpu_info(char* cpui strncpy(cpuinfo, "IA64", length); #elif defined(PPC) strncpy(cpuinfo, "PPC64", length); diff -Nru openjdk-17-17.0.2+8/debian/patches/series openjdk-17-17.0.3+7/debian/patches/series --- openjdk-17-17.0.2+8/debian/patches/series 2021-10-20 14:58:22.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/series 2022-05-02 18:04:05.000000000 +0000 @@ -32,7 +32,6 @@ reproducible-character-data.diff reproducible-module-info.diff reproducible-copyright-headers.diff -reproducible-build-user.diff riscv64.diff reproducible-build-jmod.diff mips.diff diff -Nru openjdk-17-17.0.2+8/debian/patches/system-pcsclite.diff openjdk-17-17.0.3+7/debian/patches/system-pcsclite.diff --- openjdk-17-17.0.2+8/debian/patches/system-pcsclite.diff 2021-05-27 09:28:42.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/patches/system-pcsclite.diff 2022-05-02 18:04:05.000000000 +0000 @@ -70,7 +70,7 @@ --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in -@@ -764,6 +764,7 @@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TR +@@ -769,6 +769,7 @@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TR # Build setup USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ diff -Nru openjdk-17-17.0.2+8/debian/rules openjdk-17-17.0.3+7/debian/rules --- openjdk-17-17.0.2+8/debian/rules 2022-01-20 16:13:47.000000000 +0000 +++ openjdk-17-17.0.3+7/debian/rules 2022-05-02 18:04:05.000000000 +0000 @@ -1871,7 +1871,7 @@ is_release = yes #is_release = git_project = jdk17u -git_tag = jdk-17.0.2+8 +git_tag = jdk-17.0.3+7 package_version = $(subst jdk-,,$(git_tag)) package_version = $(shell echo $(PKGVERSION) | sed 's/-[^-][^-]*$$//') ifneq ($(is_release),yes) @@ -1895,8 +1895,6 @@ -type f -print -delete; \ rm -v -rf $$d/src/java.desktop/share/native/libsplashscreen/giflib; \ rm -v -rf $$d/src/java.desktop/share/native/libsplashscreen/libpng; \ - rm -v -f $$d/src/java.desktop/share/native/liblcms/cms*.c; \ - rm -v -f $$d/src/java.desktop/share/native/liblcms/lcms2*.h; \ rm -v -rf $$d/src/java.smartcardio/unix/native/libj2pcsc/MUSCLE; \ esac; \ mv $$d $(topdir); \ @@ -1907,6 +1905,10 @@ tar cfJ ../$(basename)_$(package_version).orig.tar.xz $(topdir); \ rm -rf $(topdir) +# keep these in the tarball, older lcms are not supported + rm -v -f $$d/src/java.desktop/share/native/liblcms/cms*.c; \ + rm -v -f $$d/src/java.desktop/share/native/liblcms/lcms2*.h; \ + rm -rf $(origdir) binary: binary-arch binary-indep diff -Nru openjdk-17-17.0.2+8/doc/building.html openjdk-17-17.0.3+7/doc/building.html --- openjdk-17-17.0.2+8/doc/building.html 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/building.html 2022-04-19 19:55:43.000000000 +0000 @@ -506,7 +506,7 @@

Running Tests

Most of the JDK tests are using the JTReg test framework. Make sure that your configuration knows where to find your installation of JTReg. If this is not picked up automatically, use the --with-jtreg=<path to jtreg home> option to point to the JTReg framework. Note that this option should point to the JTReg home, i.e. the top directory, containing lib/jtreg.jar etc.

-

The Adoption Group provides recent builds of jtreg here. Download the latest .tar.gz file, unpack it, and point --with-jtreg to the jtreg directory that you just unpacked.

+

The Adoption Group provides recent builds of jtreg here. Download the latest .tar.gz file, unpack it, and point --with-jtreg to the jtreg directory that you just unpacked.

Building of Hotspot Gtest suite requires the source code of Google Test framework. The top directory, which contains both googletest and googlemock directories, should be specified via --with-gtest. The supported version of Google Test is 1.8.1, whose source code can be obtained:

  • by downloading and unpacking the source bundle from here
  • diff -Nru openjdk-17-17.0.2+8/doc/building.md openjdk-17-17.0.3+7/doc/building.md --- openjdk-17-17.0.2+8/doc/building.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/building.md 2022-04-19 19:55:43.000000000 +0000 @@ -848,7 +848,7 @@ The [Adoption Group](https://wiki.openjdk.java.net/display/Adoption) provides recent builds of jtreg [here]( -https://ci.adoptopenjdk.net/view/Dependencies/job/jtreg/lastSuccessfulBuild/artifact). +https://ci.adoptopenjdk.net/view/Dependencies/job/dependency_pipeline/lastSuccessfulBuild/artifact/jtreg/). Download the latest `.tar.gz` file, unpack it, and point `--with-jtreg` to the `jtreg` directory that you just unpacked. diff -Nru openjdk-17-17.0.2+8/doc/testing.html openjdk-17-17.0.3+7/doc/testing.html --- openjdk-17-17.0.2+8/doc/testing.html 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/testing.html 2022-04-19 19:55:43.000000000 +0000 @@ -193,7 +193,9 @@

    AOT_MODULES

    Generate AOT modules before testing for the specified module, or set of modules. If multiple modules are specified, they should be separated by space (or, to help avoid quoting issues, the special value %20).

    RETRY_COUNT

    -

    Retry failed tests up to a set number of times. Defaults to 0.

    +

    Retry failed tests up to a set number of times, until they pass. This allows to pass the tests with intermittent failures. Defaults to 0.

    +

    REPEAT_COUNT

    +

    Repeat the tests up to a set number of times, stopping at first failure. This helps to reproduce intermittent test failures. Defaults to 0.

    Gtest keywords

    REPEAT

    The number of times to repeat the tests (--gtest_repeat).

    diff -Nru openjdk-17-17.0.2+8/doc/testing.md openjdk-17-17.0.3+7/doc/testing.md --- openjdk-17-17.0.2+8/doc/testing.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/doc/testing.md 2022-04-19 19:55:43.000000000 +0000 @@ -419,7 +419,15 @@ #### RETRY_COUNT -Retry failed tests up to a set number of times. Defaults to 0. +Retry failed tests up to a set number of times, until they pass. +This allows to pass the tests with intermittent failures. +Defaults to 0. + +#### REPEAT_COUNT + +Repeat the tests up to a set number of times, stopping at first failure. +This helps to reproduce intermittent test failures. +Defaults to 0. ### Gtest keywords diff -Nru openjdk-17-17.0.2+8/make/Main.gmk openjdk-17-17.0.3+7/make/Main.gmk --- openjdk-17-17.0.2+8/make/Main.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/Main.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -324,7 +324,7 @@ # aren't built until after libjava and libjvm are available to link to. $(eval $(call SetupTarget, demos-jdk, \ MAKEFILE := CompileDemos, \ - DEPS := java.base-libs exploded-image, \ + DEPS := java.base-libs exploded-image buildtools-jdk, \ )) $(eval $(call SetupTarget, test-image-demos-jdk, \ @@ -383,12 +383,12 @@ $(eval $(call SetupTarget, zip-security, \ MAKEFILE := ZipSecurity, \ - DEPS := java.base-java java.security.jgss-java java.security.jgss-libs, \ + DEPS := buildtools-jdk java.base-java java.security.jgss-java java.security.jgss-libs, \ )) $(eval $(call SetupTarget, zip-source, \ MAKEFILE := ZipSource, \ - DEPS := gensrc, \ + DEPS := buildtools-jdk gensrc, \ )) $(eval $(call SetupTarget, jrtfs-jar, \ @@ -508,13 +508,13 @@ $(eval $(call SetupTarget, docs-zip, \ MAKEFILE := Docs, \ TARGET := docs-zip, \ - DEPS := docs-jdk, \ + DEPS := docs-jdk buildtools-jdk, \ )) $(eval $(call SetupTarget, docs-specs-zip, \ MAKEFILE := Docs, \ TARGET := docs-specs-zip, \ - DEPS := docs-jdk-specs, \ + DEPS := docs-jdk-specs buildtools-jdk, \ )) $(eval $(call SetupTarget, update-build-docs, \ diff -Nru openjdk-17-17.0.2+8/make/RunTests.gmk openjdk-17-17.0.3+7/make/RunTests.gmk --- openjdk-17-17.0.2+8/make/RunTests.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/RunTests.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -200,7 +200,7 @@ $(eval $(call ParseKeywordVariable, JTREG, \ SINGLE_KEYWORDS := JOBS TIMEOUT_FACTOR FAILURE_HANDLER_TIMEOUT \ TEST_MODE ASSERT VERBOSE RETAIN MAX_MEM RUN_PROBLEM_LISTS \ - RETRY_COUNT MAX_OUTPUT, \ + RETRY_COUNT REPEAT_COUNT MAX_OUTPUT, \ STRING_KEYWORDS := OPTIONS JAVA_OPTIONS VM_OPTIONS KEYWORDS \ EXTRA_PROBLEM_LISTS LAUNCHER_OPTIONS, \ )) @@ -744,6 +744,15 @@ JTREG_RETAIN ?= fail,error JTREG_RUN_PROBLEM_LISTS ?= false JTREG_RETRY_COUNT ?= 0 + JTREG_REPEAT_COUNT ?= 0 + + ifneq ($$(JTREG_RETRY_COUNT), 0) + ifneq ($$(JTREG_REPEAT_COUNT), 0) + $$(info Error: Cannot use both JTREG_RETRY_COUNT and JTREG_REPEAT_COUNT together.) + $$(info Please choose one or the other.) + $$(error Cannot continue) + endif + endif ifneq ($$(JTREG_LAUNCHER_OPTIONS), ) $1_JTREG_LAUNCHER_OPTIONS += $$(JTREG_LAUNCHER_OPTIONS) @@ -866,6 +875,18 @@ done endif + ifneq ($$(JTREG_REPEAT_COUNT), 0) + $1_COMMAND_LINE := \ + for i in {1..$$(JTREG_REPEAT_COUNT)}; do \ + $$(PRINTF) "\nRepeating Jtreg run: $$$$i out of $$(JTREG_REPEAT_COUNT)\n"; \ + $$($1_COMMAND_LINE); \ + if [ "`$$(CAT) $$($1_EXITCODE)`" != "0" ]; then \ + $$(PRINTF) "\nFailures detected, no more repeats.\n"; \ + break; \ + fi; \ + done + endif + run-test-$1: pre-run-test clean-workdir-$1 $$(call LogWarn) $$(call LogWarn, Running test '$$($1_TEST)') diff -Nru openjdk-17-17.0.2+8/make/ToolsJdk.gmk openjdk-17-17.0.3+7/make/ToolsJdk.gmk --- openjdk-17-17.0.2+8/make/ToolsJdk.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/ToolsJdk.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -80,6 +80,8 @@ TOOL_GENERATEEMOJIDATA = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ build.tools.generateemojidata.GenerateEmojiData +TOOL_MAKEZIPREPRODUCIBLE = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \ + build.tools.makezipreproducible.MakeZipReproducible # TODO: There are references to the jdwpgen.jar in jdk/make/netbeans/jdwpgen/build.xml # and nbproject/project.properties in the same dir. Needs to be looked at. diff -Nru openjdk-17-17.0.2+8/make/autoconf/basic.m4 openjdk-17-17.0.3+7/make/autoconf/basic.m4 --- openjdk-17-17.0.2+8/make/autoconf/basic.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/basic.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, 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 @@ -94,11 +94,6 @@ # Locate the directory of this script. AUTOCONF_DIR=$TOPDIR/make/autoconf - - # Setup username (for use in adhoc version strings etc) - # Outer [ ] to quote m4. - [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] - AC_SUBST(USERNAME) ]) ############################################################################### diff -Nru openjdk-17-17.0.2+8/make/autoconf/jdk-options.m4 openjdk-17-17.0.3+7/make/autoconf/jdk-options.m4 --- openjdk-17-17.0.2+8/make/autoconf/jdk-options.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/jdk-options.m4 2022-04-19 19:55:43.000000000 +0000 @@ -169,6 +169,23 @@ fi AC_SUBST(CACERTS_FILE) + # Choose cacerts source folder for user provided PEM files + AC_ARG_WITH(cacerts-src, [AS_HELP_STRING([--with-cacerts-src], + [specify alternative cacerts source folder containing certificates])]) + CACERTS_SRC="" + AC_MSG_CHECKING([for cacerts source]) + if test "x$with_cacerts_src" == x; then + AC_MSG_RESULT([default]) + else + CACERTS_SRC=$with_cacerts_src + if test ! -d "$CACERTS_SRC"; then + AC_MSG_RESULT([fail]) + AC_MSG_ERROR([Specified cacerts source folder "$CACERTS_SRC" does not exist]) + fi + AC_MSG_RESULT([$CACERTS_SRC]) + fi + AC_SUBST(CACERTS_SRC) + # Enable or disable unlimited crypto UTIL_ARG_ENABLE(NAME: unlimited-crypto, DEFAULT: true, RESULT: UNLIMITED_CRYPTO, DESC: [enable unlimited crypto policy]) diff -Nru openjdk-17-17.0.2+8/make/autoconf/jdk-version.m4 openjdk-17-17.0.3+7/make/autoconf/jdk-version.m4 --- openjdk-17-17.0.2+8/make/autoconf/jdk-version.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/jdk-version.m4 2022-04-19 19:55:43.000000000 +0000 @@ -69,6 +69,17 @@ AC_SUBST(JDK_RC_PLATFORM_NAME) AC_SUBST(HOTSPOT_VM_DISTRO) + # Setup username (for use in adhoc version strings etc) + AC_ARG_WITH([build-user], [AS_HELP_STRING([--with-build-user], + [build username to use in version strings])]) + if test "x$with_build_user" != x; then + USERNAME="$with_build_user" + else + # Outer [ ] to quote m4. + [ USERNAME=`$ECHO "$USER" | $TR -d -c '[a-z][A-Z][0-9]'` ] + fi + AC_SUBST(USERNAME) + # Set the JDK RC name AC_ARG_WITH(jdk-rc-name, [AS_HELP_STRING([--with-jdk-rc-name], [Set JDK RC name. This is used for FileDescription and ProductName properties diff -Nru openjdk-17-17.0.2+8/make/autoconf/spec.gmk.in openjdk-17-17.0.3+7/make/autoconf/spec.gmk.in --- openjdk-17-17.0.2+8/make/autoconf/spec.gmk.in 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/spec.gmk.in 2022-04-19 19:55:43.000000000 +0000 @@ -409,6 +409,8 @@ # Source file for cacerts CACERTS_FILE=@CACERTS_FILE@ +# Source folder for user provided cacerts PEM files +CACERTS_SRC=@CACERTS_SRC@ # Enable unlimited crypto policy UNLIMITED_CRYPTO=@UNLIMITED_CRYPTO@ diff -Nru openjdk-17-17.0.2+8/make/autoconf/toolchain.m4 openjdk-17-17.0.3+7/make/autoconf/toolchain.m4 --- openjdk-17-17.0.2+8/make/autoconf/toolchain.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/toolchain.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2022, 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 @@ -234,7 +234,7 @@ if test "x$OPENJDK_TARGET_OS" = xmacosx; then if test -n "$XCODEBUILD"; then # On Mac OS X, default toolchain to clang after Xcode 5 - XCODE_VERSION_OUTPUT=`"$XCODEBUILD" -version 2>&1 | $HEAD -n 1` + XCODE_VERSION_OUTPUT=`"$XCODEBUILD" -version | $HEAD -n 1` $ECHO "$XCODE_VERSION_OUTPUT" | $GREP "Xcode " > /dev/null if test $? -ne 0; then AC_MSG_NOTICE([xcodebuild output: $XCODE_VERSION_OUTPUT]) diff -Nru openjdk-17-17.0.2+8/make/autoconf/toolchain_microsoft.m4 openjdk-17-17.0.3+7/make/autoconf/toolchain_microsoft.m4 --- openjdk-17-17.0.2+8/make/autoconf/toolchain_microsoft.m4 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/autoconf/toolchain_microsoft.m4 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, 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,7 +25,7 @@ ################################################################################ # The order of these defines the priority by which we try to find them. -VALID_VS_VERSIONS="2019 2017" +VALID_VS_VERSIONS="2019 2017 2022" VS_DESCRIPTION_2017="Microsoft Visual Studio 2017" VS_VERSION_INTERNAL_2017=141 @@ -56,6 +56,21 @@ VS_SUPPORTED_2019=true VS_TOOLSET_SUPPORTED_2019=true +VS_DESCRIPTION_2022="Microsoft Visual Studio 2022" +VS_VERSION_INTERNAL_2022=143 +VS_MSVCR_2022=vcruntime140.dll +VS_VCRUNTIME_1_2022=vcruntime140_1.dll +VS_MSVCP_2022=msvcp140.dll +VS_ENVVAR_2022="VS170COMNTOOLS" +VS_USE_UCRT_2022="true" +VS_VS_INSTALLDIR_2022="Microsoft Visual Studio/2022" +VS_EDITIONS_2022="BuildTools Community Professional Enterprise" +VS_SDK_INSTALLDIR_2022= +VS_VS_PLATFORM_NAME_2022="v143" +VS_SDK_PLATFORM_NAME_2022= +VS_SUPPORTED_2022=true +VS_TOOLSET_SUPPORTED_2022=true + ################################################################################ AC_DEFUN([TOOLCHAIN_CHECK_POSSIBLE_VISUAL_STUDIO_ROOT], diff -Nru openjdk-17-17.0.2+8/make/common/ZipArchive.gmk openjdk-17-17.0.3+7/make/common/ZipArchive.gmk --- openjdk-17-17.0.2+8/make/common/ZipArchive.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/common/ZipArchive.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 2021, 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,9 @@ ifndef _ZIP_ARCHIVE_GMK _ZIP_ARCHIVE_GMK := 1 +# Depends on build tools for MakeZipReproducible +include ../ToolsJdk.gmk + ifeq (,$(_MAKEBASE_GMK)) $(error You must include MakeBase.gmk prior to including ZipArchive.gmk) endif @@ -51,6 +54,8 @@ # FOLLOW_SYMLINKS - Set to explicitly follow symlinks. Affects performance of # finding files. # ZIP_OPTIONS extra options to pass to zip +# REPRODUCIBLE override ENABLE_REPRODUCIBLE_BUILD (to make zip reproducible or not) + SetupZipArchive = $(NamedParamsMacroTemplate) define SetupZipArchiveBody @@ -124,6 +129,10 @@ ) \ ) + ifeq ($$($1_REPRODUCIBLE), ) + $1_REPRODUCIBLE := $$(ENABLE_REPRODUCIBLE_BUILD) + endif + # Use a slightly shorter name for logging, but with enough path to identify this zip. $1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_ZIP)) @@ -134,6 +143,8 @@ # dir is very small. # If zip has nothing to do, it returns 12 and would fail the build. Check for 12 # and only fail if it's not. + # For reproducible builds set the zip access & modify times to SOURCE_DATE_EPOCH + # by using a ziptmp folder to generate final zip from using MakeZipReproducible. $$($1_ZIP) : $$($1_ALL_SRCS) $$($1_EXTRA_DEPS) $$(call LogWarn, Updating $$($1_NAME)) $$(call MakeTargetDir) @@ -163,7 +174,18 @@ $$($1_ZIP_EXCLUDES_$$s) \ || test "$$$$?" = "12" \ ))$$(NEWLINE) \ - ) true \ + ) true + ifeq ($$($1_REPRODUCIBLE), true) + $$(call ExecuteWithLog, \ + $$(SUPPORT_OUTPUTDIR)/makezipreproducible/$$(patsubst $$(OUTPUTDIR)/%,%, $$@), \ + ($(RM) $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip && \ + $(MKDIR) -p $$(SUPPORT_OUTPUTDIR)/ziptmp/$1 && \ + $(TOOL_MAKEZIPREPRODUCIBLE) -f $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip \ + -t $(SOURCE_DATE_EPOCH) $$@ && \ + $(RM) $$@ && \ + $(MV) $$(SUPPORT_OUTPUTDIR)/ziptmp/$1/tmp.zip $$@ \ + )) + endif $(TOUCH) $$@ # Add zip to target list diff -Nru openjdk-17-17.0.2+8/make/conf/version-numbers.conf openjdk-17-17.0.3+7/make/conf/version-numbers.conf --- openjdk-17-17.0.2+8/make/conf/version-numbers.conf 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/conf/version-numbers.conf 2022-04-19 19:55:43.000000000 +0000 @@ -28,12 +28,12 @@ DEFAULT_VERSION_FEATURE=17 DEFAULT_VERSION_INTERIM=0 -DEFAULT_VERSION_UPDATE=2 +DEFAULT_VERSION_UPDATE=3 DEFAULT_VERSION_PATCH=0 DEFAULT_VERSION_EXTRA1=0 DEFAULT_VERSION_EXTRA2=0 DEFAULT_VERSION_EXTRA3=0 -DEFAULT_VERSION_DATE=2022-01-18 +DEFAULT_VERSION_DATE=2022-04-19 DEFAULT_VERSION_CLASSFILE_MAJOR=61 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`" DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 diff -Nru openjdk-17-17.0.2+8/make/data/currency/CurrencyData.properties openjdk-17-17.0.3+7/make/data/currency/CurrencyData.properties --- openjdk-17-17.0.2+8/make/data/currency/CurrencyData.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/data/currency/CurrencyData.properties 2022-04-19 19:55:43.000000000 +0000 @@ -32,7 +32,7 @@ # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=169 +dataVersion=170 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -54,7 +54,7 @@ SBD090-SCR690-SDD736-SDG938-SEK752-SGD702-SHP654-SIT705-SKK703-SLL694-SOS706-\ SRD968-SRG740-SSP728-STD678-STN930-SVC222-SYP760-SZL748-THB764-TJS972-TMM795-TMT934-TND788-TOP776-\ TPE626-TRL792-TRY949-TTD780-TWD901-TZS834-UAH980-UGX800-USD840-USN997-USS998-UYI940-\ - UYU858-UZS860-VEB862-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ + UYU858-UZS860-VEB862-VED926-VEF937-VES928-VND704-VUV548-WST882-XAF950-XAG961-XAU959-XBA955-\ XBB956-XBC957-XBD958-XCD951-XDR960-XFO000-XFU000-XOF952-XPD964-XPF953-\ XPT962-XSU994-XTS963-XUA965-XXX999-YER886-YUM891-ZAR710-ZMK894-ZMW967-ZWD716-ZWL932-\ ZWN942-ZWR935 diff -Nru openjdk-17-17.0.2+8/make/devkit/createJMHBundle.sh openjdk-17-17.0.3+7/make/devkit/createJMHBundle.sh --- openjdk-17-17.0.2+8/make/devkit/createJMHBundle.sh 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/devkit/createJMHBundle.sh 2022-04-19 19:55:43.000000000 +0000 @@ -26,7 +26,7 @@ # Create a bundle in the build directory, containing what's needed to # build and run JMH microbenchmarks from the OpenJDK build. -JMH_VERSION=1.32 +JMH_VERSION=1.34 COMMONS_MATH3_VERSION=3.2 JOPT_SIMPLE_VERSION=4.6 diff -Nru openjdk-17-17.0.2+8/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java openjdk-17-17.0.3+7/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java --- openjdk-17-17.0.2+8/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/make/jdk/src/classes/build/tools/makezipreproducible/MakeZipReproducible.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2021, 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 build.tools.makezipreproducible; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +/** + * Generate a zip file in a "reproducible" manner from the input zip file. + * Standard zip tools rely on OS file list querying whose ordering can vary + * by platform architecture, this class ensures the zip entries are ordered + * and also supports SOURCE_DATE_EPOCH timestamps. + */ +public class MakeZipReproducible { + String input_file = null; + String fname = null; + String zname = ""; + long timestamp = -1L; + boolean verbose = false; + + // Keep a sorted Set of ZipEntrys to be processed, so that the zip is reproducible + SortedMap entries = new TreeMap(); + + private boolean ok; + + public MakeZipReproducible() { + } + + public synchronized boolean run(String args[]) { + ok = true; + if (!parseArgs(args)) { + return false; + } + try { + zname = fname.replace(File.separatorChar, '/'); + if (zname.startsWith("./")) { + zname = zname.substring(2); + } + + if (verbose) System.out.println("Input zip file: " + input_file); + + File inFile = new File(input_file); + if (!inFile.exists()) { + error("Input zip file does not exist"); + ok = false; + } else { + File zipFile = new File(fname); + // Check archive to create does not exist + if (!zipFile.exists()) { + // Process input ZipEntries + ok = processInputEntries(inFile); + if (ok) { + try (FileOutputStream out = new FileOutputStream(fname)) { + ok = create(inFile, new BufferedOutputStream(out, 4096)); + } + } else { + } + } else { + error("Target zip file "+fname+" already exists."); + ok = false; + } + } + } catch (IOException e) { + fatalError(e); + ok = false; + } catch (Error ee) { + ee.printStackTrace(); + ok = false; + } catch (Throwable t) { + t.printStackTrace(); + ok = false; + } + return ok; + } + + boolean parseArgs(String args[]) { + try { + boolean parsingIncludes = false; + boolean parsingExcludes = false; + int count = 0; + while(count < args.length) { + if (args[count].startsWith("-")) { + String flag = args[count].substring(1); + switch (flag.charAt(0)) { + case 'f': + fname = args[++count]; + break; + case 't': + // SOURCE_DATE_EPOCH timestamp specified + timestamp = Long.parseLong(args[++count]) * 1000; + break; + case 'v': + verbose = true; + break; + default: + error(String.format("Illegal option -%s", String.valueOf(flag.charAt(0)))); + usageError(); + return false; + } + } else { + // input zip file + if (input_file != null) { + error("Input zip file already specified"); + usageError(); + return false; + } + input_file = args[count]; + } + count++; + } + } catch (ArrayIndexOutOfBoundsException e) { + usageError(); + return false; + } catch (NumberFormatException e) { + usageError(); + return false; + } + if (fname == null) { + error("-f must be specified"); + usageError(); + return false; + } + // If no files specified then default to current directory + if (input_file == null) { + error("No input zip file specified"); + usageError(); + return false; + } + + return true; + } + + // Process input zip file and add to sorted entries set + boolean processInputEntries(File inFile) throws IOException { + ZipFile zipFile = new ZipFile(inFile); + zipFile.stream().forEach(entry -> entries.put(entry.getName(), entry)); + + return true; + } + + // Create new zip from entries + boolean create(File inFile, OutputStream out) throws IOException + { + try (ZipFile zipFile = new ZipFile(inFile); + ZipOutputStream zos = new ZipOutputStream(out)) { + for (Map.Entry entry : entries.entrySet()) { + ZipEntry zipEntry = entry.getValue(); + if (zipEntry.getSize() > 0) { + try (InputStream eis = zipFile.getInputStream(zipEntry)) { + addEntry(zos, zipEntry, eis); + } + } else { + addEntry(zos, zipEntry, null); + } + } + } + return true; + } + + // Add Entry and data to Zip + void addEntry(ZipOutputStream zos, ZipEntry entry, InputStream entryInputStream) throws IOException { + if (verbose) { + System.out.println("Adding: "+entry.getName()); + } + + // Set to specified timestamp if set otherwise leave as original lastModified time + if (timestamp != -1L) { + entry.setTime(timestamp); + } + + zos.putNextEntry(entry); + if (entry.getSize() > 0 && entryInputStream != null) { + entryInputStream.transferTo(zos); + } + zos.closeEntry(); + } + + void usageError() { + error( + "Usage: MakeZipReproducible [-v] [-t ] -f \n" + + "Options:\n" + + " -v verbose output\n" + + " -f specify archive file name to create\n" + + " -t specific SOURCE_DATE_EPOCH value to use for timestamps\n" + + " input_zip_file re-written as a reproducible zip output_zip_file.\n"); + } + + void fatalError(Exception e) { + e.printStackTrace(); + } + + protected void error(String s) { + System.err.println(s); + } + + public static void main(String args[]) { + MakeZipReproducible z = new MakeZipReproducible(); + System.exit(z.run(args) ? 0 : 1); + } +} + diff -Nru openjdk-17-17.0.2+8/make/modules/java.base/Gendata.gmk openjdk-17-17.0.3+7/make/modules/java.base/Gendata.gmk --- openjdk-17-17.0.2+8/make/modules/java.base/Gendata.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/modules/java.base/Gendata.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -60,7 +60,11 @@ ################################################################################ -GENDATA_CACERTS_SRC := $(TOPDIR)/make/data/cacerts/ +ifneq ($(CACERTS_SRC), ) + GENDATA_CACERTS_SRC := $(CACERTS_SRC) +else + GENDATA_CACERTS_SRC := $(TOPDIR)/make/data/cacerts/ +endif GENDATA_CACERTS := $(SUPPORT_OUTPUTDIR)/modules_libs/java.base/security/cacerts $(GENDATA_CACERTS): $(BUILD_TOOLS_JDK) $(wildcard $(GENDATA_CACERTS_SRC)/*) diff -Nru openjdk-17-17.0.2+8/make/modules/jdk.javadoc/Gendata.gmk openjdk-17-17.0.3+7/make/modules/jdk.javadoc/Gendata.gmk --- openjdk-17-17.0.2+8/make/modules/jdk.javadoc/Gendata.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/modules/jdk.javadoc/Gendata.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -72,7 +72,9 @@ $(MODULE_INFOS) $(call MakeTargetDir) $(call LogInfo, Creating javadoc element lists) - $(RM) -r $(ELEMENT_LISTS_DIR) + $(RM) $(ELEMENT_LISTS_DIR)/element-list-{$(call CommaList, \ + $(call sequence, $(GENERATE_SYMBOLS_FROM_JDK_VERSION), \ + $(JDK_SOURCE_TARGET_VERSION)))}.txt # Generate element-list files for JDK 11 to current-1 $(call ExecuteWithLog, $@_historic, \ $(JAVA_SMALL) $(INTERIM_LANGTOOLS_ARGS) \ diff -Nru openjdk-17-17.0.2+8/make/test/JtregNativeHotspot.gmk openjdk-17-17.0.3+7/make/test/JtregNativeHotspot.gmk --- openjdk-17-17.0.2+8/make/test/JtregNativeHotspot.gmk 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/make/test/JtregNativeHotspot.gmk 2022-04-19 19:55:43.000000000 +0000 @@ -863,7 +863,7 @@ BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl else - BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c libTestJNI.c \ + BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \ exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp endif @@ -871,7 +871,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT - BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c + BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libatExit := jvm.lib else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread diff -Nru openjdk-17-17.0.2+8/src/demo/share/jfc/SwingSet2/TableDemo.java openjdk-17-17.0.3+7/src/demo/share/jfc/SwingSet2/TableDemo.java --- openjdk-17-17.0.2+8/src/demo/share/jfc/SwingSet2/TableDemo.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/demo/share/jfc/SwingSet2/TableDemo.java 2022-04-19 19:55:43.000000000 +0000 @@ -741,4 +741,13 @@ footerTextField.setDragEnabled(dragEnabled); } + @Override + public ImageIcon createImageIcon(String filename, String description) { + ImageIcon imageIcon = super.createImageIcon(filename, description); + AccessibleContext context = imageIcon.getAccessibleContext(); + if (context!= null) { + context.setAccessibleName(description); + } + return imageIcon; + } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/aarch64.ad openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/aarch64.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/aarch64.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/aarch64.ad 2022-04-19 19:55:43.000000000 +0000 @@ -2373,6 +2373,8 @@ bool ret_value = true; switch (opcode) { + case Op_OnSpinWait: + return VM_Version::supports_on_spin_wait(); case Op_CacheWB: case Op_CacheWBPreSync: case Op_CacheWBPostSync: @@ -3848,7 +3850,7 @@ // Try to CAS m->owner from NULL to current thread. __ add(tmp, disp_hdr, (ObjectMonitor::owner_offset_in_bytes()-markWord::monitor_value)); __ cmpxchg(tmp, zr, rthread, Assembler::xword, /*acquire*/ true, - /*release*/ true, /*weak*/ false, noreg); // Sets flags for result + /*release*/ true, /*weak*/ false, rscratch1); // Sets flags for result // Store a non-null value into the box to avoid looking like a re-entrant // lock. The fast-path monitor unlock code checks for @@ -3857,6 +3859,15 @@ __ mov(tmp, (address)markWord::unused_mark().value()); __ str(tmp, Address(box, BasicLock::displaced_header_offset_in_bytes())); + __ br(Assembler::EQ, cont); // CAS success means locking succeeded + + __ cmp(rscratch1, rthread); + __ br(Assembler::NE, cont); // Check for recursive locking + + // Recursive lock case + __ increment(Address(disp_hdr, ObjectMonitor::recursions_offset_in_bytes() - markWord::monitor_value), 1); + // flag == EQ still from the cmp above, checking if this is a reentrant lock + __ bind(cont); // flag == EQ indicates success // flag == NE indicates failure @@ -3904,11 +3915,20 @@ __ add(tmp, tmp, -(int)markWord::monitor_value); // monitor __ ldr(rscratch1, Address(tmp, ObjectMonitor::owner_offset_in_bytes())); __ ldr(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); - __ eor(rscratch1, rscratch1, rthread); // Will be 0 if we are the owner. - __ orr(rscratch1, rscratch1, disp_hdr); // Will be 0 if there are 0 recursions - __ cmp(rscratch1, zr); // Sets flags for result + + Label notRecursive; + __ cmp(rscratch1, rthread); __ br(Assembler::NE, cont); + __ cbz(disp_hdr, notRecursive); + + // Recursive lock + __ sub(disp_hdr, disp_hdr, 1u); + __ str(disp_hdr, Address(tmp, ObjectMonitor::recursions_offset_in_bytes())); + // flag == EQ was set in the ownership check above + __ b(cont); + + __ bind(notRecursive); __ ldr(rscratch1, Address(tmp, ObjectMonitor::EntryList_offset_in_bytes())); __ ldr(disp_hdr, Address(tmp, ObjectMonitor::cxq_offset_in_bytes())); __ orr(rscratch1, rscratch1, disp_hdr); // Will be 0 if both are 0. @@ -14321,6 +14341,18 @@ ins_pipe(fp_uop_d); %} +instruct onspinwait() %{ + match(OnSpinWait); + ins_cost(INSN_COST); + + format %{ "onspinwait" %} + + ins_encode %{ + __ spin_wait(); + %} + ins_pipe(pipe_class_empty); +%} + // ============================================================================ // Logical Instructions diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2988,7 +2988,7 @@ void LIR_Assembler::membar_storeload() { __ membar(MacroAssembler::StoreLoad); } void LIR_Assembler::on_spin_wait() { - Unimplemented(); + __ spin_wait(); } void LIR_Assembler::get_thread(LIR_Opr result_reg) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/c1_MacroAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -355,7 +355,7 @@ } -void C1_MacroAssembler::verified_entry() { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { // If we have to make this method not-entrant we'll overwrite its // first instruction with a jump. For this action to be legal we // must ensure that this first instruction is a B, BL, NOP, BKPT, diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/globals_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/globals_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/globals_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/globals_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -111,7 +111,15 @@ product(int, SoftwarePrefetchHintDistance, -1, \ "Use prfm hint with specified distance in compiled code." \ "Value -1 means off.") \ - range(-1, 4096) + range(-1, 4096) \ + product(ccstr, OnSpinWaitInst, "none", DIAGNOSTIC, \ + "The instruction to use to implement " \ + "java.lang.Thread.onSpinWait()." \ + "Options: none, nop, isb, yield.") \ + product(uint, OnSpinWaitInstCount, 1, DIAGNOSTIC, \ + "The number of OnSpinWaitInst instructions to generate." \ + "It cannot be used with OnSpinWaitInst=none.") \ + range(1, 99) // end of ARCH_FLAGS diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2027,15 +2027,6 @@ str(rscratch1, dst); } - -void MacroAssembler::pusha() { - push(0x7fffffff, sp); -} - -void MacroAssembler::popa() { - pop(0x7fffffff, sp); -} - // Push lots of registers in the bit set supplied. Don't push sp. // Return the number of words pushed int MacroAssembler::push(unsigned int bitset, Register stack) { @@ -2677,7 +2668,7 @@ void MacroAssembler::push_CPU_state(bool save_vectors, bool use_sve, int sve_vector_size_in_bytes) { - push(0x3fffffff, sp); // integer registers except lr & sp + push(RegSet::range(r0, r29), sp); // integer registers except lr & sp if (save_vectors && use_sve && sve_vector_size_in_bytes > 16) { sub(sp, sp, sve_vector_size_in_bytes * FloatRegisterImpl::number_of_registers); for (int i = 0; i < FloatRegisterImpl::number_of_registers; i++) { @@ -2713,7 +2704,14 @@ reinitialize_ptrue(); } - pop(0x3fffffff, sp); // integer registers except lr & sp + // integer registers except lr & sp + pop(RegSet::range(r0, r17), sp); +#ifdef R18_RESERVED + ldp(zr, r19, Address(post(sp, 2 * wordSize))); + pop(RegSet::range(r20, r29), sp); +#else + pop(RegSet::range(r18_tls, r29), sp); +#endif } /** @@ -5354,3 +5352,21 @@ } } #endif + +void MacroAssembler::spin_wait() { + for (int i = 0; i < VM_Version::spin_wait_desc().inst_count(); ++i) { + switch (VM_Version::spin_wait_desc().inst()) { + case SpinWait::NOP: + nop(); + break; + case SpinWait::ISB: + isb(); + break; + case SpinWait::YIELD: + yield(); + break; + default: + ShouldNotReachHere(); + } + } +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1123,10 +1123,6 @@ void push(Register src); void pop(Register dst); - // push all registers onto the stack - void pusha(); - void popa(); - void repne_scan(Register addr, Register value, Register count, Register scratch); void repne_scanw(Register addr, Register value, Register count, @@ -1414,6 +1410,9 @@ void cache_wb(Address line); void cache_wbsync(bool is_pre); + // Code for java.lang.Thread::onSpinWait() intrinsic. + void spin_wait(); + private: // Check the current thread doesn't need a cross modify fence. void verify_cross_modify_fence_not_required() PRODUCT_RETURN; diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/spin_wait_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. 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 CPU_AARCH64_SPIN_WAIT_AARCH64_HPP +#define CPU_AARCH64_SPIN_WAIT_AARCH64_HPP + +class SpinWait { +public: + enum Inst { + NONE = -1, + NOP, + ISB, + YIELD + }; + +private: + Inst _inst; + int _count; + +public: + SpinWait(Inst inst = NONE, int count = 0) : _inst(inst), _count(count) {} + + Inst inst() const { return _inst; } + int inst_count() const { return _count; } +}; + +#endif // CPU_AARCH64_SPIN_WAIT_AARCH64_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -3231,6 +3231,194 @@ // c_rarg2 - int offset // c_rarg3 - int limit // + address generate_md5_implCompress(bool multi_block, const char *name) { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", name); + address start = __ pc(); + + Register buf = c_rarg0; + Register state = c_rarg1; + Register ofs = c_rarg2; + Register limit = c_rarg3; + Register a = r4; + Register b = r5; + Register c = r6; + Register d = r7; + Register rscratch3 = r10; + Register rscratch4 = r11; + + Label keys; + Label md5_loop; + + __ BIND(md5_loop); + + // Save hash values for addition after rounds + __ ldrw(a, Address(state, 0)); + __ ldrw(b, Address(state, 4)); + __ ldrw(c, Address(state, 8)); + __ ldrw(d, Address(state, 12)); + +#define FF(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch3, r3, r4); \ + __ movw(rscratch2, t); \ + __ andw(rscratch3, rscratch3, r2); \ + __ addw(rscratch4, r1, rscratch2); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch3, r4); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define GG(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch2, r2, r3); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ andw(rscratch3, rscratch2, r4); \ + __ movw(rscratch2, t); \ + __ eorw(rscratch3, rscratch3, r3); \ + __ addw(rscratch4, r1, rscratch2); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define HH(r1, r2, r3, r4, k, s, t) \ + __ eorw(rscratch3, r3, r4); \ + __ movw(rscratch2, t); \ + __ addw(rscratch4, r1, rscratch2); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch3, r2); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + +#define II(r1, r2, r3, r4, k, s, t) \ + __ movw(rscratch3, t); \ + __ ornw(rscratch2, r2, r4); \ + __ addw(rscratch4, r1, rscratch3); \ + __ ldrw(rscratch1, Address(buf, k*4)); \ + __ eorw(rscratch3, rscratch2, r3); \ + __ addw(rscratch3, rscratch3, rscratch1); \ + __ addw(rscratch3, rscratch3, rscratch4); \ + __ rorw(rscratch2, rscratch3, 32 - s); \ + __ addw(r1, rscratch2, r2); + + // Round 1 + FF(a, b, c, d, 0, 7, 0xd76aa478) + FF(d, a, b, c, 1, 12, 0xe8c7b756) + FF(c, d, a, b, 2, 17, 0x242070db) + FF(b, c, d, a, 3, 22, 0xc1bdceee) + FF(a, b, c, d, 4, 7, 0xf57c0faf) + FF(d, a, b, c, 5, 12, 0x4787c62a) + FF(c, d, a, b, 6, 17, 0xa8304613) + FF(b, c, d, a, 7, 22, 0xfd469501) + FF(a, b, c, d, 8, 7, 0x698098d8) + FF(d, a, b, c, 9, 12, 0x8b44f7af) + FF(c, d, a, b, 10, 17, 0xffff5bb1) + FF(b, c, d, a, 11, 22, 0x895cd7be) + FF(a, b, c, d, 12, 7, 0x6b901122) + FF(d, a, b, c, 13, 12, 0xfd987193) + FF(c, d, a, b, 14, 17, 0xa679438e) + FF(b, c, d, a, 15, 22, 0x49b40821) + + // Round 2 + GG(a, b, c, d, 1, 5, 0xf61e2562) + GG(d, a, b, c, 6, 9, 0xc040b340) + GG(c, d, a, b, 11, 14, 0x265e5a51) + GG(b, c, d, a, 0, 20, 0xe9b6c7aa) + GG(a, b, c, d, 5, 5, 0xd62f105d) + GG(d, a, b, c, 10, 9, 0x02441453) + GG(c, d, a, b, 15, 14, 0xd8a1e681) + GG(b, c, d, a, 4, 20, 0xe7d3fbc8) + GG(a, b, c, d, 9, 5, 0x21e1cde6) + GG(d, a, b, c, 14, 9, 0xc33707d6) + GG(c, d, a, b, 3, 14, 0xf4d50d87) + GG(b, c, d, a, 8, 20, 0x455a14ed) + GG(a, b, c, d, 13, 5, 0xa9e3e905) + GG(d, a, b, c, 2, 9, 0xfcefa3f8) + GG(c, d, a, b, 7, 14, 0x676f02d9) + GG(b, c, d, a, 12, 20, 0x8d2a4c8a) + + // Round 3 + HH(a, b, c, d, 5, 4, 0xfffa3942) + HH(d, a, b, c, 8, 11, 0x8771f681) + HH(c, d, a, b, 11, 16, 0x6d9d6122) + HH(b, c, d, a, 14, 23, 0xfde5380c) + HH(a, b, c, d, 1, 4, 0xa4beea44) + HH(d, a, b, c, 4, 11, 0x4bdecfa9) + HH(c, d, a, b, 7, 16, 0xf6bb4b60) + HH(b, c, d, a, 10, 23, 0xbebfbc70) + HH(a, b, c, d, 13, 4, 0x289b7ec6) + HH(d, a, b, c, 0, 11, 0xeaa127fa) + HH(c, d, a, b, 3, 16, 0xd4ef3085) + HH(b, c, d, a, 6, 23, 0x04881d05) + HH(a, b, c, d, 9, 4, 0xd9d4d039) + HH(d, a, b, c, 12, 11, 0xe6db99e5) + HH(c, d, a, b, 15, 16, 0x1fa27cf8) + HH(b, c, d, a, 2, 23, 0xc4ac5665) + + // Round 4 + II(a, b, c, d, 0, 6, 0xf4292244) + II(d, a, b, c, 7, 10, 0x432aff97) + II(c, d, a, b, 14, 15, 0xab9423a7) + II(b, c, d, a, 5, 21, 0xfc93a039) + II(a, b, c, d, 12, 6, 0x655b59c3) + II(d, a, b, c, 3, 10, 0x8f0ccc92) + II(c, d, a, b, 10, 15, 0xffeff47d) + II(b, c, d, a, 1, 21, 0x85845dd1) + II(a, b, c, d, 8, 6, 0x6fa87e4f) + II(d, a, b, c, 15, 10, 0xfe2ce6e0) + II(c, d, a, b, 6, 15, 0xa3014314) + II(b, c, d, a, 13, 21, 0x4e0811a1) + II(a, b, c, d, 4, 6, 0xf7537e82) + II(d, a, b, c, 11, 10, 0xbd3af235) + II(c, d, a, b, 2, 15, 0x2ad7d2bb) + II(b, c, d, a, 9, 21, 0xeb86d391) + +#undef FF +#undef GG +#undef HH +#undef II + + // write hash values back in the correct order + __ ldrw(rscratch1, Address(state, 0)); + __ addw(rscratch1, rscratch1, a); + __ strw(rscratch1, Address(state, 0)); + + __ ldrw(rscratch2, Address(state, 4)); + __ addw(rscratch2, rscratch2, b); + __ strw(rscratch2, Address(state, 4)); + + __ ldrw(rscratch3, Address(state, 8)); + __ addw(rscratch3, rscratch3, c); + __ strw(rscratch3, Address(state, 8)); + + __ ldrw(rscratch4, Address(state, 12)); + __ addw(rscratch4, rscratch4, d); + __ strw(rscratch4, Address(state, 12)); + + if (multi_block) { + __ add(buf, buf, 64); + __ add(ofs, ofs, 64); + __ cmp(ofs, limit); + __ br(Assembler::LE, md5_loop); + __ mov(c_rarg0, ofs); // return ofs + } + + __ ret(lr); + + return start; + } + + // Arguments: + // + // Inputs: + // c_rarg0 - byte[] source+offset + // c_rarg1 - int[] SHA.state + // c_rarg2 - int offset + // c_rarg3 - int limit + // address generate_sha1_implCompress(bool multi_block, const char *name) { __ align(CodeEntryAlignment); StubCodeMark mark(this, "StubRoutines", name); @@ -6182,6 +6370,18 @@ return start; } + // Support for spin waits. + address generate_spin_wait() { + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "spin_wait"); + address start = __ pc(); + + __ spin_wait(); + __ ret(lr); + + return start; + } + #ifdef LINUX // ARMv8.1 LSE versions of the atomic stubs used by Atomic::PlatformXX. @@ -7464,6 +7664,10 @@ StubRoutines::_counterMode_AESCrypt = generate_counterMode_AESCrypt(); } + if (UseMD5Intrinsics) { + StubRoutines::_md5_implCompress = generate_md5_implCompress(false, "md5_implCompress"); + StubRoutines::_md5_implCompressMB = generate_md5_implCompress(true, "md5_implCompressMB"); + } if (UseSHA1Intrinsics) { StubRoutines::_sha1_implCompress = generate_sha1_implCompress(false, "sha1_implCompress"); StubRoutines::_sha1_implCompressMB = generate_sha1_implCompress(true, "sha1_implCompressMB"); @@ -7486,6 +7690,8 @@ StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); } + StubRoutines::aarch64::_spin_wait = generate_spin_wait(); + #ifdef LINUX generate_atomic_entry_points(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -57,6 +57,10 @@ address StubRoutines::aarch64::_string_indexof_linear_ul = NULL; address StubRoutines::aarch64::_large_byte_array_inflate = NULL; address StubRoutines::aarch64::_method_entry_barrier = NULL; + +static void empty_spin_wait() { } +address StubRoutines::aarch64::_spin_wait = CAST_FROM_FN_PTR(address, empty_spin_wait); + bool StubRoutines::aarch64::_completed = false; /** diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/stubRoutines_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -36,7 +36,7 @@ enum platform_dependent_constants { code_size1 = 19000, // simply increase if too small (assembler will crash if too small) - code_size2 = 32000 // simply increase if too small (assembler will crash if too small) + code_size2 = 45000 // simply increase if too small (assembler will crash if too small) }; class aarch64 { @@ -72,6 +72,8 @@ static address _method_entry_barrier; + static address _spin_wait; + static bool _completed; public: @@ -177,6 +179,10 @@ return _method_entry_barrier; } + static address spin_wait() { + return _spin_wait; + } + static bool complete() { return _completed; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1397,11 +1397,12 @@ __ cmp(rscratch1, (u1)StackOverflow::stack_guard_yellow_reserved_disabled); __ br(Assembler::NE, no_reguard); - __ pusha(); // XXX only save smashed registers + __ push_call_clobbered_registers(); __ mov(c_rarg0, rthread); __ mov(rscratch2, CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)); __ blr(rscratch2); - __ popa(); // XXX only restore smashed registers + __ pop_call_clobbered_registers(); + __ bind(no_reguard); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -46,6 +46,26 @@ int VM_Version::_icache_line_size; int VM_Version::_initial_sve_vector_length; +SpinWait VM_Version::_spin_wait; + +static SpinWait get_spin_wait_desc() { + if (strcmp(OnSpinWaitInst, "nop") == 0) { + return SpinWait(SpinWait::NOP, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "isb") == 0) { + return SpinWait(SpinWait::ISB, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "yield") == 0) { + return SpinWait(SpinWait::YIELD, OnSpinWaitInstCount); + } else if (strcmp(OnSpinWaitInst, "none") != 0) { + vm_exit_during_initialization("The options for OnSpinWaitInst are nop, isb, yield, and none", OnSpinWaitInst); + } + + if (!FLAG_IS_DEFAULT(OnSpinWaitInstCount) && OnSpinWaitInstCount > 0) { + vm_exit_during_initialization("OnSpinWaitInstCount cannot be used for OnSpinWaitInst 'none'"); + } + + return SpinWait{}; +} + void VM_Version::initialize() { _supports_cx8 = true; _supports_atomic_getset4 = true; @@ -182,6 +202,14 @@ if (FLAG_IS_DEFAULT(UseSIMDForMemoryOps)) { FLAG_SET_DEFAULT(UseSIMDForMemoryOps, true); } + + if (FLAG_IS_DEFAULT(OnSpinWaitInst)) { + FLAG_SET_DEFAULT(OnSpinWaitInst, "isb"); + } + + if (FLAG_IS_DEFAULT(OnSpinWaitInstCount)) { + FLAG_SET_DEFAULT(OnSpinWaitInstCount, 1); + } } if (_cpu == CPU_ARM) { @@ -272,9 +300,8 @@ FLAG_SET_DEFAULT(UseFMA, true); } - if (UseMD5Intrinsics) { - warning("MD5 intrinsics are not available on this CPU"); - FLAG_SET_DEFAULT(UseMD5Intrinsics, false); + if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { + UseMD5Intrinsics = true; } if (_features & (CPU_SHA1 | CPU_SHA2 | CPU_SHA3 | CPU_SHA512)) { @@ -450,5 +477,7 @@ } #endif + _spin_wait = get_spin_wait_desc(); + UNSUPPORTED_OPTION(CriticalJNINatives); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,6 +26,7 @@ #ifndef CPU_AARCH64_VM_VERSION_AARCH64_HPP #define CPU_AARCH64_VM_VERSION_AARCH64_HPP +#include "spin_wait_aarch64.hpp" #include "runtime/abstract_vm_version.hpp" #include "utilities/sizes.hpp" @@ -45,6 +46,8 @@ static int _icache_line_size; static int _initial_sve_vector_length; + static SpinWait _spin_wait; + // Read additional info using OS-specific interfaces static void get_os_cpu_info(); @@ -142,6 +145,10 @@ static void get_compatible_board(char *buf, int buflen); + static const SpinWait& spin_wait_desc() { return _spin_wait; } + + static bool supports_on_spin_wait() { return _spin_wait.inst() != SpinWait::NONE; } + #ifdef __APPLE__ // Is the CPU running emulated (for example macOS Rosetta running x86_64 code on M1 ARM (aarch64) static bool is_cpu_emulated(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/assembler_arm_32.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/assembler_arm_32.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/assembler_arm_32.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/assembler_arm_32.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -47,7 +47,7 @@ // Convert the raw encoding form into the form expected by the // constructor for Address. Address Address::make_raw(int base, int index, int scale, int disp, relocInfo::relocType disp_reloc) { - RelocationHolder rspec; + RelocationHolder rspec = RelocationHolder::none; if (disp_reloc != relocInfo::none) { rspec = Relocation::spec_simple(disp_reloc); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_LIRAssembler_arm.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1685,6 +1685,9 @@ } else { assert(right->is_constant(), "must be"); const uint c = (uint)right->as_constant_ptr()->as_jint(); + if (!Assembler::is_arith_imm_in_range(c)) { + BAILOUT("illegal arithmetic operand"); + } switch (code) { case lir_logic_and: __ and_32(res, lreg, c); break; case lir_logic_or: __ orr_32(res, lreg, c); break; @@ -1825,8 +1828,8 @@ __ teq(xhi, yhi); __ teq(xlo, ylo, eq); } else { - __ subs(xlo, xlo, ylo); - __ sbcs(xhi, xhi, yhi); + __ subs(Rtemp, xlo, ylo); + __ sbcs(Rtemp, xhi, yhi); } } else { ShouldNotReachHere(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/arm/c1_MacroAssembler_arm.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -70,8 +70,8 @@ raw_pop(FP, LR); } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) { breakpoint(); } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_CodeStubs_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -81,8 +81,6 @@ if (_info->deoptimize_on_exception()) { address a = Runtime1::entry_for(Runtime1::predicate_failed_trap_id); - // May be used by optimizations like LoopInvariantCodeMotion or RangeCheckEliminator. - DEBUG_ONLY( __ untested("RangeCheckStub: predicate_failed_trap_id"); ) //__ load_const_optimized(R0, a); __ add_const_optimized(R0, R29_TOC, MacroAssembler::offset_to_global_toc(a)); __ mtctr(R0); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -87,8 +87,8 @@ } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) illtrap(); +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) illtrap(); // build frame } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -41,17 +41,18 @@ // Compress char[] to byte[] by compressing 16 bytes at once. void C2_MacroAssembler::string_compress_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, - Label& Lfailure) { + Label& Lfailure, bool ascii) { const Register tmp0 = R0; + const int byte_mask = ascii ? 0x7F : 0xFF; assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5); Label Lloop, Lslow; // Check if cnt >= 8 (= 16 bytes) - lis(tmp1, 0xFF); // tmp1 = 0x00FF00FF00FF00FF + lis(tmp1, byte_mask); // tmp1 = 0x00FF00FF00FF00FF (non ascii case) srwi_(tmp2, cnt, 3); beq(CCR0, Lslow); - ori(tmp1, tmp1, 0xFF); + ori(tmp1, tmp1, byte_mask); rldimi(tmp1, tmp1, 32, 0); mtctr(tmp2); @@ -67,7 +68,7 @@ rldimi(tmp4, tmp4, 2*8, 2*8); // _4_6_7_7 andc_(tmp0, tmp0, tmp1); - bne(CCR0, Lfailure); // Not latin1. + bne(CCR0, Lfailure); // Not latin1/ascii. addi(src, src, 16); rlwimi(tmp3, tmp2, 0*8, 24, 31);// _____1_3 @@ -87,20 +88,49 @@ } // Compress char[] to byte[]. cnt must be positive int. -void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp, Label& Lfailure) { +void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp, + Label& Lfailure, bool ascii) { + const int byte_mask = ascii ? 0x7F : 0xFF; Label Lloop; mtctr(cnt); bind(Lloop); lhz(tmp, 0, src); - cmplwi(CCR0, tmp, 0xff); - bgt(CCR0, Lfailure); // Not latin1. + cmplwi(CCR0, tmp, byte_mask); + bgt(CCR0, Lfailure); // Not latin1/ascii. addi(src, src, 2); stb(tmp, 0, dst); addi(dst, dst, 1); bdnz(Lloop); } +void C2_MacroAssembler::encode_iso_array(Register src, Register dst, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register result, bool ascii) { + Label Lslow, Lfailure1, Lfailure2, Ldone; + + string_compress_16(src, dst, len, tmp1, tmp2, tmp3, tmp4, tmp5, Lfailure1, ascii); + rldicl_(result, len, 0, 64-3); // Remaining characters. + beq(CCR0, Ldone); + bind(Lslow); + string_compress(src, dst, result, tmp2, Lfailure2, ascii); + li(result, 0); + b(Ldone); + + bind(Lfailure1); + mr(result, len); + mfctr(tmp1); + rldimi_(result, tmp1, 3, 0); // Remaining characters. + beq(CCR0, Ldone); + b(Lslow); + + bind(Lfailure2); + mfctr(result); // Remaining characters. + + bind(Ldone); + subf(result, result, len); +} + // Inflate byte[] to char[] by inflating 16 bytes at once. void C2_MacroAssembler::string_inflate_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -32,10 +32,16 @@ // Compress char[] to byte[] by compressing 16 bytes at once. void string_compress_16(Register src, Register dst, Register cnt, Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, - Label& Lfailure); + Label& Lfailure, bool ascii = false); // Compress char[] to byte[]. cnt must be positive int. - void string_compress(Register src, Register dst, Register cnt, Register tmp, Label& Lfailure); + void string_compress(Register src, Register dst, Register cnt, Register tmp, + Label& Lfailure, bool ascii = false); + + // Encode UTF16 to ISO_8859_1 or ASCII. Return len on success or position of first mismatch. + void encode_iso_array(Register src, Register dst, Register len, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register result, bool ascii); // Inflate byte[] to char[] by inflating 16 bytes at once. void string_inflate_16(Register src, Register dst, Register cnt, diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/frame_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/frame_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/frame_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/frame_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2021 SAP SE. All rights reserved. + * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -294,9 +294,57 @@ } bool frame::is_interpreted_frame_valid(JavaThread* thread) const { - // Is there anything to do? assert(is_interpreted_frame(), "Not an interpreted frame"); - return true; + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + return false; + } + int min_frame_slots = (abi_minframe_size + ijava_state_size) / sizeof(intptr_t); + if (fp() - min_frame_slots < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + + // do some validation of frame elements + + // first the method + + Method* m = *interpreter_frame_method_addr(); + + // validate the method we'd find in this potential sender + if (!Method::is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + // this test requires the use of unextended_sp which is the sp as seen by + // the current frame, and not sp which is the "raw" pc which could point + // further because of local variables of the callee method inserted after + // method arguments + if (fp() - unextended_sp() > 1024 + m->max_stack()*Interpreter::stackElementSize) { + return false; + } + + // validate bci/bcx + + address bcp = interpreter_frame_bcp(); + if (m->validate_bci_from_bcp(bcp) < 0) { + return false; + } + + // validate constantPoolCache* + ConstantPoolCache* cp = *interpreter_frame_cache_addr(); + if (MetaspaceObj::is_valid(cp) == false) return false; + + // validate locals + + address locals = (address) *interpreter_frame_locals_addr(); + return thread->is_in_stack_range_incl(locals, (address)fp()); } BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/matcher_ppc.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/matcher_ppc.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/matcher_ppc.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/matcher_ppc.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -157,6 +157,6 @@ } // Implements a variant of EncodeISOArrayNode that encode ASCII only - static const bool supports_encode_ascii_array = false; + static const bool supports_encode_ascii_array = true; #endif // CPU_PPC_MATCHER_PPC_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/ppc.ad openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/ppc.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/ppc/ppc.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/ppc/ppc.ad 2022-04-19 19:55:43.000000000 +0000 @@ -12798,30 +12798,26 @@ effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); ins_cost(300); - format %{ "Encode array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} + format %{ "Encode iso array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} ins_encode %{ - Label Lslow, Lfailure1, Lfailure2, Ldone; - __ string_compress_16($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, - $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, Lfailure1); - __ rldicl_($result$$Register, $len$$Register, 0, 64-3); // Remaining characters. - __ beq(CCR0, Ldone); - __ bind(Lslow); - __ string_compress($src$$Register, $dst$$Register, $result$$Register, $tmp2$$Register, Lfailure2); - __ li($result$$Register, 0); - __ b(Ldone); - - __ bind(Lfailure1); - __ mr($result$$Register, $len$$Register); - __ mfctr($tmp1$$Register); - __ rldimi_($result$$Register, $tmp1$$Register, 3, 0); // Remaining characters. - __ beq(CCR0, Ldone); - __ b(Lslow); - - __ bind(Lfailure2); - __ mfctr($result$$Register); // Remaining characters. + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, $tmp2$$Register, + $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, $result$$Register, false); + %} + ins_pipe(pipe_class_default); +%} - __ bind(Ldone); - __ subf($result$$Register, $result$$Register, $len$$Register); +// encode char[] to byte[] in ASCII +instruct encode_ascii_array(rarg1RegP src, rarg2RegP dst, iRegIsrc len, iRegIdst result, iRegLdst tmp1, + iRegLdst tmp2, iRegLdst tmp3, iRegLdst tmp4, iRegLdst tmp5, regCTR ctr, flagsRegCR0 cr0) %{ + predicate(((EncodeISOArrayNode*)n)->is_ascii()); + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP_DEF result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, + USE_KILL src, USE_KILL dst, KILL ctr, KILL cr0); + ins_cost(300); + format %{ "Encode ascii array $src,$dst,$len -> $result \t// KILL $tmp1, $tmp2, $tmp3, $tmp4, $tmp5" %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, $tmp1$$Register, $tmp2$$Register, + $tmp3$$Register, $tmp4$$Register, $tmp5$$Register, $result$$Register, true); %} ins_pipe(pipe_class_default); %} diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/s390/c1_MacroAssembler_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -74,8 +74,8 @@ push_frame(frame_size_in_bytes); } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint) z_illtrap(0xC1); +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry) z_illtrap(0xC1); } void C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr, Label& slow_case) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/s390/frame_s390.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/s390/frame_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/s390/frame_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/s390/frame_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2019 SAP SE. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 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 @@ -117,8 +117,8 @@ return false; } - z_abi_160* sender_abi = (z_abi_160*) fp; - intptr_t* sender_sp = (intptr_t*) sender_abi->callers_sp; + z_abi_16* sender_abi = (z_abi_16*)fp; + intptr_t* sender_sp = (intptr_t*) fp; address sender_pc = (address) sender_abi->return_pc; // We must always be able to find a recognizable pc. @@ -142,7 +142,7 @@ // sender_fp must be within the stack and above (but not // equal) current frame's fp. if (!thread->is_in_stack_range_excl(sender_fp, fp)) { - return false; + return false; } // If the potential sender is the interpreter then we can do some more checking. @@ -298,9 +298,55 @@ } bool frame::is_interpreted_frame_valid(JavaThread* thread) const { - // Is there anything to do? assert(is_interpreted_frame(), "Not an interpreted frame"); - return true; + // These are reasonable sanity checks + if (fp() == 0 || (intptr_t(fp()) & (wordSize-1)) != 0) { + return false; + } + if (sp() == 0 || (intptr_t(sp()) & (wordSize-1)) != 0) { + return false; + } + int min_frame_slots = (z_abi_16_size + z_ijava_state_size) / sizeof(intptr_t); + if (fp() - min_frame_slots < sp()) { + return false; + } + // These are hacks to keep us out of trouble. + // The problem with these is that they mask other problems + if (fp() <= sp()) { // this attempts to deal with unsigned comparison above + return false; + } + + // do some validation of frame elements + + // first the method + // Need to use "unchecked" versions to avoid "z_istate_magic_number" assertion. + Method* m = (Method*)(ijava_state_unchecked()->method); + + // validate the method we'd find in this potential sender + if (!Method::is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + // this test requires the use of unextended_sp which is the sp as seen by + // the current frame, and not sp which is the "raw" pc which could point + // further because of local variables of the callee method inserted after + // method arguments + if (fp() - unextended_sp() > 1024 + m->max_stack()*Interpreter::stackElementSize) { + return false; + } + + // validate bci/bcx + address bcp = (address)(ijava_state_unchecked()->bcp); + if (m->validate_bci_from_bcp(bcp) < 0) { + return false; + } + + // validate constantPoolCache* + ConstantPoolCache* cp = (ConstantPoolCache*)(ijava_state_unchecked()->cpoolCache); + if (MetaspaceObj::is_valid(cp) == false) return false; + + // validate locals + address locals = (address)(ijava_state_unchecked()->locals); + return thread->is_in_stack_range_incl(locals, (address)fp()); } BasicType frame::interpreter_frame_result(oop* oop_result, jvalue* value_result) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/assembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/assembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/assembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/assembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -187,7 +187,7 @@ // Address. An index of 4 (rsp) corresponds to having no index, so convert // that to noreg for the Address constructor. Address Address::make_raw(int base, int index, int scale, int disp, relocInfo::relocType disp_reloc) { - RelocationHolder rspec; + RelocationHolder rspec = RelocationHolder::none; if (disp_reloc != relocInfo::none) { rspec = Relocation::spec_simple(disp_reloc); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c1_MacroAssembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -354,18 +354,18 @@ } -void C1_MacroAssembler::verified_entry() { - if (C1Breakpoint || VerifyFPU) { +void C1_MacroAssembler::verified_entry(bool breakAtEntry) { + if (breakAtEntry || VerifyFPU) { // Verified Entry first instruction should be 5 bytes long for correct // patching by patch_verified_entry(). // - // C1Breakpoint and VerifyFPU have one byte first instruction. + // Breakpoint and VerifyFPU have one byte first instruction. // Also first instruction will be one byte "push(rbp)" if stack banging // code is not generated (see build_frame() above). // For all these cases generate long instruction first. fat_nop(); } - if (C1Breakpoint)int3(); + if (breakAtEntry) int3(); // build frame IA32_ONLY( verify_FPU(0, "method_entry"); ) } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -602,8 +602,13 @@ // Unconditionally set box->_displaced_header = markWord::unused_mark(). // Without cast to int32_t this style of movptr will destroy r10 which is typically obj. movptr(Address(boxReg, 0), (int32_t)intptr_t(markWord::unused_mark().value())); - // Intentional fall-through into DONE_LABEL ... // Propagate ICC.ZF from CAS above into DONE_LABEL. + jcc(Assembler::equal, DONE_LABEL); // CAS above succeeded; propagate ZF = 1 (success) + + cmpptr(r15_thread, rax); // Check if we are already the owner (recursive lock) + jcc(Assembler::notEqual, DONE_LABEL); // If not recursive, ZF = 0 at this point (fail) + incq(Address(scrReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); + xorq(rax, rax); // Set ZF = 1 (success) for recursive lock, denoting locking success #endif // _LP64 #if INCLUDE_RTM_OPT } // use_rtm() @@ -705,10 +710,6 @@ // Refer to the comments in synchronizer.cpp for how we might encode extra // state in _succ so we can avoid fetching EntryList|cxq. // - // I'd like to add more cases in fast_lock() and fast_unlock() -- - // such as recursive enter and exit -- but we have to be wary of - // I$ bloat, T$ effects and BP$ effects. - // // If there's no contention try a 1-0 exit. That is, exit without // a costly MEMBAR or CAS. See synchronizer.cpp for details on how // we detect and recover from the race that the 1-0 exit admits. @@ -756,9 +757,16 @@ bind (CheckSucc); #else // _LP64 // It's inflated - xorptr(boxReg, boxReg); - orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); - jccb (Assembler::notZero, DONE_LABEL); + Label LNotRecursive, LSuccess, LGoSlowPath; + + cmpptr(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0); + jccb(Assembler::equal, LNotRecursive); + + // Recursive inflated unlock + decq(Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions))); + jmpb(LSuccess); + + bind(LNotRecursive); movptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq))); orptr(boxReg, Address(tmpReg, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList))); jccb (Assembler::notZero, CheckSucc); @@ -767,7 +775,6 @@ jmpb (DONE_LABEL); // Try to avoid passing control into the slow_path ... - Label LSuccess, LGoSlowPath ; bind (CheckSucc); // The following optional optimization can be elided if necessary diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/jvmciCodeInstaller_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -155,14 +155,15 @@ method = JVMCIENV->asMethod(hotspot_method); } #endif + NativeCall* call = NULL; switch (_next_call_type) { case INLINE_INVOKE: - break; + return; case INVOKEVIRTUAL: case INVOKEINTERFACE: { assert(method == NULL || !method->is_static(), "cannot call static method with invokeinterface"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_virtual_call_stub()); _instructions->relocate(call->instruction_address(), virtual_call_Relocation::spec(_invoke_mark_pc), @@ -172,7 +173,7 @@ case INVOKESTATIC: { assert(method == NULL || method->is_static(), "cannot call non-static method with invokestatic"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_static_call_stub()); _instructions->relocate(call->instruction_address(), relocInfo::static_call_type, Assembler::call32_operand); @@ -180,15 +181,18 @@ } case INVOKESPECIAL: { assert(method == NULL || !method->is_static(), "cannot call static method with invokespecial"); - NativeCall* call = nativeCall_at(_instructions->start() + pc_offset); + call = nativeCall_at(_instructions->start() + pc_offset); call->set_destination(SharedRuntime::get_resolve_opt_virtual_call_stub()); _instructions->relocate(call->instruction_address(), relocInfo::opt_virtual_call_type, Assembler::call32_operand); break; } default: - JVMCI_ERROR("invalid _next_call_type value"); - break; + JVMCI_ERROR("invalid _next_call_type value: %d", _next_call_type); + return; + } + if (!call->is_displacement_aligned()) { + JVMCI_ERROR("unaligned displacement for call at offset %d", pc_offset); } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -260,6 +260,9 @@ } +bool NativeCall::is_displacement_aligned() { + return (uintptr_t) displacement_address() % 4 == 0; +} // Similar to replace_mt_safe, but just changes the destination. The // important thing is that free-running threads are able to execute this @@ -282,8 +285,7 @@ CompiledICLocker::is_safe(instruction_address()), "concurrent code patching"); // Both C1 and C2 should now be generating code which aligns the patched address // to be within a single cache line. - bool is_aligned = ((uintptr_t)displacement_address() + 0) / cache_line_size == - ((uintptr_t)displacement_address() + 3) / cache_line_size; + bool is_aligned = is_displacement_aligned(); guarantee(is_aligned, "destination must be aligned"); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/nativeInst_x86.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/nativeInst_x86.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -160,8 +160,6 @@ return_address_offset = 5 }; - enum { cache_line_size = BytesPerWord }; // conservative estimate! - address instruction_address() const { return addr_at(instruction_offset); } address next_instruction_address() const { return addr_at(return_address_offset); } int displacement() const { return (jint) int_at(displacement_offset); } @@ -175,9 +173,11 @@ #endif // AMD64 set_int_at(displacement_offset, dest - return_address()); } + // Returns whether the 4-byte displacement operand is 4-byte aligned. + bool is_displacement_aligned(); void set_destination_mt_safe(address dest); - void verify_alignment() { assert((intptr_t)addr_at(displacement_offset) % BytesPerInt == 0, "must be aligned"); } + void verify_alignment() { assert(is_displacement_aligned(), "displacement of call is not aligned"); } void verify(); void print(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/vmreg_x86.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/x86/vmreg_x86.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/vmreg_x86.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/vmreg_x86.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -90,7 +90,13 @@ #ifndef AMD64 if (is_Register()) return true; #endif // AMD64 - return is_even(value()); + // Do not use is_XMMRegister() here as it depends on the UseAVX setting. + if (value() >= ConcreteRegisterImpl::max_fpr && value() < ConcreteRegisterImpl::max_xmm) { + int base = value() - ConcreteRegisterImpl::max_fpr; + return base % XMMRegisterImpl::max_slots_per_register == 0; + } else { + return is_even(value()); // General, float, and K registers are all two slots wide + } } #endif // CPU_X86_VMREG_X86_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86.ad openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86.ad 2022-04-19 19:55:43.000000000 +0000 @@ -1621,6 +1621,21 @@ return false; } break; + case Op_SqrtF: + if (UseSSE < 1) { + return false; + } + break; + case Op_SqrtD: +#ifdef _LP64 + if (UseSSE < 2) { + return false; + } +#else + // x86_32.ad has a special match rule for SqrtD. + // Together with common x86 rules, this handles all UseSSE cases. +#endif + break; } return true; // Match rules are supported by default. } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86_32.ad openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86_32.ad --- openjdk-17-17.0.2+8/src/hotspot/cpu/x86/x86_32.ad 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/x86/x86_32.ad 2022-04-19 19:55:43.000000000 +0000 @@ -755,6 +755,7 @@ assert(UseSSE < 2, "shouldn't be used in SSE2+ mode"); return rc_float; } + if (r->is_KRegister()) return rc_kreg; assert(r->is_XMMRegister(), "must be"); return rc_xmm; } @@ -1249,26 +1250,6 @@ return size; } - assert( size > 0, "missed a case" ); - - // -------------------------------------------------------------------- - // Check for second bits still needing moving. - if( src_second == dst_second ) - return size; // Self copy; no move - assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); - - // Check for second word int-int move - if( src_second_rc == rc_int && dst_second_rc == rc_int ) - return impl_mov_helper(cbuf,do_size,src_second,dst_second,size, st); - - // Check for second word integer store - if( src_second_rc == rc_int && dst_second_rc == rc_stack ) - return impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),src_second,0x89,"MOV ",size, st); - - // Check for second word integer load - if( dst_second_rc == rc_int && src_second_rc == rc_stack ) - return impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),dst_second,0x8B,"MOV ",size, st); - // AVX-512 opmask specific spilling. if (src_first_rc == rc_stack && dst_first_rc == rc_kreg) { assert((src_first & 1) == 0 && src_first + 1 == src_second, "invalid register pair"); @@ -1306,6 +1287,26 @@ return 0; } + assert( size > 0, "missed a case" ); + + // -------------------------------------------------------------------- + // Check for second bits still needing moving. + if( src_second == dst_second ) + return size; // Self copy; no move + assert( src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad" ); + + // Check for second word int-int move + if( src_second_rc == rc_int && dst_second_rc == rc_int ) + return impl_mov_helper(cbuf,do_size,src_second,dst_second,size, st); + + // Check for second word integer store + if( src_second_rc == rc_int && dst_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,false,ra_->reg2offset(dst_second),src_second,0x89,"MOV ",size, st); + + // Check for second word integer load + if( dst_second_rc == rc_int && src_second_rc == rc_stack ) + return impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_second),dst_second,0x8B,"MOV ",size, st); + Unimplemented(); return 0; // Mute compiler } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/globals_zero.hpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/globals_zero.hpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/globals_zero.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/globals_zero.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -70,8 +70,7 @@ define_pd_global(bool, PreserveFramePointer, false); -// No performance work done here yet. -define_pd_global(bool, CompactStrings, false); +define_pd_global(bool, CompactStrings, true); #define ARCH_FLAGS(develop, \ product, \ diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/vm_version_zero.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/vm_version_zero.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/vm_version_zero.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/vm_version_zero.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -52,4 +52,8 @@ // Not implemented UNSUPPORTED_OPTION(CriticalJNINatives); + UNSUPPORTED_OPTION(UseCompiler); +#ifdef ASSERT + UNSUPPORTED_OPTION(CountCompiledCalls); +#endif } diff -Nru openjdk-17-17.0.2+8/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp openjdk-17-17.0.3+7/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp --- openjdk-17-17.0.2+8/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/cpu/zero/zeroInterpreter_zero.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -68,14 +68,6 @@ ZeroInterpreterGenerator g(_code); if (PrintInterpreter) print(); } - - // 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::run(&start_msg); - } else { - BytecodeInterpreter::run(&start_msg); - } } void ZeroInterpreter::invoke_method(Method* method, address entry_point, TRAPS) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/bsd/decoder_machO.cpp openjdk-17-17.0.3+7/src/hotspot/os/bsd/decoder_machO.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/bsd/decoder_machO.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/bsd/decoder_machO.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -52,6 +52,12 @@ bool MachODecoder::decode(address addr, char *buf, int buflen, int *offset, const void *mach_base) { + if (addr == (address)(intptr_t)-1) { + // dladdr() in macOS12/Monterey returns success for -1, but that addr value + // won't work in this function. Should have been handled by the caller. + ShouldNotReachHere(); + return false; + } struct symtab_command * symt = (struct symtab_command *) mach_find_command((struct mach_header_64 *)mach_base, LC_SYMTAB); if (symt == NULL) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/bsd/os_bsd.cpp openjdk-17-17.0.3+7/src/hotspot/os/bsd/os_bsd.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/bsd/os_bsd.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/bsd/os_bsd.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -903,6 +903,17 @@ const char* os::dll_file_extension() { return JNI_LIB_SUFFIX; } +static int local_dladdr(const void* addr, Dl_info* info) { +#ifdef __APPLE__ + if (addr == (void*)-1) { + // dladdr() in macOS12/Monterey returns success for -1, but that addr + // value should not be allowed to work to avoid confusion. + return 0; + } +#endif + return dladdr(addr, info); +} + // This must be hard coded because it's the system's temporary // directory not the java application's temp directory, ala java.io.tmpdir. #ifdef __APPLE__ @@ -942,9 +953,6 @@ return false; } - -#define MACH_MAXSYMLEN 256 - bool os::dll_address_to_function_name(address addr, char *buf, int buflen, int *offset, bool demangle) { @@ -952,9 +960,8 @@ assert(buf != NULL, "sanity check"); Dl_info dlinfo; - char localbuf[MACH_MAXSYMLEN]; - if (dladdr((void*)addr, &dlinfo) != 0) { + if (local_dladdr((void*)addr, &dlinfo) != 0) { // see if we have a matching symbol if (dlinfo.dli_saddr != NULL && dlinfo.dli_sname != NULL) { if (!(demangle && Decoder::demangle(dlinfo.dli_sname, buf, buflen))) { @@ -963,6 +970,14 @@ if (offset != NULL) *offset = addr - (address)dlinfo.dli_saddr; return true; } + +#ifndef __APPLE__ + // The 6-parameter Decoder::decode() function is not implemented on macOS. + // The Mach-O binary format does not contain a "list of files" with address + // ranges like ELF. That makes sense since Mach-O can contain binaries for + // than one instruction set so there can be more than one address range for + // each "file". + // no matching symbol so try for just file info if (dlinfo.dli_fname != NULL && dlinfo.dli_fbase != NULL) { if (Decoder::decode((address)(addr - (address)dlinfo.dli_fbase), @@ -971,6 +986,10 @@ } } +#else // __APPLE__ + #define MACH_MAXSYMLEN 256 + + char localbuf[MACH_MAXSYMLEN]; // Handle non-dynamic manually: if (dlinfo.dli_fbase != NULL && Decoder::decode(addr, localbuf, MACH_MAXSYMLEN, offset, @@ -980,6 +999,9 @@ } return true; } + + #undef MACH_MAXSYMLEN +#endif // __APPLE__ } buf[0] = '\0'; if (offset != NULL) *offset = -1; @@ -994,7 +1016,7 @@ Dl_info dlinfo; - if (dladdr((void*)addr, &dlinfo) != 0) { + if (local_dladdr((void*)addr, &dlinfo) != 0) { if (dlinfo.dli_fname != NULL) { jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); } @@ -1392,13 +1414,17 @@ strncpy(machine, "", sizeof(machine)); } - const char* emulated = ""; #if defined(__APPLE__) && !defined(ZERO) if (VM_Version::is_cpu_emulated()) { - emulated = " (EMULATED)"; + snprintf(buf, buflen, "\"%s\" %s (EMULATED) %d MHz", model, machine, mhz); + } else { + NOT_AARCH64(snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz)); + // aarch64 CPU doesn't report its speed + AARCH64_ONLY(snprintf(buf, buflen, "\"%s\" %s", model, machine)); } +#else + snprintf(buf, buflen, "\"%s\" %s %d MHz", model, machine, mhz); #endif - snprintf(buf, buflen, "\"%s\" %s%s %d MHz", model, machine, emulated, mhz); } void os::print_memory_info(outputStream* st) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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,11 +34,15 @@ #include "runtime/os.hpp" #include "utilities/globalDefinitions.hpp" +// controller names have to match the *_IDX indices +static const char* cg_controller_name[] = { "cpu", "cpuset", "cpuacct", "memory", "pids" }; + CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV1MemoryController* memory = NULL; CgroupV1Controller* cpuset = NULL; CgroupV1Controller* cpu = NULL; CgroupV1Controller* cpuacct = NULL; + CgroupV1Controller* pids = NULL; CgroupInfo cg_infos[CG_INFO_LENGTH]; u1 cg_type_flags = INVALID_CGROUPS_GENERIC; const char* proc_cgroups = "/proc/cgroups"; @@ -93,22 +97,29 @@ assert(is_cgroup_v1(&cg_type_flags), "Cgroup v1 expected"); for (int i = 0; i < CG_INFO_LENGTH; i++) { CgroupInfo info = cg_infos[i]; - if (strcmp(info._name, "memory") == 0) { - memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path); - memory->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpuset") == 0) { - cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpuset->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpu") == 0) { - cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpu->set_subsystem_path(info._cgroup_path); - } else if (strcmp(info._name, "cpuacct") == 0) { - cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); - cpuacct->set_subsystem_path(info._cgroup_path); + if (info._data_complete) { // pids controller might have incomplete data + if (strcmp(info._name, "memory") == 0) { + memory = new CgroupV1MemoryController(info._root_mount_path, info._mount_path); + memory->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpuset") == 0) { + cpuset = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpuset->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpu") == 0) { + cpu = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpu->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "cpuacct") == 0) { + cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path); + cpuacct->set_subsystem_path(info._cgroup_path); + } else if (strcmp(info._name, "pids") == 0) { + pids = new CgroupV1Controller(info._root_mount_path, info._mount_path); + pids->set_subsystem_path(info._cgroup_path); + } + } else { + log_debug(os, container)("CgroupInfo for %s not complete", cg_controller_name[i]); } } cleanup(cg_infos); - return new CgroupV1Subsystem(cpuset, cpu, cpuacct, memory); + return new CgroupV1Subsystem(cpuset, cpu, cpuacct, pids, memory); } bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, @@ -122,9 +133,10 @@ char buf[MAXPATHLEN+1]; char *p; bool is_cgroupsV2; - // true iff all controllers, memory, cpu, cpuset, cpuacct are enabled + // true iff all required controllers, memory, cpu, cpuset, cpuacct are enabled // at the kernel level. - bool all_controllers_enabled; + // pids might not be enabled on older Linux distros (SLES 12.1, RHEL 7.1) + bool all_required_controllers_enabled; /* * Read /proc/cgroups so as to be able to distinguish cgroups v2 vs cgroups v1. @@ -136,10 +148,9 @@ */ cgroups = fopen(proc_cgroups, "r"); if (cgroups == NULL) { - log_debug(os, container)("Can't open %s, %s", - proc_cgroups, os::strerror(errno)); - *flags = INVALID_CGROUPS_GENERIC; - return false; + log_debug(os, container)("Can't open %s, %s", proc_cgroups, os::strerror(errno)); + *flags = INVALID_CGROUPS_GENERIC; + return false; } while ((p = fgets(buf, MAXPATHLEN, cgroups)) != NULL) { @@ -167,19 +178,30 @@ cg_infos[CPUACCT_IDX]._name = os::strdup(name); cg_infos[CPUACCT_IDX]._hierarchy_id = hierarchy_id; cg_infos[CPUACCT_IDX]._enabled = (enabled == 1); + } else if (strcmp(name, "pids") == 0) { + log_debug(os, container)("Detected optional pids controller entry in %s", proc_cgroups); + cg_infos[PIDS_IDX]._name = os::strdup(name); + cg_infos[PIDS_IDX]._hierarchy_id = hierarchy_id; + cg_infos[PIDS_IDX]._enabled = (enabled == 1); } } fclose(cgroups); is_cgroupsV2 = true; - all_controllers_enabled = true; + all_required_controllers_enabled = true; for (int i = 0; i < CG_INFO_LENGTH; i++) { - is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; - all_controllers_enabled = all_controllers_enabled && cg_infos[i]._enabled; + // pids controller is optional. All other controllers are required + if (i != PIDS_IDX) { + is_cgroupsV2 = is_cgroupsV2 && cg_infos[i]._hierarchy_id == 0; + all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; + } + if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { + log_debug(os, container)("controller %s is not enabled\n", cg_controller_name[i]); + } } - if (!all_controllers_enabled) { - // one or more controllers disabled, disable container support + if (!all_required_controllers_enabled) { + // one or more required controllers disabled, disable container support log_debug(os, container)("One or more required controllers disabled at kernel level."); cleanup(cg_infos); *flags = INVALID_CGROUPS_GENERIC; @@ -220,17 +242,21 @@ while (!is_cgroupsV2 && (token = strsep(&controllers, ",")) != NULL) { if (strcmp(token, "memory") == 0) { - assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[MEMORY_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for memory"); cg_infos[MEMORY_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpuset") == 0) { - assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPUSET_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuset"); cg_infos[CPUSET_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpu") == 0) { - assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPU_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpu"); cg_infos[CPU_IDX]._cgroup_path = os::strdup(cgroup_path); } else if (strcmp(token, "cpuacct") == 0) { - assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch"); + assert(hierarchy_id == cg_infos[CPUACCT_IDX]._hierarchy_id, "/proc/cgroups and /proc/self/cgroup hierarchy mismatch for cpuacc"); cg_infos[CPUACCT_IDX]._cgroup_path = os::strdup(cgroup_path); + } else if (strcmp(token, "pids") == 0) { + assert(hierarchy_id == cg_infos[PIDS_IDX]._hierarchy_id, "/proc/cgroups (%d) and /proc/self/cgroup (%d) hierarchy mismatch for pids", + cg_infos[PIDS_IDX]._hierarchy_id, hierarchy_id); + cg_infos[PIDS_IDX]._cgroup_path = os::strdup(cgroup_path); } } if (is_cgroupsV2) { @@ -281,13 +307,15 @@ /* Cgroup v1 relevant info * - * Find the cgroup mount point for memory, cpuset, cpu, cpuacct + * Find the cgroup mount point for memory, cpuset, cpu, cpuacct, pids * * Example for docker: * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory * * Example for host: * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory + * + * 44 31 0:39 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,pids */ if (sscanf(p, "%*d %*d %*d:%*d %s %s %*[^-]- %s %*s %s", tmproot, tmpmount, tmp_fs_type, tmpcgroups) == 4) { if (strcmp("cgroup", tmp_fs_type) != 0) { @@ -333,6 +361,12 @@ cg_infos[CPUACCT_IDX]._mount_path = os::strdup(tmpmount); cg_infos[CPUACCT_IDX]._root_mount_path = os::strdup(tmproot); cg_infos[CPUACCT_IDX]._data_complete = true; + } else if (strcmp(token, "pids") == 0) { + any_cgroup_mounts_found = true; + assert(cg_infos[PIDS_IDX]._mount_path == NULL, "stomping of _mount_path"); + cg_infos[PIDS_IDX]._mount_path = os::strdup(tmpmount); + cg_infos[PIDS_IDX]._root_mount_path = os::strdup(tmproot); + cg_infos[PIDS_IDX]._data_complete = true; } } } @@ -387,10 +421,13 @@ *flags = INVALID_CGROUPS_V1; return false; } + if (log_is_enabled(Debug, os, container) && !cg_infos[PIDS_IDX]._data_complete) { + log_debug(os, container)("Optional cgroup v1 pids subsystem not found"); + // keep the other controller info, pids is optional + } // Cgroups v1 case, we have all the info we need. *flags = CGROUPS_V1; return true; - }; void CgroupSubsystemFactory::cleanup(CgroupInfo* cg_infos) { @@ -514,3 +551,22 @@ memory_limit->set_value(mem_limit, OSCONTAINER_CACHE_TIMEOUT); return mem_limit; } + +jlong CgroupSubsystem::limit_from_str(char* limit_str) { + if (limit_str == NULL) { + return OSCONTAINER_ERROR; + } + // Unlimited memory in cgroups is the literal string 'max' for + // some controllers, for example the pids controller. + if (strcmp("max", limit_str) == 0) { + os::free(limit_str); + return (jlong)-1; + } + julong limit; + if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { + os::free(limit_str); + return OSCONTAINER_ERROR; + } + os::free(limit_str); + return (jlong)limit; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupSubsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupSubsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -61,12 +61,13 @@ #define INVALID_CGROUPS_NO_MOUNT 5 #define INVALID_CGROUPS_GENERIC 6 -// Four controllers: cpu, cpuset, cpuacct, memory -#define CG_INFO_LENGTH 4 +// Five controllers: cpu, cpuset, cpuacct, memory, pids +#define CG_INFO_LENGTH 5 #define CPUSET_IDX 0 #define CPU_IDX 1 #define CPUACCT_IDX 2 #define MEMORY_IDX 3 +#define PIDS_IDX 4 typedef char * cptr; @@ -156,8 +157,10 @@ NULL, \ scan_fmt, \ &variable); \ - if (err != 0) \ + if (err != 0) { \ + log_trace(os, container)(logstring, (return_type) OSCONTAINER_ERROR); \ return (return_type) OSCONTAINER_ERROR; \ + } \ \ log_trace(os, container)(logstring, variable); \ } @@ -238,10 +241,13 @@ public: jlong memory_limit_in_bytes(); int active_processor_count(); + jlong limit_from_str(char* limit_str); virtual int cpu_quota() = 0; virtual int cpu_period() = 0; virtual int cpu_shares() = 0; + virtual jlong pids_max() = 0; + virtual jlong pids_current() = 0; virtual jlong memory_usage_in_bytes() = 0; virtual jlong memory_and_swap_limit_in_bytes() = 0; virtual jlong memory_soft_limit_in_bytes() = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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,3 +241,43 @@ return shares; } + + +char* CgroupV1Subsystem::pids_max_val() { + GET_CONTAINER_INFO_CPTR(cptr, _pids, "/pids.max", + "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); + if (pidsmax == NULL) { + return NULL; + } + return os::strdup(pidsmax); +} + +/* pids_max + * + * Return the maximum number of tasks available to the process + * + * return: + * maximum number of tasks + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV1Subsystem::pids_max() { + if (_pids == NULL) return OSCONTAINER_ERROR; + char * pidsmax_str = pids_max_val(); + return limit_from_str(pidsmax_str); +} + +/* pids_current + * + * The number of tasks currently in the cgroup (and its descendants) of the process + * + * return: + * current number of tasks + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV1Subsystem::pids_current() { + if (_pids == NULL) return OSCONTAINER_ERROR; + GET_CONTAINER_INFO(jlong, _pids, "/pids.current", + "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + return pids_current; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -87,6 +87,9 @@ int cpu_shares(); + jlong pids_max(); + jlong pids_current(); + const char * container_type() { return "cgroupv1"; } @@ -101,15 +104,20 @@ CgroupV1Controller* _cpuset = NULL; CachingCgroupController* _cpu = NULL; CgroupV1Controller* _cpuacct = NULL; + CgroupV1Controller* _pids = NULL; + + char * pids_max_val(); public: CgroupV1Subsystem(CgroupV1Controller* cpuset, CgroupV1Controller* cpu, CgroupV1Controller* cpuacct, + CgroupV1Controller* pids, CgroupV1MemoryController* memory) { _cpuset = cpuset; _cpu = new CachingCgroupController(cpu); _cpuacct = cpuacct; + _pids = pids; _memory = new CachingCgroupController(memory); _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2022, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ */ int CgroupV2Subsystem::cpu_shares() { GET_CONTAINER_INFO(int, _unified, "/cpu.weight", - "Raw value for CPU shares is: %d", "%d", shares); + "Raw value for CPU Shares is: %d", "%d", shares); // Convert default value of 100 to no shares setup if (shares == 100) { log_debug(os, container)("CPU Shares is: %d", -1); @@ -203,24 +203,6 @@ return limit; } -jlong CgroupV2Subsystem::limit_from_str(char* limit_str) { - if (limit_str == NULL) { - return OSCONTAINER_ERROR; - } - // Unlimited memory in Cgroups V2 is the literal string 'max' - if (strcmp("max", limit_str) == 0) { - os::free(limit_str); - return (jlong)-1; - } - julong limit; - if (sscanf(limit_str, JULONG_FORMAT, &limit) != 1) { - os::free(limit_str); - return OSCONTAINER_ERROR; - } - os::free(limit_str); - return (jlong)limit; -} - char* CgroupV2Subsystem::mem_limit_val() { GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.max", "Raw value for memory limit is: %s", "%s", mem_limit_str, 1024); @@ -244,3 +226,39 @@ return os::strdup(buf); } +char* CgroupV2Subsystem::pids_max_val() { + GET_CONTAINER_INFO_CPTR(cptr, _unified, "/pids.max", + "Maximum number of tasks is: %s", "%s %*d", pidsmax, 1024); + if (pidsmax == NULL) { + return NULL; + } + return os::strdup(pidsmax); +} + +/* pids_max + * + * Return the maximum number of tasks available to the process + * + * return: + * maximum number of tasks + * -1 for unlimited + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV2Subsystem::pids_max() { + char * pidsmax_str = pids_max_val(); + return limit_from_str(pidsmax_str); +} + +/* pids_current + * + * The number of tasks currently in the cgroup (and its descendants) of the process + * + * return: + * current number of tasks + * OSCONTAINER_ERROR for not supported + */ +jlong CgroupV2Subsystem::pids_current() { + GET_CONTAINER_INFO(jlong, _unified, "/pids.current", + "Current number of tasks is: " JLONG_FORMAT, JLONG_FORMAT, pids_current); + return pids_current; +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -60,7 +60,7 @@ char *mem_swp_limit_val(); char *mem_soft_limit_val(); char *cpu_quota_val(); - jlong limit_from_str(char* limit_str); + char *pids_max_val(); public: CgroupV2Subsystem(CgroupController * unified) { @@ -79,6 +79,9 @@ jlong memory_max_usage_in_bytes(); char * cpu_cpuset_cpus(); char * cpu_cpuset_memory_nodes(); + jlong pids_max(); + jlong pids_current(); + const char * container_type() { return "cgroupv2"; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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 @@ -129,3 +129,13 @@ assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); return cgroup_subsystem->cpu_shares(); } + +jlong OSContainer::pids_max() { + assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); + return cgroup_subsystem->pids_max(); +} + +jlong OSContainer::pids_current() { + assert(cgroup_subsystem != NULL, "cgroup subsystem not available"); + return cgroup_subsystem->pids_current(); +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.hpp openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.hpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/osContainer_linux.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/osContainer_linux.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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 @@ -62,6 +62,8 @@ static int cpu_shares(); + static jlong pids_max(); + static jlong pids_current(); }; inline bool OSContainer::is_containerized() { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/linux/os_linux.cpp openjdk-17-17.0.3+7/src/hotspot/os/linux/os_linux.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/linux/os_linux.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/linux/os_linux.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2233,6 +2233,7 @@ bool os::Linux::print_container_info(outputStream* st) { if (!OSContainer::is_containerized()) { + st->print_cr("container information not found."); return false; } @@ -2321,6 +2322,24 @@ st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited"); } + j = OSContainer::OSContainer::pids_max(); + st->print("maximum number of tasks: "); + if (j > 0) { + st->print_cr(JLONG_FORMAT, j); + } else { + st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "unlimited"); + } + + j = OSContainer::OSContainer::pids_current(); + st->print("current number of tasks: "); + if (j > 0) { + st->print_cr(JLONG_FORMAT, j); + } else { + if (j == OSCONTAINER_ERROR) { + st->print_cr("not supported"); + } + } + return true; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/posix/signals_posix.cpp openjdk-17-17.0.3+7/src/hotspot/os/posix/signals_posix.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/posix/signals_posix.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/posix/signals_posix.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -562,6 +562,11 @@ { assert(info != NULL && ucVoid != NULL, "sanity"); + if (sig == BREAK_SIGNAL) { + assert(!ReduceSignalUsage, "Should not happen with -Xrs/-XX:+ReduceSignalUsage"); + return true; // ignore it + } + // 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, @@ -1197,7 +1202,7 @@ return -1; } -void set_signal_handler(int sig) { +void set_signal_handler(int sig, bool do_check = true) { // Check for overwrite. struct sigaction oldAct; sigaction(sig, (struct sigaction*)NULL, &oldAct); @@ -1241,7 +1246,7 @@ // Save handler setup for later checking vm_handlers.set(sig, &sigAct); - do_check_signal_periodically[sig] = true; + do_check_signal_periodically[sig] = do_check; int ret = sigaction(sig, &sigAct, &oldAct); assert(ret == 0, "check"); @@ -1279,7 +1284,12 @@ set_signal_handler(SIGFPE); PPC64_ONLY(set_signal_handler(SIGTRAP);) set_signal_handler(SIGXFSZ); - + if (!ReduceSignalUsage) { + // This is just for early initialization phase. Intercepting the signal here reduces the risk + // that an attach client accidentally forces HotSpot to quit prematurely. We skip the periodic + // check because late initialization will overwrite it to UserHandler. + set_signal_handler(BREAK_SIGNAL, false); + } #if defined(__APPLE__) // lldb (gdb) installs both standard BSD signal handlers, and mach exception // handlers. By replacing the existing task exception handler, we disable lldb's mach diff -Nru openjdk-17-17.0.2+8/src/hotspot/os/windows/os_windows.cpp openjdk-17-17.0.3+7/src/hotspot/os/windows/os_windows.cpp --- openjdk-17-17.0.2+8/src/hotspot/os/windows/os_windows.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os/windows/os_windows.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -681,6 +681,9 @@ return false; } + // Initial state is ALLOCATED but not INITIALIZED + osthread->set_state(ALLOCATED); + // Initialize the JDK library's interrupt event. // This should really be done when OSThread is constructed, // but there is no way for a constructor to report failure to @@ -777,7 +780,7 @@ osthread->set_thread_handle(thread_handle); osthread->set_thread_id(thread_id); - // Initial thread state is INITIALIZED, not SUSPENDED + // Thread state now is INITIALIZED, not SUSPENDED osthread->set_state(INITIALIZED); // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_aarch64/os_linux_aarch64.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -382,7 +382,20 @@ extern "C" { int SpinPause() { - return 0; + using spin_wait_func_ptr_t = void (*)(); + spin_wait_func_ptr_t func = CAST_TO_FN_PTR(spin_wait_func_ptr_t, StubRoutines::aarch64::spin_wait()); + assert(func != nullptr, "StubRoutines::aarch64::spin_wait must not be null."); + (*func)(); + // If StubRoutines::aarch64::spin_wait consists of only a RET, + // SpinPause can be considered as implemented. There will be a sequence + // of instructions for: + // - call of SpinPause + // - load of StubRoutines::aarch64::spin_wait stub pointer + // - indirect call of the stub + // - return from the stub + // - return from SpinPause + // So '1' always is returned. + return 1; } void _Copy_conjoint_jshorts_atomic(const jshort* from, jshort* to, size_t count) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_ppc/thread_linux_ppc.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, 2019 SAP SE. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022 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 @@ -58,14 +58,15 @@ // if we were running Java code when SIGPROF came in. if (isInJava) { ucontext_t* uc = (ucontext_t*) ucontext; - frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], - (address)uc->uc_mcontext.regs->nip); + address pc = (address)uc->uc_mcontext.regs->nip; - if (ret_frame.pc() == NULL) { + if (pc == NULL) { // ucontext wasn't useful return false; } + frame ret_frame((intptr_t*)uc->uc_mcontext.regs->gpr[1/*REG_SP*/], pc); + if (ret_frame.fp() == NULL) { // The found frame does not have a valid frame pointer. // Bail out because this will create big trouble later on, either diff -Nru openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp --- openjdk-17-17.0.2+8/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/os_cpu/linux_s390/thread_linux_s390.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2019 SAP SE. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022 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 @@ -58,14 +58,15 @@ // if we were running Java code when SIGPROF came in. if (isInJava) { ucontext_t* uc = (ucontext_t*) ucontext; - frame ret_frame((intptr_t*)uc->uc_mcontext.gregs[15/*Z_SP*/], - (address)uc->uc_mcontext.psw.addr); + address pc = (address)uc->uc_mcontext.psw.addr; - if (ret_frame.pc() == NULL) { + if (pc == NULL) { // ucontext wasn't useful return false; } + frame ret_frame((intptr_t*)uc->uc_mcontext.gregs[15/*Z_SP*/], pc); + if (ret_frame.fp() == NULL) { // The found frame does not have a valid frame pointer. // Bail out because this will create big trouble later on, either @@ -100,7 +101,7 @@ if (ret_frame.is_interpreted_frame()) { frame::z_ijava_state* istate = ret_frame.ijava_state_unchecked(); - if (is_in_full_stack((address)istate)) { + if (!is_in_full_stack((address)istate)) { return false; } const Method *m = (const Method*)(istate->method); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -191,7 +191,8 @@ , _oop_map(NULL) , _stack(stack) , _is_method_handle_invoke(false) - , _deoptimize_on_exception(deoptimize_on_exception) { + , _deoptimize_on_exception(deoptimize_on_exception) + , _force_reexecute(false) { assert(_stack != NULL, "must be non null"); } @@ -203,7 +204,8 @@ , _oop_map(NULL) , _stack(stack == NULL ? info->_stack : stack) , _is_method_handle_invoke(info->_is_method_handle_invoke) - , _deoptimize_on_exception(info->_deoptimize_on_exception) { + , _deoptimize_on_exception(info->_deoptimize_on_exception) + , _force_reexecute(info->_force_reexecute) { // deep copy of exception handlers if (info->_exception_handlers != NULL) { @@ -215,7 +217,8 @@ void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { // record the safepoint before recording the debug info for enclosing scopes recorder->add_safepoint(pc_offset, _oop_map->deep_copy()); - _scope_debug_info->record_debug_info(recorder, pc_offset, true/*topmost*/, _is_method_handle_invoke); + bool reexecute = _force_reexecute || _scope_debug_info->should_reexecute(); + _scope_debug_info->record_debug_info(recorder, pc_offset, reexecute, _is_method_handle_invoke); recorder->end_safepoint(pc_offset); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.hpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_IR.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_IR.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -232,16 +232,15 @@ //Whether we should reexecute this bytecode for deopt bool should_reexecute(); - void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool topmost, bool is_method_handle_invoke = false) { + void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool reexecute, bool is_method_handle_invoke = false) { if (caller() != NULL) { // Order is significant: Must record caller first. - caller()->record_debug_info(recorder, pc_offset, false/*topmost*/); + caller()->record_debug_info(recorder, pc_offset, false/*reexecute*/); } DebugToken* locvals = recorder->create_scope_values(locals()); DebugToken* expvals = recorder->create_scope_values(expressions()); DebugToken* monvals = recorder->create_monitor_values(monitors()); // reexecute allowed only for the topmost frame - bool reexecute = topmost ? should_reexecute() : false; bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis. bool rethrow_exception = false; bool is_opt_native = false; @@ -264,6 +263,7 @@ ValueStack* _stack; // used by deoptimization (contains also monitors bool _is_method_handle_invoke; // true if the associated call site is a MethodHandle call site. bool _deoptimize_on_exception; + bool _force_reexecute; // force the reexecute flag on, used for patching stub FrameMap* frame_map() const { return scope()->compilation()->frame_map(); } Compilation* compilation() const { return scope()->compilation(); } @@ -290,7 +290,11 @@ bool is_method_handle_invoke() const { return _is_method_handle_invoke; } void set_is_method_handle_invoke(bool x) { _is_method_handle_invoke = x; } + bool force_reexecute() const { return _force_reexecute; } + void set_force_reexecute() { _force_reexecute = true; } + int interpreter_frame_size() const; + }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_Instruction.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_Instruction.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_Instruction.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_Instruction.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -837,6 +837,11 @@ existing_state->invalidate_local(index); TRACE_PHI(tty->print_cr("invalidating local %d because of type mismatch", index)); } + + if (existing_value != new_state->local_at(index) && existing_value->as_Phi() == NULL) { + TRACE_PHI(tty->print_cr("required phi for local %d is missing, irreducible loop?", index)); + return false; // BAILOUT in caller + } } #ifdef ASSERT diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRAssembler.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRAssembler.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRAssembler.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRAssembler.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -43,6 +43,7 @@ while ((intx) _masm->pc() - (intx) patch->pc_start() < NativeGeneralJump::instruction_size) { _masm->nop(); } + info->set_force_reexecute(); patch->install(_masm, patch_code, obj, info); append_code_stub(patch); @@ -607,7 +608,7 @@ check_icache(); } offsets()->set_value(CodeOffsets::Verified_Entry, _masm->offset()); - _masm->verified_entry(); + _masm->verified_entry(compilation()->directive()->BreakAtExecuteOption); if (needs_clinit_barrier_on_entry(compilation()->method())) { clinit_barrier(compilation()->method()); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRGenerator.cpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRGenerator.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_LIRGenerator.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_LIRGenerator.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -979,6 +979,14 @@ Phi* phi = sux_val->as_Phi(); // cur_val can be null without phi being null in conjunction with inlining if (phi != NULL && cur_val != NULL && cur_val != phi && !phi->is_illegal()) { + if (phi->is_local()) { + for (int i = 0; i < phi->operand_count(); i++) { + Value op = phi->operand_at(i); + if (op != NULL && op->type()->is_illegal()) { + bailout("illegal phi operand"); + } + } + } Phi* cur_phi = cur_val->as_Phi(); if (cur_phi != NULL && cur_phi->is_illegal()) { // Phi and local would need to get invalidated diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_MacroAssembler.hpp openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_MacroAssembler.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/c1/c1_MacroAssembler.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/c1/c1_MacroAssembler.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2021, 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,7 @@ void build_frame(int frame_size_in_bytes, int bang_size_in_bytes); void remove_frame(int frame_size_in_bytes); - void verified_entry(); + void verified_entry(bool breakAtEntry); void verify_stack_oop(int offset) PRODUCT_RETURN; void verify_not_null_oop(Register r) PRODUCT_RETURN; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/cds/dynamicArchive.cpp openjdk-17-17.0.3+7/src/hotspot/share/cds/dynamicArchive.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/cds/dynamicArchive.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/cds/dynamicArchive.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -111,6 +111,7 @@ // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); init_header(); gather_source_objs(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/cds/metaspaceShared.cpp openjdk-17-17.0.3+7/src/hotspot/share/cds/metaspaceShared.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/cds/metaspaceShared.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/cds/metaspaceShared.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -487,6 +487,7 @@ // Block concurrent class unloading from changing the _dumptime_table MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); SystemDictionaryShared::check_excluded_classes(); + SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary(); StaticArchiveBuilder builder; builder.gather_source_objs(); @@ -550,20 +551,19 @@ class CollectCLDClosure : public CLDClosure { GrowableArray _loaded_cld; - GrowableArray _loaded_cld_handles; // keep the CLDs alive + GrowableArray _loaded_cld_handles; // keep the CLDs alive Thread* _current_thread; public: CollectCLDClosure(Thread* thread) : _current_thread(thread) {} ~CollectCLDClosure() { - for (int i = 0; i < _loaded_cld.length(); i++) { - ClassLoaderData* cld = _loaded_cld.at(i); + for (int i = 0; i < _loaded_cld_handles.length(); i++) { + _loaded_cld_handles.at(i).release(Universe::vm_global()); } } void do_cld(ClassLoaderData* cld) { - if (!cld->is_unloading()) { - _loaded_cld.append(cld); - _loaded_cld_handles.append(Handle(_current_thread, cld->holder_phantom())); - } + assert(cld->is_alive(), "must be"); + _loaded_cld.append(cld); + _loaded_cld_handles.append(OopHandle(Universe::vm_global(), cld->holder_phantom())); } int nof_cld() const { return _loaded_cld.length(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,18 +26,23 @@ * halfsiphash code adapted from reference implementation * (https://github.com/veorq/SipHash/blob/master/halfsiphash.c) * which is distributed with the following copyright: - * - * SipHash reference C implementation - * - * Copyright (c) 2016 Jean-Philippe Aumasson - * - * To the extent possible under law, the author(s) have dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication along - * with this software. If not, see - * . + */ + +/* + SipHash reference C implementation + + Copyright (c) 2012-2021 Jean-Philippe Aumasson + + Copyright (c) 2012-2014 Daniel J. Bernstein + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along + with + this software. If not, see + . */ #include "precompiled.hpp" @@ -135,7 +140,9 @@ } // HalfSipHash-2-4 (32-bit output) for Symbols -uint32_t AltHashing::halfsiphash_32(uint64_t seed, const uint8_t* data, int len) { +uint32_t AltHashing::halfsiphash_32(uint64_t seed, const void* in, int len) { + + const unsigned char* data = (const unsigned char*)in; uint32_t v[4]; uint32_t newdata; int off = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.hpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/altHashing.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/altHashing.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, 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 @@ -43,7 +43,7 @@ static uint64_t compute_seed(); // For Symbols - static uint32_t halfsiphash_32(uint64_t seed, const uint8_t* data, int len); + static uint32_t halfsiphash_32(uint64_t seed, const void* in, int len); // For Strings static uint32_t halfsiphash_32(uint64_t seed, const uint16_t* data, int len); }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/classLoaderData.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/classLoaderData.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/classLoaderData.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/classLoaderData.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -55,6 +55,7 @@ #include "classfile/packageEntry.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmClasses.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" @@ -886,6 +887,10 @@ // Remove the class so unloading events aren't triggered for // this class (scratch or error class) in do_unloading(). remove_class(ik); + // But still have to remove it from the dumptime_table. + if (Arguments::is_dumping_archive()) { + SystemDictionaryShared::remove_dumptime_info(ik); + } } } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/symbolTable.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/symbolTable.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/symbolTable.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/symbolTable.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -91,7 +91,14 @@ static volatile bool _alt_hash = false; + +#ifdef USE_LIBRARY_BASED_TLS_ONLY static volatile bool _lookup_shared_first = false; +#else +// "_lookup_shared_first" can get highly contended with many cores if multiple threads +// are updating "lookup success history" in a global shared variable. If built-in TLS is available, use it. +static THREAD_LOCAL bool _lookup_shared_first = false; +#endif // Static arena for symbols that are not deallocated Arena* SymbolTable::_arena = NULL; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2405,6 +2405,28 @@ return false; } +class CleanupDumpTimeLambdaProxyClassTable: StackObj { + public: + bool do_entry(LambdaProxyClassKey& key, DumpTimeLambdaProxyClassInfo& info) { + assert_lock_strong(DumpTimeTable_lock); + for (int i = 0; i < info._proxy_klasses->length(); i++) { + InstanceKlass* ik = info._proxy_klasses->at(i); + if (!ik->can_be_verified_at_dumptime()) { + info._proxy_klasses->remove_at(i); + } + } + return info._proxy_klasses->length() == 0 ? true /* delete the node*/ : false; + } +}; + +void SystemDictionaryShared::cleanup_lambda_proxy_class_dictionary() { + assert_lock_strong(DumpTimeTable_lock); + if (_dumptime_lambda_proxy_class_dictionary != NULL) { + CleanupDumpTimeLambdaProxyClassTable cleanup_proxy_classes; + _dumptime_lambda_proxy_class_dictionary->unlink(&cleanup_proxy_classes); + } +} + #if INCLUDE_CDS_JAVA_HEAP class ArchivedMirrorPatcher { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.hpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/systemDictionaryShared.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/systemDictionaryShared.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -331,6 +331,7 @@ static size_t estimate_size_for_archive(); static void write_to_archive(bool is_static_archive = true); static void adjust_lambda_proxy_class_dictionary(); + static void cleanup_lambda_proxy_class_dictionary(); static void serialize_dictionary_headers(class SerializeClosure* soc, bool is_static_archive = true); static void serialize_vm_classes(class SerializeClosure* soc); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/classfile/verifier.cpp openjdk-17-17.0.3+7/src/hotspot/share/classfile/verifier.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/classfile/verifier.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/classfile/verifier.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1805,7 +1805,7 @@ no_control_flow = true; break; default: // We only need to check the valid bytecodes in class file. - // And jsr and ret are not in the new class file format in JDK1.5. + // And jsr and ret are not in the new class file format in JDK1.6. verify_error(ErrorContext::bad_code(bci), "Bad instruction: %02x", opcode); no_control_flow = false; @@ -2424,7 +2424,8 @@ verify_error(ErrorContext::bad_type(bci, current_frame->stack_top_ctx(), TypeOrigin::implicit(current_type())), - "Bad access to protected data in getfield"); + "Bad access to protected data in %s", + is_getfield ? "getfield" : "putfield"); return; } } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/code/codeCache.cpp openjdk-17-17.0.3+7/src/hotspot/share/code/codeCache.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/code/codeCache.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/code/codeCache.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -678,7 +678,7 @@ void CodeCache::metadata_do(MetadataClosure* f) { assert_locked_or_safepoint(CodeCache_lock); - NMethodIterator iter(NMethodIterator::only_alive_and_not_unloading); + NMethodIterator iter(NMethodIterator::only_alive); while(iter.next()) { iter.method()->metadata_do(f); } @@ -1032,7 +1032,7 @@ } #if INCLUDE_JVMTI -// RedefineClasses support for unloading nmethods that are dependent on "old" methods. +// RedefineClasses support for saving nmethods that are dependent on "old" methods. // We don't really expect this table to grow very large. If it does, it can become a hashtable. static GrowableArray* old_compiled_method_table = NULL; @@ -1091,7 +1091,7 @@ reset_old_method_table(); int number_of_marked_CodeBlobs = 0; - CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); while(iter.next()) { CompiledMethod* nm = iter.method(); // Walk all alive nmethods to check for old Methods. @@ -1111,7 +1111,7 @@ void CodeCache::mark_all_nmethods_for_evol_deoptimization() { assert(SafepointSynchronize::is_at_safepoint(), "Can only do this at a safepoint!"); - CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading); + CompiledMethodIterator iter(CompiledMethodIterator::only_alive); while(iter.next()) { CompiledMethod* nm = iter.method(); if (!nm->method()->is_method_handle_intrinsic()) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/compiler/compileBroker.cpp openjdk-17-17.0.3+7/src/hotspot/share/compiler/compileBroker.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/compiler/compileBroker.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/compiler/compileBroker.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -410,6 +410,7 @@ CompileTask::free(current); } _first = NULL; + _last = NULL; // Wake up all threads that block on the queue. MethodCompileQueue_lock->notify_all(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/compiler/compilerDirectives.cpp openjdk-17-17.0.3+7/src/hotspot/share/compiler/compilerDirectives.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/compiler/compilerDirectives.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/compiler/compilerDirectives.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -334,9 +334,21 @@ if (!CompilerDirectivesIgnoreCompileCommandsOption && CompilerOracle::has_any_command_set()) { DirectiveSetPtr set(this); +#ifdef COMPILER1 + if (C1Breakpoint) { + // If the directives didn't have 'BreakAtExecute', + // the command 'C1Breakpoint' would become effective. + if (!_modified[BreakAtExecuteIndex]) { + set.cloned()->BreakAtExecuteOption = true; + } + } +#endif + // All CompileCommands are not equal so this gets a bit verbose // When CompileCommands have been refactored less clutter will remain. if (CompilerOracle::should_break_at(method)) { + // If the directives didn't have 'BreakAtCompile' or 'BreakAtExecute', + // the sub-command 'Break' of the 'CompileCommand' would become effective. if (!_modified[BreakAtCompileIndex]) { set.cloned()->BreakAtCompileOption = true; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/g1/g1RemSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/g1/g1RemSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/g1/g1RemSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/g1/g1RemSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -107,7 +107,7 @@ // within a region to claim. Dependent on the region size as proxy for the heap // size, we limit the total number of chunks to limit memory usage and maintenance // effort of that table vs. granularity of distributing scanning work. - // Testing showed that 8 for 1M/2M region, 16 for 4M/8M regions, 32 for 16/32M regions + // Testing showed that 64 for 1M/2M region, 128 for 4M/8M regions, 256 for 16/32M regions // seems to be such a good trade-off. static uint get_chunks_per_region(uint log_region_size) { // Limit the expected input values to current known possible values of the @@ -115,7 +115,7 @@ // values for region size. assert(log_region_size >= 20 && log_region_size <= 25, "expected value in [20,25], but got %u", log_region_size); - return 1u << (log_region_size / 2 - 7); + return 1u << (log_region_size / 2 - 4); } uint _scan_chunks_per_region; // Number of chunks per region. diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -29,10 +29,10 @@ #include "runtime/semaphore.hpp" #include "runtime/thread.inline.hpp" -uint SuspendibleThreadSet::_nthreads = 0; -uint SuspendibleThreadSet::_nthreads_stopped = 0; -bool SuspendibleThreadSet::_suspend_all = false; -double SuspendibleThreadSet::_suspend_all_start = 0.0; +uint SuspendibleThreadSet::_nthreads = 0; +uint SuspendibleThreadSet::_nthreads_stopped = 0; +volatile bool SuspendibleThreadSet::_suspend_all = false; +double SuspendibleThreadSet::_suspend_all_start = 0.0; static Semaphore* _synchronize_wakeup = NULL; @@ -50,7 +50,7 @@ void SuspendibleThreadSet::join() { assert(!Thread::current()->is_suspendible_thread(), "Thread already joined"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - while (_suspend_all) { + while (suspend_all()) { ml.wait(); } _nthreads++; @@ -63,7 +63,7 @@ assert(_nthreads > 0, "Invalid"); DEBUG_ONLY(Thread::current()->clear_suspendible_thread();) _nthreads--; - if (_suspend_all && is_synchronized()) { + if (suspend_all() && is_synchronized()) { // This leave completes a request, so inform the requestor. _synchronize_wakeup->signal(); } @@ -72,7 +72,7 @@ void SuspendibleThreadSet::yield() { assert(Thread::current()->is_suspendible_thread(), "Must have joined"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - if (_suspend_all) { + if (suspend_all()) { _nthreads_stopped++; if (is_synchronized()) { if (ConcGCYieldTimeout > 0) { @@ -82,7 +82,7 @@ // This yield completes the request, so inform the requestor. _synchronize_wakeup->signal(); } - while (_suspend_all) { + while (suspend_all()) { ml.wait(); } assert(_nthreads_stopped > 0, "Invalid"); @@ -97,8 +97,8 @@ } { MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(!_suspend_all, "Only one at a time"); - _suspend_all = true; + assert(!suspend_all(), "Only one at a time"); + Atomic::store(&_suspend_all, true); if (is_synchronized()) { return; } @@ -120,7 +120,7 @@ #ifdef ASSERT MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(_suspend_all, "STS not synchronizing"); + assert(suspend_all(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); #endif } @@ -128,8 +128,8 @@ void SuspendibleThreadSet::desynchronize() { assert(Thread::current()->is_VM_thread(), "Must be the VM thread"); MonitorLocker ml(STS_lock, Mutex::_no_safepoint_check_flag); - assert(_suspend_all, "STS not synchronizing"); + assert(suspend_all(), "STS not synchronizing"); assert(is_synchronized(), "STS not synchronized"); - _suspend_all = false; + Atomic::store(&_suspend_all, false); ml.notify_all(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shared/suspendibleThreadSet.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -26,6 +26,7 @@ #define SHARE_GC_SHARED_SUSPENDIBLETHREADSET_HPP #include "memory/allocation.hpp" +#include "runtime/atomic.hpp" // A SuspendibleThreadSet is a set of threads that can be suspended. // A thread can join and later leave the set, and periodically yield. @@ -40,9 +41,10 @@ friend class SuspendibleThreadSetLeaver; private: + static volatile bool _suspend_all; + static uint _nthreads; static uint _nthreads_stopped; - static bool _suspend_all; static double _suspend_all_start; static bool is_synchronized(); @@ -53,9 +55,11 @@ // Removes the current thread from the set. static void leave(); + static bool suspend_all() { return Atomic::load(&_suspend_all); } + public: // Returns true if an suspension is in progress. - static bool should_yield() { return _suspend_all; } + static bool should_yield() { return suspend_all(); } // Suspends the current thread if a suspension is in progress. static void yield(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahClosures.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -227,7 +227,7 @@ void ShenandoahCodeBlobAndDisarmClosure::do_code_blob(CodeBlob* cb) { nmethod* const nm = cb->as_nmethod_or_null(); - if (nm != NULL && nm->oops_do_try_claim()) { + if (nm != NULL) { assert(!ShenandoahNMethod::gc_data(nm)->is_unregistered(), "Should not be here"); CodeBlobToOopClosure::do_code_blob(cb); _bs->disarm(nm); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahMark.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -261,7 +261,9 @@ if ((STRING_DEDUP == ENQUEUE_DEDUP) && ShenandoahStringDedup::is_candidate(obj)) { assert(ShenandoahStringDedup::is_enabled(), "Must be enabled"); req->add(obj); - } else if ((STRING_DEDUP == ALWAYS_DEDUP) && ShenandoahStringDedup::is_string_candidate(obj)) { + } else if ((STRING_DEDUP == ALWAYS_DEDUP) && + ShenandoahStringDedup::is_string_candidate(obj) && + !ShenandoahStringDedup::dedup_requested(obj)) { assert(ShenandoahStringDedup::is_enabled(), "Must be enabled"); req->add(obj); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -79,7 +79,6 @@ } ShenandoahCodeCacheRoots::ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase) : _phase(phase) { - nmethod::oops_do_marking_prologue(); } void ShenandoahCodeCacheRoots::code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id) { @@ -87,10 +86,6 @@ _coderoots_iterator.possibly_parallel_blobs_do(blob_cl); } -ShenandoahCodeCacheRoots::~ShenandoahCodeCacheRoots() { - nmethod::oops_do_marking_epilogue(); -} - ShenandoahRootProcessor::ShenandoahRootProcessor(ShenandoahPhaseTimings::Phase phase) : _heap(ShenandoahHeap::heap()), _phase(phase), @@ -258,22 +253,44 @@ _code_roots(ShenandoahPhaseTimings::heap_iteration_roots) { } - void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) { - assert(Thread::current()->is_VM_thread(), "Only by VM thread"); - // Must use _claim_none to avoid interfering with concurrent CLDG iteration - CLDToOopClosure clds(oops, ClassLoaderData::_claim_none); - MarkingCodeBlobClosure code(oops, !CodeBlobToOopClosure::FixRelocations); - ShenandoahParallelOopsDoThreadClosure tc_cl(oops, &code, NULL); - AlwaysTrueClosure always_true; - - ResourceMark rm; - - // Process light-weight/limited parallel roots then - _vm_roots.oops_do(oops, 0); - _weak_roots.oops_do(oops, 0); - _cld_roots.cld_do(&clds, 0); - - // Process heavy-weight/fully parallel roots the last - _code_roots.code_blobs_do(&code, 0); - _thread_roots.threads_do(&tc_cl, 0); - } +class ShenandoahMarkCodeBlobClosure : public CodeBlobClosure { +private: + OopClosure* const _oops; + BarrierSetNMethod* const _bs_nm; + +public: + ShenandoahMarkCodeBlobClosure(OopClosure* oops) : + _oops(oops), + _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {} + + virtual void do_code_blob(CodeBlob* cb) { + nmethod* const nm = cb->as_nmethod_or_null(); + if (nm != nullptr) { + if (_bs_nm != nullptr) { + // Make sure it only sees to-space objects + _bs_nm->nmethod_entry_barrier(nm); + } + ShenandoahNMethod* const snm = ShenandoahNMethod::gc_data(nm); + assert(snm != nullptr, "Sanity"); + snm->oops_do(_oops, false /*fix_relocations*/); + } + } +}; + +void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) { + // Must use _claim_none to avoid interfering with concurrent CLDG iteration + CLDToOopClosure clds(oops, ClassLoaderData::_claim_none); + ShenandoahMarkCodeBlobClosure code(oops); + ShenandoahParallelOopsDoThreadClosure tc_cl(oops, &code, NULL); + + ResourceMark rm; + + // Process light-weight/limited parallel roots then + _vm_roots.oops_do(oops, 0); + _weak_roots.oops_do(oops, 0); + _cld_roots.cld_do(&clds, 0); + + // Process heavy-weight/fully parallel roots the last + _code_roots.code_blobs_do(&code, 0); + _thread_roots.threads_do(&tc_cl, 0); +} diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -102,7 +102,6 @@ ShenandoahCodeRootsIterator _coderoots_iterator; public: ShenandoahCodeCacheRoots(ShenandoahPhaseTimings::Phase phase); - ~ShenandoahCodeCacheRoots(); void code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id); }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -31,6 +31,7 @@ public: static inline bool is_string_candidate(oop obj); static inline bool is_candidate(oop obj); + static inline bool dedup_requested(oop obj); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHSTRINGDEDUP_HPP diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/gc/shenandoah/shenandoahStringDedup.inline.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -36,6 +36,10 @@ java_lang_String::value(obj) != nullptr; } +bool ShenandoahStringDedup::dedup_requested(oop obj) { + return java_lang_String::test_and_set_deduplication_requested(obj); +} + bool ShenandoahStringDedup::is_candidate(oop obj) { if (!is_string_candidate(obj)) { return false; @@ -51,7 +55,8 @@ // Increase string age and enqueue it when it rearches age threshold markWord new_mark = mark.incr_age(); if (mark == obj->cas_set_mark(new_mark, mark)) { - return StringDedup::is_threshold_age(new_mark.age()); + return StringDedup::is_threshold_age(new_mark.age()) && + !dedup_requested(obj); } } return false; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodeUtils.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodeUtils.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodeUtils.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodeUtils.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1029,7 +1029,6 @@ break; case Bytecodes::_arraylength: - // The return type of arraylength is wrong in the bytecodes table (T_VOID). stack->pop(1); stack->push(bci, T_INT); break; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodes.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodes.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/bytecodes.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/bytecodes.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, 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 @@ -471,7 +471,7 @@ def(_new , "new" , "bkk" , NULL , T_OBJECT , 1, true ); def(_newarray , "newarray" , "bc" , NULL , T_OBJECT , 0, true ); def(_anewarray , "anewarray" , "bkk" , NULL , T_OBJECT , 0, true ); - def(_arraylength , "arraylength" , "b" , NULL , T_VOID , 0, true ); + def(_arraylength , "arraylength" , "b" , NULL , T_INT , 0, true ); def(_athrow , "athrow" , "b" , NULL , T_VOID , -1, true ); def(_checkcast , "checkcast" , "bkk" , NULL , T_OBJECT , 0, true ); def(_instanceof , "instanceof" , "bkk" , NULL , T_INT , 0, true ); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp openjdk-17-17.0.3+7/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/interpreter/zero/bytecodeInterpreter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -403,25 +403,25 @@ 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 - // and set simpler to to test variables rather than method calls or complex - // boolean expressions. - - static int initialized = 0; - static int checkit = 0; - static intptr_t* c_addr = NULL; - static intptr_t c_value; - - if (checkit && *c_addr != c_value) { - os::breakpoint(); - } + intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ + address pc = istate->bcp(); + jubyte opcode; + intptr_t* locals = istate->locals(); + ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache() +#ifdef LOTS_OF_REGS + JavaThread* THREAD = istate->thread(); +#else +#undef THREAD +#define THREAD istate->thread() +#endif #ifdef ASSERT - if (istate->_msg != initialize) { - assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); - } + assert(labs(istate->stack_base() - istate->stack_limit()) == (istate->method()->max_stack() + 1), + "Bad stack limit"); + /* QQQ this should be a stack method so we don't know actual direction */ + assert(topOfStack >= istate->stack_limit() && topOfStack < istate->stack_base(), + "Stack top out of range"); + // Verify linkages. interpreterState l = istate; do { @@ -433,18 +433,6 @@ interpreterState orig = istate; #endif - intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ - address pc = istate->bcp(); - jubyte opcode; - intptr_t* locals = istate->locals(); - ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache() -#ifdef LOTS_OF_REGS - JavaThread* THREAD = istate->thread(); -#else -#undef THREAD -#define THREAD istate->thread() -#endif - #ifdef USELABELS const static void* const opclabels_data[256] = { /* 0x00 */ &&opc_nop, &&opc_aconst_null, &&opc_iconst_m1, &&opc_iconst_0, @@ -531,38 +519,13 @@ uintptr_t *dispatch_table = (uintptr_t*)&opclabels_data[0]; #endif /* USELABELS */ -#ifdef ASSERT - // this will trigger a VERIFY_OOP on entry - if (istate->msg() != initialize && ! METHOD->is_static()) { - oop rcvr = LOCALS_OBJECT(0); - VERIFY_OOP(rcvr); - } -#endif - - /* QQQ this should be a stack method so we don't know actual direction */ - guarantee(istate->msg() == initialize || - topOfStack >= istate->stack_limit() && - topOfStack < istate->stack_base(), - "Stack top out of range"); - - assert(!UseCompiler, "Zero does not support compilers"); - assert(!CountCompiledCalls, "Zero does not support counting compiled calls"); - switch (istate->msg()) { case initialize: { - if (initialized++) ShouldNotReachHere(); // Only one initialize call. + ShouldNotCallThis(); return; } - break; case method_entry: { THREAD->set_do_not_unlock(); - // count invocations - assert(initialized, "Interpreter not initialized"); - - if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { - // initialize - os::breakpoint(); - } // Lock method if synchronized. if (METHOD->is_synchronized()) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -243,6 +243,7 @@ int write__klass__leakp(JfrCheckpointWriter* writer, const void* k) { assert(k != NULL, "invariant"); KlassPtr klass = (KlassPtr)k; + CLEAR_LEAKP(klass); return write_klass(writer, klass, true); } @@ -848,7 +849,7 @@ private: MethodCallback _method_cb; KlassCallback _klass_cb; - MethodUsedPredicate _method_used_predicate; + MethodUsedPredicate _method_used_predicate; MethodFlagPredicate _method_flag_predicate; public: MethodIteratorHost(JfrCheckpointWriter* writer, diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -146,16 +146,12 @@ } }; -template class MethodUsedPredicate { bool _current_epoch; public: MethodUsedPredicate(bool current_epoch) : _current_epoch(current_epoch) {} bool operator()(const Klass* klass) { - if (_current_epoch) { - return leakp ? IS_LEAKP(klass) : METHOD_USED_THIS_EPOCH(klass); - } - return leakp ? IS_LEAKP(klass) : METHOD_USED_PREVIOUS_EPOCH(klass); + return _current_epoch ? METHOD_USED_THIS_EPOCH(klass) : METHOD_USED_PREVIOUS_EPOCH(klass); } }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jfr/recorder/repository/jfrChunkWriter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -207,7 +207,7 @@ const u4 checkpoint_size = current_offset() - event_size_offset; write_padded_at_offset(checkpoint_size, event_size_offset); set_last_checkpoint_offset(event_size_offset); - const size_t sz_written = size_written(); + const int64_t sz_written = size_written(); write_be_at_offset(sz_written, chunk_size_offset); return sz_written; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp openjdk-17-17.0.3+7/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1832,7 +1832,7 @@ return JVMCIENV->get_jobjectArray(methods); C2V_END -C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jboolean is_volatile, jobject kind_object)) +C2V_VMENTRY_NULL(jobject, readFieldValue, (JNIEnv* env, jobject, jobject object, jobject expected_type, long displacement, jobject kind_object)) if (object == NULL || kind_object == NULL) { JVMCI_THROW_0(NullPointerException); } @@ -1872,13 +1872,18 @@ ShouldNotReachHere(); } - if (displacement < 0 || ((long) displacement + type2aelembytes(basic_type) > HeapWordSize * obj->size())) { + int basic_type_elemsize = type2aelembytes(basic_type); + if (displacement < 0 || ((long) displacement + basic_type_elemsize > HeapWordSize * obj->size())) { // Reading outside of the object bounds JVMCI_THROW_MSG_NULL(IllegalArgumentException, "reading outside object bounds"); } // Perform basic sanity checks on the read. Primitive reads are permitted to read outside the // bounds of their fields but object reads must map exactly onto the underlying oop slot. + bool aligned = (displacement % basic_type_elemsize) == 0; + if (!aligned) { + JVMCI_THROW_MSG_NULL(IllegalArgumentException, "read is unaligned"); + } if (basic_type == T_OBJECT) { if (obj->is_objArray()) { if (displacement < arrayOopDesc::base_offset_in_bytes(T_OBJECT)) { @@ -1916,15 +1921,20 @@ } jlong value = 0; + + // Treat all reads as volatile for simplicity as this function can be used + // both for reading Java fields declared as volatile as well as for constant + // folding Unsafe.get* methods with volatile semantics. + switch (basic_type) { - case T_BOOLEAN: value = is_volatile ? obj->bool_field_acquire(displacement) : obj->bool_field(displacement); break; - case T_BYTE: value = is_volatile ? obj->byte_field_acquire(displacement) : obj->byte_field(displacement); break; - case T_SHORT: value = is_volatile ? obj->short_field_acquire(displacement) : obj->short_field(displacement); break; - case T_CHAR: value = is_volatile ? obj->char_field_acquire(displacement) : obj->char_field(displacement); break; + case T_BOOLEAN: value = obj->bool_field_acquire(displacement); break; + case T_BYTE: value = obj->byte_field_acquire(displacement); break; + case T_SHORT: value = obj->short_field_acquire(displacement); break; + case T_CHAR: value = obj->char_field_acquire(displacement); break; case T_FLOAT: - case T_INT: value = is_volatile ? obj->int_field_acquire(displacement) : obj->int_field(displacement); break; + case T_INT: value = obj->int_field_acquire(displacement); break; case T_DOUBLE: - case T_LONG: value = is_volatile ? obj->long_field_acquire(displacement) : obj->long_field(displacement); break; + case T_LONG: value = obj->long_field_acquire(displacement); break; case T_OBJECT: { if (displacement == java_lang_Class::component_mirror_offset() && java_lang_Class::is_instance(obj()) && @@ -1934,7 +1944,8 @@ return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } - oop value = is_volatile ? obj->obj_field_acquire(displacement) : obj->obj_field(displacement); + oop value = obj->obj_field_acquire(displacement); + if (value == NULL) { return JVMCIENV->get_jobject(JVMCIENV->get_JavaConstant_NULL_POINTER()); } else { @@ -2680,8 +2691,8 @@ {CC "boxPrimitive", CC "(" OBJECT ")" OBJECTCONSTANT, FN_PTR(boxPrimitive)}, {CC "getDeclaredConstructors", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredConstructors)}, {CC "getDeclaredMethods", CC "(" HS_RESOLVED_KLASS ")[" RESOLVED_METHOD, FN_PTR(getDeclaredMethods)}, - {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, - {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JZLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, + {CC "readFieldValue", CC "(" OBJECTCONSTANT HS_RESOLVED_KLASS "JLjdk/vm/ci/meta/JavaKind;)" JAVACONSTANT, FN_PTR(readFieldValue)}, {CC "isInstance", CC "(" HS_RESOLVED_KLASS OBJECTCONSTANT ")Z", FN_PTR(isInstance)}, {CC "isAssignableFrom", CC "(" HS_RESOLVED_KLASS HS_RESOLVED_KLASS ")Z", FN_PTR(isAssignableFrom)}, {CC "isTrustedForIntrinsics", CC "(" HS_RESOLVED_KLASS ")Z", FN_PTR(isTrustedForIntrinsics)}, diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/jvmci/vmStructs_jvmci.cpp openjdk-17-17.0.3+7/src/hotspot/share/jvmci/vmStructs_jvmci.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/jvmci/vmStructs_jvmci.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/jvmci/vmStructs_jvmci.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -565,8 +565,8 @@ declare_constant(Deoptimization::Reason_not_compiled_exception_handler) \ declare_constant(Deoptimization::Reason_unresolved) \ declare_constant(Deoptimization::Reason_jsr_mismatch) \ - declare_constant(Deoptimization::Reason_LIMIT) \ - declare_constant(Deoptimization::_support_large_access_byte_array_virtualization) \ + declare_constant(Deoptimization::Reason_TRAP_HISTORY_LENGTH) \ + declare_constant(Deoptimization::_support_large_access_byte_array_virtualization) \ \ declare_constant(FieldInfo::access_flags_offset) \ declare_constant(FieldInfo::name_index_offset) \ diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/allocation.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/allocation.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/allocation.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/allocation.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -145,6 +145,7 @@ f(mtServiceability, "Serviceability") \ f(mtMetaspace, "Metaspace") \ f(mtStringDedup, "String Deduplication") \ + f(mtObjectMonitor, "Object Monitors") \ f(mtNone, "Unknown") \ //end diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -42,6 +42,7 @@ _by_spacetype("by-spacetype", "Break down numbers by loader type.", "BOOLEAN", false, "false"), _by_chunktype("by-chunktype", "Break down numbers by chunk type.", "BOOLEAN", false, "false"), _show_vslist("vslist", "Shows details about the underlying virtual space.", "BOOLEAN", false, "false"), + _show_chunkfreelist("chunkfreelist", "Shows details about global chunk free lists (ChunkManager).", "BOOLEAN", false, "false"), _scale("scale", "Memory usage in which to scale. Valid values are: 1, KB, MB or GB (fixed scale) " "or \"dynamic\" for a dynamically choosen scale.", "STRING", false, "dynamic"), @@ -53,6 +54,7 @@ _dcmdparser.add_dcmd_option(&_by_chunktype); _dcmdparser.add_dcmd_option(&_by_spacetype); _dcmdparser.add_dcmd_option(&_show_vslist); + _dcmdparser.add_dcmd_option(&_show_chunkfreelist); _dcmdparser.add_dcmd_option(&_scale); } @@ -96,6 +98,7 @@ if (_by_chunktype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownByChunkType; if (_by_spacetype.value()) flags |= (int)MetaspaceReporter::Option::BreakDownBySpaceType; if (_show_vslist.value()) flags |= (int)MetaspaceReporter::Option::ShowVSList; + if (_show_chunkfreelist.value()) flags |= (int)MetaspaceReporter::Option::ShowChunkFreeList; VM_PrintMetadata op(output(), scale, flags); VMThread::execute(&op); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceDCmd.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -38,6 +38,7 @@ DCmdArgument _by_spacetype; DCmdArgument _by_chunktype; DCmdArgument _show_vslist; + DCmdArgument _show_chunkfreelist; DCmdArgument _scale; DCmdArgument _show_classes; public: diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -300,6 +300,24 @@ out->cr(); } + // -- Print Chunkmanager details. + if ((flags & (int)Option::ShowChunkFreeList) > 0) { + out->cr(); + out->print_cr("Chunk freelist details:"); + if (Metaspace::using_class_space()) { + out->print_cr(" Non-Class:"); + } + ChunkManager::chunkmanager_nonclass()->print_on(out); + out->cr(); + if (Metaspace::using_class_space()) { + out->print_cr(" Class:"); + ChunkManager::chunkmanager_class()->print_on(out); + out->cr(); + } + } + out->cr(); + + //////////// Waste section /////////////////////////// // As a convenience, print a summary of common waste. out->cr(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/memory/metaspace/metaspaceReporter.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -44,7 +44,9 @@ // Print details about the underlying virtual spaces. ShowVSList = (1 << 3), // If show_loaders: show loaded classes for each loader. - ShowClasses = (1 << 4) + ShowClasses = (1 << 4), + // Print details about the underlying virtual spaces. + ShowChunkFreeList = (1 << 5) }; // This will print out a basic metaspace usage report but diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -589,7 +589,10 @@ // Release C heap allocated data that this points to, which includes // reference counting symbol names. - release_C_heap_structures_internal(); + // Can't release the constant pool here because the constant pool can be + // deallocated separately from the InstanceKlass for default methods and + // redefine classes. + release_C_heap_structures(/* release_constant_pool */ false); deallocate_methods(loader_data, methods()); set_methods(NULL); @@ -1593,7 +1596,8 @@ void InstanceKlass::methods_do(void f(Method* method)) { // Methods aren't stable until they are loaded. This can be read outside // a lock through the ClassLoaderData for profiling - if (!is_loaded()) { + // Redefined scratch classes are on the list and need to be cleaned + if (!is_loaded() && !is_scratch_class()) { return; } @@ -2009,29 +2013,18 @@ return NULL; } -/* jni_id_for_impl for jfieldIds only */ -JNIid* InstanceKlass::jni_id_for_impl(int offset) { +/* jni_id_for for jfieldIds only */ +JNIid* InstanceKlass::jni_id_for(int offset) { MutexLocker ml(JfieldIdCreation_lock); - // Retry lookup after we got the lock JNIid* probe = jni_ids() == NULL ? NULL : jni_ids()->find(offset); if (probe == NULL) { - // Slow case, allocate new static field identifier + // Allocate new static field identifier probe = new JNIid(this, offset, jni_ids()); set_jni_ids(probe); } return probe; } - -/* jni_id_for for jfieldIds only */ -JNIid* InstanceKlass::jni_id_for(int offset) { - JNIid* probe = jni_ids() == NULL ? NULL : jni_ids()->find(offset); - if (probe == NULL) { - probe = jni_id_for_impl(offset); - } - return probe; -} - u2 InstanceKlass::enclosing_method_data(int offset) const { const Array* const inner_class_list = inner_classes(); if (inner_class_list == NULL) { @@ -2641,22 +2634,13 @@ m->release_C_heap_structures(); } -void InstanceKlass::release_C_heap_structures() { - +// Called also by InstanceKlass::deallocate_contents, with false for release_constant_pool. +void InstanceKlass::release_C_heap_structures(bool release_constant_pool) { // Clean up C heap - release_C_heap_structures_internal(); - constants()->release_C_heap_structures(); + Klass::release_C_heap_structures(); // Deallocate and call destructors for MDO mutexes methods_do(method_release_C_heap_structures); -} - -void InstanceKlass::release_C_heap_structures_internal() { - Klass::release_C_heap_structures(); - - // Can't release the constant pool here because the constant pool can be - // deallocated separately from the InstanceKlass for default methods and - // redefine classes. // Deallocate oop map cache if (_oop_map_cache != NULL) { @@ -2692,6 +2676,10 @@ #endif FREE_C_HEAP_ARRAY(char, _source_debug_extension); + + if (release_constant_pool) { + constants()->release_C_heap_structures(); + } } void InstanceKlass::set_source_debug_extension(const char* array, int length) { @@ -3956,8 +3944,6 @@ // so will be deallocated during the next phase of class unloading. log_trace(redefine, class, iklass, purge) ("previous version " INTPTR_FORMAT " is dead.", p2i(pv_node)); - // For debugging purposes. - pv_node->set_is_scratch_class(); // Unlink from previous version list. assert(pv_node->class_loader_data() == loader_data, "wrong loader_data"); InstanceKlass* next = pv_node->previous_versions(); @@ -4072,8 +4058,6 @@ ConstantPool* cp_ref = scratch_class->constants(); if (!cp_ref->on_stack()) { log_trace(redefine, class, iklass, add)("scratch class not added; no methods are running"); - // For debugging purposes. - scratch_class->set_is_scratch_class(); scratch_class->class_loader_data()->add_to_deallocate_list(scratch_class); return; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/instanceKlass.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/instanceKlass.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -254,8 +254,7 @@ _misc_is_shared_platform_class = 1 << 11, // defining class loader is platform class loader _misc_is_shared_app_class = 1 << 12, // defining class loader is app class loader _misc_has_resolved_methods = 1 << 13, // resolved methods table entries added for this class - _misc_is_being_redefined = 1 << 14, // used for locking redefinition - _misc_has_contended_annotations = 1 << 15 // has @Contended annotation + _misc_has_contended_annotations = 1 << 14 // has @Contended annotation }; u2 shared_loader_type_bits() const { return _misc_is_shared_boot_class|_misc_is_shared_platform_class|_misc_is_shared_app_class; @@ -738,14 +737,16 @@ #if INCLUDE_JVMTI // Redefinition locking. Class can only be redefined by one thread at a time. + // The flag is in access_flags so that it can be set and reset using atomic + // operations, and not be reset by other misc_flag settings. bool is_being_redefined() const { - return ((_misc_flags & _misc_is_being_redefined) != 0); + return _access_flags.is_being_redefined(); } void set_is_being_redefined(bool value) { if (value) { - _misc_flags |= _misc_is_being_redefined; + _access_flags.set_is_being_redefined(); } else { - _misc_flags &= ~_misc_is_being_redefined; + _access_flags.clear_is_being_redefined(); } } @@ -1107,7 +1108,7 @@ // callbacks for actions during class unloading static void unload_class(InstanceKlass* ik); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); // Naming const char* signature_name() const; @@ -1203,8 +1204,6 @@ void initialize_impl (TRAPS); void initialize_super_interfaces (TRAPS); void eager_initialize_impl (); - /* jni_id_for_impl for jfieldID only */ - JNIid* jni_id_for_impl (int offset); // find a local method (returns NULL if not found) Method* find_method_impl(const Symbol* name, @@ -1220,9 +1219,6 @@ StaticLookupMode static_mode, PrivateLookupMode private_mode); - // Free CHeap allocated fields. - void release_C_heap_structures_internal(); - #if INCLUDE_JVMTI // RedefineClasses support void link_previous_versions(InstanceKlass* pv) { _previous_versions = pv; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -105,7 +105,7 @@ return false; } -void Klass::release_C_heap_structures() { +void Klass::release_C_heap_structures(bool release_constant_pool) { if (_name != NULL) _name->decrement_refcount(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/klass.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/klass.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -117,8 +117,8 @@ // Klass identifier used to implement devirtualized oop closure dispatching. const KlassID _id; - // vtable length - int _vtable_len; + // Processed access flags, for use by Class.getModifiers. + jint _modifier_flags; // The fields _super_check_offset, _secondary_super_cache, _secondary_supers // and _primary_supers all help make fast subtype checks. See big discussion @@ -154,7 +154,10 @@ // Provide access the corresponding instance java.lang.ClassLoader. ClassLoaderData* _class_loader_data; - jint _modifier_flags; // Processed access flags, for use by Class.getModifiers. + int _vtable_len; // vtable length. This field may be read very often when we + // have lots of itable dispatches (e.g., lambdas and streams). + // Keep it away from the beginning of a Klass to avoid cacheline + // contention that may happen when a nearby object is modified. AccessFlags _access_flags; // Access flags. The class/interface distinction is stored here. JFR_ONLY(DEFINE_TRACE_ID_FIELD;) @@ -693,7 +696,7 @@ Symbol* name() const { return _name; } void set_name(Symbol* n); - virtual void release_C_heap_structures(); + virtual void release_C_heap_structures(bool release_constant_pool = true); public: virtual jint compute_modifier_flags() const = 0; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/method.cpp openjdk-17-17.0.3+7/src/hotspot/share/oops/method.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/method.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/method.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -324,14 +324,11 @@ if (is_native() && bcp == 0) { return 0; } -#ifdef ASSERT - { - ResourceMark rm; - assert(is_native() && bcp == code_base() || contains(bcp) || VMError::is_error_reported(), - "bcp doesn't belong to this method: bcp: " INTPTR_FORMAT ", method: %s", - p2i(bcp), name_and_sig_as_C_string()); - } -#endif + // Do not have a ResourceMark here because AsyncGetCallTrace stack walking code + // may call this after interrupting a nested ResourceMark. + assert(is_native() && bcp == code_base() || contains(bcp) || VMError::is_error_reported(), + "bcp doesn't belong to this method. bcp: " INTPTR_FORMAT, p2i(bcp)); + return bcp - code_base(); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/oops/methodData.hpp openjdk-17-17.0.3+7/src/hotspot/share/oops/methodData.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/oops/methodData.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/oops/methodData.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -30,6 +30,7 @@ #include "oops/method.hpp" #include "oops/oop.hpp" #include "runtime/atomic.hpp" +#include "runtime/deoptimization.hpp" #include "runtime/mutex.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" @@ -1965,7 +1966,7 @@ // Whole-method sticky bits and flags enum { - _trap_hist_limit = 25 JVMCI_ONLY(+5), // decoupled from Deoptimization::Reason_LIMIT + _trap_hist_limit = Deoptimization::Reason_TRAP_HISTORY_LENGTH, _trap_hist_mask = max_jubyte, _extra_data_count = 4 // extra DataLayout headers, for trap history }; // Public flag values @@ -1980,6 +1981,7 @@ uint _nof_overflow_traps; // trap count, excluding _trap_hist union { intptr_t _align; + // JVMCI separates trap history for OSR compilations from normal compilations u1 _array[JVMCI_ONLY(2 *) MethodData::_trap_hist_limit]; } _trap_hist; @@ -1996,14 +1998,14 @@ // Return (uint)-1 for overflow. uint trap_count(int reason) const { - assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob"); + assert((uint)reason < ARRAY_SIZE(_trap_hist._array), "oob"); return (int)((_trap_hist._array[reason]+1) & _trap_hist_mask) - 1; } uint inc_trap_count(int reason) { // Count another trap, anywhere in this method. assert(reason >= 0, "must be single trap"); - assert((uint)reason < JVMCI_ONLY(2*) _trap_hist_limit, "oob"); + assert((uint)reason < ARRAY_SIZE(_trap_hist._array), "oob"); uint cnt1 = 1 + _trap_hist._array[reason]; if ((cnt1 & _trap_hist_mask) != 0) { // if no counter overflow... _trap_hist._array[reason] = cnt1; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/buildOopMap.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/buildOopMap.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/buildOopMap.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/buildOopMap.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2021, 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 @@ -231,10 +231,6 @@ VMReg r = OptoReg::as_VMReg(OptoReg::Name(reg), framesize, max_inarg_slot); - if (false && r->is_reg() && !r->is_concrete()) { - continue; - } - // See if dead (no reaching def). Node *def = _defs[reg]; // Get reaching def assert( def, "since live better have reaching def" ); @@ -312,14 +308,10 @@ set_live_bit(live,breg); // Already missed our turn? if( breg < reg ) { - if (b->is_stack() || b->is_concrete() || true ) { - omap->set_oop( b); - } + omap->set_oop(b); } } - if (b->is_stack() || b->is_concrete() || true ) { - omap->set_derived_oop( r, b); - } + omap->set_derived_oop(r, b); } } else if( t->isa_narrowoop() ) { @@ -347,9 +339,7 @@ assert( dup_check[_callees[reg]]==0, "trying to callee save same reg twice" ); debug_only( dup_check[_callees[reg]]=1; ) VMReg callee = OptoReg::as_VMReg(OptoReg::Name(_callees[reg])); - if ( callee->is_concrete() || true ) { - omap->set_callee_saved( r, callee); - } + omap->set_callee_saved(r, callee); } else { // Other - some reaching non-oop value diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/cfgnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/cfgnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/cfgnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/cfgnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1366,9 +1366,12 @@ } int true_path = is_diamond_phi(); - if (true_path != 0) { + // Delay CMove'ing identity if Ideal has not had the chance to handle unsafe cases, yet. + if (true_path != 0 && !(phase->is_IterGVN() && wait_for_region_igvn(phase))) { Node* id = is_cmove_id(phase, true_path); - if (id != NULL) return id; + if (id != NULL) { + return id; + } } // Looking for phis with identical inputs. If we find one that has @@ -2269,13 +2272,13 @@ // Phi(...MergeMem(m0, m1:AT1, m2:AT2)...) into // MergeMem(Phi(...m0...), Phi:AT1(...m1...), Phi:AT2(...m2...)) PhaseIterGVN* igvn = phase->is_IterGVN(); + assert(igvn != NULL, "sanity check"); Node* hook = new Node(1); PhiNode* new_base = (PhiNode*) clone(); // Must eagerly register phis, since they participate in loops. - if (igvn) { - igvn->register_new_node_with_optimizer(new_base); - hook->add_req(new_base); - } + igvn->register_new_node_with_optimizer(new_base); + hook->add_req(new_base); + MergeMemNode* result = MergeMemNode::make(new_base); for (uint i = 1; i < req(); ++i) { Node *ii = in(i); @@ -2287,10 +2290,8 @@ if (mms.is_empty()) { Node* new_phi = new_base->slice_memory(mms.adr_type(phase->C)); made_new_phi = true; - if (igvn) { - igvn->register_new_node_with_optimizer(new_phi); - hook->add_req(new_phi); - } + igvn->register_new_node_with_optimizer(new_phi); + hook->add_req(new_phi); mms.set_memory(new_phi); } Node* phi = mms.memory(); @@ -2308,6 +2309,13 @@ } } } + // Already replace this phi node to cut it off from the graph to not interfere in dead loop checks during the + // transformations of the new phi nodes below. Otherwise, we could wrongly conclude that there is no dead loop + // because we are finding this phi node again. Also set the type of the new MergeMem node in case we are also + // visiting it in the transformations below. + igvn->replace_node(this, result); + igvn->set_type(result, result->bottom_type()); + // now transform the new nodes, and return the mergemem for (MergeMemStream mms(result); mms.next_non_empty(); ) { Node* phi = mms.memory(); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/library_call.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/library_call.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/library_call.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/library_call.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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 @@ -1561,7 +1561,7 @@ if (is_store) { access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED); } else { - ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD); + ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD | C2_UNKNOWN_CONTROL_LOAD); set_result(ch); } return true; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopPredicate.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopPredicate.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopPredicate.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopPredicate.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -341,9 +341,9 @@ assert(predicate->is_Proj() && predicate->as_Proj()->is_IfProj(), "predicate must be a projection of an if node"); IfProjNode* predicate_proj = predicate->as_IfProj(); - ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, iffast_pred, loop); + ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, iffast_pred); assert(skeleton_predicate_has_opaque(fast_proj->in(0)->as_If()), "must find skeleton predicate for fast loop"); - ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, ifslow_pred, loop); + ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, reason, ifslow_pred); assert(skeleton_predicate_has_opaque(slow_proj->in(0)->as_If()), "must find skeleton predicate for slow loop"); // Update control dependent data nodes. @@ -397,10 +397,10 @@ // Clone a skeleton predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon // traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned // predicate again). -ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, - Deoptimization::DeoptReason reason, ProjNode* output_proj, - IdealLoopTree* loop) { - Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, predicate, uncommon_proj, output_proj, loop); +ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj) { + Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, output_proj); ProjNode* proj = create_new_if_for_predicate(output_proj, NULL, reason, iff->Opcode(), predicate->is_IfTrue()); _igvn.replace_input_of(proj->in(0), 1, bol); _igvn.replace_input_of(output_proj->in(0), 0, proj); @@ -816,7 +816,7 @@ BoolNode* PhaseIdealLoop::rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, Node* init, Node* limit, jint stride, - Node* range, bool upper, bool &overflow) { + Node* range, bool upper, bool &overflow, bool negate) { jint con_limit = (limit != NULL && limit->is_Con()) ? limit->get_int() : 0; jint con_init = init->is_Con() ? init->get_int() : 0; jint con_offset = offset->is_Con() ? offset->get_int() : 0; @@ -942,7 +942,7 @@ cmp = new CmpUNode(max_idx_expr, range); } register_new_node(cmp, ctrl); - BoolNode* bol = new BoolNode(cmp, BoolTest::lt); + BoolNode* bol = new BoolNode(cmp, negate ? BoolTest::ge : BoolTest::lt); register_new_node(bol, ctrl); if (TraceLoopPredicate) { @@ -1309,36 +1309,26 @@ } // If predicate expressions may overflow in the integer range, longs are used. bool overflow = false; + bool negate = (proj->_con != predicate_proj->_con); // Test the lower bound - BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow); - // Negate test if necessary - bool negated = false; - if (proj->_con != predicate_proj->_con) { - lower_bound_bol = new BoolNode(lower_bound_bol->in(1), lower_bound_bol->_test.negate()); - register_new_node(lower_bound_bol, ctrl); - negated = true; - } + BoolNode* lower_bound_bol = rc_predicate(loop, ctrl, scale, offset, init, limit, stride, rng, false, overflow, negate); + ProjNode* lower_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, reason, overflow ? Op_If : iff->Opcode()); IfNode* lower_bound_iff = lower_bound_proj->in(0)->as_If(); _igvn.hash_delete(lower_bound_iff); lower_bound_iff->set_req(1, lower_bound_bol); - if (TraceLoopPredicate) tty->print_cr("lower bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("lower bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); // Test the upper bound - BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow); - negated = false; - if (proj->_con != predicate_proj->_con) { - upper_bound_bol = new BoolNode(upper_bound_bol->in(1), upper_bound_bol->_test.negate()); - register_new_node(upper_bound_bol, ctrl); - negated = true; - } + BoolNode* upper_bound_bol = rc_predicate(loop, lower_bound_proj, scale, offset, init, limit, stride, rng, true, overflow, negate); + ProjNode* upper_bound_proj = create_new_if_for_predicate(predicate_proj, NULL, reason, overflow ? Op_If : iff->Opcode()); assert(upper_bound_proj->in(0)->as_If()->in(0) == lower_bound_proj, "should dominate"); IfNode* upper_bound_iff = upper_bound_proj->in(0)->as_If(); _igvn.hash_delete(upper_bound_iff); upper_bound_iff->set_req(1, upper_bound_bol); - if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negated ? " negated" : "", lower_bound_iff->_idx); + if (TraceLoopPredicate) tty->print_cr("upper bound check if: %s %d ", negate ? " negated" : "", lower_bound_iff->_idx); // Fall through into rest of the clean up code which will move // any dependent nodes onto the upper bound test. @@ -1384,10 +1374,10 @@ Node* rng, bool &overflow, Deoptimization::DeoptReason reason) { // First predicate for the initial value on first loop iteration - assert(proj->_con && predicate_proj->_con, "not a range check?"); Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, upper_bound_proj); - BoolNode* bol = rc_predicate(loop, upper_bound_proj, scale, offset, opaque_init, limit, stride, rng, (stride > 0) != (scale > 0), overflow); + bool negate = (proj->_con != predicate_proj->_con); + BoolNode* bol = rc_predicate(loop, upper_bound_proj, scale, offset, opaque_init, limit, stride, rng, (stride > 0) != (scale > 0), overflow, negate); Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); // This will go away once loop opts are over C->add_skeleton_predicate_opaq(opaque_bol); register_new_node(opaque_bol, upper_bound_proj); @@ -1405,7 +1395,7 @@ register_new_node(max_value, new_proj); max_value = new AddINode(opaque_init, max_value); register_new_node(max_value, new_proj); - bol = rc_predicate(loop, new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow); + bol = rc_predicate(loop, new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow, negate); opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); C->add_skeleton_predicate_opaq(opaque_bol); register_new_node(opaque_bol, new_proj); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopTransform.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopTransform.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopTransform.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopTransform.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1264,10 +1264,12 @@ // Clone the skeleton predicate twice and initialize one with the initial // value of the loop induction variable. Leave the other predicate // to be initialized when increasing the stride during loop unrolling. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, opaque_init, NULL, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, opaque_init, NULL, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, stride, predicate, uncommon_proj, current_proj, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, predicate, uncommon_proj, + current_proj, outer_loop, prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), ""); // Rewire any control inputs from the cloned skeleton predicates down to the main and post loop for data nodes that are part of the @@ -1344,8 +1346,7 @@ // Clone the skeleton predicate bool for a main or unswitched loop: // Main loop: Set new_init and new_stride nodes as new inputs. // Unswitched loop: new_init and new_stride are both NULL. Clone OpaqueLoopInit and OpaqueLoopStride instead. -Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop) { +Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control) { Node_Stack to_clone(2); to_clone.push(iff->in(1), 1); uint current = C->unique(); @@ -1421,9 +1422,9 @@ // Clone a skeleton predicate for the main loop. new_init and new_stride are set as new inputs. Since the predicates cannot fail at runtime, // Halt nodes are inserted instead of uncommon traps. -Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, - Node* control, IdealLoopTree* outer_loop, Node* input_proj) { - Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, predicate, uncommon_proj, control, outer_loop); +Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, + Node* control, IdealLoopTree* outer_loop, Node* input_proj) { + Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, control); Node* proj = predicate->clone(); Node* other_proj = uncommon_proj->clone(); Node* new_iff = iff->clone(); @@ -1437,8 +1438,8 @@ C->root()->add_req(halt); new_iff->set_req(0, input_proj); - register_control(new_iff, outer_loop->_parent, input_proj); - register_control(proj, outer_loop->_parent, new_iff); + register_control(new_iff, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, input_proj); + register_control(proj, outer_loop == _ltree_root ? _ltree_root : outer_loop->_parent, new_iff); register_control(other_proj, _ltree_root, new_iff); register_control(halt, _ltree_root, other_proj); return proj; @@ -1521,7 +1522,8 @@ // Add the post loop const uint idx_before_pre_post = Compile::current()->unique(); CountedLoopNode *post_head = NULL; - Node *main_exit = insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + Node* post_incr = incr; + Node* main_exit = insert_post_loop(loop, old_new, main_head, main_end, post_incr, limit, post_head); const uint idx_after_post_before_pre = Compile::current()->unique(); //------------------------------ @@ -1620,6 +1622,7 @@ assert(post_head->in(1)->is_IfProj(), "must be zero-trip guard If node projection of the post loop"); copy_skeleton_predicates_to_main_loop(pre_head, castii, stride, outer_loop, outer_main_head, dd_main_head, idx_before_pre_post, idx_after_post_before_pre, min_taken, post_head->in(1), old_new); + copy_skeleton_predicates_to_post_loop(outer_main_head, post_head, post_incr, stride); // Step B4: Shorten the pre-loop to run only 1 iteration (for now). // RCE and alignment may change this later. @@ -1742,6 +1745,7 @@ // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1788,6 +1792,7 @@ // In this case we throw away the result as we are not using it to connect anything else. CountedLoopNode *post_head = NULL; insert_post_loop(loop, old_new, main_head, main_end, incr, limit, post_head); + copy_skeleton_predicates_to_post_loop(main_head->skip_strip_mined(), post_head, incr, main_head->stride()); // It's difficult to be precise about the trip-counts // for post loops. They are usually very short, @@ -1804,9 +1809,9 @@ //------------------------------insert_post_loop------------------------------- // Insert post loops. Add a post loop to the given loop passed. -Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head) { +Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head) { IfNode* outer_main_end = main_end; IdealLoopTree* outer_loop = loop; if (main_head->is_strip_mined()) { @@ -1890,8 +1895,8 @@ } // CastII for the new post loop: - Node* castii = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); - assert(castii != NULL, "no castII inserted"); + incr = cast_incr_before_loop(zer_opaq->in(1), zer_taken, post_head); + assert(incr != NULL, "no castII inserted"); return new_main_exit; } @@ -1933,7 +1938,8 @@ _igvn.replace_input_of(iff, 1, iff->in(1)->in(2)); } else { // Add back predicates updated for the new stride. - prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, prev_proj); + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, + prev_proj); assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); } } @@ -1945,6 +1951,34 @@ } } +void PhaseIdealLoop::copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride) { + // Go over the skeleton predicates of the main loop and make a copy for the post loop with its initial iv value and + // stride as inputs. + Node* post_loop_entry = post_loop_head->in(LoopNode::EntryControl); + Node* main_loop_entry = main_loop_head->in(LoopNode::EntryControl); + IdealLoopTree* post_loop = get_loop(post_loop_head); + + Node* ctrl = main_loop_entry; + Node* prev_proj = post_loop_entry; + while (ctrl != NULL && ctrl->is_Proj() && ctrl->in(0)->is_If()) { + IfNode* iff = ctrl->in(0)->as_If(); + ProjNode* proj = iff->proj_out(1 - ctrl->as_Proj()->_con); + if (proj->unique_ctrl_out()->Opcode() != Op_Halt) { + break; + } + if (iff->in(1)->Opcode() == Op_Opaque4 && skeleton_predicate_has_opaque(iff)) { + prev_proj = clone_skeleton_predicate_for_main_or_post_loop(iff, init, stride, ctrl, proj, post_loop_entry, + post_loop, prev_proj); + assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected"); + } + ctrl = ctrl->in(0)->in(0); + } + if (prev_proj != post_loop_entry) { + _igvn.replace_input_of(post_loop_head, LoopNode::EntryControl, prev_proj); + set_idom(post_loop_head, prev_proj, dom_depth(post_loop_head)); + } +} + //------------------------------do_unroll-------------------------------------- // Unroll the loop body one step - make each trip do 2 iterations. void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adjust_min_trip) { @@ -2555,7 +2589,7 @@ Node* predicate_proj, int scale_con, Node* offset, Node* limit, jint stride_con, Node* value) { bool overflow = false; - BoolNode* bol = rc_predicate(loop, predicate_proj, scale_con, offset, value, NULL, stride_con, limit, (stride_con > 0) != (scale_con > 0), overflow); + BoolNode* bol = rc_predicate(loop, predicate_proj, scale_con, offset, value, NULL, stride_con, limit, (stride_con > 0) != (scale_con > 0), overflow, false); Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); register_new_node(opaque_bol, predicate_proj); IfNode* new_iff = NULL; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2150,12 +2150,14 @@ } Node* CountedLoopNode::skip_predicates() { + Node* ctrl = in(LoopNode::EntryControl); if (is_main_loop()) { - Node* ctrl = skip_strip_mined()->in(LoopNode::EntryControl); - + ctrl = skip_strip_mined()->in(LoopNode::EntryControl); + } + if (is_main_loop() || is_post_loop()) { return skip_predicates_from_entry(ctrl); } - return in(LoopNode::EntryControl); + return ctrl; } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.hpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopnode.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopnode.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -915,13 +915,13 @@ void copy_skeleton_predicates_to_main_loop(CountedLoopNode* pre_head, Node* init, Node* stride, IdealLoopTree* outer_loop, LoopNode* outer_main_head, uint dd_main_head, const uint idx_before_pre_post, const uint idx_after_post_before_pre, Node* zero_trip_guard_proj_main, Node* zero_trip_guard_proj_post, const Node_List &old_new); - Node* clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop, Node* input_proj); - Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, - IdealLoopTree* outer_loop); + Node* clone_skeleton_predicate_for_main_or_post_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control, + IdealLoopTree* outer_loop, Node* input_proj); + Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* control); static bool skeleton_predicate_has_opaque(IfNode* iff); static void get_skeleton_predicates(Node* predicate, Unique_Node_List& list, bool get_opaque = false); void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con); + void copy_skeleton_predicates_to_post_loop(LoopNode* main_loop_head, CountedLoopNode* post_loop_head, Node* init, Node* stride); void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); #ifdef ASSERT bool only_has_infinite_loops(); @@ -1244,9 +1244,9 @@ void insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_new, bool peel_only ); // Add post loop after the given loop. - Node *insert_post_loop(IdealLoopTree *loop, Node_List &old_new, - CountedLoopNode *main_head, CountedLoopEndNode *main_end, - Node *incr, Node *limit, CountedLoopNode *&post_head); + Node *insert_post_loop(IdealLoopTree* loop, Node_List& old_new, + CountedLoopNode* main_head, CountedLoopEndNode* main_end, + Node*& incr, Node* limit, CountedLoopNode*& post_head); // Add an RCE'd post loop which we will multi-version adapt for run time test path usage void insert_scalar_rced_post_loop( IdealLoopTree *loop, Node_List &old_new ); @@ -1303,7 +1303,8 @@ BoolNode* rc_predicate(IdealLoopTree *loop, Node* ctrl, int scale, Node* offset, Node* init, Node* limit, jint stride, - Node* range, bool upper, bool &overflow); + Node* range, bool upper, bool &overflow, + bool negate); // Implementation of the loop predication to promote checks outside the loop bool loop_predication_impl(IdealLoopTree *loop); @@ -1579,8 +1580,9 @@ Node_List* old_new = NULL); void clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, Deoptimization::DeoptReason reason, ProjNode* old_predicate_proj, ProjNode* iffast_pred, ProjNode* ifslow_pred); - ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, Deoptimization::DeoptReason reason, - ProjNode* output_proj, IdealLoopTree* loop); + ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, + Deoptimization::DeoptReason reason, + ProjNode* output_proj); static void check_created_predicate_for_unswitching(const Node* new_entry) PRODUCT_RETURN; bool _created_loop_node; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/loopopts.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/loopopts.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/loopopts.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/loopopts.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1174,6 +1174,16 @@ if (!n->in(0)->is_Region()) { return false; } + + IfNode* n_if = n->as_If(); + if (n_if->proj_out(0)->outcnt() > 1 || n_if->proj_out(1)->outcnt() > 1) { + // Removing the dominated If node by using the split-if optimization does not work if there are data dependencies. + // Some data dependencies depend on its immediate dominator If node and should not be separated from it (e.g. null + // checks, division by zero checks etc.). Bail out for now until data dependencies are correctly handled when + // optimizing back-to-back ifs. + return false; + } + Node* region = n->in(0); Node* dom = idom(region); if (!dom->is_If() || dom->in(1) != n->in(1)) { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/macro.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/macro.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/macro.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/macro.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2562,6 +2562,7 @@ assert(n->Opcode() == Op_LoopLimit || n->Opcode() == Op_Opaque2 || n->Opcode() == Op_Opaque3 || + n->Opcode() == Op_Opaque4 || BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n), "unknown node type in macro list"); } @@ -2623,6 +2624,19 @@ _igvn.replace_node(n, repl); success = true; #endif + } else if (n->Opcode() == Op_Opaque4) { + // With Opaque4 nodes, the expectation is that the test of input 1 + // is always equal to the constant value of input 2. So we can + // remove the Opaque4 and replace it by input 2. In debug builds, + // leave the non constant test in instead to sanity check that it + // never fails (if it does, that subgraph was constructed so, at + // runtime, a Halt node is executed). +#ifdef ASSERT + _igvn.replace_node(n, n->in(1)); +#else + _igvn.replace_node(n, n->in(2)); +#endif + success = true; } else if (n->Opcode() == Op_OuterStripMinedLoop) { n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn); C->remove_macro_node(n); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/mulnode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/mulnode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/mulnode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/mulnode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -59,13 +59,11 @@ // We also canonicalize the Node, moving constants to the right input, // and flatten expressions (so that 1+x+2 becomes x+3). Node *MulNode::Ideal(PhaseGVN *phase, bool can_reshape) { - const Type *t1 = phase->type( in(1) ); - const Type *t2 = phase->type( in(2) ); - Node *progress = NULL; // Progress flag + Node* in1 = in(1); + Node* in2 = in(2); + Node* progress = NULL; // Progress flag // convert "max(a,b) * min(a,b)" into "a*b". - Node *in1 = in(1); - Node *in2 = in(2); if ((in(1)->Opcode() == max_opcode() && in(2)->Opcode() == min_opcode()) || (in(1)->Opcode() == min_opcode() && in(2)->Opcode() == max_opcode())) { Node *in11 = in(1)->in(1); @@ -83,10 +81,15 @@ igvn->_worklist.push(in1); igvn->_worklist.push(in2); } + in1 = in(1); + in2 = in(2); progress = this; } } + const Type* t1 = phase->type(in1); + const Type* t2 = phase->type(in2); + // We are OK if right is a constant, or right is a load and // left is a non-constant. if( !(t2->singleton() || diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -60,25 +60,6 @@ return (&n == this); // Always fail except on self } -Node* Opaque4Node::Identity(PhaseGVN* phase) { - if (phase->C->post_loop_opts_phase()) { - // With Opaque4 nodes, the expectation is that the test of input 1 - // is always equal to the constant value of input 2. So we can - // remove the Opaque4 and replace it by input 2. In debug builds, - // leave the non constant test in instead to sanity check that it - // never fails (if it does, that subgraph was constructed so, at - // runtime, a Halt node is executed). -#ifdef ASSERT - return this->in(1); -#else - return this->in(2); -#endif - } else { - phase->C->record_for_post_loop_opts_igvn(this); - } - return this; -} - const Type* Opaque4Node::Value(PhaseGVN* phase) const { return phase->type(in(1)); } diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.hpp openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/opaquenode.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/opaquenode.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -114,11 +114,13 @@ // GraphKit::must_be_not_null(). class Opaque4Node : public Node { public: - Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) {} + Opaque4Node(Compile* C, Node *tst, Node* final_tst) : Node(NULL, tst, final_tst) { + init_flags(Flag_is_macro); + C->add_macro_node(this); + } virtual int Opcode() const; virtual const Type *bottom_type() const { return TypeInt::BOOL; } - virtual Node* Identity(PhaseGVN* phase); virtual const Type* Value(PhaseGVN* phase) const; }; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/opto/output.cpp openjdk-17-17.0.3+7/src/hotspot/share/opto/output.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/opto/output.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/opto/output.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -2911,6 +2911,16 @@ if( !OptoReg::is_valid(def_reg) ) // Ignore stores & control flow return; + if (OptoReg::is_reg(def_reg)) { + VMReg vmreg = OptoReg::as_VMReg(def_reg); + if (vmreg->is_reg() && !vmreg->is_concrete() && !vmreg->prev()->is_concrete()) { + // This is one of the high slots of a vector register. + // ScheduleAndBundle already checked there are no live wide + // vectors in this method so it can be safely ignored. + return; + } + } + Node *pinch = _reg_node[def_reg]; // Get pinch point if ((pinch == NULL) || _cfg->get_block_for_node(pinch) != b || // No pinch-point yet? is_def ) { // Check for a true def (not a kill) diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiEnvBase.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiEnvBase.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiEnvBase.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiEnvBase.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -830,7 +830,7 @@ "call by myself / at safepoint / at handshake"); int count = 0; if (java_thread->has_last_Java_frame()) { - RegisterMap reg_map(java_thread); + RegisterMap reg_map(java_thread, false /* update_map */, false /* process_frames */); ResourceMark rm(current_thread); javaVFrame *jvf = java_thread->last_java_vframe(®_map); HandleMark hm(current_thread); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiRedefineClasses.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiRedefineClasses.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -4419,6 +4419,9 @@ the_class->set_has_been_redefined(); + // Scratch class is unloaded but still needs cleaning, and skipping for CDS. + scratch_class->set_is_scratch_class(); + // keep track of previous versions of this class the_class->add_previous_version(scratch_class, emcp_method_count); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/vectorSupport.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/vectorSupport.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/vectorSupport.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/vectorSupport.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -291,6 +291,7 @@ case T_BYTE: // fall-through case T_SHORT: // fall-through case T_INT: return Op_NegI; + case T_LONG: return Op_NegL; case T_FLOAT: return Op_NegF; case T_DOUBLE: return Op_NegD; default: fatal("NEG: %s", type2name(bt)); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/prims/whitebox.cpp openjdk-17-17.0.3+7/src/hotspot/share/prims/whitebox.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/prims/whitebox.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/prims/whitebox.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -995,7 +995,7 @@ const char* proc_self_cgroup, const char* proc_self_mountinfo, u1* cg_flags) { - CgroupInfo cg_infos[4]; + CgroupInfo cg_infos[CG_INFO_LENGTH]; return CgroupSubsystemFactory::determine_type(cg_infos, proc_cgroups, proc_self_cgroup, proc_self_mountinfo, cg_flags); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.cpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -775,6 +775,7 @@ // at an uncommon trap for an invoke (where the compiler // generates debug info before the invoke has executed) Bytecodes::Code cur_code = str.next(); + Bytecodes::Code next_code = Bytecodes::_shouldnotreachhere; if (Bytecodes::is_invoke(cur_code)) { Bytecode_invoke invoke(mh, iframe->interpreter_frame_bci()); cur_invoke_parameter_size = invoke.size_of_parameters(); @@ -783,7 +784,7 @@ } } if (str.bci() < max_bci) { - Bytecodes::Code next_code = str.next(); + next_code = str.next(); if (next_code >= 0) { // The interpreter oop map generator reports results before // the current bytecode has executed except in the case of @@ -833,6 +834,10 @@ // Print out some information that will help us debug the problem tty->print_cr("Wrong number of expression stack elements during deoptimization"); tty->print_cr(" Error occurred while verifying frame %d (0..%d, 0 is topmost)", i, cur_array->frames() - 1); + tty->print_cr(" Current code %s", Bytecodes::name(cur_code)); + if (try_next_mask) { + tty->print_cr(" Next code %s", Bytecodes::name(next_code)); + } tty->print_cr(" Fabricated interpreter frame had %d expression stack elements", iframe->interpreter_frame_expression_stack_size()); tty->print_cr(" Interpreter oop map had %d expression stack elements", mask.expression_stack_size()); @@ -2384,7 +2389,8 @@ uint idx = reason; #if INCLUDE_JVMCI if (is_osr) { - idx += Reason_LIMIT; + // Upper half of history array used for traps in OSR compilations + idx += Reason_TRAP_HISTORY_LENGTH; } #endif uint prior_trap_count = trap_mdo->trap_count(idx); diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.hpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/deoptimization.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/deoptimization.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -46,6 +46,7 @@ public: // What condition caused the deoptimization? + // Note: Keep this enum in sync. with Deoptimization::_trap_reason_name. enum DeoptReason { Reason_many = -1, // indicates presence of several reasons Reason_none = 0, // indicates absence of a relevant deopt. @@ -98,20 +99,22 @@ Reason_jsr_mismatch, #endif + // Used to define MethodData::_trap_hist_limit where Reason_tenured isn't included + Reason_TRAP_HISTORY_LENGTH, + // Reason_tenured is counted separately, add normal counted Reasons above. - // Related to MethodData::_trap_hist_limit where Reason_tenured isn't included - Reason_tenured, // age of the code has reached the limit + Reason_tenured = Reason_TRAP_HISTORY_LENGTH, // age of the code has reached the limit Reason_LIMIT, - // Note: Keep this enum in sync. with _trap_reason_name. - Reason_RECORDED_LIMIT = Reason_profile_predicate // some are not recorded per bc // Note: Reason_RECORDED_LIMIT should fit into 31 bits of // DataLayout::trap_bits. This dependency is enforced indirectly // via asserts, to avoid excessive direct header-to-header dependencies. // See Deoptimization::trap_state_reason and class DataLayout. + Reason_RECORDED_LIMIT = Reason_profile_predicate, // some are not recorded per bc }; // What action must be taken by the runtime? + // Note: Keep this enum in sync. with Deoptimization::_trap_action_name. enum DeoptAction { Action_none, // just interpret, do not invalidate nmethod Action_maybe_recompile, // recompile the nmethod; need not invalidate @@ -119,7 +122,6 @@ Action_make_not_entrant, // invalidate the nmethod, recompile (probably) Action_make_not_compilable, // invalidate the nmethod and do not compile Action_LIMIT - // Note: Keep this enum in sync. with _trap_action_name. }; enum { diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/objectMonitor.hpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/objectMonitor.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/objectMonitor.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/objectMonitor.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -127,7 +127,7 @@ #define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE #endif -class ObjectMonitor : public CHeapObj { +class ObjectMonitor : public CHeapObj { friend class ObjectSynchronizer; friend class ObjectWaiter; friend class VMStructs; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/runtime/os.cpp openjdk-17-17.0.3+7/src/hotspot/share/runtime/os.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/runtime/os.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/runtime/os.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -916,7 +916,7 @@ addr = addr2; } } -#endif // HANDLE_FUNCTION_DESCRIPTORS +#endif // HAVE_FUNCTION_DESCRIPTORS if (have_function_name) { // Print function name, optionally demangled diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/services/attachListener.cpp openjdk-17-17.0.3+7/src/hotspot/share/services/attachListener.cpp --- openjdk-17-17.0.2+8/src/hotspot/share/services/attachListener.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/services/attachListener.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, 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 @@ -292,6 +292,7 @@ uintx num; if (!Arguments::parse_uintx(num_str, &num, 0)) { out->print_cr("Invalid parallel thread number: [%s]", num_str); + delete fs; return JNI_ERR; } parallel_thread_num = num == 0 ? parallel_thread_num : (uint)num; diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/utilities/accessFlags.hpp openjdk-17-17.0.3+7/src/hotspot/share/utilities/accessFlags.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/utilities/accessFlags.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/utilities/accessFlags.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -69,6 +69,7 @@ JVM_ACC_IS_SHARED_CLASS = 0x02000000, // True if klass is shared JVM_ACC_IS_HIDDEN_CLASS = 0x04000000, // True if klass is hidden JVM_ACC_IS_VALUE_BASED_CLASS = 0x08000000, // True if klass is marked as a ValueBased class + JVM_ACC_IS_BEING_REDEFINED = 0x00100000, // True if the klass is being redefined. // Klass* and Method* flags JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000, @@ -159,6 +160,10 @@ void set_has_localvariable_table() { atomic_set_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } void clear_has_localvariable_table() { atomic_clear_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + bool is_being_redefined() const { return (_flags & JVM_ACC_IS_BEING_REDEFINED) != 0; } + void set_is_being_redefined() { atomic_set_bits(JVM_ACC_IS_BEING_REDEFINED); } + void clear_is_being_redefined() { atomic_clear_bits(JVM_ACC_IS_BEING_REDEFINED); } + // field flags bool is_field_access_watched() const { return (_flags & JVM_ACC_FIELD_ACCESS_WATCHED) != 0; } bool is_field_modification_watched() const diff -Nru openjdk-17-17.0.2+8/src/hotspot/share/utilities/resourceHash.hpp openjdk-17-17.0.3+7/src/hotspot/share/utilities/resourceHash.hpp --- openjdk-17-17.0.2+8/src/hotspot/share/utilities/resourceHash.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/hotspot/share/utilities/resourceHash.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -195,6 +195,32 @@ ++bucket; } } + + // ITER contains bool do_entry(K const&, V const&), which will be + // called for each entry in the table. If do_entry() returns true, + // the entry is deleted. + template + void unlink(ITER* iter) { + Node** bucket = const_cast(_table); + while (bucket < &_table[SIZE]) { + Node** ptr = bucket; + while (*ptr != NULL) { + Node* node = *ptr; + // do_entry must clean up the key and value in Node. + bool clean = iter->do_entry(node->_key, node->_value); + if (clean) { + *ptr = node->_next; + if (ALLOC_TYPE == ResourceObj::C_HEAP) { + delete node; + } + } else { + ptr = &(node->_next); + } + } + ++bucket; + } + } + }; diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2021, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -150,6 +150,16 @@ } @Override + public long getPidsMax() { + return subsystem.getPidsMax(); + } + + @Override + public long getPidsCurrent() { + return subsystem.getPidsCurrent(); + } + + @Override public long getBlkIOServiceCount() { return subsystem.getBlkIOServiceCount(); } diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -36,5 +36,13 @@ * has determined that no limit is being imposed. */ public static final long LONG_RETVAL_UNLIMITED = -1; + public static final String MAX_VAL = "max"; + + public static long limitFromString(String strVal) { + if (strVal == null || MAX_VAL.equals(strVal)) { + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; + } + return Long.parseLong(strVal); + } } diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -51,6 +51,7 @@ private static final String CPUSET_CTRL = "cpuset"; private static final String BLKIO_CTRL = "blkio"; private static final String MEMORY_CTRL = "memory"; + private static final String PIDS_CTRL = "pids"; /* * From https://www.kernel.org/doc/Documentation/filesystems/proc.txt @@ -149,6 +150,7 @@ case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break; case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break; case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break; + case PIDS_CTRL: infos.put(PIDS_CTRL, info); break; } } @@ -252,6 +254,7 @@ case CPUACCT_CTRL: case CPU_CTRL: case BLKIO_CTRL: + case PIDS_CTRL: CgroupInfo info = infos.get(cName); info.setCgroupPath(cgroupPath); break; @@ -303,6 +306,7 @@ case MEMORY_CTRL: // fall-through case CPU_CTRL: case CPUACCT_CTRL: + case PIDS_CTRL: case BLKIO_CTRL: { CgroupInfo info = infos.get(controllerName); assert info.getMountPoint() == null; diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, 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,6 +38,7 @@ private CgroupV1SubsystemController cpuacct; private CgroupV1SubsystemController cpuset; private CgroupV1SubsystemController blkio; + private CgroupV1SubsystemController pids; private static volatile CgroupV1Subsystem INSTANCE; @@ -126,6 +127,15 @@ } break; } + case "pids": { + if (info.getMountRoot() != null && info.getMountPoint() != null) { + CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint()); + controller.setPath(info.getCgroupPath()); + subsystem.setPidsController(controller); + anyActiveControllers = true; + } + break; + } default: throw new AssertionError("Unrecognized controller in infos: " + info.getName()); } @@ -170,6 +180,10 @@ this.blkio = blkio; } + private void setPidsController(CgroupV1SubsystemController pids) { + this.pids = pids; + } + private static long getLongValue(CgroupSubsystemController controller, String parm) { return CgroupSubsystemController.getLongValue(controller, @@ -394,6 +408,17 @@ return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes")); } + /***************************************************************** + * pids subsystem + ****************************************************************/ + public long getPidsMax() { + String pidsMaxStr = CgroupSubsystemController.getStringValue(pids, "pids.max"); + return CgroupSubsystem.limitFromString(pidsMaxStr); + } + + public long getPidsCurrent() { + return getLongValue(pids, "pids.current"); + } /***************************************************************** * BlKIO Subsystem diff -Nru openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java --- openjdk-17-17.0.2+8/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2021, Red Hat Inc. * 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 @@ private final CgroupSubsystemController unified; private static final String PROVIDER_NAME = "cgroupv2"; private static final int PER_CPU_SHARES = 1024; - private static final String MAX_VAL = "max"; private static final Object EMPTY_STR = ""; private static final long NO_SWAP = 0; @@ -149,14 +148,7 @@ return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } String quota = tokens[tokenIdx]; - return limitFromString(quota); - } - - private long limitFromString(String strVal) { - if (strVal == null || MAX_VAL.equals(strVal)) { - return CgroupSubsystem.LONG_RETVAL_UNLIMITED; - } - return Long.parseLong(strVal); + return CgroupSubsystem.limitFromString(quota); } @Override @@ -251,7 +243,7 @@ @Override public long getMemoryLimit() { String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max"); - return limitFromString(strVal); + return CgroupSubsystem.limitFromString(strVal); } @Override @@ -279,7 +271,7 @@ if (strVal == null) { return getMemoryLimit(); } - long swapLimit = limitFromString(strVal); + long swapLimit = CgroupSubsystem.limitFromString(strVal); if (swapLimit >= 0) { long memoryLimit = getMemoryLimit(); assert memoryLimit >= 0; @@ -310,7 +302,18 @@ @Override public long getMemorySoftLimit() { String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.low"); - return limitFromString(softLimitStr); + return CgroupSubsystem.limitFromString(softLimitStr); + } + + @Override + public long getPidsMax() { + String pidsMaxStr = CgroupSubsystemController.getStringValue(unified, "pids.max"); + return CgroupSubsystem.limitFromString(pidsMaxStr); + } + + @Override + public long getPidsCurrent() { + return getLongVal("pids.current"); } @Override diff -Nru openjdk-17-17.0.2+8/src/java.base/macosx/classes/apple/security/KeychainStore.java openjdk-17-17.0.3+7/src/java.base/macosx/classes/apple/security/KeychainStore.java --- openjdk-17-17.0.2+8/src/java.base/macosx/classes/apple/security/KeychainStore.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/macosx/classes/apple/security/KeychainStore.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, 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 @@ -68,6 +68,25 @@ Certificate cert; long certRef; // SecCertificateRef for this key + + // Each KeyStore.TrustedCertificateEntry have 2 attributes: + // 1. "trustSettings" -> trustSettings.toString() + // 2. "2.16.840.1.113894.746875.1.1" -> trustedKeyUsageValue + // The 1st one is mainly for debugging use. The 2nd one is similar + // to the attribute with the same key in a PKCS12KeyStore. + + // The SecTrustSettingsCopyTrustSettings() output for this certificate + // inside the KeyChain in its original array of CFDictionaryRef objects + // structure with values dumped as strings. For each trust, an extra + // entry "SecPolicyOid" is added whose value is the OID for this trust. + // The extra entries are used to construct trustedKeyUsageValue. + List> trustSettings; + + // One or more OIDs defined in http://oidref.com/1.2.840.113635.100.1. + // It can also be "2.5.29.37.0" for a self-signed certificate with + // an empty trust settings. This value is never empty. When there are + // multiple OID values, it takes the form of "[1.1.1, 1.1.2]". + String trustedKeyUsageValue; }; /** @@ -300,6 +319,35 @@ } } + private record LocalAttr(String name, String value) + implements KeyStore.Entry.Attribute { + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException { + if (engineIsCertificateEntry(alias)) { + Object entry = entries.get(alias.toLowerCase()); + if (entry instanceof TrustedCertEntry tEntry) { + return new KeyStore.TrustedCertificateEntry( + tEntry.cert, Set.of( + new LocalAttr(KnownOIDs.ORACLE_TrustedKeyUsage.value(), tEntry.trustedKeyUsageValue), + new LocalAttr("trustSettings", tEntry.trustSettings.toString()))); + } + } + return super.engineGetEntry(alias, protParam); + } + /** * Returns the creation date of the entry identified by the given alias. * @@ -453,55 +501,12 @@ } /** - * Assigns the given certificate to the given alias. - * - *

    If the given alias already exists in this keystore and identifies a - * trusted certificate entry, the certificate associated with it is - * overridden by the given certificate. - * - * @param alias the alias name - * @param cert the certificate - * - * @exception KeyStoreException if the given alias already exists and does - * not identify a trusted certificate entry, or this operation - * fails for some other reason. + * Adding trusted certificate entry is not supported. */ public void engineSetCertificateEntry(String alias, Certificate cert) - throws KeyStoreException - { - permissionCheck(); - - synchronized(entries) { - - Object entry = entries.get(alias.toLowerCase()); - if ((entry != null) && (entry instanceof KeyEntry)) { - throw new KeyStoreException - ("Cannot overwrite key entry with certificate"); - } - - // This will be slow, but necessary. Enumerate the values and then see if the cert matches the one in the trusted cert entry. - // Security framework doesn't support the same certificate twice in a keychain. - Collection allValues = entries.values(); - - for (Object value : allValues) { - if (value instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)value; - if (tce.cert.equals(cert)) { - throw new KeyStoreException("Keychain does not support mulitple copies of same certificate."); - } - } - } - - TrustedCertEntry trustedCertEntry = new TrustedCertEntry(); - trustedCertEntry.cert = cert; - trustedCertEntry.date = new Date(); - String lowerAlias = alias.toLowerCase(); - if (entries.get(lowerAlias) != null) { - deletedEntries.put(lowerAlias, entries.get(lowerAlias)); - } - entries.put(lowerAlias, trustedCertEntry); - addedEntries.put(lowerAlias, trustedCertEntry); - } + throws KeyStoreException { + throw new KeyStoreException("Cannot set trusted certificate entry." + + " Use the macOS \"security add-trusted-cert\" command instead."); } /** @@ -680,10 +685,7 @@ String alias = e.nextElement(); Object entry = addedEntries.get(alias); if (entry instanceof TrustedCertEntry) { - TrustedCertEntry tce = (TrustedCertEntry)entry; - Certificate certElem; - certElem = tce.cert; - tce.certRef = addCertificateToKeychain(alias, certElem); + // Cannot set trusted certificate entry } else { KeyEntry keyEntry = (KeyEntry)entry; @@ -778,9 +780,28 @@ private native void _scanKeychain(); /** - * Callback method from _scanKeychain. If a trusted certificate is found, this method will be called. + * Callback method from _scanKeychain. If a trusted certificate is found, + * this method will be called. + * + * inputTrust is a list of strings in groups. Each group contains key/value + * pairs for one trust setting and ends with a null. Thus the size of the + * whole list is (2 * s_1 + 1) + (2 * s_2 + 1) + ... + (2 * s_n + 1), + * where s_i is the size of mapping for the i'th trust setting, + * and n is the number of trust settings. Ex: + * + * key1 for trust1 + * value1 for trust1 + * .. + * null (end of trust1) + * key1 for trust2 + * value1 for trust2 + * ... + * null (end of trust2) + * ... + * null (end if trust_n) */ - private void createTrustedCertEntry(String alias, long keychainItemRef, long creationDate, byte[] derStream) { + private void createTrustedCertEntry(String alias, List inputTrust, + long keychainItemRef, long creationDate, byte[] derStream) { TrustedCertEntry tce = new TrustedCertEntry(); try { @@ -791,6 +812,69 @@ tce.cert = cert; tce.certRef = keychainItemRef; + tce.trustSettings = new ArrayList<>(); + Map tmpMap = new LinkedHashMap<>(); + for (int i = 0; i < inputTrust.size(); i++) { + if (inputTrust.get(i) == null) { + tce.trustSettings.add(tmpMap); + if (i < inputTrust.size() - 1) { + // Prepare an empty map for the next trust setting. + // Do not just clear(), must be a new object. + // Only create if not at end of list. + tmpMap = new LinkedHashMap<>(); + } + } else { + tmpMap.put(inputTrust.get(i), inputTrust.get(i+1)); + i++; + } + } + + boolean isSelfSigned; + try { + cert.verify(cert.getPublicKey()); + isSelfSigned = true; + } catch (Exception e) { + isSelfSigned = false; + } + if (tce.trustSettings.isEmpty()) { + if (isSelfSigned) { + // If a self-signed certificate has an empty trust settings, + // trust it for all purposes + tce.trustedKeyUsageValue = KnownOIDs.anyExtendedKeyUsage.value(); + } else { + // Otherwise, return immediately. The certificate is not + // added into entries. + return; + } + } else { + List values = new ArrayList<>(); + for (var oneTrust : tce.trustSettings) { + var result = oneTrust.get("kSecTrustSettingsResult"); + // https://developer.apple.com/documentation/security/sectrustsettingsresult?language=objc + // 1 = kSecTrustSettingsResultTrustRoot, 2 = kSecTrustSettingsResultTrustAsRoot + // If missing, a default value of kSecTrustSettingsResultTrustRoot is assumed + // for self-signed certificates (see doc for SecTrustSettingsCopyTrustSettings). + // Note that the same SecPolicyOid can appear in multiple trust settings + // for different kSecTrustSettingsAllowedError and/or kSecTrustSettingsPolicyString. + if ((result == null && isSelfSigned) + || "1".equals(result) || "2".equals(result)) { + // When no kSecTrustSettingsPolicy, it means everything + String oid = oneTrust.getOrDefault("SecPolicyOid", + KnownOIDs.anyExtendedKeyUsage.value()); + if (!values.contains(oid)) { + values.add(oid); + } + } + } + if (values.isEmpty()) { + return; + } + if (values.size() == 1) { + tce.trustedKeyUsageValue = values.get(0); + } else { + tce.trustedKeyUsageValue = values.toString(); + } + } // Make a creation date. if (creationDate != 0) tce.date = new Date(creationDate); diff -Nru openjdk-17-17.0.2+8/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m openjdk-17-17.0.3+7/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m --- openjdk-17-17.0.2+8/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/macosx/native/libosxsecurity/KeystoreImpl.m 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, 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 @@ -368,6 +368,14 @@ } } +#define ADD(list, str) { \ + jobject localeObj = (*env)->NewStringUTF(env, [str UTF8String]); \ + (*env)->CallBooleanMethod(env, list, jm_listAdd, localeObj); \ + (*env)->DeleteLocalRef(env, localeObj); \ +} + +#define ADDNULL(list) (*env)->CallBooleanMethod(env, list, jm_listAdd, NULL) + static void addCertificatesToKeystore(JNIEnv *env, jobject keyStore) { // Search the user keychain list for all X509 certificates. @@ -379,8 +387,15 @@ jclass jc_KeychainStore = (*env)->FindClass(env, "apple/security/KeychainStore"); CHECK_NULL(jc_KeychainStore); jmethodID jm_createTrustedCertEntry = (*env)->GetMethodID( - env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;JJ[B)V"); + env, jc_KeychainStore, "createTrustedCertEntry", "(Ljava/lang/String;Ljava/util/List;JJ[B)V"); CHECK_NULL(jm_createTrustedCertEntry); + jclass jc_arrayListClass = (*env)->FindClass(env, "java/util/ArrayList"); + CHECK_NULL(jc_arrayListClass); + jmethodID jm_arrayListCons = (*env)->GetMethodID(env, jc_arrayListClass, "", "()V"); + CHECK_NULL(jm_arrayListCons); + jmethodID jm_listAdd = (*env)->GetMethodID(env, jc_arrayListClass, "add", "(Ljava/lang/Object;)Z"); + CHECK_NULL(jm_listAdd); + do { searchResult = SecKeychainSearchCopyNext(keychainItemSearch, &theItem); @@ -401,12 +416,50 @@ goto errOut; } + // Only add certificates with trusted settings + CFArrayRef trustSettings; + if (SecTrustSettingsCopyTrustSettings(certRef, kSecTrustSettingsDomainUser, &trustSettings) + == errSecItemNotFound) { + continue; + } + + // See KeychainStore::createTrustedCertEntry for content of inputTrust + jobject inputTrust = (*env)->NewObject(env, jc_arrayListClass, jm_arrayListCons); + CHECK_NULL(inputTrust); + + // Dump everything inside trustSettings into inputTrust + CFIndex count = CFArrayGetCount(trustSettings); + for (int i = 0; i < count; i++) { + CFDictionaryRef oneTrust = (CFDictionaryRef) CFArrayGetValueAtIndex(trustSettings, i); + CFIndex size = CFDictionaryGetCount(oneTrust); + const void * keys [size]; + const void * values [size]; + CFDictionaryGetKeysAndValues(oneTrust, keys, values); + for (int j = 0; j < size; j++) { + NSString* s = [NSString stringWithFormat:@"%@", keys[j]]; + ADD(inputTrust, s); + s = [NSString stringWithFormat:@"%@", values[j]]; + ADD(inputTrust, s); + } + SecPolicyRef certPolicy; + certPolicy = (SecPolicyRef)CFDictionaryGetValue(oneTrust, kSecTrustSettingsPolicy); + if (certPolicy != NULL) { + CFDictionaryRef policyDict = SecPolicyCopyProperties(certPolicy); + ADD(inputTrust, @"SecPolicyOid"); + NSString* s = [NSString stringWithFormat:@"%@", CFDictionaryGetValue(policyDict, @"SecPolicyOid")]; + ADD(inputTrust, s); + CFRelease(policyDict); + } + ADDNULL(inputTrust); + } + CFRelease(trustSettings); + // Find the creation date. jlong creationDate = getModDateFromItem(env, theItem); // Call back to the Java object to create Java objects corresponding to this security object. jlong nativeRef = ptr_to_jlong(certRef); - (*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, nativeRef, creationDate, certData); + (*env)->CallVoidMethod(env, keyStore, jm_createTrustedCertEntry, alias, inputTrust, nativeRef, creationDate, certData); JNU_CHECK_EXCEPTION(env); } } while (searchResult == noErr); @@ -522,8 +575,8 @@ /* * Class: apple_security_KeychainStore * Method: _addItemToKeychain - * Signature: (Ljava/lang/String;[B)I -*/ + * Signature: (Ljava/lang/String;Z[B[C)J + */ JNIEXPORT jlong JNICALL Java_apple_security_KeychainStore__1addItemToKeychain (JNIEnv *env, jobject this, jstring alias, jboolean isCertificate, jbyteArray rawDataObj, jcharArray passwordObj) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/File.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/File.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/File.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/File.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, 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 @@ -175,8 +175,9 @@ /** * Check if the file has an invalid path. Currently, the inspection of - * a file path is very limited, and it only covers Nul character check. - * Returning true means the path is definitely invalid/garbage. But + * a file path is very limited, and it only covers Nul character check + * unless further checking is explicitly enabled by a system property. + * Returning true means the path is definitely invalid/garbage, but * returning false does not guarantee that the path is valid. * * @return true if the file path is invalid. @@ -184,8 +185,7 @@ final boolean isInvalid() { PathStatus s = status; if (s == null) { - s = (this.path.indexOf('\u0000') < 0) ? PathStatus.CHECKED - : PathStatus.INVALID; + s = fs.isInvalid(this) ? PathStatus.INVALID : PathStatus.CHECKED; status = s; } return s == PathStatus.INVALID; diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/FileSystem.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/FileSystem.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/io/FileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/io/FileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2022, 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 @@ -88,6 +88,11 @@ public abstract boolean isAbsolute(File f); /** + * Tell whether the given abstract pathname is invalid. + */ + public abstract boolean isInvalid(File f); + + /** * Resolve the given abstract pathname into absolute form. Invoked by the * getAbsolutePath and getCanonicalPath methods in the File class. */ diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/lang/String.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/lang/String.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/lang/String.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/lang/String.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2022, 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 @@ -1284,14 +1284,17 @@ int sp = 0; int sl = val.length >> 1; byte[] dst = new byte[sl * 3]; - char c; - while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') { + while (sp < sl) { // ascii fast loop; + char c = StringUTF16.getChar(val, sp); + if (c >= '\u0080') { + break; + } dst[dp++] = (byte)c; sp++; } while (sp < sl) { - c = StringUTF16.getChar(val, sp++); + char c = StringUTF16.getChar(val, sp++); if (c < 0x80) { dst[dp++] = (byte)c; } else if (c < 0x800) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/math/BigInteger.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/math/BigInteger.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/math/BigInteger.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/math/BigInteger.java 2022-04-19 19:55:43.000000000 +0000 @@ -1670,8 +1670,8 @@ // are only considering the magnitudes as non-negative. The // Toom-Cook multiplication algorithm determines the sign // at its end from the two signum values. - if (bitLength(mag, mag.length) + - bitLength(val.mag, val.mag.length) > + if ((long)bitLength(mag, mag.length) + + (long)bitLength(val.mag, val.mag.length) > 32L*MAX_MAG_LENGTH) { reportOverflow(); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/java/util/random/package-info.java openjdk-17-17.0.3+7/src/java.base/share/classes/java/util/random/package-info.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/java/util/random/package-info.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/java/util/random/package-info.java 2022-04-19 19:55:43.000000000 +0000 @@ -599,32 +599,32 @@ * {@code 0xd1342543de82ef95L} * {@code xoroshiro128}, version 1.0 * {@code (24, 16, 37)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L64X256MixRandom" * {@code 0xd1342543de82ef95L} * {@code xoshiro256}, version 1.0 * {@code (17, 45)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L64X1024MixRandom" * {@code 0xd1342543de82ef95L} * {@code xoroshiro1024}, version 1.0 * {@code (25, 27, 36)} - * mixLea32{@code (s+x0)} + * mixLea64{@code (s+x0)} * "L128X128MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoroshiro128}, version 1.0 * {@code (24, 16, 37)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * "L128X256MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoshiro256}, version 1.0 * {@code (17, 45)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * "L128X1024MixRandom" * {@code 0x1d605bbb58c8abbfdL} * {@code xoroshiro1024}, version 1.0 * {@code (25, 27, 36)} - * mixLea32{@code (sh+x0)} + * mixLea64{@code (sh+x0)} * * * diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java openjdk-17-17.0.3+7/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/javax/crypto/EncryptedPrivateKeyInfo.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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 @@ -83,12 +83,12 @@ "must be non-null"); } - DerValue val = new DerValue(encoded); + this.encoded = encoded.clone(); + DerValue val = DerValue.wrap(this.encoded); if (val.tag != DerValue.tag_Sequence) { throw new IOException("DER header error: no SEQ tag"); } - this.encoded = encoded.clone(); DerValue[] seq = new DerValue[2]; seq[0] = val.data.getDerValue(); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/jimage/ImageStringsReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -309,7 +309,7 @@ return -1; } } - return length; + return current - offset + length; } static int mutf8FromStringLength(String s) { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/platform/Metrics.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/platform/Metrics.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -353,6 +353,27 @@ public long getMemorySoftLimit(); /***************************************************************** + * pids subsystem + ****************************************************************/ + + /** + * Returns the maximum number of tasks that may be created in the Isolation Group. + * + * @return The maximum number of tasks, -1 if the quota is unlimited or + * -2 if not supported. + * + */ + public long getPidsMax(); + + /** + * Returns the current number of tasks in the Isolation Group. + * + * @return The current number of tasks or -2 if not supported + * + */ + public long getPidsCurrent(); + + /***************************************************************** * BlKIO Subsystem ****************************************************************/ diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/jdk/internal/util/random/RandomSupport.java 2022-04-19 19:55:43.000000000 +0000 @@ -2380,7 +2380,7 @@ long bits = nextLong(); long multiplier = (1L << SALT_SHIFT) - 1; long salt = multiplier << (64 - SALT_SHIFT); - while ((salt & multiplier) != 0) { + while ((salt & multiplier) == 0) { long digit = Math.multiplyHigh(bits, multiplier); salt = (salt >>> SALT_SHIFT) | (digit << (64 - SALT_SHIFT)); bits *= multiplier; diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/launcher/LauncherHelper.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/launcher/LauncherHelper.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -405,12 +405,23 @@ limit = c.getMemoryAndSwapLimit(); ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ", longRetvalNotSupported)); + limit = c.getPidsMax(); + ostream.println(formatLimitString(limit, INDENT + "Maximum Processes Limit: ", + longRetvalNotSupported, false)); ostream.println(""); } private static String formatLimitString(long limit, String prefix, long unavailable) { + return formatLimitString(limit, prefix, unavailable, true); + } + + private static String formatLimitString(long limit, String prefix, long unavailable, boolean scale) { if (limit >= 0) { - return prefix + SizePrefix.scaleValue(limit); + if (scale) { + return prefix + SizePrefix.scaleValue(limit); + } else { + return prefix + limit; + } } else if (limit == unavailable) { return prefix + "N/A"; } else { diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java 2022-04-19 19:55:43.000000000 +0000 @@ -658,8 +658,8 @@ if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) { value = new AnnotationTypeMismatchExceptionProxy( - value.getClass() + "[" + value + "]").setMember( - annotationType.members().get(name)); + objectToString(value)) + .setMember(annotationType.members().get(name)); } } mv.put(name, value); @@ -669,6 +669,15 @@ UnsafeAccessor.setMemberValues(this, mv); } + /* + * Create a textual representation of the argument without calling + * any overridable methods of the argument. + */ + private static String objectToString(Object value) { + return value.getClass().getName() + "@" + + Integer.toHexString(System.identityHashCode(value)); + } + private static class UnsafeAccessor { private static final jdk.internal.misc.Unsafe unsafe = jdk.internal.misc.Unsafe.getUnsafe(); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/DSA.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/DSA.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/DSA.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/DSA.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, 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 @@ -362,7 +362,8 @@ s = new BigInteger(1, s.toByteArray()); } - if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1)) { + if ((r.compareTo(presetQ) == -1) && (s.compareTo(presetQ) == -1) + && r.signum() > 0 && s.signum() > 0) { BigInteger w = generateW(presetP, presetQ, presetG, s); BigInteger v = generateV(presetY, presetP, presetQ, presetG, w, r); return v.equals(r); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSP.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2022, 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 @@ -226,22 +226,26 @@ List extensions) throws IOException { OCSPRequest request = new OCSPRequest(certIds, extensions); byte[] bytes = request.encodeBytes(); + String responder = responderURI.toString(); if (debug != null) { - debug.println("connecting to OCSP service at: " + responderURI); + debug.println("connecting to OCSP service at: " + responder); } Event.report(Event.ReporterCategory.CRLCHECK, "event.ocsp.check", - responderURI.toString()); + responder); URL url; HttpURLConnection con = null; try { - String encodedGetReq = responderURI.toString() + "/" + - URLEncoder.encode(Base64.getEncoder().encodeToString(bytes), - "UTF-8"); + StringBuilder encodedGetReq = new StringBuilder(responder); + if (!responder.endsWith("/")) { + encodedGetReq.append("/"); + } + encodedGetReq.append(URLEncoder.encode( + Base64.getEncoder().encodeToString(bytes), "UTF-8")); if (encodedGetReq.length() <= 255) { - url = new URL(encodedGetReq); + url = new URL(encodedGetReq.toString()); con = (HttpURLConnection)url.openConnection(); con.setDoOutput(true); con.setDoInput(true); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/provider/certpath/OCSPResponse.java 2022-04-19 19:55:43.000000000 +0000 @@ -638,7 +638,10 @@ try { Signature respSignature = Signature.getInstance(sigAlgId.getName()); - respSignature.initVerify(cert.getPublicKey()); + SignatureUtil.initVerifyWithParam(respSignature, + cert.getPublicKey(), + SignatureUtil.getParamSpec(sigAlgId.getName(), + sigAlgId.getEncodedParams())); respSignature.update(tbsResponseData); if (respSignature.verify(signature)) { @@ -654,8 +657,8 @@ } return false; } - } catch (InvalidKeyException | NoSuchAlgorithmException | - SignatureException e) + } catch (InvalidAlgorithmParameterException | InvalidKeyException + | NoSuchAlgorithmException | SignatureException e) { throw new CertPathValidatorException(e); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPSSSignature.java 2022-04-19 19:55:43.000000000 +0000 @@ -123,12 +123,14 @@ @Override protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { - if (!(publicKey instanceof RSAPublicKey)) { + if (publicKey instanceof RSAPublicKey rsaPubKey) { + isPublicKeyValid(rsaPubKey); + this.pubKey = rsaPubKey; + this.privKey = null; + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPublicKey"); } - this.pubKey = (RSAPublicKey) isValid((RSAKey)publicKey); - this.privKey = null; - resetDigest(); } // initialize for signing. See JCA doc @@ -142,14 +144,16 @@ @Override protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { - if (!(privateKey instanceof RSAPrivateKey)) { + if (privateKey instanceof RSAPrivateKey rsaPrivateKey) { + isPrivateKeyValid(rsaPrivateKey); + this.privKey = rsaPrivateKey; + this.pubKey = null; + this.random = + (random == null ? JCAUtil.getSecureRandom() : random); + resetDigest(); + } else { throw new InvalidKeyException("key must be RSAPrivateKey"); } - this.privKey = (RSAPrivateKey) isValid((RSAKey)privateKey); - this.pubKey = null; - this.random = - (random == null? JCAUtil.getSecureRandom() : random); - resetDigest(); } /** @@ -202,10 +206,55 @@ } /** + * Validate the specified RSAPrivateKey + */ + private void isPrivateKeyValid(RSAPrivateKey prKey) throws InvalidKeyException { + try { + if (prKey instanceof RSAPrivateCrtKey crtKey) { + if (RSAPrivateCrtKeyImpl.checkComponents(crtKey)) { + RSAKeyFactory.checkRSAProviderKeyLengths( + crtKey.getModulus().bitLength(), + crtKey.getPublicExponent()); + } else { + throw new InvalidKeyException( + "Some of the CRT-specific components are not available"); + } + } else { + RSAKeyFactory.checkRSAProviderKeyLengths( + prKey.getModulus().bitLength(), + null); + } + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access private key components", e); + } + isValid(prKey); + } + + /** + * Validate the specified RSAPublicKey + */ + private void isPublicKeyValid(RSAPublicKey pKey) throws InvalidKeyException { + try { + RSAKeyFactory.checkRSAProviderKeyLengths( + pKey.getModulus().bitLength(), + pKey.getPublicExponent()); + } catch (InvalidKeyException ikEx) { + throw ikEx; + } catch (Exception e) { + throw new InvalidKeyException( + "Can not access public key components", e); + } + isValid(pKey); + } + + /** * Validate the specified RSAKey and its associated parameters against * internal signature parameters. */ - private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { + private void isValid(RSAKey rsaKey) throws InvalidKeyException { AlgorithmParameterSpec keyParams = rsaKey.getParams(); // validate key parameters if (!isCompatible(rsaKey.getParams(), this.sigParams)) { @@ -232,7 +281,6 @@ ("Unrecognized digest algo: " + digestAlgo); } } - return rsaKey; } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/rsa/RSAPrivateCrtKeyImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -91,16 +91,11 @@ RSAKeyFactory.checkKeyAlgo(key, type.keyAlgo); // check all CRT-specific components are available, if any one // missing, return a non-CRT key instead - if ((key.getPublicExponent().signum() == 0) || - (key.getPrimeExponentP().signum() == 0) || - (key.getPrimeExponentQ().signum() == 0) || - (key.getPrimeP().signum() == 0) || - (key.getPrimeQ().signum() == 0) || - (key.getCrtCoefficient().signum() == 0)) { + if (checkComponents(key)) { + return key; + } else { return new RSAPrivateKeyImpl(key.type, key.keyParams, key.getModulus(), key.getPrivateExponent()); - } else { - return key; } case "PKCS#1": try { @@ -125,6 +120,18 @@ } /** + * Validate if all CRT-specific components are available. + */ + static boolean checkComponents(RSAPrivateCrtKey key) { + return !((key.getPublicExponent().signum() == 0) || + (key.getPrimeExponentP().signum() == 0) || + (key.getPrimeExponentQ().signum() == 0) || + (key.getPrimeP().signum() == 0) || + (key.getPrimeQ().signum() == 0) || + (key.getCrtCoefficient().signum() == 0)); + } + + /** * Generate a new key from the specified type and components. * Returns a CRT key if possible and a non-CRT key otherwise. * Used by SunPKCS11 provider. diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -107,6 +107,11 @@ private static final boolean trustNameService = Utilities.getBooleanProperty("jdk.tls.trustNameService", false); + /* + * Default timeout to skip bytes from the open socket + */ + private static final int DEFAULT_SKIP_TIMEOUT = 1; + /** * Package-private constructor used to instantiate an unconnected * socket. @@ -1781,9 +1786,21 @@ if (conContext.inputRecord instanceof SSLSocketInputRecord inputRecord && isConnected) { if (appInput.readLock.tryLock()) { + int soTimeout = getSoTimeout(); try { + // deplete could hang on the skip operation + // in case of infinite socket read timeout. + // Change read timeout to avoid deadlock. + // This workaround could be replaced later + // with the right synchronization + if (soTimeout == 0) + setSoTimeout(DEFAULT_SKIP_TIMEOUT); inputRecord.deplete(false); + } catch (java.net.SocketTimeoutException stEx) { + // skip timeout exception during deplete } finally { + if (soTimeout == 0) + setSoTimeout(soTimeout); appInput.readLock.unlock(); } } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/tools/keytool/Main.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/tools/keytool/Main.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/tools/keytool/Main.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, 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 @@ -2208,6 +2208,9 @@ out.println(mf); dumpCert(cert, out); } else if (debug) { + for (var attr : keyStore.getEntry(alias, null).getAttributes()) { + System.out.println("Attribute " + attr.getName() + ": " + attr.getValue()); + } out.println(cert.toString()); } else { out.println("trustedCertEntry, "); diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java 2022-04-19 19:55:43.000000000 +0000 @@ -144,15 +144,14 @@ * then skip the tag and its 1 byte length of zero. */ private void writeTag() { - if (dataPos == dataSize) { - return; - } - assert dataPos + 1 < dataSize; - if (isEOC(data, dataPos)) { - dataPos += 2; // skip tag and length - writeTag(); - } else { - newData[newDataPos++] = data[dataPos++]; + while (dataPos < dataSize) { + assert dataPos + 1 < dataSize; + if (isEOC(data, dataPos)) { + dataPos += 2; // skip tag and length + } else { + newData[newDataPos++] = data[dataPos++]; + break; + } } } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerValue.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerValue.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/DerValue.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/DerValue.java 2022-04-19 19:55:43.000000000 +0000 @@ -303,6 +303,34 @@ } /** + * Wraps a byte array as a single DerValue. + * + * Attention: no cloning is made. + * + * @param buf the byte array containing the DER-encoded datum + * @returns a new DerValue + */ + public static DerValue wrap(byte[] buf) + throws IOException { + return wrap(buf, 0, buf.length); + } + + /** + * Wraps a byte array as a single DerValue. + * + * Attention: no cloning is made. + * + * @param buf the byte array containing the DER-encoded datum + * @param offset where the encoded datum starts inside {@code buf} + * @param len length of bytes to parse inside {@code buf} + * @returns a new DerValue + */ + public static DerValue wrap(byte[] buf, int offset, int len) + throws IOException { + return new DerValue(buf, offset, len, true, false); + } + + /** * Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly * one datum, including its tag and length. * diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/ObjectIdentifier.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, 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 @@ -365,7 +365,7 @@ if ((encoding[i] & 0x80) == 0) { // one section [fromPos..i] if (i - fromPos + 1 > 4) { - BigInteger big = new BigInteger(pack(encoding, + BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { result[which++] = 2; @@ -434,7 +434,7 @@ sb.append('.'); } if (i - fromPos + 1 > 4) { // maybe big integer - BigInteger big = new BigInteger( + BigInteger big = new BigInteger(1, pack(encoding, fromPos, i-fromPos+1, 7, 8)); if (fromPos == 0) { // first section encoded with more than 4 bytes, @@ -684,6 +684,10 @@ } private static void checkOidSize(int oidLength) throws IOException { + if (oidLength < 0) { + throw new IOException("ObjectIdentifier encoded length was " + + "negative: " + oidLength); + } if (oidLength > MAXIMUM_OID_SIZE) { throw new IOException( "ObjectIdentifier encoded length exceeds " + diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/SignatureUtil.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/SignatureUtil.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/util/SignatureUtil.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/util/SignatureUtil.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2021, 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 @@ -170,8 +170,7 @@ // for verification with the specified key and params (may be null) public static void initVerifyWithParam(Signature s, PublicKey key, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, key, params); } @@ -180,8 +179,7 @@ public static void initVerifyWithParam(Signature s, java.security.cert.Certificate cert, AlgorithmParameterSpec params) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initVerify(s, cert, params); } @@ -189,8 +187,7 @@ // for signing with the specified key and params (may be null) public static void initSignWithParam(Signature s, PrivateKey key, AlgorithmParameterSpec params, SecureRandom sr) - throws ProviderException, InvalidAlgorithmParameterException, - InvalidKeyException { + throws InvalidAlgorithmParameterException, InvalidKeyException { SharedSecrets.getJavaSecuritySignatureAccess().initSign(s, key, params, sr); } @@ -342,10 +339,10 @@ * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, String provider) + public static Signature fromKey(String sigAlg, PrivateKey key, String provider) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException{ Signature sigEngine = (provider == null || provider.isEmpty()) @@ -358,10 +355,10 @@ * Create a Signature that has been initialized with proper key and params. * * @param sigAlg signature algorithms - * @param key public or private key + * @param key private key * @param provider (optional) provider */ - public static Signature fromKey(String sigAlg, Key key, Provider provider) + public static Signature fromKey(String sigAlg, PrivateKey key, Provider provider) throws NoSuchAlgorithmException, InvalidKeyException{ Signature sigEngine = (provider == null) ? Signature.getInstance(sigAlg) @@ -369,17 +366,12 @@ return autoInitInternal(sigAlg, key, sigEngine); } - private static Signature autoInitInternal(String alg, Key key, Signature s) + private static Signature autoInitInternal(String alg, PrivateKey key, Signature s) throws InvalidKeyException { AlgorithmParameterSpec params = SignatureUtil .getDefaultParamSpec(alg, key); try { - if (key instanceof PrivateKey) { - SignatureUtil.initSignWithParam(s, (PrivateKey) key, params, - null); - } else { - SignatureUtil.initVerifyWithParam(s, (PublicKey) key, params); - } + SignatureUtil.initSignWithParam(s, key, params, null); } catch (InvalidAlgorithmParameterException e) { throw new AssertionError("Should not happen", e); } diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/AlgorithmId.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/AlgorithmId.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/AlgorithmId.java 2022-04-19 19:55:43.000000000 +0000 @@ -312,7 +312,7 @@ * * @return DER encoded parameters, or null not present. */ - public byte[] getEncodedParams() throws IOException { + public byte[] getEncodedParams() { return (encodedParams == null || algid.toString().equals(KnownOIDs.SpecifiedSHA2withECDSA.value())) ? null diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, 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 @@ -820,13 +820,7 @@ * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (sigAlgId == null) - return null; - try { - return sigAlgId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return sigAlgId == null ? null : sigAlgId.getEncodedParams(); } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CertImpl.java openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CertImpl.java --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/security/x509/X509CertImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1030,13 +1030,7 @@ * null if no parameters are present. */ public byte[] getSigAlgParams() { - if (algId == null) - return null; - try { - return algId.getEncodedParams(); - } catch (IOException e) { - return null; - } + return algId == null ? null : algId.getEncodedParams(); } /** diff -Nru openjdk-17-17.0.2+8/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties openjdk-17-17.0.3+7/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties --- openjdk-17-17.0.2+8/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/share/classes/sun/util/resources/CurrencyNames.properties 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2005, 2021, 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 @@ -251,6 +251,7 @@ UYU=UYU UZS=UZS VEB=VEB +VED=VED VEF=VEF VES=VES VND=VND @@ -474,6 +475,7 @@ uyu=Uruguayan Peso uzs=Uzbekistan Som veb=Venezuelan Bol\u00edvar (1871-2008) +ved=Venezuelan Bol\u00edvar Soberano vef=Venezuelan Bol\u00edvar ves=Venezuelan Bol\u00edvar Soberano vnd=Vietnamese Dong diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/classes/java/io/UnixFileSystem.java openjdk-17-17.0.3+7/src/java.base/unix/classes/java/io/UnixFileSystem.java --- openjdk-17-17.0.2+8/src/java.base/unix/classes/java/io/UnixFileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/classes/java/io/UnixFileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2022, 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 @@ -142,6 +142,11 @@ } @Override + public boolean isInvalid(File f) { + return f.getPath().indexOf('\u0000') < 0 ? false : true; + } + + @Override public String resolve(File f) { if (isAbsolute(f)) return f.getPath(); @SuppressWarnings("removal") diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/classes/sun/net/www/content-types.properties openjdk-17-17.0.3+7/src/java.base/unix/classes/sun/net/www/content-types.properties --- openjdk-17-17.0.2+8/src/java.base/unix/classes/sun/net/www/content-types.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/classes/sun/net/www/content-types.properties 2022-04-19 19:55:43.000000000 +0000 @@ -27,7 +27,7 @@ # application/octet-stream: \ description=Generic Binary Stream;\ - file_extensions=.saveme,.dump,.hqx,.arc,.o,.a,.bin,.exe,.z,.gz + file_extensions=.saveme,.dump,.hqx,.arc,.o,.a,.bin,.exe,.z application/oda: \ description=ODA Document;\ @@ -253,14 +253,26 @@ description=Bitmap Image;\ file_extensions=.bmp; +image/webp: \ + description=WEBP image;\ + file_extensions=.webp; + +text/css: \ + description=CSS File;\ + file_extensions=.css; + text/html: \ description=HTML Document;\ file_extensions=.htm,.html;\ icon=html +text/javascript: \ + description=JavaScript File;\ + file_extensions=.js; + text/plain: \ description=Plain Text;\ - file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el;\ + file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el,.php,.adoc,.py;\ icon=text;\ action=browser @@ -272,6 +284,14 @@ description=Structure Enhanced Text;\ file_extensions=.etx +text/csv: \ + description=CSV File;\ + file_extensions=.csv; + +text/markdown: \ + description=Markdown File;\ + file_extensions=.md,.markdown + video/mp4: \ description=MPEG-4 Video;\ file_extensions=.m4v,.mp4 @@ -311,3 +331,67 @@ application/xml: \ description=XML document;\ file_extensions=.xml + +application/rtf: \ + description=WordPad Document;\ + file_extensions=.rtf; + +application/gzip: \ + description=GZip File;\ + file_extensions=.gz; + +application/vnd.oasis.opendocument.presentation: \ + description=OpenDocument presentation document;\ + file_extensions=.odp; + +application/vnd.oasis.opendocument.spreadsheet: \ + description=OpenDocument spreadsheet document;\ + file_extensions=.ods; + +application/vnd.oasis.opendocument.text: \ + description=OpenDocument text document;\ + file_extensions=.odt; + +application/vnd.ms-excel: \ + description=Microsoft Excel File;\ + file_extensions=.xls; + +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: \ + description=XLSX File;\ + file_extensions=.xlsx; + +application/vnd.openxmlformats-officedocument.presentationml.presentation: \ + description=PPTX File;\ + file_extensions=.pptx; + +application/vnd.ms-powerpoint: \ + description=Microsoft PowerPoint File;\ + file_extensions=.ppt; + +application/x-7z-compressed: \ + description=7-Zip File;\ + file_extensions=.7z; + +application/msword: \ + description=Microsoft Word File;\ + file_extensions=.doc; + +application/vnd.openxmlformats-officedocument.wordprocessingml.document: \ + description=DOCX File;\ + file_extensions=.docx; + +application/vnd.rar: \ + description=RAR File;\ + file_extensions=.rar; + +application/json: \ + description=JSON File;\ + file_extensions=.json; + +application/bz2: \ + description=BZ2 File;\ + file_extensions=.bz2; + +application/java-archive: \ + description=JAR File;\ + file_extensions=.jar; diff -Nru openjdk-17-17.0.2+8/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c openjdk-17-17.0.3+7/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c --- openjdk-17-17.0.2+8/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c 2022-04-19 19:55:43.000000000 +0000 @@ -317,7 +317,7 @@ /* supports extended attributes */ -#ifdef _SYS_XATTR_H_ +#if defined(_SYS_XATTR_H) || defined(_SYS_XATTR_H_) capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_XATTR; #endif @@ -1330,4 +1330,4 @@ if (res == (size_t)-1) throwUnixException(env, errno); return (jint)res; -} \ No newline at end of file +} diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/classes/java/io/WinNTFileSystem.java openjdk-17-17.0.3+7/src/java.base/windows/classes/java/io/WinNTFileSystem.java --- openjdk-17-17.0.2+8/src/java.base/windows/classes/java/io/WinNTFileSystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/classes/java/io/WinNTFileSystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2022, 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 java.io; import java.io.File; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.BitSet; import java.util.Locale; @@ -45,6 +46,21 @@ private final char semicolon; private final String userDir; + // Whether to enable alternative data streams (ADS) by suppressing + // checking the path for invalid characters, in particular ":". + // ADS support will be enabled if and only if the property is set and + // is the empty string or is equal, ignoring case, to the string "true". + // By default ADS support is disabled. + private static final boolean ENABLE_ADS; + static { + String enableADS = GetPropertyAction.privilegedGetProperty("jdk.io.File.enableADS"); + if (enableADS != null) { + ENABLE_ADS = "".equals(enableADS) || Boolean.parseBoolean(enableADS); + } else { + ENABLE_ADS = false; + } + } + public WinNTFileSystem() { Properties props = GetPropertyAction.privilegedGetProperties(); slash = props.getProperty("file.separator").charAt(0); @@ -306,6 +322,36 @@ } @Override + public boolean isInvalid(File f) { + if (f.getPath().indexOf('\u0000') >= 0) + return true; + + if (ENABLE_ADS) + return false; + + // Invalid if there is a ":" at a position greater than 1, or if there + // is a ":" at position 1 and the first character is not a letter + String pathname = f.getPath(); + int lastColon = pathname.lastIndexOf(":"); + + // Valid if there is no ":" present or if the last ":" present is + // at index 1 and the first character is a latter + if (lastColon < 0 || + (lastColon == 1 && isLetter(pathname.charAt(0)))) + return false; + + // Invalid if path creation fails + Path path = null; + try { + path = sun.nio.fs.DefaultFileSystemProvider.theFileSystem().getPath(pathname); + return false; + } catch (InvalidPathException ignored) { + } + + return true; + } + + @Override public String resolve(File f) { String path = f.getPath(); int pl = f.getPrefixLength(); diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/classes/sun/net/www/content-types.properties openjdk-17-17.0.3+7/src/java.base/windows/classes/sun/net/www/content-types.properties --- openjdk-17-17.0.2+8/src/java.base/windows/classes/sun/net/www/content-types.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/classes/sun/net/www/content-types.properties 2022-04-19 19:55:43.000000000 +0000 @@ -27,7 +27,7 @@ # application/octet-stream: \ description=Generic Binary Stream;\ - file_extensions=.saveme,.dump,.hqx,.arc,.obj,.lib,.bin,.exe,.zip,.gz + file_extensions=.saveme,.dump,.hqx,.arc,.obj,.lib,.bin,.exe application/oda: \ description=ODA Document;\ @@ -43,11 +43,15 @@ icon=ps application/rtf: \ - description=Wordpad Document;\ + description=WordPad Document;\ file_extensions=.rtf;\ action=application;\ application=wordpad.exe %s +application/gzip: \ + description=GZip File;\ + file_extensions=.gz; + application/x-dvi: \ description=TeX DVI File;\ file_extensions=.dvi @@ -250,14 +254,26 @@ description=Bitmap Image;\ file_extensions=.bmp; +image/webp: \ + description=WEBP image;\ + file_extensions=.webp; + +text/css: \ + description=CSS File;\ + file_extensions=.css; + text/html: \ description=HTML Document;\ file_extensions=.htm,.html;\ icon=html +text/javascript: \ + description=JavaScript File;\ + file_extensions=.js; + text/plain: \ description=Plain Text;\ - file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el;\ + file_extensions=.text,.c,.cc,.c++,.h,.pl,.txt,.java,.el,.php,.adoc,.py;\ icon=text;\ action=browser @@ -269,6 +285,14 @@ description=Structure Enhanced Text;\ file_extensions=.etx +text/csv: \ + description=CSV File;\ + file_extensions=.csv; + +text/markdown: \ + description=Markdown File;\ + file_extensions=.md,.markdown + video/mp4: \ description=MPEG-4 Video;\ file_extensions=.m4v,.mp4 @@ -308,3 +332,59 @@ application/xml: \ description=XML document;\ file_extensions=.xml + +application/vnd.oasis.opendocument.presentation: \ + description=OpenDocument presentation document;\ + file_extensions=.odp; + +application/vnd.oasis.opendocument.spreadsheet: \ + description=OpenDocument spreadsheet document;\ + file_extensions=.ods; + +application/vnd.oasis.opendocument.text: \ + description=OpenDocument text document;\ + file_extensions=.odt; + +application/vnd.ms-excel: \ + description=Microsoft Excel File;\ + file_extensions=.xls; + +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet: \ + description=XLSX File;\ + file_extensions=.xlsx; + +application/vnd.openxmlformats-officedocument.presentationml.presentation: \ + description=PPTX File;\ + file_extensions=.pptx; + +application/vnd.ms-powerpoint: \ + description=Microsoft PowerPoint File;\ + file_extensions=.ppt; + +application/x-7z-compressed: \ + description=7-Zip File;\ + file_extensions=.7z; + +application/msword: \ + description=Microsoft Word File;\ + file_extensions=.doc; + +application/vnd.openxmlformats-officedocument.wordprocessingml.document: \ + description=DOCX File;\ + file_extensions=.docx; + +application/vnd.rar: \ + description=RAR File;\ + file_extensions=.rar; + +application/json: \ + description=JSON File;\ + file_extensions=.json; + +application/bz2: \ + description=BZ2 File;\ + file_extensions=.bz2; + +application/java-archive: \ + description=JAR File;\ + file_extensions=.jar; diff -Nru openjdk-17-17.0.2+8/src/java.base/windows/native/libnet/DefaultProxySelector.c openjdk-17-17.0.3+7/src/java.base/windows/native/libnet/DefaultProxySelector.c --- openjdk-17-17.0.2+8/src/java.base/windows/native/libnet/DefaultProxySelector.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.base/windows/native/libnet/DefaultProxySelector.c 2022-04-19 19:55:43.000000000 +0000 @@ -104,7 +104,6 @@ int nr_elems = 0; wchar_t *context = NULL; wchar_t *current_proxy = NULL; - BOOL error = FALSE; /* * The proxy server list contains one or more of the following strings @@ -116,7 +115,6 @@ LPWSTR pport; LPWSTR phost; int portVal = 0; - wchar_t *next_proxy = NULL; list_item *proxy = NULL; wchar_t* pos = NULL; @@ -290,7 +288,6 @@ } if (win_proxy != NULL) { - wchar_t *context = NULL; int defport = 0; int nr_elems = 0; @@ -313,27 +310,28 @@ nr_elems = createProxyList(win_proxy, lpProto, &head); if (nr_elems != 0 && head != NULL) { int index = 0; + list_item *current = head; proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL); if (proxy_array == NULL || (*env)->ExceptionCheck(env)) { goto noproxy; } - while (head != NULL && index < nr_elems) { + while (current != NULL && index < nr_elems) { jstring jhost; jobject isa; jobject proxy; - if (head->host != NULL && proxy_array != NULL) { + if (current->host != NULL && proxy_array != NULL) { /* Let's create the appropriate Proxy object then. */ - if (head->port == 0) { - head->port = defport; + if (current->port == 0) { + current->port = defport; } - jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host)); + jhost = (*env)->NewString(env, current->host, (jsize)wcslen(current->host)); if (jhost == NULL || (*env)->ExceptionCheck(env)) { proxy_array = NULL; } isa = (*env)->CallStaticObjectMethod(env, isaddr_class, isaddr_createUnresolvedID, jhost, - head->port); + current->port); if (isa == NULL || (*env)->ExceptionCheck(env)) { proxy_array = NULL; } @@ -347,7 +345,7 @@ } index++; } - head = head->next; + current = current->next; } } } diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -67,6 +67,7 @@ import com.sun.imageio.plugins.common.I18N; import com.sun.imageio.plugins.common.ImageUtil; +import com.sun.imageio.plugins.common.ReaderUtil; /** This class is the Java Image IO plugin reader for BMP images. * It may subsample the image, clip the image, select sub-bands, @@ -1542,9 +1543,8 @@ } // Read till we have the whole image - byte[] values = new byte[imSize]; - int bytesRead = 0; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Since data is compressed, decompress it decodeRLE8(imSize, padding, values, bdata); @@ -1726,8 +1726,8 @@ } // Read till we have the whole image - byte[] values = new byte[imSize]; - iis.readFully(values, 0, imSize); + byte[] values = ReaderUtil. + staggeredReadByteStream(iis, imSize); // Decompress the RLE4 compressed data. decodeRLE4(imSize, padding, values, bdata); diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/com/sun/media/sound/AudioFileSoundbankReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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,12 +78,26 @@ public Soundbank getSoundbank(AudioInputStream ais) throws InvalidMidiDataException, IOException { + int MEGABYTE = 1048576; + int DEFAULT_BUFFER_SIZE = 65536; + int MAX_FRAME_SIZE = 1024; try { byte[] buffer; - if (ais.getFrameLength() == -1) { + int frameSize = ais.getFormat().getFrameSize(); + if (frameSize <= 0 || frameSize > MAX_FRAME_SIZE) { + throw new InvalidMidiDataException("Formats with frame size " + + frameSize + " are not supported"); + } + + long totalSize = ais.getFrameLength() * frameSize; + if (totalSize >= Integer.MAX_VALUE - 2) { + throw new InvalidMidiDataException( + "Can not allocate enough memory to read audio data."); + } + + if (ais.getFrameLength() == -1 || totalSize > MEGABYTE) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buff = new byte[1024 - - (1024 % ais.getFormat().getFrameSize())]; + byte[] buff = new byte[DEFAULT_BUFFER_SIZE - (DEFAULT_BUFFER_SIZE % frameSize)]; int ret; while ((ret = ais.read(buff)) != -1) { baos.write(buff, 0, ret); @@ -91,8 +105,7 @@ ais.close(); buffer = baos.toByteArray(); } else { - buffer = new byte[(int) (ais.getFrameLength() - * ais.getFormat().getFrameSize())]; + buffer = new byte[(int) totalSize]; new DataInputStream(ais).readFully(buffer); } ModelByteBufferWavetable osc = new ModelByteBufferWavetable( diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/javax/swing/JLabel.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/javax/swing/JLabel.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/javax/swing/JLabel.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/javax/swing/JLabel.java 2022-04-19 19:55:43.000000000 +0000 @@ -1075,6 +1075,10 @@ * @see AccessibleContext#setAccessibleName */ public String getAccessibleName() { + return getAccessibleNameCheckIcon(getAccessibleNameImpl()); + } + + private String getAccessibleNameImpl() { String name = accessibleName; if (name == null) { @@ -1089,6 +1093,19 @@ return name; } + private String getAccessibleNameCheckIcon(String name) { + if (((name == null) || name.isEmpty()) && + (JLabel.this.getIcon() != null)) { + if (JLabel.this.getIcon() instanceof Accessible) { + AccessibleContext ac = ((Accessible) JLabel.this.getIcon()).getAccessibleContext(); + if (ac != null) { + name = ac.getAccessibleName(); + } + } + } + return name; + } + /** * Get the role of this object. * @@ -1097,6 +1114,11 @@ * @see AccessibleRole */ public AccessibleRole getAccessibleRole() { + String name = getAccessibleNameImpl(); + if (((name == null) || name.isEmpty()) && + (JLabel.this.getIcon() != null)) { + return AccessibleRole.ICON; + } return AccessibleRole.LABEL; } diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMS.java 2022-04-19 19:55:43.000000000 +0000 @@ -148,7 +148,7 @@ } /* methods invoked from LCMSTransform */ - public static native void colorConvert(LCMSTransform trans, + public static native void colorConvert(long trans, LCMSImageLayout src, LCMSImageLayout dest); diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java --- openjdk-17-17.0.2+8/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/classes/sun/java2d/cmm/lcms/LCMSTransform.java 2022-04-19 19:55:43.000000000 +0000 @@ -44,18 +44,30 @@ import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; +import java.lang.ref.Reference; import sun.java2d.cmm.ColorTransform; import static sun.java2d.cmm.lcms.LCMSImageLayout.ImageLayoutException; final class LCMSTransform implements ColorTransform { - long ID; - private int inFormatter = 0; - private boolean isInIntPacked = false; - private int outFormatter = 0; - private boolean isOutIntPacked = false; + private final static class NativeTransform { + private long ID; + private int inFormatter; + private boolean isInIntPacked; + private int outFormatter; + private boolean isOutIntPacked; + + private boolean match(LCMSImageLayout in, LCMSImageLayout out) { + return inFormatter == in.pixelType + && isInIntPacked == in.isIntPacked + && outFormatter == out.pixelType + && isOutIntPacked == out.isIntPacked; + } + } + + private volatile NativeTransform transform; ICC_Profile[] profiles; LCMSProfile[] lcmsProfiles; int renderType; @@ -64,8 +76,6 @@ private int numInComponents = -1; private int numOutComponents = -1; - private Object disposerReferent = new Object(); - public LCMSTransform(ICC_Profile profile, int renderType, int transformType) { @@ -122,31 +132,32 @@ return numOutComponents; } - private synchronized void doTransform(LCMSImageLayout in, - LCMSImageLayout out) { - // update native transfrom if needed - if (ID == 0L || - inFormatter != in.pixelType || isInIntPacked != in.isIntPacked || - outFormatter != out.pixelType || isOutIntPacked != out.isIntPacked) - { - - if (ID != 0L) { - // Disposer will destroy forgotten transform - disposerReferent = new Object(); + private void doTransform(LCMSImageLayout in, LCMSImageLayout out) { + NativeTransform tfm = transform; + // update native transform if needed + if (tfm == null || !tfm.match(in, out)) { + synchronized (this) { + tfm = transform; + if (tfm == null || !tfm.match(in, out)) { + tfm = new NativeTransform(); + tfm.inFormatter = in.pixelType; + tfm.isInIntPacked = in.isIntPacked; + + tfm.outFormatter = out.pixelType; + tfm.isOutIntPacked = out.isIntPacked; + + tfm.ID = LCMS.createTransform(lcmsProfiles, renderType, + tfm.inFormatter, + tfm.isInIntPacked, + tfm.outFormatter, + tfm.isOutIntPacked, tfm); + // Disposer will destroy forgotten transform + transform = tfm; + } } - inFormatter = in.pixelType; - isInIntPacked = in.isIntPacked; - - outFormatter = out.pixelType; - isOutIntPacked = out.isIntPacked; - - ID = LCMS.createTransform(lcmsProfiles, renderType, - inFormatter, isInIntPacked, - outFormatter, isOutIntPacked, - disposerReferent); } - - LCMS.colorConvert(this, in, out); + LCMS.colorConvert(tfm.ID, in, out); + Reference.reachabilityFence(tfm); // prevent deallocation of "tfm.ID" } /** diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/LCMS.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/LCMS.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/LCMS.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/LCMS.c 2022-04-19 19:55:43.000000000 +0000 @@ -71,7 +71,6 @@ } TagSignature_t, *TagSignature_p; static jfieldID Trans_renderType_fID; -static jfieldID Trans_ID_fID; static jfieldID IL_isIntPacked_fID; static jfieldID IL_dataType_fID; static jfieldID IL_pixelType_fID; @@ -510,9 +509,9 @@ * Signature: (Lsun/java2d/cmm/lcms/LCMSTransform;Lsun/java2d/cmm/lcms/LCMSImageLayout;Lsun/java2d/cmm/lcms/LCMSImageLayout;)V */ JNIEXPORT void JNICALL Java_sun_java2d_cmm_lcms_LCMS_colorConvert - (JNIEnv *env, jclass cls, jobject trans, jobject src, jobject dst) + (JNIEnv *env, jclass cls, jlong ID, jobject src, jobject dst) { - cmsHTRANSFORM sTrans = NULL; + cmsHTRANSFORM sTrans = jlong_to_ptr(ID); int srcDType, dstDType; int srcOffset, srcNextRowOffset, dstOffset, dstNextRowOffset; int width, height, i; @@ -533,8 +532,6 @@ srcAtOnce = (*env)->GetBooleanField(env, src, IL_imageAtOnce_fID); dstAtOnce = (*env)->GetBooleanField(env, dst, IL_imageAtOnce_fID); - sTrans = jlong_to_ptr((*env)->GetLongField (env, trans, Trans_ID_fID)); - if (sTrans == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "LCMS_colorConvert: transform == NULL"); JNU_ThrowByName(env, "java/awt/color/CMMException", @@ -626,11 +623,6 @@ if (Trans_renderType_fID == NULL) { return; } - Trans_ID_fID = (*env)->GetFieldID (env, Trans, "ID", "J"); - if (Trans_ID_fID == NULL) { - return; - } - IL_isIntPacked_fID = (*env)->GetFieldID (env, IL, "isIntPacked", "Z"); if (IL_isIntPacked_fID == NULL) { return; diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsalpha.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsalpha.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsalpha.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsalpha.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,675 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Alpha copy ------------------------------------------------------------------------------------------------------------------ + +// This macro return words stored as big endian +#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) + + +// Floor to byte, taking care of saturation +cmsINLINE cmsUInt8Number _cmsQuickSaturateByte(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 255.0) return 255; + + return (cmsUInt8Number) _cmsQuickFloorWord(d); +} + + +// Return the size in bytes of a given formatter +static +cmsUInt32Number trueBytesSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field returns zero + if (fmt_bytes == 0) + return sizeof(double); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + + +// Several format converters + +typedef void(*cmsFormatterAlphaFn)(void* dst, const void* src); + + +// From 8 + +static +void copy8(void* dst, const void* src) +{ + memmove(dst, src, 1); +} + +static +void from8to16(void* dst, const void* src) +{ + cmsUInt8Number n = *(cmsUInt8Number*)src; + *(cmsUInt16Number*) dst = (cmsUInt16Number) FROM_8_TO_16(n); +} + +static +void from8to16SE(void* dst, const void* src) +{ + cmsUInt8Number n = *(cmsUInt8Number*)src; + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(FROM_8_TO_16(n)); +} + +static +void from8toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (cmsFloat32Number) (*(cmsUInt8Number*)src) / 255.0f; +} + +static +void from8toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt8Number*)src) / 255.0; +} + +static +void from8toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt8Number*)src) / 255.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From 16 + +static +void from16to8(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt8Number*) dst = FROM_16_TO_8(n); +} + +static +void from16SEto8(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt8Number*)dst = FROM_16_TO_8(CHANGE_ENDIAN(n)); +} + +static +void copy16(void* dst, const void* src) +{ + memmove(dst, src, 2); +} + +static +void from16to16(void* dst, const void* src) +{ + cmsUInt16Number n = *(cmsUInt16Number*)src; + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(n); +} + +static +void from16toFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (*(cmsUInt16Number*)src) / 65535.0f; +} + +static +void from16SEtoFLT(void* dst, const void* src) +{ + *(cmsFloat32Number*)dst = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f; +} + +static +void from16toDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (*(cmsUInt16Number*)src) / 65535.0; +} + +static +void from16SEtoDBL(void* dst, const void* src) +{ + *(cmsFloat64Number*)dst = (cmsFloat64Number) (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0; +} + +static +void from16toHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (*(cmsUInt16Number*)src) / 65535.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void from16SEtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (CHANGE_ENDIAN(*(cmsUInt16Number*)src)) / 65535.0f; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} +// From Float + +static +void fromFLTto8(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +} + +static +void fromFLTto16(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void fromFLTto16SE(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +} + +static +void copy32(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat32Number)); +} + +static +void fromFLTtoDBL(void* dst, const void* src) +{ + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsFloat64Number*)dst = (cmsFloat64Number)n; +} + +static +void fromFLTtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = *(cmsFloat32Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + + +// From HALF + +static +void fromHLFto8(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif + +} + +static +void fromHLFto16(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFto16SE(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoFLT(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat32Number*)dst = _cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void fromHLFtoDBL(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + *(cmsFloat64Number*)dst = (cmsFloat64Number)_cmsHalf2Float(*(cmsUInt16Number*)src); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +// From double +static +void fromDBLto8(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); +} + +static +void fromDBLto16(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); +} + +static +void fromDBLto16SE(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); +} + +static +void fromDBLtoFLT(void* dst, const void* src) +{ + cmsFloat64Number n = *(cmsFloat64Number*)src; + *(cmsFloat32Number*)dst = (cmsFloat32Number) n; +} + +static +void fromDBLtoHLF(void* dst, const void* src) +{ +#ifndef CMS_NO_HALF_SUPPORT + cmsFloat32Number n = (cmsFloat32Number) *(cmsFloat64Number*)src; + *(cmsUInt16Number*)dst = _cmsFloat2Half(n); +#else + cmsUNUSED_PARAMETER(dst); + cmsUNUSED_PARAMETER(src); +#endif +} + +static +void copy64(void* dst, const void* src) +{ + memmove(dst, src, sizeof(cmsFloat64Number)); +} + + +// Returns the position (x or y) of the formatter in the table of functions +static +int FormatterPos(cmsUInt32Number frm) +{ + cmsUInt32Number b = T_BYTES(frm); + + if (b == 0 && T_FLOAT(frm)) + return 5; // DBL +#ifndef CMS_NO_HALF_SUPPORT + if (b == 2 && T_FLOAT(frm)) + return 3; // HLF +#endif + if (b == 4 && T_FLOAT(frm)) + return 4; // FLT + if (b == 2 && !T_FLOAT(frm)) + { + if (T_ENDIAN16(frm)) + return 2; // 16SE + else + return 1; // 16 + } + if (b == 1 && !T_FLOAT(frm)) + return 0; // 8 + return -1; // not recognized +} + +// Obtains an alpha-to-alpha function formatter +static +cmsFormatterAlphaFn _cmsGetFormatterAlpha(cmsContext id, cmsUInt32Number in, cmsUInt32Number out) +{ +static cmsFormatterAlphaFn FormattersAlpha[6][6] = { + + /* from 8 */ { copy8, from8to16, from8to16SE, from8toHLF, from8toFLT, from8toDBL }, + /* from 16*/ { from16to8, copy16, from16to16, from16toHLF, from16toFLT, from16toDBL }, + /* from 16SE*/{ from16SEto8, from16to16, copy16, from16SEtoHLF,from16SEtoFLT, from16SEtoDBL }, + /* from HLF*/ { fromHLFto8, fromHLFto16, fromHLFto16SE, copy16, fromHLFtoFLT, fromHLFtoDBL }, + /* from FLT*/ { fromFLTto8, fromFLTto16, fromFLTto16SE, fromFLTtoHLF, copy32, fromFLTtoDBL }, + /* from DBL*/ { fromDBLto8, fromDBLto16, fromDBLto16SE, fromDBLtoHLF, fromDBLtoFLT, copy64 }}; + + int in_n = FormatterPos(in); + int out_n = FormatterPos(out); + + if (in_n < 0 || out_n < 0 || in_n > 5 || out_n > 5) { + + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized alpha channel width"); + return NULL; + } + + return FormattersAlpha[in_n][out_n]; +} + + + +// This function computes the distance from each component to the next one in bytes. +static +void ComputeIncrementsForChunky(cmsUInt32Number Format, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + cmsUInt32Number pixelSize = channelSize * total_chans; + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = pixelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 1) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans-1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + if (channelSize > 1) + for (i = 0; i < total_chans; i++) { + channels[i] *= channelSize; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// On planar configurations, the distance is the stride added to any non-negative +static +void ComputeIncrementsForPlanar(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + cmsUInt32Number channels[cmsMAXCHANNELS]; + cmsUInt32Number extra = T_EXTRA(Format); + cmsUInt32Number nchannels = T_CHANNELS(Format); + cmsUInt32Number total_chans = nchannels + extra; + cmsUInt32Number i; + cmsUInt32Number channelSize = trueBytesSize(Format); + + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + return; + + memset(channels, 0, sizeof(channels)); + + // Separation is independent of starting point and only depends on channel size + for (i = 0; i < extra; i++) + ComponentPointerIncrements[i] = channelSize; + + // Handle do swap + for (i = 0; i < total_chans; i++) + { + if (T_DOSWAP(Format)) { + channels[i] = total_chans - i - 1; + } + else { + channels[i] = i; + } + } + + // Handle swap first (ROL of positions), example CMYK -> KCMY | 0123 -> 3012 + if (T_SWAPFIRST(Format) && total_chans > 0) { + + cmsUInt32Number tmp = channels[0]; + for (i = 0; i < total_chans - 1; i++) + channels[i] = channels[i + 1]; + + channels[total_chans - 1] = tmp; + } + + // Handle size + for (i = 0; i < total_chans; i++) { + channels[i] *= BytesPerPlane; + } + + for (i = 0; i < extra; i++) + ComponentStartingOrder[i] = channels[i + nchannels]; +} + + + +// Dispatcher por chunky and planar RGB +static +void ComputeComponentIncrements(cmsUInt32Number Format, + cmsUInt32Number BytesPerPlane, + cmsUInt32Number ComponentStartingOrder[], + cmsUInt32Number ComponentPointerIncrements[]) +{ + if (T_PLANAR(Format)) { + + ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements); + } + else { + ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements); + } + +} + + + +// Handles extra channels copying alpha if requested by the flags +void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt32Number i, j, k; + cmsUInt32Number nExtra; + cmsUInt32Number SourceStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number SourceIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStartingOrder[cmsMAXCHANNELS]; + cmsUInt32Number DestIncrements[cmsMAXCHANNELS]; + + cmsFormatterAlphaFn copyValueFn; + + // Make sure we need some copy + if (!(p->dwOriginalFlags & cmsFLAGS_COPY_ALPHA)) + return; + + // Exit early if in-place color-management is occurring - no need to copy extra channels to themselves. + if (p->InputFormat == p->OutputFormat && in == out) + return; + + // Make sure we have same number of alpha channels. If not, just return as this should be checked at transform creation time. + nExtra = T_EXTRA(p->InputFormat); + if (nExtra != T_EXTRA(p->OutputFormat)) + return; + + // Anything to do? + if (nExtra == 0) + return; + + // Compute the increments + ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements); + ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements); + + // Check for conversions 8, 16, half, float, dbl + copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat); + if (copyValueFn == NULL) + return; + + if (nExtra == 1) { // Optimized routine for copying a single extra channel quickly + + cmsUInt8Number* SourcePtr; + cmsUInt8Number* DestPtr; + + cmsUInt32Number SourceStrideIncrement = 0; + cmsUInt32Number DestStrideIncrement = 0; + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + SourcePtr = (cmsUInt8Number*)in + SourceStartingOrder[0] + SourceStrideIncrement; + DestPtr = (cmsUInt8Number*)out + DestStartingOrder[0] + DestStrideIncrement; + + for (j = 0; j < PixelsPerLine; j++) { + + copyValueFn(DestPtr, SourcePtr); + + SourcePtr += SourceIncrements[0]; + DestPtr += DestIncrements[0]; + } + + SourceStrideIncrement += Stride->BytesPerLineIn; + DestStrideIncrement += Stride->BytesPerLineOut; + } + + } + else { // General case with more than one extra channel + + cmsUInt8Number* SourcePtr[cmsMAXCHANNELS]; + cmsUInt8Number* DestPtr[cmsMAXCHANNELS]; + + cmsUInt32Number SourceStrideIncrements[cmsMAXCHANNELS]; + cmsUInt32Number DestStrideIncrements[cmsMAXCHANNELS]; + + memset(SourceStrideIncrements, 0, sizeof(SourceStrideIncrements)); + memset(DestStrideIncrements, 0, sizeof(DestStrideIncrements)); + + // The loop itself + for (i = 0; i < LineCount; i++) { + + // Prepare pointers for the loop + for (j = 0; j < nExtra; j++) { + + SourcePtr[j] = (cmsUInt8Number*)in + SourceStartingOrder[j] + SourceStrideIncrements[j]; + DestPtr[j] = (cmsUInt8Number*)out + DestStartingOrder[j] + DestStrideIncrements[j]; + } + + for (j = 0; j < PixelsPerLine; j++) { + + for (k = 0; k < nExtra; k++) { + + copyValueFn(DestPtr[k], SourcePtr[k]); + + SourcePtr[k] += SourceIncrements[k]; + DestPtr[k] += DestIncrements[k]; + } + } + + for (j = 0; j < nExtra; j++) { + + SourceStrideIncrements[j] += Stride->BytesPerLineIn; + DestStrideIncrements[j] += Stride->BytesPerLineOut; + } + } + } +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscam02.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscam02.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscam02.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscam02.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,515 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. + +// ---------- Implementation -------------------------------------------- + +typedef struct { + + cmsFloat64Number XYZ[3]; + cmsFloat64Number RGB[3]; + cmsFloat64Number RGBc[3]; + cmsFloat64Number RGBp[3]; + cmsFloat64Number RGBpa[3]; + cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; + cmsFloat64Number abC[2]; + cmsFloat64Number abs[2]; + cmsFloat64Number abM[2]; + +} CAM02COLOR; + +typedef struct { + + CAM02COLOR adoptedWhite; + cmsFloat64Number LA, Yb; + cmsFloat64Number F, c, Nc; + cmsUInt32Number surround; + cmsFloat64Number n, Nbb, Ncb, z, FL, D; + + cmsContext ContextID; + +} cmsCIECAM02; + + +static +cmsFloat64Number compute_n(cmsCIECAM02* pMod) +{ + return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); +} + +static +cmsFloat64Number compute_z(cmsCIECAM02* pMod) +{ + return (1.48 + pow(pMod -> n, 0.5)); +} + +static +cmsFloat64Number computeNbb(cmsCIECAM02* pMod) +{ + return (0.725 * pow((1.0 / pMod -> n), 0.2)); +} + +static +cmsFloat64Number computeFL(cmsCIECAM02* pMod) +{ + cmsFloat64Number k, FL; + + k = 1.0 / ((5.0 * pMod->LA) + 1.0); + FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * + (pow((1.0 - pow(k, 4.0)), 2.0)) * + (pow((5.0 * pMod->LA), (1.0 / 3.0))); + + return FL; +} + +static +cmsFloat64Number computeD(cmsCIECAM02* pMod) +{ + cmsFloat64Number D; + + D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); + + return D; +} + + +static +CAM02COLOR XYZtoCAT02(CAM02COLOR clr) +{ + clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624); + clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061); + clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834); + + return clr; +} + +static +CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + + for (i = 0; i < 3; i++) { + clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] * + (pMod->D / pMod -> adoptedWhite.RGB[i])) + + (1.0 - pMod->D)) * clr.RGB[i]; + } + + return clr; +} + + +static +CAM02COLOR CAT02toHPE(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628)); + M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698)); + M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326)); + M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628)); + M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698)); + M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326)); + M[6] =(-0.009628); + M[7] =(-0.005698); + M[8] =( 1.015326); + + clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]); + clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]); + clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]); + + return clr; +} + +static +CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number temp; + + for (i = 0; i < 3; i++) { + if (clr.RGBp[i] < 0) { + + temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1; + } + else { + temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1; + } + } + + clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] + + (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb; + + return clr; +} + +static +CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsFloat64Number a, b, temp, e, t, r2d, d2r; + + a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0); + b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0; + + r2d = (180.0 / 3.141592654); + if (a == 0) { + if (b == 0) clr.h = 0; + else if (b > 0) clr.h = 90; + else clr.h = 270; + } + else if (a > 0) { + temp = b / a; + if (b > 0) clr.h = (r2d * atan(temp)); + else if (b == 0) clr.h = 0; + else clr.h = (r2d * atan(temp)) + 360; + } + else { + temp = b / a; + clr.h = (r2d * atan(temp)) + 180; + } + + d2r = (3.141592654 / 180.0); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + if (clr.h < 20.14) { + temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8); + clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp; + } + else if (clr.h < 90.0) { + temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7); + clr.H = (100*((clr.h - 20.14)/0.8)) / temp; + } + else if (clr.h < 164.25) { + temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0); + clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp); + } + else if (clr.h < 237.53) { + temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2); + clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp); + } + else { + temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8); + clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp); + } + + clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A), + (pMod->c * pMod->z)); + + clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) * + (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25); + + t = (e * pow(((a * a) + (b * b)), 0.5)) / + (clr.RGBpa[0] + clr.RGBpa[1] + + ((21.0 / 20.0) * clr.RGBpa[2])); + + clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) * + pow((1.64 - pow(0.29, pMod->n)), 0.73); + + clr.M = clr.C * pow(pMod->FL, 0.25); + clr.s = 100.0 * pow((clr.M / clr.Q), 0.5); + + return clr; +} + + +static +CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + + cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; + d2r = 3.141592654 / 180.0; + + t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * + (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), + (1.0 / 0.9) ); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + clr.A = pMod->adoptedWhite.A * pow( + (clr.J / 100.0), + (1.0 / (pMod->c * pMod->z))); + + p1 = e / t; + p2 = (clr.A / pMod->Nbb) + 0.305; + p3 = 21.0 / 20.0; + + hr = clr.h * d2r; + + if (fabs(sin(hr)) >= fabs(cos(hr))) { + p4 = p1 / sin(hr); + clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p4 + (2.0 + p3) * (220.0 / 1403.0) * + (cos(hr) / sin(hr)) - (27.0 / 1403.0) + + p3 * (6300.0 / 1403.0)); + clr.a = clr.b * (cos(hr) / sin(hr)); + } + else { + p5 = p1 / cos(hr); + clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p5 + (2.0 + p3) * (220.0 / 1403.0) - + ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) * + (sin(hr) / cos(hr))); + clr.b = clr.a * (sin(hr) / cos(hr)); + } + + clr.RGBpa[0] = ((460.0 / 1403.0) * p2) + + ((451.0 / 1403.0) * clr.a) + + ((288.0 / 1403.0) * clr.b); + clr.RGBpa[1] = ((460.0 / 1403.0) * p2) - + ((891.0 / 1403.0) * clr.a) - + ((261.0 / 1403.0) * clr.b); + clr.RGBpa[2] = ((460.0 / 1403.0) * p2) - + ((220.0 / 1403.0) * clr.a) - + ((6300.0 / 1403.0) * clr.b); + + return clr; +} + +static +CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number c1; + + for (i = 0; i < 3; i++) { + if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1; + else c1 = 1; + clr.RGBp[i] = c1 * (100.0 / pMod->FL) * + pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) / + (400.0 - fabs(clr.RGBpa[i] - 0.1))), + (1.0 / 0.42)); + } + + return clr; +} + +static +CAM02COLOR HPEtoCAT02(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950)); + M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054)); + M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624); + M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950)); + M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054)); + M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061); + M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950)); + M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054)); + M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; + + clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); + clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); + clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); + return clr; +} + + +static +CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + for (i = 0; i < 3; i++) { + clr.RGB[i] = clr.RGBc[i] / + ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); + } + return clr; +} + + +static +CAM02COLOR CAT02toXYZ(CAM02COLOR clr) +{ + clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); + clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); + clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); + + return clr; +} + + +cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) +{ + cmsCIECAM02* lpMod; + + _cmsAssert(pVC != NULL); + + if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { + return NULL; + } + + lpMod ->ContextID = ContextID; + + lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; + lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; + lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; + + lpMod -> LA = pVC ->La; + lpMod -> Yb = pVC ->Yb; + lpMod -> D = pVC ->D_value; + lpMod -> surround = pVC ->surround; + + switch (lpMod -> surround) { + + + case CUTSHEET_SURROUND: + lpMod->F = 0.8; + lpMod->c = 0.41; + lpMod->Nc = 0.8; + break; + + case DARK_SURROUND: + lpMod -> F = 0.8; + lpMod -> c = 0.525; + lpMod -> Nc = 0.8; + break; + + case DIM_SURROUND: + lpMod -> F = 0.9; + lpMod -> c = 0.59; + lpMod -> Nc = 0.95; + break; + + default: + // Average surround + lpMod -> F = 1.0; + lpMod -> c = 0.69; + lpMod -> Nc = 1.0; + } + + lpMod -> n = compute_n(lpMod); + lpMod -> z = compute_z(lpMod); + lpMod -> Nbb = computeNbb(lpMod); + lpMod -> FL = computeFL(lpMod); + + if (lpMod -> D == D_CALCULATE) { + lpMod -> D = computeD(lpMod); + } + + lpMod -> Ncb = lpMod -> Nbb; + + lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); + lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); + + return (cmsHANDLE) lpMod; + +} + +void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) +{ + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); +} + + +void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); + + clr.XYZ[0] = pIn ->X; + clr.XYZ[1] = pIn ->Y; + clr.XYZ[2] = pIn ->Z; + + clr = XYZtoCAT02(clr); + clr = ChromaticAdaptation(clr, lpMod); + clr = CAT02toHPE(clr); + clr = NonlinearCompression(clr, lpMod); + clr = ComputeCorrelates(clr, lpMod); + + pOut ->J = clr.J; + pOut ->C = clr.C; + pOut ->h = clr.h; +} + +void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _cmsAssert(lpMod != NULL); + _cmsAssert(pIn != NULL); + _cmsAssert(pOut != NULL); + + memset(&clr, 0, sizeof(clr)); + + clr.J = pIn -> J; + clr.C = pIn -> C; + clr.h = pIn -> h; + + clr = InverseCorrelates(clr, lpMod); + clr = InverseNonlinearity(clr, lpMod); + clr = HPEtoCAT02(clr); + clr = InverseChromaticAdaptation(clr, lpMod); + clr = CAT02toXYZ(clr); + + pOut ->X = clr.XYZ[0]; + pOut ->Y = clr.XYZ[1]; + pOut ->Z = clr.XYZ[2]; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscgats.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscgats.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscgats.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscgats.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,2815 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + + +#define MAXID 128 // Max length of identifier +#define MAXSTR 1024 // Max length of string +#define MAXTABLES 255 // Max Number of tables in a single stream +#define MAXINCLUDE 20 // Max number of nested includes + +#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting + +#ifdef CMS_IS_WINDOWS_ +# include +# define DIR_CHAR '\\' +#else +# define DIR_CHAR '/' +#endif + + +// Symbols +typedef enum { + + SUNDEFINED, + SINUM, // Integer + SDNUM, // Real + SIDENT, // Identifier + SSTRING, // string + SCOMMENT, // comment + SEOLN, // End of line + SEOF, // End of stream + SSYNERROR, // Syntax error found on stream + + // Keywords + + SBEGIN_DATA, + SBEGIN_DATA_FORMAT, + SEND_DATA, + SEND_DATA_FORMAT, + SKEYWORD, + SDATA_FORMAT_ID, + SINCLUDE + + } SYMBOL; + + +// How to write the value +typedef enum { + + WRITE_UNCOOKED, + WRITE_STRINGIFY, + WRITE_HEXADECIMAL, + WRITE_BINARY, + WRITE_PAIR + + } WRITEMODE; + +// Linked list of variable names +typedef struct _KeyVal { + + struct _KeyVal* Next; + char* Keyword; // Name of variable + struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item + char* Subkey; // If key is a dictionary, points to the subkey name + char* Value; // Points to value + WRITEMODE WriteAs; // How to write the value + + } KEYVALUE; + + +// Linked list of memory chunks (Memory sink) +typedef struct _OwnedMem { + + struct _OwnedMem* Next; + void * Ptr; // Point to value + + } OWNEDMEM; + +// Suballocator +typedef struct _SubAllocator { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + } SUBALLOCATOR; + +// Table. Each individual table can hold properties and rows & cols +typedef struct _Table { + + char SheetType[MAXSTR]; // The first row of the IT8 (the type) + + int nSamples, nPatches; // Cols, Rows + int SampleID; // Pos of ID + + KEYVALUE* HeaderList; // The properties + + char** DataFormat; // The binary stream descriptor + char** Data; // The binary stream + + } TABLE; + +// File stream being parsed +typedef struct _FileContext { + char FileName[cmsMAX_PATH]; // File name if being read from file + FILE* Stream; // File stream or NULL if holded in memory + } FILECTX; + +// This struct hold all information about an open IT8 handler. +typedef struct { + + + cmsUInt32Number TablesCount; // How many tables in this stream + cmsUInt32Number nTable; // The actual table + + TABLE Tab[MAXTABLES]; + + // Memory management + OWNEDMEM* MemorySink; // The storage backend + SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast + + // Parser state machine + SYMBOL sy; // Current symbol + int ch; // Current character + + cmsInt32Number inum; // integer value + cmsFloat64Number dnum; // real value + + char id[MAXID]; // identifier + char str[MAXSTR]; // string + + // Allowed keywords & datasets. They have visibility on whole stream + KEYVALUE* ValidKeywords; + KEYVALUE* ValidSampleID; + + char* Source; // Points to loc. being parsed + cmsInt32Number lineno; // line counter for error reporting + + FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed + cmsInt32Number IncludeSP; // Include Stack Pointer + + char* MemoryBlock; // The stream if holded in memory + + char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter + + cmsContext ContextID; // The threading context + + } cmsIT8; + + +// The stream for save operations +typedef struct { + + FILE* stream; // For save-to-file behaviour + + cmsUInt8Number* Base; + cmsUInt8Number* Ptr; // For save-to-mem behaviour + cmsUInt32Number Used; + cmsUInt32Number Max; + + } SAVESTREAM; + + +// ------------------------------------------------------ cmsIT8 parsing routines + + +// A keyword +typedef struct { + + const char *id; + SYMBOL sy; + + } KEYWORD; + +// The keyword->symbol translation table. Sorting is required. +static const KEYWORD TabKeys[] = { + + {"$INCLUDE", SINCLUDE}, // This is an extension! + {".INCLUDE", SINCLUDE}, // This is an extension! + + {"BEGIN_DATA", SBEGIN_DATA }, + {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, + {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID}, + {"END_DATA", SEND_DATA}, + {"END_DATA_FORMAT", SEND_DATA_FORMAT}, + {"KEYWORD", SKEYWORD} + }; + +#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) + +// Predefined properties + +// A property +typedef struct { + const char *id; // The identifier + WRITEMODE as; // How is supposed to be written + } PROPERTY; + +static PROPERTY PredefinedProperties[] = { + + {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS + {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS + {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file. + {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file. + {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal". + {"MANUFACTURER", WRITE_STRINGIFY}, + {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value + {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm. + {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target. + + {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code + // uniquely identifying th e material. This is intend ed to be used for IT8.7 + // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). + + {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and + // model number) to generate the data reported. This data will often + // provide more information about the particular data collected than an + // extensive list of specific details. This is particularly important for + // spectral data or data derived from spectrophotometry. + + {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide + // a guide to the potential for issues of paper fluorescence, etc. + + {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. + // Where standard conditions have been defined (e.g., SWOP at nominal) + // named conditions may suffice. Otherwise, detailed information is + // needed. + + {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during + // measurement. Allowed values are "black", "white", or {"na". + + {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic + // below properties are new in recent specs: + + {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated + // along with details of the geometry and the aperture size and shape. For example, + // for transmission measurements it is important to identify 0/diffuse, diffuse/0, + // opal or integrating sphere, etc. For reflection it is important to identify 0/45, + // 45/0, sphere (specular included or excluded), etc. + + {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to + // denote the use of filters such as none, D65, Red, Green or Blue. + + {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed + // values are {"yes", "white", "none" or "na". + + {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the + // calculation of various data parameters (2 degree and 10 degree), CIE standard + // illuminant functions used in the calculation of various data parameters (e.g., D50, + // D65, etc.), density status response, etc. If used there shall be at least one + // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute + // in the set shall be {"name" and shall identify the particular parameter used. + // The second shall be {"value" and shall provide the value associated with that name. + // For ASCII data, a string containing the Name and Value attribute pairs shall follow + // the weighting function keyword. A semi-colon separates attribute pairs from each + // other and within the attribute the name and value are separated by a comma. + + {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name + // of the calculation, parameter is the name of the parameter used in the calculation + // and value is the value of the parameter. + + {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. + + {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. + + {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. + + {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. +}; + +#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY)) + + +// Predefined sample types on dataset +static const char* PredefinedSampleID[] = { + "SAMPLE_ID", // Identifies sample that data represents + "STRING", // Identifies label, or other non-machine readable value. + // Value must begin and end with a " symbol + + "CMYK_C", // Cyan component of CMYK data expressed as a percentage + "CMYK_M", // Magenta component of CMYK data expressed as a percentage + "CMYK_Y", // Yellow component of CMYK data expressed as a percentage + "CMYK_K", // Black component of CMYK data expressed as a percentage + "D_RED", // Red filter density + "D_GREEN", // Green filter density + "D_BLUE", // Blue filter density + "D_VIS", // Visual filter density + "D_MAJOR_FILTER", // Major filter d ensity + "RGB_R", // Red component of RGB data + "RGB_G", // Green component of RGB data + "RGB_B", // Blue com ponent of RGB data + "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers + "SPECTRAL_PCT", // Percentage reflectance/transmittance + "SPECTRAL_DEC", // Reflectance/transmittance + "XYZ_X", // X component of tristimulus data + "XYZ_Y", // Y component of tristimulus data + "XYZ_Z", // Z component of tristimulus data + "XYY_X", // x component of chromaticity data + "XYY_Y", // y component of chromaticity data + "XYY_CAPY", // Y component of tristimulus data + "LAB_L", // L* component of Lab data + "LAB_A", // a* component of Lab data + "LAB_B", // b* component of Lab data + "LAB_C", // C*ab component of Lab data + "LAB_H", // hab component of Lab data + "LAB_DE", // CIE dE + "LAB_DE_94", // CIE dE using CIE 94 + "LAB_DE_CMC", // dE using CMC + "LAB_DE_2000", // CIE dE using CIE DE 2000 + "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average + // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) + "STDEV_X", // Standard deviation of X (tristimulus data) + "STDEV_Y", // Standard deviation of Y (tristimulus data) + "STDEV_Z", // Standard deviation of Z (tristimulus data) + "STDEV_L", // Standard deviation of L* + "STDEV_A", // Standard deviation of a* + "STDEV_B", // Standard deviation of b* + "STDEV_DE", // Standard deviation of CIE dE + "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is + // used to derive an estimate of the chi-squared parameter which is + // recommended as the predictor of the variability of dE + +#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) + +//Forward declaration of some internal functions +static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); + +// Checks whatever c is a separator +static +cmsBool isseparator(int c) +{ + return (c == ' ') || (c == '\t') ; +} + +// Checks whatever c is a valid identifier char +static +cmsBool ismiddle(int c) +{ + return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); +} + +// Checks whatsever c is a valid identifier middle char. +static +cmsBool isidchar(int c) +{ + return isalnum(c) || ismiddle(c); +} + +// Checks whatsever c is a valid identifier first char. +static +cmsBool isfirstidchar(int c) +{ + return !isdigit(c) && ismiddle(c); +} + +// Guess whether the supplied path looks like an absolute path +static +cmsBool isabsolutepath(const char *path) +{ + char ThreeChars[4]; + + if(path == NULL) + return FALSE; + if (path[0] == 0) + return FALSE; + + strncpy(ThreeChars, path, 3); + ThreeChars[3] = 0; + + if(ThreeChars[0] == DIR_CHAR) + return TRUE; + +#ifdef CMS_IS_WINDOWS_ + if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':') + return TRUE; +#endif + return FALSE; +} + + +// Makes a file path based on a given reference path +// NOTE: this function doesn't check if the path exists or even if it's legal +static +cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen) +{ + char *tail; + cmsUInt32Number len; + + // Already absolute? + if (isabsolutepath(relPath)) { + + strncpy(buffer, relPath, MaxLen); + buffer[MaxLen-1] = 0; + return TRUE; + } + + // No, search for last + strncpy(buffer, basePath, MaxLen); + buffer[MaxLen-1] = 0; + + tail = strrchr(buffer, DIR_CHAR); + if (tail == NULL) return FALSE; // Is not absolute and has no separators?? + + len = (cmsUInt32Number) (tail - buffer); + if (len >= MaxLen) return FALSE; + + // No need to assure zero terminator over here + strncpy(tail + 1, relPath, MaxLen - len); + + return TRUE; +} + + +// Make sure no exploit is being even tried +static +const char* NoMeta(const char* str) +{ + if (strchr(str, '%') != NULL) + return "**** CORRUPTED FORMAT STRING ***"; + + return str; +} + +// Syntax error +static +cmsBool SynError(cmsIT8* it8, const char *Txt, ...) +{ + char Buffer[256], ErrMsg[1024]; + va_list args; + + va_start(args, Txt); + vsnprintf(Buffer, 255, Txt, args); + Buffer[255] = 0; + va_end(args); + + snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); + ErrMsg[1023] = 0; + it8->sy = SSYNERROR; + cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg); + return FALSE; +} + +// Check if current symbol is same as specified. issue an error else. +static +cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err) +{ + if (it8 -> sy != sy) + return SynError(it8, NoMeta(Err)); + return TRUE; +} + +// Read Next character from stream +static +void NextCh(cmsIT8* it8) +{ + if (it8 -> FileStack[it8 ->IncludeSP]->Stream) { + + it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream); + + if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) { + + if (it8 ->IncludeSP > 0) { + + fclose(it8 ->FileStack[it8->IncludeSP--]->Stream); + it8 -> ch = ' '; // Whitespace to be ignored + + } else + it8 ->ch = 0; // EOF + } + } + else { + it8->ch = *it8->Source; + if (it8->ch) it8->Source++; + } +} + + +// Try to see if current identifier is a keyword, if so return the referred symbol +static +SYMBOL BinSrchKey(const char *id) +{ + int l = 1; + int r = NUMKEYS; + int x, res; + + while (r >= l) + { + x = (l+r)/2; + res = cmsstrcasecmp(id, TabKeys[x-1].id); + if (res == 0) return TabKeys[x-1].sy; + if (res < 0) r = x - 1; + else l = x + 1; + } + + return SUNDEFINED; +} + + +// 10 ^n +static +cmsFloat64Number xpow10(int n) +{ + return pow(10, (cmsFloat64Number) n); +} + + +// Reads a Real number, tries to follow from integer number +static +void ReadReal(cmsIT8* it8, cmsInt32Number inum) +{ + it8->dnum = (cmsFloat64Number)inum; + + while (isdigit(it8->ch)) { + + it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { // Decimal point + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + NextCh(it8); // Eats dec. point + + while (isdigit(it8->ch)) { + + frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0'); + prec++; + NextCh(it8); + } + + it8->dnum = it8->dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (toupper(it8->ch) == 'E') { + + cmsInt32Number e; + cmsInt32Number sgn; + + NextCh(it8); sgn = 1; + + if (it8->ch == '-') { + + sgn = -1; NextCh(it8); + } + else + if (it8->ch == '+') { + + sgn = +1; + NextCh(it8); + } + + e = 0; + while (isdigit(it8->ch)) { + + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + NextCh(it8); + } + + e = sgn*e; + it8->dnum = it8->dnum * xpow10(e); + } +} + +// Parses a float number +// This can not call directly atof because it uses locale dependent +// parsing, while CCMX files always use . as decimal separator +static +cmsFloat64Number ParseFloatNumber(const char *Buffer) +{ + cmsFloat64Number dnum = 0.0; + int sign = 1; + + // keep safe + if (Buffer == NULL) return 0.0; + + if (*Buffer == '-' || *Buffer == '+') { + + sign = (*Buffer == '-') ? -1 : 1; + Buffer++; + } + + + while (*Buffer && isdigit((int)*Buffer)) { + + dnum = dnum * 10.0 + (*Buffer - '0'); + if (*Buffer) Buffer++; + } + + if (*Buffer == '.') { + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + if (*Buffer) Buffer++; + + while (*Buffer && isdigit((int)*Buffer)) { + + frac = frac * 10.0 + (*Buffer - '0'); + prec++; + if (*Buffer) Buffer++; + } + + dnum = dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (*Buffer && toupper(*Buffer) == 'E') { + + int e; + int sgn; + + if (*Buffer) Buffer++; + sgn = 1; + + if (*Buffer == '-') { + + sgn = -1; + if (*Buffer) Buffer++; + } + else + if (*Buffer == '+') { + + sgn = +1; + if (*Buffer) Buffer++; + } + + e = 0; + while (*Buffer && isdigit((int)*Buffer)) { + + cmsInt32Number digit = (*Buffer - '0'); + + if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0) + e = e * 10 + digit; + + if (*Buffer) Buffer++; + } + + e = sgn*e; + dnum = dnum * xpow10(e); + } + + return sign * dnum; +} + + +// Reads next symbol +static +void InSymbol(cmsIT8* it8) +{ + CMSREGISTER char *idptr; + CMSREGISTER int k; + SYMBOL key; + int sng; + + do { + + while (isseparator(it8->ch)) + NextCh(it8); + + if (isfirstidchar(it8->ch)) { // Identifier + + k = 0; + idptr = it8->id; + + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + + + key = BinSrchKey(it8->id); + if (key == SUNDEFINED) it8->sy = SIDENT; + else it8->sy = key; + + } + else // Is a number? + if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+') + { + int sign = 1; + + if (it8->ch == '-') { + sign = -1; + NextCh(it8); + } + + it8->inum = 0; + it8->sy = SINUM; + + if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary) + + NextCh(it8); + if (toupper(it8->ch) == 'X') { + + int j; + + NextCh(it8); + while (isxdigit(it8->ch)) + { + it8->ch = toupper(it8->ch); + if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10; + else j = it8->ch - '0'; + + if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0) + { + SynError(it8, "Invalid hexadecimal number"); + return; + } + + it8->inum = it8->inum * 16 + j; + NextCh(it8); + } + return; + } + + if (toupper(it8->ch) == 'B') { // Binary + + int j; + + NextCh(it8); + while (it8->ch == '0' || it8->ch == '1') + { + j = it8->ch - '0'; + + if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0) + { + SynError(it8, "Invalid binary number"); + return; + } + + it8->inum = it8->inum * 2 + j; + NextCh(it8); + } + return; + } + } + + + while (isdigit(it8->ch)) { + + cmsInt32Number digit = (it8->ch - '0'); + + if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) { + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8->inum = it8->inum * 10 + digit; + NextCh(it8); + } + + if (it8->ch == '.') { + + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8 -> inum *= sign; + + // Special case. Numbers followed by letters are taken as identifiers + + if (isidchar(it8 ->ch)) { + + if (it8 ->sy == SINUM) { + + snprintf(it8->id, 127, "%d", it8->inum); + } + else { + + snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum); + } + + k = (int) strlen(it8 ->id); + idptr = it8 ->id + k; + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + it8->sy = SIDENT; + } + return; + + } + else + switch ((int) it8->ch) { + + // EOF marker -- ignore it + case '\x1a': + NextCh(it8); + break; + + // Eof stream markers + case 0: + case -1: + it8->sy = SEOF; + break; + + + // Next line + case '\r': + NextCh(it8); + if (it8 ->ch == '\n') + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + case '\n': + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + // Comment + case '#': + NextCh(it8); + while (it8->ch && it8->ch != '\n' && it8->ch != '\r') + NextCh(it8); + + it8->sy = SCOMMENT; + break; + + // String. + case '\'': + case '\"': + idptr = it8->str; + sng = it8->ch; + k = 0; + NextCh(it8); + + while (k < (MAXSTR-1) && it8->ch != sng) { + + if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; + else { + *idptr++ = (char) it8->ch; + NextCh(it8); + k++; + } + } + + it8->sy = SSTRING; + *idptr = '\0'; + NextCh(it8); + break; + + + default: + SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); + return; + } + + } while (it8->sy == SCOMMENT); + + // Handle the include special token + + if (it8 -> sy == SINCLUDE) { + + FILECTX* FileNest; + + if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { + + SynError(it8, "Too many recursion levels"); + return; + } + + InSymbol(it8); + if (!Check(it8, SSTRING, "Filename expected")) return; + + FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; + if(FileNest == NULL) { + + FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + //if(FileNest == NULL) + // TODO: how to manage out-of-memory conditions? + } + + if (BuildAbsolutePath(it8->str, + it8->FileStack[it8->IncludeSP]->FileName, + FileNest->FileName, cmsMAX_PATH-1) == FALSE) { + SynError(it8, "File path too long"); + return; + } + + FileNest->Stream = fopen(FileNest->FileName, "rt"); + if (FileNest->Stream == NULL) { + + SynError(it8, "File %s not found", FileNest->FileName); + return; + } + it8->IncludeSP++; + + it8 ->ch = ' '; + InSymbol(it8); + } + +} + +// Checks end of line separator +static +cmsBool CheckEOLN(cmsIT8* it8) +{ + if (!Check(it8, SEOLN, "Expected separator")) return FALSE; + while (it8 -> sy == SEOLN) + InSymbol(it8); + return TRUE; + +} + +// Skip a symbol + +static +void Skip(cmsIT8* it8, SYMBOL sy) +{ + if (it8->sy == sy && it8->sy != SEOF) + InSymbol(it8); +} + + +// Skip multiple EOLN +static +void SkipEOLN(cmsIT8* it8) +{ + while (it8->sy == SEOLN) { + InSymbol(it8); + } +} + + +// Returns a string holding current value +static +cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle) +{ + switch (it8->sy) { + + case SEOLN: // Empty value + Buffer[0]=0; + break; + case SIDENT: strncpy(Buffer, it8->id, max); + Buffer[max-1]=0; + break; + case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break; + case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break; + case SSTRING: strncpy(Buffer, it8->str, max); + Buffer[max-1] = 0; + break; + + + default: + return SynError(it8, "%s", ErrorTitle); + } + + Buffer[max] = 0; + return TRUE; +} + +// ---------------------------------------------------------- Table + +static +TABLE* GetTable(cmsIT8* it8) +{ + if ((it8 -> nTable >= it8 ->TablesCount)) { + + SynError(it8, "Table %d out of sequence", it8 -> nTable); + return it8 -> Tab; + } + + return it8 ->Tab + it8 ->nTable; +} + +// ---------------------------------------------------------- Memory management + + +// Frees an allocator and owned memory +void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (it8 == NULL) + return; + + if (it8->MemorySink) { + + OWNEDMEM* p; + OWNEDMEM* n; + + for (p = it8->MemorySink; p != NULL; p = n) { + + n = p->Next; + if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr); + _cmsFree(it8 ->ContextID, p); + } + } + + if (it8->MemoryBlock) + _cmsFree(it8 ->ContextID, it8->MemoryBlock); + + _cmsFree(it8 ->ContextID, it8); +} + + +// Allocates a chunk of data, keep linked list +static +void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size) +{ + OWNEDMEM* ptr1; + void* ptr = _cmsMallocZero(it8->ContextID, size); + + if (ptr != NULL) { + + ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM)); + + if (ptr1 == NULL) { + + _cmsFree(it8 ->ContextID, ptr); + return NULL; + } + + ptr1-> Ptr = ptr; + ptr1-> Next = it8 -> MemorySink; + it8 -> MemorySink = ptr1; + } + + return ptr; +} + + +// Suballocator. +static +void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) +{ + cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + if (size > Free) { + + if (it8 -> Allocator.BlockSize == 0) + + it8 -> Allocator.BlockSize = 20*1024; + else + it8 ->Allocator.BlockSize *= 2; + + if (it8 ->Allocator.BlockSize < size) + it8 ->Allocator.BlockSize = size; + + it8 ->Allocator.Used = 0; + it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + } + + ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; + it8 ->Allocator.Used += size; + + return (void*) ptr; + +} + + +// Allocates a string +static +char *AllocString(cmsIT8* it8, const char* str) +{ + cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; + char *ptr; + + + ptr = (char *) AllocChunk(it8, Size); + if (ptr) strncpy (ptr, str, Size-1); + + return ptr; +} + +// Searches through linked list + +static +cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr) +{ + if (LastPtr) *LastPtr = p; + + for (; p != NULL; p = p->Next) { + + if (LastPtr) *LastPtr = p; + + if (*Key != '#') { // Comments are ignored + + if (cmsstrcasecmp(Key, p->Keyword) == 0) + break; + } + } + + if (p == NULL) + return FALSE; + + if (Subkey == 0) + return TRUE; + + for (; p != NULL; p = p->NextSubkey) { + + if (p ->Subkey == NULL) continue; + + if (LastPtr) *LastPtr = p; + + if (cmsstrcasecmp(Subkey, p->Subkey) == 0) + return TRUE; + } + + return FALSE; +} + + + +// Add a property into a linked list +static +KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) +{ + KEYVALUE* p; + KEYVALUE* last; + + + // Check if property is already in list + + if (IsAvailableOnList(*Head, Key, Subkey, &p)) { + + // This may work for editing properties + + // return SynError(it8, "duplicate key <%s>", Key); + } + else { + + last = p; + + // Allocate the container + p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE)); + if (p == NULL) + { + SynError(it8, "AddToList: out of memory"); + return NULL; + } + + // Store name and value + p->Keyword = AllocString(it8, Key); + p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey); + + // Keep the container in our list + if (*Head == NULL) { + *Head = p; + } + else + { + if (Subkey != NULL && last != NULL) { + + last->NextSubkey = p; + + // If Subkey is not null, then last is the last property with the same key, + // but not necessarily is the last property in the list, so we need to move + // to the actual list end + while (last->Next != NULL) + last = last->Next; + } + + if (last != NULL) last->Next = p; + } + + p->Next = NULL; + p->NextSubkey = NULL; + } + + p->WriteAs = WriteAs; + + if (xValue != NULL) { + + p->Value = AllocString(it8, xValue); + } + else { + p->Value = NULL; + } + + return p; +} + +static +KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) +{ + return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); +} + + +static +KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) +{ + return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); +} + + +static +void AllocTable(cmsIT8* it8) +{ + TABLE* t; + + t = it8 ->Tab + it8 ->TablesCount; + + t->HeaderList = NULL; + t->DataFormat = NULL; + t->Data = NULL; + + it8 ->TablesCount++; +} + + +cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) +{ + cmsIT8* it8 = (cmsIT8*) IT8; + + if (nTable >= it8 ->TablesCount) { + + if (nTable == it8 ->TablesCount) { + + AllocTable(it8); + } + else { + SynError(it8, "Table %d is out of sequence", nTable); + return -1; + } + } + + it8 ->nTable = nTable; + + return (cmsInt32Number) nTable; +} + + + +// Init an empty container +cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) +{ + cmsIT8* it8; + cmsUInt32Number i; + + it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); + if (it8 == NULL) return NULL; + + AllocTable(it8); + + it8->MemoryBlock = NULL; + it8->MemorySink = NULL; + + it8 ->nTable = 0; + + it8->ContextID = ContextID; + it8->Allocator.Used = 0; + it8->Allocator.Block = NULL; + it8->Allocator.BlockSize = 0; + + it8->ValidKeywords = NULL; + it8->ValidSampleID = NULL; + + it8 -> sy = SUNDEFINED; + it8 -> ch = ' '; + it8 -> Source = NULL; + it8 -> inum = 0; + it8 -> dnum = 0.0; + + it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + it8->IncludeSP = 0; + it8 -> lineno = 1; + + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); + + // Initialize predefined properties & data + + for (i=0; i < NUMPREDEFINEDPROPS; i++) + AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as); + + for (i=0; i < NUMPREDEFINEDSAMPLEID; i++) + AddAvailableSampleID(it8, PredefinedSampleID[i]); + + + return (cmsHANDLE) it8; +} + + +const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) +{ + return GetTable((cmsIT8*) hIT8)->SheetType; +} + +cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) +{ + TABLE* t = GetTable((cmsIT8*) hIT8); + + strncpy(t ->SheetType, Type, MAXSTR-1); + t ->SheetType[MAXSTR-1] = 0; + return TRUE; +} + +cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL; +} + +// Sets a property +cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + snprintf(Buffer, 1023, it8->DoubleFormatter, Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + snprintf(Buffer, 1023, "%u", Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL; +} + +// Gets a property +const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p)) + { + return p -> Value; + } + return NULL; +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp) +{ + const char *v = cmsIT8GetProperty(hIT8, cProp); + + if (v == NULL) return 0.0; + + return ParseFloatNumber(v); +} + +const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) { + return p -> Value; + } + return NULL; +} + +// ----------------------------------------------------------------- Datasets + + +static +void AllocateDataFormat(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> DataFormat) return; // Already allocated + + t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS"); + + if (t -> nSamples <= 0) { + + SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); + t -> nSamples = 10; + } + + t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); + if (t->DataFormat == NULL) { + + SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); + } + +} + +static +const char *GetDataFormat(cmsIT8* it8, int n) +{ + TABLE* t = GetTable(it8); + + if (t->DataFormat) + return t->DataFormat[n]; + + return NULL; +} + +static +cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) +{ + TABLE* t = GetTable(it8); + + if (!t->DataFormat) + AllocateDataFormat(it8); + + if (n > t -> nSamples) { + SynError(it8, "More than NUMBER_OF_FIELDS fields."); + return FALSE; + } + + if (t->DataFormat) { + t->DataFormat[n] = AllocString(it8, label); + } + + return TRUE; +} + + +cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) +{ + cmsIT8* it8 = (cmsIT8*)h; + return SetDataFormat(it8, n, Sample); +} + +// A safe atoi that returns 0 when NULL input is given +static +cmsInt32Number satoi(const char* b) +{ + if (b == NULL) return 0; + return atoi(b); +} + +static +void AllocateDataSet(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> Data) return; // Already allocated + + t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe) + { + SynError(it8, "AllocateDataSet: too much data"); + } + else { + // Some dumb analizers warns of possible overflow here, just take a look couple of lines above. + t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*)); + if (t->Data == NULL) { + + SynError(it8, "AllocateDataSet: Unable to allocate data array"); + } + } + +} + +static +char* GetData(cmsIT8* it8, int nSet, int nField) +{ + TABLE* t = GetTable(it8); + int nSamples = t -> nSamples; + int nPatches = t -> nPatches; + + if (nSet >= nPatches || nField >= nSamples) + return NULL; + + if (!t->Data) return NULL; + return t->Data [nSet * nSamples + nField]; +} + +static +cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) +{ + TABLE* t = GetTable(it8); + + if (!t->Data) + AllocateDataSet(it8); + + if (!t->Data) return FALSE; + + if (nSet > t -> nPatches || nSet < 0) { + + return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches); + } + + if (nField > t ->nSamples || nField < 0) { + return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples); + + } + + t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); + return TRUE; +} + + +// --------------------------------------------------------------- File I/O + + +// Writes a string to file +static +void WriteStr(SAVESTREAM* f, const char *str) +{ + cmsUInt32Number len; + + if (str == NULL) + str = " "; + + // Length to write + len = (cmsUInt32Number) strlen(str); + f ->Used += len; + + + if (f ->stream) { // Should I write it to a file? + + if (fwrite(str, 1, len, f->stream) != len) { + cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser"); + return; + } + + } + else { // Or to a memory block? + + if (f ->Base) { // Am I just counting the bytes? + + if (f ->Used > f ->Max) { + + cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser"); + return; + } + + memmove(f ->Ptr, str, len); + f->Ptr += len; + } + + } +} + + +// Write formatted + +static +void Writef(SAVESTREAM* f, const char* frm, ...) +{ + char Buffer[4096]; + va_list args; + + va_start(args, frm); + vsnprintf(Buffer, 4095, frm, args); + Buffer[4095] = 0; + WriteStr(f, Buffer); + va_end(args); + +} + +// Writes full header +static +void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) +{ + KEYVALUE* p; + TABLE* t = GetTable(it8); + + // Writes the type + WriteStr(fp, t->SheetType); + WriteStr(fp, "\n"); + + for (p = t->HeaderList; (p != NULL); p = p->Next) + { + if (*p ->Keyword == '#') { + + char* Pt; + + WriteStr(fp, "#\n# "); + for (Pt = p ->Value; *Pt; Pt++) { + + + Writef(fp, "%c", *Pt); + + if (*Pt == '\n') { + WriteStr(fp, "# "); + } + } + + WriteStr(fp, "\n#\n"); + continue; + } + + + if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) { + +#ifdef CMS_STRICT_CGATS + WriteStr(fp, "KEYWORD\t\""); + WriteStr(fp, p->Keyword); + WriteStr(fp, "\"\n"); +#endif + + AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED); + } + + WriteStr(fp, p->Keyword); + if (p->Value) { + + switch (p ->WriteAs) { + + case WRITE_UNCOOKED: + Writef(fp, "\t%s", p ->Value); + break; + + case WRITE_STRINGIFY: + Writef(fp, "\t\"%s\"", p->Value ); + break; + + case WRITE_HEXADECIMAL: + Writef(fp, "\t0x%X", satoi(p ->Value)); + break; + + case WRITE_BINARY: + Writef(fp, "\t0x%B", satoi(p ->Value)); + break; + + case WRITE_PAIR: + Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value); + break; + + default: SynError(it8, "Unknown write mode %d", p ->WriteAs); + return; + } + } + + WriteStr (fp, "\n"); + } + +} + + +// Writes the data format +static +void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, nSamples; + TABLE* t = GetTable(it8); + + if (!t -> DataFormat) return; + + WriteStr(fp, "BEGIN_DATA_FORMAT\n"); + WriteStr(fp, " "); + nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + + for (i = 0; i < nSamples; i++) { + + WriteStr(fp, t->DataFormat[i]); + WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t")); + } + + WriteStr (fp, "END_DATA_FORMAT\n"); +} + + +// Writes data array +static +void WriteData(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, j; + TABLE* t = GetTable(it8); + + if (!t->Data) return; + + WriteStr (fp, "BEGIN_DATA\n"); + + t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + for (i = 0; i < t-> nPatches; i++) { + + WriteStr(fp, " "); + + for (j = 0; j < t->nSamples; j++) { + + char *ptr = t->Data[i*t->nSamples+j]; + + if (ptr == NULL) WriteStr(fp, "\"\""); + else { + // If value contains whitespace, enclose within quote + + if (strchr(ptr, ' ') != NULL) { + + WriteStr(fp, "\""); + WriteStr(fp, ptr); + WriteStr(fp, "\""); + } + else + WriteStr(fp, ptr); + } + + WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t")); + } + } + WriteStr (fp, "END_DATA\n"); +} + + + +// Saves whole file +cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = fopen(cFileName, "wt"); + if (!sd.stream) return FALSE; + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + if (fclose(sd.stream) != 0) return FALSE; + + return TRUE; +} + + +// Saves to memory +cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = NULL; + sd.Base = (cmsUInt8Number*) MemPtr; + sd.Ptr = sd.Base; + + sd.Used = 0; + + if (sd.Base) + sd.Max = *BytesNeeded; // Write to memory? + else + sd.Max = 0; // Just counting the needed bytes + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + sd.Used++; // The \0 at the very end + + if (sd.Base) + *sd.Ptr = 0; + + *BytesNeeded = sd.Used; + + return TRUE; +} + + +// -------------------------------------------------------------- Higher level parsing + +static +cmsBool DataFormatSection(cmsIT8* it8) +{ + int iField = 0; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA_FORMAT" + CheckEOLN(it8); + + while (it8->sy != SEND_DATA_FORMAT && + it8->sy != SEOLN && + it8->sy != SEOF && + it8->sy != SSYNERROR) { + + if (it8->sy != SIDENT) { + + return SynError(it8, "Sample type expected"); + } + + if (!SetDataFormat(it8, iField, it8->id)) return FALSE; + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA_FORMAT); + SkipEOLN(it8); + + if (iField != t ->nSamples) { + SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField); + + + } + + return TRUE; +} + + + +static +cmsBool DataSection (cmsIT8* it8) +{ + int iField = 0; + int iSet = 0; + char Buffer[256]; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA" + CheckEOLN(it8); + + if (!t->Data) + AllocateDataSet(it8); + + while (it8->sy != SEND_DATA && it8->sy != SEOF) + { + if (iField >= t -> nSamples) { + iField = 0; + iSet++; + + } + + if (it8->sy != SEND_DATA && it8->sy != SEOF) { + + if (!GetVal(it8, Buffer, 255, "Sample data expected")) + return FALSE; + + if (!SetData(it8, iSet, iField, Buffer)) + return FALSE; + + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA); + SkipEOLN(it8); + + // Check for data completion. + + if ((iSet+1) != t -> nPatches) + return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1); + + return TRUE; +} + + + + +static +cmsBool HeaderSection(cmsIT8* it8) +{ + char VarName[MAXID]; + char Buffer[MAXSTR]; + KEYVALUE* Key; + + while (it8->sy != SEOF && + it8->sy != SSYNERROR && + it8->sy != SBEGIN_DATA_FORMAT && + it8->sy != SBEGIN_DATA) { + + + switch (it8 -> sy) { + + case SKEYWORD: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE; + InSymbol(it8); + break; + + + case SDATA_FORMAT_ID: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableSampleID(it8, Buffer)) return FALSE; + InSymbol(it8); + break; + + + case SIDENT: + strncpy(VarName, it8->id, MAXID - 1); + VarName[MAXID - 1] = 0; + + if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) { + +#ifdef CMS_STRICT_CGATS + return SynError(it8, "Undefined keyword '%s'", VarName); +#else + Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); + if (Key == NULL) return FALSE; +#endif + } + + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE; + + if (Key->WriteAs != WRITE_PAIR) { + AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, + (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + } + else { + const char *Subkey; + char *Nextkey; + if (it8->sy != SSTRING) + return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); + + // chop the string as a list of "subkey, value" pairs, using ';' as a separator + for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) + { + char *Value, *temp; + + // identify token pair boundary + Nextkey = (char*)strchr(Subkey, ';'); + if (Nextkey) + *Nextkey++ = '\0'; + + // for each pair, split the subkey and the value + Value = (char*)strrchr(Subkey, ','); + if (Value == NULL) + return SynError(it8, "Invalid value for property '%s'.", VarName); + + // gobble the spaces before the coma, and the coma itself + temp = Value++; + do *temp-- = '\0'; while (temp >= Subkey && *temp == ' '); + + // gobble any space at the right + temp = Value + strlen(Value) - 1; + while (*temp == ' ') *temp-- = '\0'; + + // trim the strings from the left + Subkey += strspn(Subkey, " "); + Value += strspn(Value, " "); + + if (Subkey[0] == 0 || Value[0] == 0) + return SynError(it8, "Invalid value for property '%s'.", VarName); + AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); + } + } + + InSymbol(it8); + break; + + + case SEOLN: break; + + default: + return SynError(it8, "expected keyword or identifier"); + } + + SkipEOLN(it8); + } + + return TRUE; + +} + + +static +void ReadType(cmsIT8* it8, char* SheetTypePtr) +{ + cmsInt32Number cnt = 0; + + // First line is a very special case. + + while (isseparator(it8->ch)) + NextCh(it8); + + while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) { + + if (cnt++ < MAXSTR) + *SheetTypePtr++= (char) it8 ->ch; + NextCh(it8); + } + + *SheetTypePtr = 0; +} + + +static +cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) +{ + char* SheetTypePtr = it8 ->Tab[0].SheetType; + + if (nosheet == 0) { + ReadType(it8, SheetTypePtr); + } + + InSymbol(it8); + + SkipEOLN(it8); + + while (it8-> sy != SEOF && + it8-> sy != SSYNERROR) { + + switch (it8 -> sy) { + + case SBEGIN_DATA_FORMAT: + if (!DataFormatSection(it8)) return FALSE; + break; + + case SBEGIN_DATA: + + if (!DataSection(it8)) return FALSE; + + if (it8 -> sy != SEOF) { + + AllocTable(it8); + it8 ->nTable = it8 ->TablesCount - 1; + + // Read sheet type if present. We only support identifier and string. + // is a type string + // anything else, is not a type string + if (nosheet == 0) { + + if (it8 ->sy == SIDENT) { + + // May be a type sheet or may be a prop value statement. We cannot use insymbol in + // this special case... + while (isseparator(it8->ch)) + NextCh(it8); + + // If a newline is found, then this is a type string + if (it8 ->ch == '\n' || it8->ch == '\r') { + + cmsIT8SetSheetType(it8, it8 ->id); + InSymbol(it8); + } + else + { + // It is not. Just continue + cmsIT8SetSheetType(it8, ""); + } + } + else + // Validate quoted strings + if (it8 ->sy == SSTRING) { + cmsIT8SetSheetType(it8, it8 ->str); + InSymbol(it8); + } + } + + } + break; + + case SEOLN: + SkipEOLN(it8); + break; + + default: + if (!HeaderSection(it8)) return FALSE; + } + + } + + return (it8 -> sy != SSYNERROR); +} + + + +// Init useful pointers + +static +void CookPointers(cmsIT8* it8) +{ + int idField, i; + char* Fld; + cmsUInt32Number j; + cmsUInt32Number nOldTable = it8 ->nTable; + + for (j=0; j < it8 ->TablesCount; j++) { + + TABLE* t = it8 ->Tab + j; + + t -> SampleID = 0; + it8 ->nTable = j; + + for (idField = 0; idField < t -> nSamples; idField++) + { + if (t ->DataFormat == NULL){ + SynError(it8, "Undefined DATA_FORMAT"); + return; + } + + Fld = t->DataFormat[idField]; + if (!Fld) continue; + + + if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { + + t -> SampleID = idField; + } + + // "LABEL" is an extension. It keeps references to forward tables + + if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') { + + // Search for table references... + for (i = 0; i < t->nPatches; i++) { + + char* Label = GetData(it8, i, idField); + + if (Label) { + + cmsUInt32Number k; + + // This is the label, search for a table containing + // this property + + for (k = 0; k < it8->TablesCount; k++) { + + TABLE* Table = it8->Tab + k; + KEYVALUE* p; + + if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { + + // Available, keep type and table + char Buffer[256]; + + char* Type = p->Value; + int nTable = (int)k; + + snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type); + + SetData(it8, i, idField, Buffer); + } + } + + + } + + } + + + } + + } + } + + it8 ->nTable = nOldTable; +} + +// Try to infere if the file is a CGATS/IT8 file at all. Read first line +// that should be something like some printable characters plus a \n +// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line? +static +int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n) +{ + int words = 1, space = 0, quot = 0; + cmsUInt32Number i; + + if (n < 10) return 0; // Too small + + if (n > 132) + n = 132; + + for (i = 1; i < n; i++) { + + switch(Buffer[i]) + { + case '\n': + case '\r': + return ((quot == 1) || (words > 2)) ? 0 : words; + case '\t': + case ' ': + if(!quot && !space) + space = 1; + break; + case '\"': + quot = !quot; + break; + default: + if (Buffer[i] < 32) return 0; + if (Buffer[i] > 127) return 0; + words += space; + space = 0; + break; + } + } + + return 0; +} + + +static +cmsBool IsMyFile(const char* FileName) +{ + FILE *fp; + cmsUInt32Number Size; + cmsUInt8Number Ptr[133]; + + fp = fopen(FileName, "rt"); + if (!fp) { + cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName); + return FALSE; + } + + Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); + + if (fclose(fp) != 0) + return FALSE; + + Ptr[Size] = '\0'; + + return IsMyBlock(Ptr, Size); +} + +// ---------------------------------------------------------- Exported routines + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len) +{ + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(Ptr != NULL); + _cmsAssert(len != 0); + + type = IsMyBlock((const cmsUInt8Number*)Ptr, len); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + if (!hIT8) return NULL; + + it8 = (cmsIT8*) hIT8; + it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1); + if (it8->MemoryBlock == NULL) + { + cmsIT8Free(hIT8); + return FALSE; + } + + strncpy(it8 ->MemoryBlock, (const char*) Ptr, len); + it8 ->MemoryBlock[len] = 0; + + strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1); + it8-> Source = it8 -> MemoryBlock; + + if (!ParseIT8(it8, type-1)) { + + cmsIT8Free(hIT8); + return FALSE; + } + + CookPointers(it8); + it8 ->nTable = 0; + + _cmsFree(ContextID, it8->MemoryBlock); + it8 -> MemoryBlock = NULL; + + return hIT8; + + +} + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName) +{ + + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(cFileName != NULL); + + type = IsMyFile(cFileName); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + it8 = (cmsIT8*) hIT8; + if (!hIT8) return NULL; + + + it8 ->FileStack[0]->Stream = fopen(cFileName, "rt"); + + if (!it8 ->FileStack[0]->Stream) { + cmsIT8Free(hIT8); + return NULL; + } + + + strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1); + it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0; + + if (!ParseIT8(it8, type-1)) { + + fclose(it8 ->FileStack[0]->Stream); + cmsIT8Free(hIT8); + return NULL; + } + + CookPointers(it8); + it8 ->nTable = 0; + + if (fclose(it8 ->FileStack[0]->Stream)!= 0) { + cmsIT8Free(hIT8); + return NULL; + } + + return hIT8; + +} + +int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + if (SampleNames) + *SampleNames = t -> DataFormat; + return t -> nSamples; +} + + +cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + cmsUInt32Number n; + char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + // Pass#1 - count properties + + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + n++; + } + + + Props = (char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + Props[n++] = p -> Keyword; + } + + *PropertyNames = Props; + return n; +} + +cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE *p, *tmp; + cmsUInt32Number n; + const char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + + t = GetTable(it8); + + if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { + *SubpropertyNames = 0; + return 0; + } + + // Pass#1 - count properties + + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + n++; + } + + + Props = (const char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + Props[n++] = p ->Subkey; + } + + *SubpropertyNames = Props; + return n; +} + +static +int LocatePatch(cmsIT8* it8, const char* cPatch) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data != NULL) { + + if (cmsstrcasecmp(data, cPatch) == 0) + return i; + } + } + + // SynError(it8, "Couldn't find patch '%s'\n", cPatch); + return -1; +} + + +static +int LocateEmptyPatch(cmsIT8* it8) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data == NULL) + return i; + + } + + return -1; +} + +static +int LocateSample(cmsIT8* it8, const char* cSample) +{ + int i; + const char *fld; + TABLE* t = GetTable(it8); + + for (i=0; i < t->nSamples; i++) { + + fld = GetDataFormat(it8, i); + if (fld != NULL) { + if (cmsstrcasecmp(fld, cSample) == 0) + return i; + } + } + + return -1; + +} + + +int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return LocateSample(it8, cSample); +} + + + +const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return GetData(it8, row, col); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col) +{ + const char* Buffer; + + Buffer = cmsIT8GetDataRowCol(hIT8, row, col); + + if (Buffer == NULL) return 0.0; + + return ParseFloatNumber(Buffer); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return SetData(it8, row, col, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + + return SetData(it8, row, col, Buff); +} + + + +const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + + _cmsAssert(hIT8 != NULL); + + iField = LocateSample(it8, cSample); + if (iField < 0) { + return NULL; + } + + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return NULL; + } + + return GetData(it8, iSet, iField); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample) +{ + const char* Buffer; + + Buffer = cmsIT8GetData(it8, cPatch, cSample); + + return ParseFloatNumber(Buffer); +} + + + +cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + iField = LocateSample(it8, cSample); + + if (iField < 0) + return FALSE; + + if (t-> nPatches == 0) { + + AllocateDataFormat(it8); + AllocateDataSet(it8); + CookPointers(it8); + } + + if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) { + + iSet = LocateEmptyPatch(it8); + if (iSet < 0) { + return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); + } + + iField = t -> SampleID; + } + else { + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return FALSE; + } + } + + return SetData(it8, iSet, iField, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + return cmsIT8SetData(hIT8, cPatch, cSample, Buff); +} + +// Buffer should get MAXSTR at least + +const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + char* Data; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + Data = GetData(it8, nPatch, t->SampleID); + + if (!Data) return NULL; + if (!buffer) return Data; + + strncpy(buffer, Data, MAXSTR-1); + buffer[MAXSTR-1] = 0; + return buffer; +} + +int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) +{ + _cmsAssert(hIT8 != NULL); + + return LocatePatch((cmsIT8*)hIT8, cPatch); +} + +cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return it8 ->TablesCount; +} + +// This handles the "LABEL" extension. +// Label, nTable, Type + +int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) +{ + const char* cLabelFld; + char Type[256], Label[256]; + cmsUInt32Number nTable; + + _cmsAssert(hIT8 != NULL); + + if (cField != NULL && *cField == 0) + cField = "LABEL"; + + if (cField == NULL) + cField = "LABEL"; + + cLabelFld = cmsIT8GetData(hIT8, cSet, cField); + if (!cLabelFld) return -1; + + if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3) + return -1; + + if (ExpectedType != NULL && *ExpectedType == 0) + ExpectedType = NULL; + + if (ExpectedType) { + + if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1; + } + + return cmsIT8SetTable(hIT8, nTable); +} + + +cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int pos; + + _cmsAssert(hIT8 != NULL); + + pos = LocateSample(it8, cSample); + if(pos == -1) + return FALSE; + + it8->Tab[it8->nTable].SampleID = pos; + return TRUE; +} + + +void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + if (Formatter == NULL) + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + else + strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter)); + + it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscnvrt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscnvrt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmscnvrt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmscnvrt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1243 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. +// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + + +// This is a structure holding implementations for all supported intents. +typedef struct _cms_intents_list { + + cmsUInt32Number Intent; + char Description[256]; + cmsIntentFn Link; + struct _cms_intents_list* Next; + +} cmsIntentsList; + + +// Built-in intents +static cmsIntentsList DefaultIntents[] = { + + { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, + { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, + { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, + { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, + { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, + { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, + { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, + { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, + { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, + { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } +}; + + +// A pointer to the beginning of the list +_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginIntentsList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsIntentsPluginChunkType newHead = { NULL }; + cmsIntentsList* entry; + cmsIntentsList* Anterior = NULL; + _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; + + // Walk the list copying all nodes + for (entry = head->Intents; + entry != NULL; + entry = entry ->Next) { + + cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Intents == NULL) + newHead.Intents = newEntry; + } + + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); +} + +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginIntentsList(ctx, src); + } + else { + static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); + } +} + + +// Search the list for a suitable intent. Returns NULL if not found +static +cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + + for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; + + for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; + + return NULL; +} + +// Black point compensation. Implemented as a linear scaling in XYZ. Black points +// should come relative to the white point. Fills an matrix/offset element m +// which is organized as a 4x4 matrix. +static +void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, + const cmsCIEXYZ* BlackPointOut, + cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; + + // Now we need to compute a matrix plus an offset m and of such of + // [m]*bpin + off = bpout + // [m]*D50 + off = D50 + // + // This is a linear scaling in the form ax+b, where + // a = (bpout - D50) / (bpin - D50) + // b = - D50* (bpout - bpin) / (bpin - D50) + + tx = BlackPointIn->X - cmsD50_XYZ()->X; + ty = BlackPointIn->Y - cmsD50_XYZ()->Y; + tz = BlackPointIn->Z - cmsD50_XYZ()->Z; + + ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; + ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; + az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; + + bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; + by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; + bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; + + _cmsVEC3init(&m ->v[0], ax, 0, 0); + _cmsVEC3init(&m ->v[1], 0, ay, 0); + _cmsVEC3init(&m ->v[2], 0, 0, az); + _cmsVEC3init(off, bx, by, bz); + +} + + +// Approximate a blackbody illuminant based on CHAD information +static +cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) +{ + // Convert D50 across inverse CHAD to get the absolute white point + cmsVEC3 d, s; + cmsCIEXYZ Dest; + cmsCIExyY DestChromaticity; + cmsFloat64Number TempK; + cmsMAT3 m1, m2; + + m1 = *Chad; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + + s.n[VX] = cmsD50_XYZ() -> X; + s.n[VY] = cmsD50_XYZ() -> Y; + s.n[VZ] = cmsD50_XYZ() -> Z; + + _cmsMAT3eval(&d, &m2, &s); + + Dest.X = d.n[VX]; + Dest.Y = d.n[VY]; + Dest.Z = d.n[VZ]; + + cmsXYZ2xyY(&DestChromaticity, &Dest); + + if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) + return -1.0; + + return TempK; +} + +// Compute a CHAD based on a given temperature +static + void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) +{ + cmsCIEXYZ White; + cmsCIExyY ChromaticityOfWhite; + + cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); + cmsxyY2XYZ(&White, &ChromaticityOfWhite); + _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); +} + +// Join scalings to obtain relative input to absolute and then to relative output. +// Result is stored in a 3x3 matrix +static +cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, + const cmsCIEXYZ* WhitePointIn, + const cmsMAT3* ChromaticAdaptationMatrixIn, + const cmsCIEXYZ* WhitePointOut, + const cmsMAT3* ChromaticAdaptationMatrixOut, + cmsMAT3* m) +{ + cmsMAT3 Scale, m1, m2, m3, m4; + + // TODO: Follow Marc Mahy's recommendation to check if CHAD is same by using M1*M2 == M2*M1. If so, do nothing. + // TODO: Add support for ArgyllArts tag + + // Adaptation state + if (AdaptationState == 1.0) { + + // Observer is fully adapted. Keep chromatic adaptation. + // That is the standard V4 behaviour + _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + } + else { + + // Incomplete adaptation. This is an advanced feature. + _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + + if (AdaptationState == 0.0) { + + m1 = *ChromaticAdaptationMatrixOut; + _cmsMAT3per(&m2, &m1, &Scale); + // m2 holds CHAD from output white to D50 times abs. col. scaling + + // Observer is not adapted, undo the chromatic adaptation + _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); + + m3 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; + _cmsMAT3per(m, &m2, &m4); + + } else { + + cmsMAT3 MixedCHAD; + cmsFloat64Number TempSrc, TempDest, Temp; + + m1 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + _cmsMAT3per(&m3, &m2, &Scale); + // m3 holds CHAD from input white to D50 times abs. col. scaling + + TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); + TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); + + if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong + + if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { + + _cmsMAT3identity(m); + return TRUE; + } + + Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; + + // Get a CHAD from whatever output temperature to D50. This replaces output CHAD + Temp2CHAD(&MixedCHAD, Temp); + + _cmsMAT3per(m, &m3, &MixedCHAD); + } + + } + return TRUE; + +} + +// Just to see if m matrix should be applied +static +cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number diff = 0; + cmsMAT3 Ident; + int i; + + if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer + if (m == NULL && off != NULL) return FALSE; // This is an internal error + + _cmsMAT3identity(&Ident); + + for (i=0; i < 3*3; i++) + diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); + + for (i=0; i < 3; i++) + diff += fabs(((cmsFloat64Number*)off)[i]); + + + return (diff < 0.002); +} + + +// Compute the conversion layer +static +cmsBool ComputeConversion(cmsUInt32Number i, + cmsHPROFILE hProfiles[], + cmsUInt32Number Intent, + cmsBool BPC, + cmsFloat64Number AdaptationState, + cmsMAT3* m, cmsVEC3* off) +{ + + int k; + + // m and off are set to identity and this is detected latter on + _cmsMAT3identity(m); + _cmsVEC3init(off, 0, 0, 0); + + // If intent is abs. colorimetric, + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { + + cmsCIEXYZ WhitePointIn, WhitePointOut; + cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; + + _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); + _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); + + _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); + _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); + + if (!ComputeAbsoluteIntent(AdaptationState, + &WhitePointIn, &ChromaticAdaptationMatrixIn, + &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; + + } + else { + // Rest of intents may apply BPC. + + if (BPC) { + + cmsCIEXYZ BlackPointIn, BlackPointOut; + + cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); + cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); + + // If black points are equal, then do nothing + if (BlackPointIn.X != BlackPointOut.X || + BlackPointIn.Y != BlackPointOut.Y || + BlackPointIn.Z != BlackPointOut.Z) + ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); + } + } + + // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, + // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so + // we have first to convert from encoded to XYZ and then convert back to encoded. + // y = Mx + Off + // x = x'c + // y = M x'c + Off + // y = y'c; y' = y / c + // y' = (Mx'c + Off) /c = Mx' + (Off / c) + + for (k=0; k < 3; k++) { + off ->n[k] /= MAX_ENCODEABLE_XYZ; + } + + return TRUE; +} + + +// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space +static +cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; + cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; + + // Handle PCS mismatches. A specialized stage is added to the LUT in such case + switch (InPCS) { + + case cmsSigXYZData: // Input profile operates in XYZ + + switch (OutPCS) { + + case cmsSigXYZData: // XYZ -> XYZ + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // XYZ -> Lab + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + break; + + default: + return FALSE; // Colorspace mismatch + } + break; + + case cmsSigLabData: // Input profile operates in Lab + + switch (OutPCS) { + + case cmsSigXYZData: // Lab -> XYZ + + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) + return FALSE; + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // Lab -> Lab + + if (!IsEmptyLayer(m, off)) { + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || + !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + } + break; + + default: + return FALSE; // Mismatch + } + break; + + // On colorspaces other than PCS, check for same space + default: + if (InPCS != OutPCS) return FALSE; + break; + } + + return TRUE; +} + + +// Is a given space compatible with another? +static +cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) +{ + // If they are same, they are compatible. + if (a == b) return TRUE; + + // Check for MCH4 substitution of CMYK + if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; + if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; + + // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. + if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; + if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; + + return FALSE; +} + + +// Default handler for ICC-style intents +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsPipeline* Lut = NULL; + cmsPipeline* Result; + cmsHPROFILE hProfile; + cmsMAT3 m; + cmsVEC3 off; + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut = cmsSigLabData, CurrentColorSpace; + cmsProfileClassSignature ClassSig; + cmsUInt32Number i, Intent; + + // For safety + if (nProfiles == 0) return NULL; + + // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' + Result = cmsPipelineAlloc(ContextID, 0, 0); + if (Result == NULL) return NULL; + + CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsBool lIsDeviceLink, lIsInput; + + hProfile = hProfiles[i]; + ClassSig = cmsGetDeviceClass(hProfile); + lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); + + // First profile is used as input unless devicelink or abstract + if ((i == 0) && !lIsDeviceLink) { + lIsInput = TRUE; + } + else { + // Else use profile in the input direction if current space is not PCS + lIsInput = (CurrentColorSpace != cmsSigXYZData) && + (CurrentColorSpace != cmsSigLabData); + } + + Intent = TheIntents[i]; + + if (lIsInput || lIsDeviceLink) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else { + + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); + goto Error; + } + + // If devicelink is found, then no custom intent is allowed and we can + // read the LUT to be applied. Settings don't apply here. + if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { + + // Get the involved LUT from the profile + Lut = _cmsReadDevicelinkLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + // What about abstract profiles? + if (ClassSig == cmsSigAbstractClass && i > 0) { + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + } + else { + _cmsMAT3identity(&m); + _cmsVEC3init(&off, 0, 0, 0); + } + + + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + else { + + if (lIsInput) { + // Input direction means non-pcs connection, so proceed like devicelinks + Lut = _cmsReadInputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + } + else { + + // Output direction means PCS connection. Intent may apply here + Lut = _cmsReadOutputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + } + + // Concatenate to the output LUT + if (!cmsPipelineCat(Result, Lut)) + goto Error; + + cmsPipelineFree(Lut); + Lut = NULL; + + // Update current space + CurrentColorSpace = ColorSpaceOut; + } + + // Check for non-negatives clip + if (dwFlags & cmsFLAGS_NONEGATIVES) { + + if (ColorSpaceOut == cmsSigGrayData || + ColorSpaceOut == cmsSigRgbData || + ColorSpaceOut == cmsSigCmykData) { + + cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut)); + if (clip == NULL) goto Error; + + if (!cmsPipelineInsertStage(Result, cmsAT_END, clip)) + goto Error; + } + + } + + return Result; + +Error: + + if (Lut != NULL) cmsPipelineFree(Lut); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + + cmsUNUSED_PARAMETER(dwFlags); +} + + +// Wrapper for DLL calling convention +cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// Black preserving intents --------------------------------------------------------------------------------------------- + +// Translate black-preserving intents to ICC ones +static +cmsUInt32Number TranslateNonICCIntents(cmsUInt32Number Intent) +{ + switch (Intent) { + case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: + case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: + return INTENT_PERCEPTUAL; + + case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: + case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: + return INTENT_RELATIVE_COLORIMETRIC; + + case INTENT_PRESERVE_K_ONLY_SATURATION: + case INTENT_PRESERVE_K_PLANE_SATURATION: + return INTENT_SATURATION; + + default: return Intent; + } +} + +// Sampler for Black-only preserving CMYK->CMYK transforms + +typedef struct { + cmsPipeline* cmyk2cmyk; // The original transform + cmsToneCurve* KTone; // Black-to-black tone curve + +} GrayOnlyParams; + + +// Preserve black only if that is the only ink used +static +int BlackPreservingGrayOnlySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + // TAC does not apply because it is black ink! + Out[0] = Out[1] = Out[2] = 0; + Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); + return TRUE; + } + + // Keep normal transform for other colors + bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); + return TRUE; +} + +// This is the entry for black-preserving K-only intents, which are non-ICC +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + GrayOnlyParams bp; + cmsPipeline* Result; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsUInt32Number lastProfilePos; + cmsUInt32Number preservationProfilesCount; + cmsHPROFILE hLastProfile; + + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + + // Trim all CMYK devicelinks at the end + lastProfilePos = nProfiles - 1; + hLastProfile = hProfiles[lastProfilePos]; + + while (lastProfilePos > 1) + { + hLastProfile = hProfiles[--lastProfilePos]; + if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass) + break; + } + + preservationProfilesCount = lastProfilePos + 1; + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + memset(&bp, 0, sizeof(bp)); + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.cmyk2cmyk == NULL) goto Error; + + // Now, compute the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, + 4096, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.KTone == NULL) goto Error; + + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + // Create the CLUT. 16 bits + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Error; + + // This is the one and only MPE in this LUT + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Error; + + // Sample it. We cannot afford pre/post linearization this time. + if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) + goto Error; + + + // Insert possible devicelinks at the end + for (i = lastProfilePos + 1; i < nProfiles; i++) + { + cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]); + if (devlink == NULL) + goto Error; + + if (!cmsPipelineCat(Result, devlink)) + goto Error; + } + + + // Get rid of xform and tone curve + cmsPipelineFree(bp.cmyk2cmyk); + cmsFreeToneCurve(bp.KTone); + + return Result; + +Error: + + if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + +} + +// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ + +typedef struct { + + cmsPipeline* cmyk2cmyk; // The original transform + cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) + cmsHTRANSFORM cmyk2Lab; // The input chain + cmsToneCurve* KTone; // Black-to-black tone curve + cmsPipeline* LabK2cmyk; // The output profile + cmsFloat64Number MaxError; + + cmsHTRANSFORM hRoundTrip; + cmsFloat64Number MaxTAC; + + +} PreserveKPlaneParams; + + +// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision +static +int BlackPreservingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + int i; + cmsFloat32Number Inf[4], Outf[4]; + cmsFloat32Number LabK[4]; + cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; + cmsCIELab ColorimetricLab, BlackPreservingLab; + PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; + + // Convert from 16 bits to floating point + for (i=0; i < 4; i++) + Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Get the K across Tone curve + LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + Out[0] = Out[1] = Out[2] = 0; + Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); + return TRUE; + } + + // Try the original transform, + cmsPipelineEvalFloat(Inf, Outf, bp ->cmyk2cmyk); + + // Store a copy of the floating point result into 16-bit + for (i=0; i < 4; i++) + Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); + + // Maybe K is already ok (mostly on K=0) + if (fabsf(Outf[3] - LabK[3]) < (3.0 / 65535.0)) { + return TRUE; + } + + // K differ, measure and keep Lab measurement for further usage + // this is done in relative colorimetric intent + cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); + + // Is not black only and the transform doesn't keep black. + // Obtain the Lab of output CMYK. After that we have Lab + K + cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); + + // Obtain the corresponding CMY using reverse interpolation + // (K is fixed in LabK[3]) + if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { + + // Cannot find a suitable value, so use colorimetric xform + // which is already stored in Out[] + return TRUE; + } + + // Make sure to pass through K (which now is fixed) + Outf[3] = LabK[3]; + + // Apply TAC if needed + SumCMY = (cmsFloat64Number) Outf[0] + Outf[1] + Outf[2]; + SumCMYK = SumCMY + Outf[3]; + + if (SumCMYK > bp ->MaxTAC) { + + Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else + Ratio = 1.0; + + Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C + Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M + Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y + Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); + + // Estimate the error (this goes 16 bits to Lab DBL) + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + if (Error > bp -> MaxError) + bp->MaxError = Error; + + return TRUE; +} + + + +// This is the entry for black-plane preserving, which are non-ICC +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + PreserveKPlaneParams bp; + + cmsPipeline* Result = NULL; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsUInt32Number lastProfilePos; + cmsUInt32Number preservationProfilesCount; + cmsHPROFILE hLastProfile; + cmsHPROFILE hLab; + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + // Trim all CMYK devicelinks at the end + lastProfilePos = nProfiles - 1; + hLastProfile = hProfiles[lastProfilePos]; + + while (lastProfilePos > 1) + { + hLastProfile = hProfiles[--lastProfilePos]; + if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass) + break; + } + + preservationProfilesCount = lastProfilePos + 1; + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hLastProfile) == cmsSigCmykData || + cmsGetDeviceClass(hLastProfile) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + memset(&bp, 0, sizeof(bp)); + + // We need the input LUT of the last profile, assuming this one is responsible of + // black generation. This LUT will be searched in inverse order. + bp.LabK2cmyk = _cmsReadInputLUT(hLastProfile, INTENT_RELATIVE_COLORIMETRIC); + if (bp.LabK2cmyk == NULL) goto Cleanup; + + // Get total area coverage (in 0..1 domain) + bp.MaxTAC = cmsDetectTAC(hLastProfile) / 100.0; + if (bp.MaxTAC <= 0) goto Cleanup; + + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.cmyk2cmyk == NULL) goto Cleanup; + + // Now the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, preservationProfilesCount, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.KTone == NULL) goto Cleanup; + + // To measure the output, Last profile to Lab + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + bp.hProofOutput = cmsCreateTransformTHR(ContextID, hLastProfile, + CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if ( bp.hProofOutput == NULL) goto Cleanup; + + // Same as anterior, but lab in the 0..1 range + bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hLastProfile, + FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, + FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if (bp.cmyk2Lab == NULL) goto Cleanup; + cmsCloseProfile(hLab); + + // Error estimation (for debug only) + bp.MaxError = 0; + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Cleanup; + + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Cleanup; + + cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); + + // Insert possible devicelinks at the end + for (i = lastProfilePos + 1; i < nProfiles; i++) + { + cmsPipeline* devlink = _cmsReadDevicelinkLUT(hProfiles[i], ICCIntents[i]); + if (devlink == NULL) + goto Cleanup; + + if (!cmsPipelineCat(Result, devlink)) + goto Cleanup; + } + + +Cleanup: + + if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); + if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); + + if (bp.KTone) cmsFreeToneCurve(bp.KTone); + if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); + + return Result; +} + + + +// Link routines ------------------------------------------------------------------------------------------------------ + +// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler +// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the +// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsIntentsList* Intent; + + // Make sure a reasonable number of profiles is provided + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); + return NULL; + } + + for (i=0; i < nProfiles; i++) { + + // Check if black point is really needed or allowed. Note that + // following Adobe's document: + // BPC does not apply to devicelink profiles, nor to abs colorimetric, + // and applies always on V4 perceptual and saturation. + + if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) + BPC[i] = FALSE; + + if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { + + // Force BPC for V4 profiles in perceptual and saturation + if (cmsGetEncodedICCversion(hProfiles[i]) >= 0x4000000) + BPC[i] = TRUE; + } + } + + // Search for a handler. The first intent in the chain defines the handler. That would + // prevent using multiple custom intents in a multiintent chain, but the behaviour of + // this case would present some issues if the custom intent tries to do things like + // preserve primaries. This solution is not perfect, but works well on most cases. + + Intent = SearchIntent(ContextID, TheIntents[0]); + if (Intent == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); + return NULL; + } + + // Call the handler + return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// ------------------------------------------------------------------------------------------------- + +// Get information about available intents. nMax is the maximum space for the supplied "Codes" +// and "Descriptions" the function returns the total number of intents, which may be greater +// than nMax, although the matrices are not populated beyond this level. +cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + cmsUInt32Number nIntents; + + + for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + nIntents++; + } + + for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + nIntents++; + } + return nIntents; +} + +cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); +} + +// The plug-in registration. User can add new intents or override default routines +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); + cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; + cmsIntentsList* fl; + + // Do we have to reset the custom intents? + if (Data == NULL) { + + ctx->Intents = NULL; + return TRUE; + } + + fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); + if (fl == NULL) return FALSE; + + + fl ->Intent = Plugin ->Intent; + strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); + fl ->Description[sizeof(fl ->Description)-1] = 0; + + fl ->Link = Plugin ->Link; + + fl ->Next = ctx ->Intents; + ctx ->Intents = fl; + + return TRUE; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmserr.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmserr.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmserr.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmserr.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,692 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- + +#include "lcms2_internal.h" + + +// This function is here to help applications to prevent mixing lcms versions on header and shared objects. +int CMSEXPORT cmsGetEncodedCMMversion(void) +{ + return LCMS_VERSION; +} + +// I am so tired about incompatibilities on those functions that here are some replacements +// that hopefully would be fully portable. + +// compare two strings ignoring case +int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2) +{ + CMSREGISTER const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; + + while (toupper(*us1) == toupper(*us2++)) + if (*us1++ == '\0') + return 0; + + return (toupper(*us1) - toupper(*--us2)); +} + +// long int because C99 specifies ftell in such way (7.19.9.2) +long int CMSEXPORT cmsfilelength(FILE* f) +{ + long int p , n; + + p = ftell(f); // register current file position + if (p == -1L) + return -1L; + + if (fseek(f, 0, SEEK_END) != 0) { + return -1L; + } + + n = ftell(f); + fseek(f, p, SEEK_SET); // file position restored + + return n; +} + + +// Memory handling ------------------------------------------------------------------ +// +// This is the interface to low-level memory management routines. By default a simple +// wrapping to malloc/free/realloc is provided, although there is a limit on the max +// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent +// bogus or evil code to allocate huge blocks that otherwise lcms would never need. + +#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) + +// User may override this behaviour by using a memory plug-in, which basically replaces +// the default memory management functions. In this case, no check is performed and it +// is up to the plug-in writter to keep in the safe side. There are only three functions +// required to be implemented: malloc, realloc and free, although the user may want to +// replace the optional mallocZero, calloc and dup as well. + +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// ********************************************************************************* + +// This is the default memory allocation function. It does a very coarse +// check of amount of memory, just to prevent exploits +static +void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum + + return (void*) malloc(size); + + cmsUNUSED_PARAMETER(ContextID); +} + +// Generic allocate & zero +static +void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + void *pt = _cmsMalloc(ContextID, size); + if (pt == NULL) return NULL; + + memset(pt, 0, size); + return pt; +} + + +// The default free function. The only check proformed is against NULL pointers +static +void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) +{ + // free(NULL) is defined a no-op by C99, therefore it is safe to + // avoid the check, but it is here just in case... + + if (Ptr) free(Ptr); + + cmsUNUSED_PARAMETER(ContextID); +} + +// The default realloc function. Again it checks for exploits. If Ptr is NULL, +// realloc behaves the same way as malloc and allocates a new block of size bytes. +static +void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb + + return realloc(Ptr, size); + + cmsUNUSED_PARAMETER(ContextID); +} + + +// The default calloc function. Allocates an array of num elements, each one of size bytes +// all memory is initialized to zero. +static +void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + cmsUInt32Number Total = num * size; + + // Preserve calloc behaviour + if (Total == 0) return NULL; + + // Safe check for overflow. + if (num >= UINT_MAX / size) return NULL; + + // Check for overflow + if (Total < num || Total < size) { + return NULL; + } + + if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb + + return _cmsMallocZero(ContextID, Total); +} + +// Generic block duplication +static +void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + void* mem; + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb + + mem = _cmsMalloc(ContextID, size); + + if (mem != NULL && Org != NULL) + memmove(mem, Org, size); + + return mem; +} + + +// Pointers to memory manager functions in Context0 +_cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn, + _cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn + }; + + +// Reset and duplicate memory manager +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate + ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType)); + } + else { + + // To reset it, we use the default allocators, which cannot be overridden + ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager; + } +} + +// Auxiliary to fill memory management functions from plugin (or context 0 defaults) +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr) +{ + if (Plugin == NULL) { + + memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk)); + } + else { + + ptr ->MallocPtr = Plugin -> MallocPtr; + ptr ->FreePtr = Plugin -> FreePtr; + ptr ->ReallocPtr = Plugin -> ReallocPtr; + + // Make sure we revert to defaults + ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn; + ptr ->CallocPtr = _cmsCallocDefaultFn; + ptr ->DupPtr = _cmsDupDefaultFn; + + if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; + if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; + if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; + + } +} + + +// Plug-in replacement entry +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data) +{ + cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + _cmsMemPluginChunkType* ptr; + + // NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure. + // Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the + // context internal data should be malloce'd by using those functions. + if (Data == NULL) { + + struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; + + // Return to the default allocators + if (ContextID != NULL) { + ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; + } + return TRUE; + } + + // Check for required callbacks + if (Plugin -> MallocPtr == NULL || + Plugin -> FreePtr == NULL || + Plugin -> ReallocPtr == NULL) return FALSE; + + // Set replacement functions + ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + if (ptr == NULL) + return FALSE; + + _cmsInstallAllocFunctions(Plugin, ptr); + return TRUE; +} + +// Generic allocate +void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->MallocPtr(ContextID, size); +} + +// Generic allocate & zero +void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->MallocZeroPtr(ContextID, size); +} + +// Generic calloc +void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->CallocPtr(ContextID, num, size); +} + +// Generic reallocate +void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->ReallocPtr(ContextID, Ptr, size); +} + +// Generic free memory +void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) +{ + if (Ptr != NULL) { + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + ptr ->FreePtr(ContextID, Ptr); + } +} + +// Generic block duplication +void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->DupPtr(ContextID, Org, size); +} + +// ******************************************************************************************** + +// Sub allocation takes care of many pointers of small size. The memory allocated in +// this way have be freed at once. Next function allocates a single chunk for linked list +// I prefer this method over realloc due to the big inpact on xput realloc may have if +// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) +static +_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator_chunk* chunk; + + // 20K by default + if (Initial == 0) + Initial = 20*1024; + + // Create the container + chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); + if (chunk == NULL) return NULL; + + // Initialize values + chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial); + if (chunk ->Block == NULL) { + + // Something went wrong + _cmsFree(ContextID, chunk); + return NULL; + } + + chunk ->BlockSize = Initial; + chunk ->Used = 0; + chunk ->next = NULL; + + return chunk; +} + +// The suballocated is nothing but a pointer to the first element in the list. We also keep +// the thread ID in this structure. +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator* sub; + + // Create the container + sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator)); + if (sub == NULL) return NULL; + + sub ->ContextID = ContextID; + + sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial); + if (sub ->h == NULL) { + _cmsFree(ContextID, sub); + return NULL; + } + + return sub; +} + + +// Get rid of whole linked list +void _cmsSubAllocDestroy(_cmsSubAllocator* sub) +{ + _cmsSubAllocator_chunk *chunk, *n; + + for (chunk = sub ->h; chunk != NULL; chunk = n) { + + n = chunk->next; + if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block); + _cmsFree(sub ->ContextID, chunk); + } + + // Free the header + _cmsFree(sub ->ContextID, sub); +} + + +// Get a pointer to small memory block. +void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) +{ + cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + // Check for memory. If there is no room, allocate a new chunk of double memory size. + if (size > Free) { + + _cmsSubAllocator_chunk* chunk; + cmsUInt32Number newSize; + + newSize = sub -> h ->BlockSize * 2; + if (newSize < size) newSize = size; + + chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize); + if (chunk == NULL) return NULL; + + // Link list + chunk ->next = sub ->h; + sub ->h = chunk; + + } + + ptr = sub -> h ->Block + sub -> h ->Used; + sub -> h -> Used += size; + + return (void*) ptr; +} + +// Duplicate in pool +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size) +{ + void *NewPtr; + + // Dup of null pointer is also NULL + if (ptr == NULL) + return NULL; + + NewPtr = _cmsSubAlloc(s, size); + + if (ptr != NULL && NewPtr != NULL) { + memcpy(NewPtr, ptr, size); + } + + return NewPtr; +} + + + +// Error logging ****************************************************************** + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other return FALSE +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function does receive +// a ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user, or just create some sort of log. +// The logging function should NOT terminate the program, as this obviously can leave +// resources. It is the programmer's responsibility to check each function return code +// to make sure it didn't fail. + +// Error messages are limited to MAX_ERROR_MESSAGE_LEN + +#define MAX_ERROR_MESSAGE_LEN 1024 + +// --------------------------------------------------------------------------------------------------------- + +// This is our default log error +static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Context0 storage, which is global +_cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction }; + +// Allocates and inits error logger container for a given context. If src is NULL, only initializes the value +// to the default. Otherwise, it duplicates the value. The interface is standard across all context clients +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction }; + void* from; + + if (src != NULL) { + from = src ->chunks[Logger]; + } + else { + from = &LogErrorChunk; + } + + ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType)); +} + +// The default error logger does nothing. +static +void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) +{ + // fprintf(stderr, "[lcms]: %s\n", Text); + // fflush(stderr); + + cmsUNUSED_PARAMETER(ContextID); + cmsUNUSED_PARAMETER(ErrorCode); + cmsUNUSED_PARAMETER(Text); +} + +// Change log error, context based +void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn) +{ + _cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + + if (lhg != NULL) { + + if (Fn == NULL) + lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction; + else + lhg -> LogErrorHandler = Fn; + } +} + +// Change log error, legacy +void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn) +{ + cmsSetLogErrorHandlerTHR(NULL, Fn); +} + +// Log an error +// ErrorText is a text holding an english description of error. +void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...) +{ + va_list args; + char Buffer[MAX_ERROR_MESSAGE_LEN]; + _cmsLogErrorChunkType* lhg; + + + va_start(args, ErrorText); + vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args); + va_end(args); + + // Check for the context, if specified go there. If not, go for the global + lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + if (lhg ->LogErrorHandler) { + lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer); + } +} + +// Utility function to print signatures +void _cmsTagSignature2String(char String[5], cmsTagSignature sig) +{ + cmsUInt32Number be; + + // Convert to big endian + be = _cmsAdjustEndianess32((cmsUInt32Number) sig); + + // Move chars + memmove(String, &be, 4); + + // Make sure of terminator + String[4] = 0; +} + +//-------------------------------------------------------------------------------------------------- + + +static +void* defMtxCreate(cmsContext id) +{ + _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex)); + _cmsInitMutexPrimitive(ptr_mutex); + return (void*) ptr_mutex; +} + +static +void defMtxDestroy(cmsContext id, void* mtx) +{ + _cmsDestroyMutexPrimitive((_cmsMutex *) mtx); + _cmsFree(id, mtx); +} + +static +cmsBool defMtxLock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; +} + +static +void defMtxUnlock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + _cmsUnlockPrimitive((_cmsMutex *) mtx); +} + + + +// Pointers to memory manager functions in Context0 +_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + void* from; + + if (src != NULL) { + from = src ->chunks[MutexPlugin]; + } + else { + from = &MutexChunk; + } + + ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType)); +} + +// Register new ways to transform +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginMutex* Plugin = (cmsPluginMutex*) Data; + _cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (Data == NULL) { + + // No lock routines + ctx->CreateMutexPtr = NULL; + ctx->DestroyMutexPtr = NULL; + ctx->LockMutexPtr = NULL; + ctx ->UnlockMutexPtr = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || + Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE; + + + ctx->CreateMutexPtr = Plugin->CreateMutexPtr; + ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr; + ctx ->LockMutexPtr = Plugin ->LockMutexPtr; + ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr; + + // All is ok + return TRUE; +} + +// Generic Mutex fns +void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->CreateMutexPtr == NULL) return NULL; + + return ptr ->CreateMutexPtr(ContextID); +} + +void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->DestroyMutexPtr != NULL) { + + ptr ->DestroyMutexPtr(ContextID, mtx); + } +} + +cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->LockMutexPtr == NULL) return TRUE; + + return ptr ->LockMutexPtr(ContextID, mtx); +} + +void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->UnlockMutexPtr != NULL) { + + ptr ->UnlockMutexPtr(ContextID, mtx); + } +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgamma.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgamma.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgamma.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgamma.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1517 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +#include "lcms2_internal.h" + +// Tone curves are powerful constructs that can contain curves specified in diverse ways. +// The curve is stored in segments, where each segment can be sampled or specified by parameters. +// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation, +// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes, +// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function, +// the plug-in should provide the type id, how many parameters each type has, and a pointer to +// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will +// be called with the type id as a negative value, and a sampled version of the reversed curve +// will be built. + +// ----------------------------------------------------------------- Implementation +// Maxim number of nodes +#define MAX_NODES_IN_CURVE 4097 +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) + +// The list of supported parametric curves +typedef struct _cmsParametricCurvesCollection_st { + + cmsUInt32Number nFunctions; // Number of supported functions in this chunk + cmsInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + + struct _cmsParametricCurvesCollection_st* Next; // Next in list + +} _cmsParametricCurvesCollection; + +// This is the default (built-in) evaluator +static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); + +// The built-in list +static _cmsParametricCurvesCollection DefaultCurves = { + 10, // # of curve types + { 1, 2, 3, 4, 5, 6, 7, 8, 108, 109 }, // Parametric curve ID + { 1, 3, 4, 5, 7, 4, 5, 5, 1, 1 }, // Parameters by type + DefaultEvalParametricFn, // Evaluator + NULL // Next in chain +}; + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginCurvesList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsCurvesPluginChunkType newHead = { NULL }; + _cmsParametricCurvesCollection* entry; + _cmsParametricCurvesCollection* Anterior = NULL; + _cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->ParametricCurves; + entry != NULL; + entry = entry ->Next) { + + _cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.ParametricCurves == NULL) + newHead.ParametricCurves = newEntry; + } + + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType)); +} + +// The allocator have to follow the chain +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Copy all linked list + DupPluginCurvesList(ctx, src); + } + else { + static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL }; + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType)); + } +} + + +// The linked list head +_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; + +// As a way to install new parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; + _cmsParametricCurvesCollection* fl; + + if (Data == NULL) { + + ctx -> ParametricCurves = NULL; + return TRUE; + } + + fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->Evaluator = Plugin ->Evaluator; + fl ->nFunctions = Plugin ->nFunctions; + + // Make sure no mem overwrites + if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN) + fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN; + + // Copy the data + memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number)); + memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); + + // Keep linked list + fl ->Next = ctx->ParametricCurves; + ctx->ParametricCurves = fl; + + // All is ok + return TRUE; +} + + +// Search in type list, return position or -1 if not found +static +int IsInSet(int Type, _cmsParametricCurvesCollection* c) +{ + int i; + + for (i=0; i < (int) c ->nFunctions; i++) + if (abs(Type) == c ->FunctionTypes[i]) return i; + + return -1; +} + + +// Search for the collection which contains a specific type +static +_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index) +{ + _cmsParametricCurvesCollection* c; + int Position; + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + + for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + // If none found, revert for defaults + for (c = &DefaultCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + + return NULL; +} + +// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case +// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the +// optimization curve is given. Both features simultaneously is an error +static +cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries, + cmsUInt32Number nSegments, const cmsCurveSegment* Segments, + const cmsUInt16Number* Values) +{ + cmsToneCurve* p; + cmsUInt32Number i; + + // We allow huge tables, which are then restricted for smoothing operations + if (nEntries > 65530) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries"); + return NULL; + } + + if (nEntries == 0 && nSegments == 0) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table"); + return NULL; + } + + // Allocate all required pointers, etc. + p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve)); + if (!p) return NULL; + + // In this case, there are no segments + if (nSegments == 0) { + p ->Segments = NULL; + p ->Evals = NULL; + } + else { + p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment)); + if (p ->Segments == NULL) goto Error; + + p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator)); + if (p ->Evals == NULL) goto Error; + } + + p -> nSegments = nSegments; + + // This 16-bit table contains a limited precision representation of the whole curve and is kept for + // increasing xput on certain operations. + if (nEntries == 0) { + p ->Table16 = NULL; + } + else { + p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number)); + if (p ->Table16 == NULL) goto Error; + } + + p -> nEntries = nEntries; + + // Initialize members if requested + if (Values != NULL && (nEntries > 0)) { + + for (i=0; i < nEntries; i++) + p ->Table16[i] = Values[i]; + } + + // Initialize the segments stuff. The evaluator for each segment is located and a pointer to it + // is placed in advance to maximize performance. + if (Segments != NULL && (nSegments > 0)) { + + _cmsParametricCurvesCollection *c; + + p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*)); + if (p ->SegInterp == NULL) goto Error; + + for (i=0; i < nSegments; i++) { + + // Type 0 is a special marker for table-based curves + if (Segments[i].Type == 0) + p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT); + + memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment)); + + if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL) + p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints); + else + p ->Segments[i].SampledPoints = NULL; + + + c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); + if (c != NULL) + p ->Evals[i] = c ->Evaluator; + } + } + + p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); + if (p->InterpParams != NULL) + return p; + +Error: + if (p -> SegInterp) _cmsFree(ContextID, p -> SegInterp); + if (p -> Segments) _cmsFree(ContextID, p -> Segments); + if (p -> Evals) _cmsFree(ContextID, p -> Evals); + if (p ->Table16) _cmsFree(ContextID, p ->Table16); + _cmsFree(ContextID, p); + return NULL; +} + + +// Generates a sigmoidal function with desired steepness. +cmsINLINE double sigmoid_base(double k, double t) +{ + return (1.0 / (1.0 + exp(-k * t))) - 0.5; +} + +cmsINLINE double inverted_sigmoid_base(double k, double t) +{ + return -log((1.0 / (t + 0.5)) - 1.0) / k; +} + +cmsINLINE double sigmoid_factory(double k, double t) +{ + double correction = 0.5 / sigmoid_base(k, 1); + + return correction * sigmoid_base(k, 2.0 * t - 1.0) + 0.5; +} + +cmsINLINE double inverse_sigmoid_factory(double k, double t) +{ + double correction = 0.5 / sigmoid_base(k, 1); + + return (inverted_sigmoid_base(k, (t - 0.5) / correction) + 1.0) / 2.0; +} + + +// Parametric Fn using floating point +static +cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R) +{ + cmsFloat64Number e, Val, disc; + + switch (Type) { + + // X = Y ^ Gamma + case 1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + Val = pow(R, Params[0]); + break; + + // Type 1 Reversed: X = Y ^1/gamma + case -1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = PLUS_INF; + else + Val = pow(R, 1 / Params[0]); + } + break; + + // CIE 122-1966 + // Y = (aX + b)^Gamma | X >= -b/a + // Y = 0 | else + case 2: + { + + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = 0; + } + } + break; + + // Type 2 Reversed + // X = (Y ^1/g - b) / a + case -2: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + if (R < 0) + Val = 0; + else + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + + if (Val < 0) + Val = 0; + } + } + break; + + + // IEC 61966-3 + // Y = (aX + b)^Gamma | X <= -b/a + // Y = c | else + case 3: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = -Params[2] / Params[1]; + if (disc < 0) + disc = 0; + + if (R >= disc) { + + e = Params[1] * R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[3]; + else + Val = 0; + } + else + Val = Params[3]; + } + } + break; + + + // Type 3 reversed + // X=((Y-c)^1/g - b)/a | (Y>=c) + // X=-b/a | (Y= Params[3]) { + + e = R - Params[3]; + + if (e > 0) + Val = (pow(e, 1 / Params[0]) - Params[2]) / Params[1]; + else + Val = 0; + } + else { + Val = -Params[2] / Params[1]; + } + } + } + break; + + + // IEC 61966-2.1 (sRGB) + // Y = (aX + b)^Gamma | X >= d + // Y = cX | X < d + case 4: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = R * Params[3]; + break; + + // Type 4 reversed + // X=((Y^1/g-b)/a) | Y >= (ad+b)^g + // X=Y/c | Y< (ad+b)^g + case -4: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = Params[1] * Params[4] + Params[2]; + if (e < 0) + disc = 0; + else + disc = pow(e, Params[0]); + + if (R >= disc) { + + Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = R / Params[3]; + } + } + } + break; + + + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + case 5: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[5]; + else + Val = Params[5]; + } + else + Val = R*Params[3] + Params[6]; + break; + + + // Reversed type 5 + // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f + // X=(Y-f)/c | else + case -5: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[3]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + disc = Params[3] * Params[4] + Params[6]; + if (R >= disc) { + + e = R - Params[5]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + else { + Val = (R - Params[6]) / Params[3]; + } + } + } + break; + + + // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf + // Type 6 is basically identical to type 5 without d + + // Y = (a * X + b) ^ Gamma + c + case 6: + e = Params[1]*R + Params[2]; + + if (e < 0) + Val = Params[3]; + else + Val = pow(e, Params[0]) + Params[3]; + break; + + // ((Y - c) ^1/Gamma - b) / a + case -6: + { + if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + e = R - Params[3]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; + } + } + break; + + + // Y = a * log (b * X^Gamma + c) + d + case 7: + + e = Params[2] * pow(R, Params[0]) + Params[3]; + if (e <= 0) + Val = Params[4]; + else + Val = Params[1]*log10(e) + Params[4]; + break; + + // (Y - d) / a = log(b * X ^Gamma + c) + // pow(10, (Y-d) / a) = b * X ^Gamma + c + // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X + case -7: + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = pow((pow(10.0, (R - Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]); + } + } + break; + + + //Y = a * b^(c*X+d) + e + case 8: + Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]); + break; + + + // Y = (log((y-e) / a) / log(b) - d ) / c + // a=0, b=1, c=2, d=3, e=4, + case -8: + + disc = R - Params[4]; + if (disc < 0) Val = 0; + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[2]) < MATRIX_DET_TOLERANCE) + { + Val = 0; + } + else + { + Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2]; + } + } + break; + + + // S-Shaped: (1 - (1-x)^1/g)^1/g + case 108: + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE) + Val = 0; + else + Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]); + break; + + // y = (1 - (1-x)^1/g)^1/g + // y^g = (1 - (1-x)^1/g) + // 1 - y^g = (1-x)^1/g + // (1 - y^g)^g = 1 - x + // 1 - (1 - y^g)^g + case -108: + Val = 1 - pow(1 - pow(R, Params[0]), Params[0]); + break; + + // Sigmoidals + case 109: + Val = sigmoid_factory(Params[0], R); + break; + + case -109: + Val = inverse_sigmoid_factory(Params[0], R); + break; + + default: + // Unsupported parametric curve. Should never reach here + return 0; + } + + return Val; +} + +// Evaluate a segmented function for a single value. Return -Inf if no valid segment found . +// If fn type is 0, perform an interpolation on the table +static +cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R) +{ + int i; + cmsFloat32Number Out32; + cmsFloat64Number Out; + + for (i = (int) g->nSegments - 1; i >= 0; --i) { + + // Check for domain + if ((R > g->Segments[i].x0) && (R <= g->Segments[i].x1)) { + + // Type == 0 means segment is sampled + if (g->Segments[i].Type == 0) { + + cmsFloat32Number R1 = (cmsFloat32Number)(R - g->Segments[i].x0) / (g->Segments[i].x1 - g->Segments[i].x0); + + // Setup the table (TODO: clean that) + g->SegInterp[i]->Table = g->Segments[i].SampledPoints; + + g->SegInterp[i]->Interpolation.LerpFloat(&R1, &Out32, g->SegInterp[i]); + Out = (cmsFloat64Number) Out32; + + } + else { + Out = g->Evals[i](g->Segments[i].Type, g->Segments[i].Params, R); + } + + if (isinf(Out)) + return PLUS_INF; + else + { + if (isinf(-Out)) + return MINUS_INF; + } + + return Out; + } + } + + return MINUS_INF; +} + +// Access to estimated low-res table +cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->nEntries; +} + +const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->Table16; +} + + +// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the +// floating point description empty. +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number Values[]) +{ + return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values); +} + +static +cmsUInt32Number EntriesByGamma(cmsFloat64Number Gamma) +{ + if (fabs(Gamma - 1.0) < 0.001) return 2; + return 4096; +} + + +// Create a segmented gamma, fill the table +cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, + cmsUInt32Number nSegments, const cmsCurveSegment Segments[]) +{ + cmsUInt32Number i; + cmsFloat64Number R, Val; + cmsToneCurve* g; + cmsUInt32Number nGridPoints = 4096; + + _cmsAssert(Segments != NULL); + + // Optimizatin for identity curves. + if (nSegments == 1 && Segments[0].Type == 1) { + + nGridPoints = EntriesByGamma(Segments[0].Params[0]); + } + + g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL); + if (g == NULL) return NULL; + + // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries + // for performance reasons. This table would normally not be used except on 8/16 bits transforms. + for (i = 0; i < nGridPoints; i++) { + + R = (cmsFloat64Number) i / (nGridPoints-1); + + Val = EvalSegmentedFn(g, R); + + // Round and saturate + g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0); + } + + return g; +} + +// Use a segmented curve to store the floating point table +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) +{ + cmsCurveSegment Seg[3]; + + // A segmented tone curve should have function segments in the first and last positions + // Initialize segmented curve part up to 0 to constant value = samples[0] + Seg[0].x0 = MINUS_INF; + Seg[0].x1 = 0; + Seg[0].Type = 6; + + Seg[0].Params[0] = 1; + Seg[0].Params[1] = 0; + Seg[0].Params[2] = 0; + Seg[0].Params[3] = values[0]; + Seg[0].Params[4] = 0; + + // From zero to 1 + Seg[1].x0 = 0; + Seg[1].x1 = 1.0; + Seg[1].Type = 0; + + Seg[1].nGridPoints = nEntries; + Seg[1].SampledPoints = (cmsFloat32Number*) values; + + // Final segment is constant = lastsample + Seg[2].x0 = 1.0; + Seg[2].x1 = PLUS_INF; + Seg[2].Type = 6; + + Seg[2].Params[0] = 1; + Seg[2].Params[1] = 0; + Seg[2].Params[2] = 0; + Seg[2].Params[3] = values[nEntries-1]; + Seg[2].Params[4] = 0; + + + return cmsBuildSegmentedToneCurve(ContextID, 3, Seg); +} + +// Parametric curves +// +// Parameters goes as: Curve, a, b, c, d, e, f +// Type is the ICC type +1 +// if type is negative, then the curve is analytically inverted +cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]) +{ + cmsCurveSegment Seg0; + int Pos = 0; + cmsUInt32Number size; + _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); + + _cmsAssert(Params != NULL); + + if (c == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); + return NULL; + } + + memset(&Seg0, 0, sizeof(Seg0)); + + Seg0.x0 = MINUS_INF; + Seg0.x1 = PLUS_INF; + Seg0.Type = Type; + + size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number); + memmove(Seg0.Params, Params, size); + + return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0); +} + + + +// Build a gamma table based on gamma constant +cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma) +{ + return cmsBuildParametricToneCurve(ContextID, 1, &Gamma); +} + + +// Free all memory taken by the gamma curve +void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve) +{ + cmsContext ContextID; + + if (Curve == NULL) return; + + ContextID = Curve ->InterpParams->ContextID; + + _cmsFreeInterpParams(Curve ->InterpParams); + + if (Curve -> Table16) + _cmsFree(ContextID, Curve ->Table16); + + if (Curve ->Segments) { + + cmsUInt32Number i; + + for (i=0; i < Curve ->nSegments; i++) { + + if (Curve ->Segments[i].SampledPoints) { + _cmsFree(ContextID, Curve ->Segments[i].SampledPoints); + } + + if (Curve ->SegInterp[i] != 0) + _cmsFreeInterpParams(Curve->SegInterp[i]); + } + + _cmsFree(ContextID, Curve ->Segments); + _cmsFree(ContextID, Curve ->SegInterp); + } + + if (Curve -> Evals) + _cmsFree(ContextID, Curve -> Evals); + + _cmsFree(ContextID, Curve); +} + +// Utility function, free 3 gamma tables +void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]) +{ + + _cmsAssert(Curve != NULL); + + if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]); + if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]); + if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]); + + Curve[0] = Curve[1] = Curve[2] = NULL; +} + + +// Duplicate a gamma table +cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In) +{ + if (In == NULL) return NULL; + + return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16); +} + +// Joins two curves for X and Y. Curves should be monotonic. +// We want to get +// +// y = Y^-1(X(t)) +// +cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, + const cmsToneCurve* X, + const cmsToneCurve* Y, cmsUInt32Number nResultingPoints) +{ + cmsToneCurve* out = NULL; + cmsToneCurve* Yreversed = NULL; + cmsFloat32Number t, x; + cmsFloat32Number* Res = NULL; + cmsUInt32Number i; + + + _cmsAssert(X != NULL); + _cmsAssert(Y != NULL); + + Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y); + if (Yreversed == NULL) goto Error; + + Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number)); + if (Res == NULL) goto Error; + + //Iterate + for (i=0; i < nResultingPoints; i++) { + + t = (cmsFloat32Number) i / (cmsFloat32Number)(nResultingPoints-1); + x = cmsEvalToneCurveFloat(X, t); + Res[i] = cmsEvalToneCurveFloat(Yreversed, x); + } + + // Allocate space for output + out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res); + +Error: + + if (Res != NULL) _cmsFree(ContextID, Res); + if (Yreversed != NULL) cmsFreeToneCurve(Yreversed); + + return out; +} + + + +// Get the surrounding nodes. This is tricky on non-monotonic tables +static +int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p) +{ + int i; + int y0, y1; + + // A 1 point table is not allowed + if (p -> Domain[0] < 1) return -1; + + // Let's see if ascending or descending. + if (LutTable[0] < LutTable[p ->Domain[0]]) { + + // Table is overall ascending + for (i = (int) p->Domain[0] - 1; i >= 0; --i) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + else { + // Table is overall descending + for (i=0; i < (int) p -> Domain[0]; i++) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + + return -1; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InCurve) +{ + cmsToneCurve *out; + cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2; + int i, j; + int Ascending; + + _cmsAssert(InCurve != NULL); + + // Try to reverse it analytically whatever possible + + if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && + /* InCurve -> Segments[0].Type <= 5 */ + GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) { + + return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID, + -(InCurve -> Segments[0].Type), + InCurve -> Segments[0].Params); + } + + // Nope, reverse the table. + out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL); + if (out == NULL) + return NULL; + + // We want to know if this is an ascending or descending table + Ascending = !cmsIsToneCurveDescending(InCurve); + + // Iterate across Y axis + for (i=0; i < (int) nResultSamples; i++) { + + y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1); + + // Find interval in which y is within. + j = GetInterval(y, InCurve->Table16, InCurve->InterpParams); + if (j >= 0) { + + + // Get limits of interval + x1 = InCurve ->Table16[j]; + x2 = InCurve ->Table16[j+1]; + + y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1); + y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1); + + // If collapsed, then use any + if (x1 == x2) { + + out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1); + continue; + + } else { + + // Interpolate + a = (y2 - y1) / (x2 - x1); + b = y2 - a * x2; + } + } + + out ->Table16[i] = _cmsQuickSaturateWord(a* y + b); + } + + + return out; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma) +{ + _cmsAssert(InGamma != NULL); + + return cmsReverseToneCurveEx(4096, InGamma); +} + +// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite +// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press. +// +// Smoothing and interpolation with second differences. +// +// Input: weights (w), data (y): vector from 1 to m. +// Input: smoothing parameter (lambda), length (m). +// Output: smoothed vector (z): vector from 1 to m. + +static +cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], + cmsFloat32Number z[], cmsFloat32Number lambda, int m) +{ + int i, i1, i2; + cmsFloat32Number *c, *d, *e; + cmsBool st; + + + c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + + if (c != NULL && d != NULL && e != NULL) { + + + d[1] = w[1] + lambda; + c[1] = -2 * lambda / d[1]; + e[1] = lambda /d[1]; + z[1] = w[1] * y[1]; + d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; + c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; + e[2] = lambda / d[2]; + z[2] = w[2] * y[2] - c[1] * z[1]; + + for (i = 3; i < m - 1; i++) { + i1 = i - 1; i2 = i - 2; + d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; + e[i] = lambda / d[i]; + z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; + } + + i1 = m - 2; i2 = m - 3; + + d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; + z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; + i1 = m - 1; i2 = m - 2; + + d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; + z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; + + for (i = m - 2; 1<= i; i--) + z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; + + st = TRUE; + } + else st = FALSE; + + if (c != NULL) _cmsFree(ContextID, c); + if (d != NULL) _cmsFree(ContextID, d); + if (e != NULL) _cmsFree(ContextID, e); + + return st; +} + +// Smooths a curve sampled at regular intervals. +cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) +{ + cmsBool SuccessStatus = TRUE; + cmsFloat32Number *w, *y, *z; + cmsUInt32Number i, nItems, Zeros, Poles; + cmsBool notCheck = FALSE; + + if (Tab != NULL && Tab->InterpParams != NULL) + { + cmsContext ContextID = Tab->InterpParams->ContextID; + + if (!cmsIsToneCurveLinear(Tab)) // Only non-linear curves need smoothing + { + nItems = Tab->nEntries; + if (nItems < MAX_NODES_IN_CURVE) + { + // Allocate one more item than needed + w = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + y = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + z = (cmsFloat32Number *)_cmsCalloc(ContextID, nItems + 1, sizeof(cmsFloat32Number)); + + if (w != NULL && y != NULL && z != NULL) // Ensure no memory allocation failure + { + memset(w, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(y, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + memset(z, 0, (nItems + 1) * sizeof(cmsFloat32Number)); + + for (i = 0; i < nItems; i++) + { + y[i + 1] = (cmsFloat32Number)Tab->Table16[i]; + w[i + 1] = 1.0; + } + + if (lambda < 0) + { + notCheck = TRUE; + lambda = -lambda; + } + + if (smooth2(ContextID, w, y, z, (cmsFloat32Number)lambda, (int)nItems)) + { + // Do some reality - checking... + + Zeros = Poles = 0; + for (i = nItems; i > 1; --i) + { + if (z[i] == 0.) Zeros++; + if (z[i] >= 65535.) Poles++; + if (z[i] < z[i - 1]) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic."); + SuccessStatus = notCheck; + break; + } + } + + if (SuccessStatus && Zeros > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros."); + SuccessStatus = notCheck; + } + + if (SuccessStatus && Poles > (nItems / 3)) + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles."); + SuccessStatus = notCheck; + } + + if (SuccessStatus) // Seems ok + { + for (i = 0; i < nItems; i++) + { + // Clamp to cmsUInt16Number + Tab->Table16[i] = _cmsQuickSaturateWord(z[i + 1]); + } + } + } + else // Could not smooth + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Function smooth2 failed."); + SuccessStatus = FALSE; + } + } + else // One or more buffers could not be allocated + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Could not allocate memory."); + SuccessStatus = FALSE; + } + + if (z != NULL) + _cmsFree(ContextID, z); + + if (y != NULL) + _cmsFree(ContextID, y); + + if (w != NULL) + _cmsFree(ContextID, w); + } + else // too many items in the table + { + cmsSignalError(ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Too many points."); + SuccessStatus = FALSE; + } + } + } + else // Tab parameter or Tab->InterpParams is NULL + { + // Can't signal an error here since the ContextID is not known at this point + SuccessStatus = FALSE; + } + + return SuccessStatus; +} + +// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting +// in a linear table. This way assures it is linear in 12 bits, which should be enough in most cases. +cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve) +{ + int i; + int diff; + + _cmsAssert(Curve != NULL); + + for (i=0; i < (int) Curve ->nEntries; i++) { + + diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries)); + if (diff > 0x0f) + return FALSE; + } + + return TRUE; +} + +// Same, but for monotonicity +cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t) +{ + cmsUInt32Number n; + int i, last; + cmsBool lDescending; + + _cmsAssert(t != NULL); + + // Degenerated curves are monotonic? Ok, let's pass them + n = t ->nEntries; + if (n < 2) return TRUE; + + // Curve direction + lDescending = cmsIsToneCurveDescending(t); + + if (lDescending) { + + last = t ->Table16[0]; + + for (i = 1; i < (int) n; i++) { + + if (t ->Table16[i] - last > 2) // We allow some ripple + return FALSE; + else + last = t ->Table16[i]; + + } + } + else { + + last = t ->Table16[n-1]; + + for (i = (int) n - 2; i >= 0; --i) { + + if (t ->Table16[i] - last > 2) + return FALSE; + else + last = t ->Table16[i]; + + } + } + + return TRUE; +} + +// Same, but for descending tables +cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t ->Table16[0] > t ->Table16[t ->nEntries-1]; +} + + +// Another info fn: is out gamma table multisegment? +cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t -> nSegments > 1; +} + +cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t -> nSegments != 1) return 0; + return t ->Segments[0].Type; +} + +// We need accuracy this time +cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v) +{ + _cmsAssert(Curve != NULL); + + // Check for 16 bits table. If so, this is a limited-precision tone curve + if (Curve ->nSegments == 0) { + + cmsUInt16Number In, Out; + + In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0); + Out = cmsEvalToneCurve16(Curve, In); + + return (cmsFloat32Number) (Out / 65535.0); + } + + return (cmsFloat32Number) EvalSegmentedFn(Curve, v); +} + +// We need xput over here +cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v) +{ + cmsUInt16Number out; + + _cmsAssert(Curve != NULL); + + Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams); + return out; +} + + +// Least squares fitting. +// A mathematical procedure for finding the best-fitting curve to a given set of points by +// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve. +// The sum of the squares of the offsets is used instead of the offset absolute values because +// this allows the residuals to be treated as a continuous differentiable quantity. +// +// y = f(x) = x ^ g +// +// R = (yi - (xi^g)) +// R2 = (yi - (xi^g))2 +// SUM R2 = SUM (yi - (xi^g))2 +// +// dR2/dg = -2 SUM x^g log(x)(y - x^g) +// solving for dR2/dg = 0 +// +// g = 1/n * SUM(log(y) / log(x)) + +cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision) +{ + cmsFloat64Number gamma, sum, sum2; + cmsFloat64Number n, x, y, Std; + cmsUInt32Number i; + + _cmsAssert(t != NULL); + + sum = sum2 = n = 0; + + // Excluding endpoints + for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) { + + x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1); + y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x); + + // Avoid 7% on lower part to prevent + // artifacts due to linear ramps + + if (y > 0. && y < 1. && x > 0.07) { + + gamma = log(y) / log(x); + sum += gamma; + sum2 += gamma * gamma; + n++; + } + } + + // Take a look on SD to see if gamma isn't exponential at all + Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); + + if (Std > Precision) + return -1.0; + + return (sum / n); // The mean +} + + +// Retrieve parameters on one-segment tone curves + +cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t->nSegments != 1) return NULL; + return t->Segments[0].Params; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgmt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgmt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsgmt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsgmt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,619 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// Auxiliary: append a Lab identity after the given sequence of profiles +// and return the transform. Lab profile is closed, rest of profiles are kept open. +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + cmsUInt32Number i; + + // This is a rather big number and there is no need of dynamic memory + // since we are adding a profile, 254 + 1 = 255 and this is the limit + if (nProfiles > 254) return NULL; + + // The output space + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + // Create a copy of parameters + for (i=0; i < nProfiles; i++) { + + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Place Lab identity at chain's end. + ProfileList[nProfiles] = hLab; + BPCList[nProfiles] = 0; + AdaptationList[nProfiles] = 1.0; + IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC; + + // Create the transform + xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + InputFormat, + OutputFormat, + dwFlags); + + cmsCloseProfile(hLab); + + return xform; +} + + +// Compute K -> L* relationship. Flags may include black point compensation. In this case, +// the relationship is assumed from the profile with BPC to a black point zero. +static +cmsToneCurve* ComputeKToLstar(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve* out = NULL; + cmsUInt32Number i; + cmsHTRANSFORM xform; + cmsCIELab Lab; + cmsFloat32Number cmyk[4]; + cmsFloat32Number* SampledPoints; + + xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (xform == NULL) return NULL; + + SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number)); + if (SampledPoints == NULL) goto Error; + + for (i=0; i < nPoints; i++) { + + cmyk[0] = 0; + cmyk[1] = 0; + cmyk[2] = 0; + cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1)); + + cmsDoTransform(xform, cmyk, &Lab, 1); + SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation + } + + out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints); + +Error: + + cmsDeleteTransform(xform); + if (SampledPoints) _cmsFree(ContextID, SampledPoints); + + return out; +} + + +// Compute Black tone curve on a CMYK -> CMYK transform. This is done by +// using the proof direction on both profiles to find K->L* relationship +// then joining both curves. dwFlags may include black point compensation. +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve *in, *out, *KTone; + + // Make sure CMYK -> CMYK + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL; + + + // Make sure last is an output profile + if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL; + + // Create individual curves. BPC works also as each K to L* is + // computed as a BPC to zero black point in case of L* + in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (in == NULL) return NULL; + + out = ComputeKToLstar(ContextID, nPoints, 1, + Intents + (nProfiles - 1), + &hProfiles [nProfiles - 1], + BPC + (nProfiles - 1), + AdaptationStates + (nProfiles - 1), + dwFlags); + if (out == NULL) { + cmsFreeToneCurve(in); + return NULL; + } + + // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but + // since this is used on black-preserving LUTs, we are not losing accuracy in any case + KTone = cmsJoinToneCurve(ContextID, in, out, nPoints); + + // Get rid of components + cmsFreeToneCurve(in); cmsFreeToneCurve(out); + + // Something went wrong... + if (KTone == NULL) return NULL; + + // Make sure it is monotonic + if (!cmsIsToneCurveMonotonic(KTone)) { + cmsFreeToneCurve(KTone); + return NULL; + } + + return KTone; +} + + +// Gamut LUT Creation ----------------------------------------------------------------------------------------- + +// Used by gamut & softproofing + +typedef struct { + + cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL + cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back + cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut + + } GAMUTCHAIN; + +// This sampler does compute gamut boundaries by comparing original +// values with a transform going back and forth. Values above ERR_THERESHOLD +// of maximum are considered out of gamut. + +#define ERR_THERESHOLD 5 + + +static +int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo; + cmsCIELab LabIn1, LabOut1; + cmsCIELab LabIn2, LabOut2; + cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS]; + cmsFloat64Number dE1, dE2, ErrorRatio; + + // Assume in-gamut by default. + ErrorRatio = 1.0; + + // Convert input to Lab + cmsDoTransform(t -> hInput, In, &LabIn1, 1); + + // converts from PCS to colorant. This always + // does return in-gamut values, + cmsDoTransform(t -> hForward, &LabIn1, Proof, 1); + + // Now, do the inverse, from colorant to PCS. + cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1); + + memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); + + // Try again, but this time taking Check as input + cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1); + cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1); + + // Take difference of direct value + dE1 = cmsDeltaE(&LabIn1, &LabOut1); + + // Take difference of converted value + dE2 = cmsDeltaE(&LabIn2, &LabOut2); + + + // if dE1 is small and dE2 is small, value is likely to be in gamut + if (dE1 < t->Thereshold && dE2 < t->Thereshold) + Out[0] = 0; + else { + + // if dE1 is small and dE2 is big, undefined. Assume in gamut + if (dE1 < t->Thereshold && dE2 > t->Thereshold) + Out[0] = 0; + else + // dE1 is big and dE2 is small, clearly out of gamut + if (dE1 > t->Thereshold && dE2 < t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); + else { + + // dE1 is big and dE2 is also big, could be due to perceptual mapping + // so take error ratio + if (dE2 == 0.0) + ErrorRatio = dE1; + else + ErrorRatio = dE1 / dE2; + + if (ErrorRatio > t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); + else + Out[0] = 0; + } + } + + + return TRUE; +} + +// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs +// the dE obtained is then annotated on the LUT. Values truly out of gamut are clipped to dE = 0xFFFE +// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well. +// +// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors, +// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should. + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut) +{ + cmsHPROFILE hLab; + cmsPipeline* Gamut; + cmsStage* CLUT; + cmsUInt32Number dwFormat; + GAMUTCHAIN Chain; + cmsUInt32Number nChannels, nGridpoints; + cmsColorSpaceSignature ColorSpace; + cmsUInt32Number i; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + + memset(&Chain, 0, sizeof(GAMUTCHAIN)); + + + if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); + return NULL; + } + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + + // The figure of merit. On matrix-shaper profiles, should be almost zero as + // the conversion is pretty exact. On LUT based profiles, different resolutions + // of input and output CLUT may result in differences. + + if (cmsIsMatrixShaper(hGamut)) { + + Chain.Thereshold = 1.0; + } + else { + Chain.Thereshold = ERR_THERESHOLD; + } + + + // Create a copy of parameters + for (i=0; i < nGamutPCSposition; i++) { + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Fill Lab identity + ProfileList[nGamutPCSposition] = hLab; + BPCList[nGamutPCSposition] = 0; + AdaptationList[nGamutPCSposition] = 1.0; + IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; + + + ColorSpace = cmsGetColorSpace(hGamut); + + nChannels = cmsChannelsOf(ColorSpace); + nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + + // 16 bits to Lab double + Chain.hInput = cmsCreateExtendedTransform(ContextID, + nGamutPCSposition + 1, + ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + dwFormat, TYPE_Lab_DBL, + cmsFLAGS_NOCACHE); + + + // Does create the forward step. Lab double to device + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + Chain.hForward = cmsCreateTransformTHR(ContextID, + hLab, TYPE_Lab_DBL, + hGamut, dwFormat, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + // Does create the backwards step + Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, + hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + + // All ok? + if (Chain.hInput && Chain.hForward && Chain.hReverse) { + + // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing + // dE when doing a transform back and forth on the colorimetric intent. + + Gamut = cmsPipelineAlloc(ContextID, 3, 1); + if (Gamut != NULL) { + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); + if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { + cmsPipelineFree(Gamut); + Gamut = NULL; + } + else { + cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); + } + } + } + else + Gamut = NULL; // Didn't work... + + // Free all needed stuff. + if (Chain.hInput) cmsDeleteTransform(Chain.hInput); + if (Chain.hForward) cmsDeleteTransform(Chain.hForward); + if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); + if (hLab) cmsCloseProfile(hLab); + + // And return computed hull + return Gamut; +} + +// Total Area Coverage estimation ---------------------------------------------------------------- + +typedef struct { + cmsUInt32Number nOutputChans; + cmsHTRANSFORM hRoundTrip; + cmsFloat32Number MaxTAC; + cmsFloat32Number MaxInput[cmsMAXCHANNELS]; + +} cmsTACestimator; + + +// This callback just accounts the maximum ink dropped in the given node. It does not populate any +// memory, as the destination table is NULL. Its only purpose it to know the global maximum. +static +int EstimateTAC(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) +{ + cmsTACestimator* bp = (cmsTACestimator*) Cargo; + cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsFloat32Number Sum; + + + // Evaluate the xform + cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); + + // All all amounts of ink + for (Sum=0, i=0; i < bp ->nOutputChans; i++) + Sum += RoundTrip[i]; + + // If above maximum, keep track of input values + if (Sum > bp ->MaxTAC) { + + bp ->MaxTAC = Sum; + + for (i=0; i < bp ->nOutputChans; i++) { + bp ->MaxInput[i] = In[i]; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(Out); +} + + +// Detect Total area coverage of the profile +cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) +{ + cmsTACestimator bp; + cmsUInt32Number dwFormatter; + cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS]; + cmsHPROFILE hLab; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // TAC only works on output profiles + if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) { + return 0; + } + + // Create a fake formatter for result + dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); + + bp.nOutputChans = T_CHANNELS(dwFormatter); + bp.MaxTAC = 0; // Initial TAC is 0 + + // for safety + if (bp.nOutputChans >= cmsMAXCHANNELS) return 0; + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return 0; + // Setup a roundtrip on perceptual intent in output profile for TAC estimation + bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16, + hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + + cmsCloseProfile(hLab); + if (bp.hRoundTrip == NULL) return 0; + + // For L* we only need black and white. For C* we need many points + GridPoints[0] = 6; + GridPoints[1] = 74; + GridPoints[2] = 74; + + + if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { + bp.MaxTAC = 0; + } + + cmsDeleteTransform(bp.hRoundTrip); + + // Results in % + return bp.MaxTAC; +} + + +// Carefully, clamp on CIELab space. + +cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin) +{ + + // Whole Luma surface to zero + + if (Lab -> L < 0) { + + Lab-> L = Lab->a = Lab-> b = 0.0; + return FALSE; + } + + // Clamp white, DISCARD HIGHLIGHTS. This is done + // in such way because icc spec doesn't allow the + // use of L>100 as a highlight means. + + if (Lab->L > 100) + Lab -> L = 100; + + // Check out gamut prism, on a, b faces + + if (Lab -> a < amin || Lab->a > amax|| + Lab -> b < bmin || Lab->b > bmax) { + + cmsCIELCh LCh; + double h, slope; + + // Falls outside a, b limits. Transports to LCh space, + // and then do the clipping + + + if (Lab -> a == 0.0) { // Is hue exactly 90? + + // atan will not work, so clamp here + Lab -> b = Lab->b < 0 ? bmin : bmax; + return TRUE; + } + + cmsLab2LCh(&LCh, Lab); + + slope = Lab -> b / Lab -> a; + h = LCh.h; + + // There are 4 zones + + if ((h >= 0. && h < 45.) || + (h >= 315 && h <= 360.)) { + + // clip by amax + Lab -> a = amax; + Lab -> b = amax * slope; + } + else + if (h >= 45. && h < 135.) + { + // clip by bmax + Lab -> b = bmax; + Lab -> a = bmax / slope; + } + else + if (h >= 135. && h < 225.) { + // clip by amin + Lab -> a = amin; + Lab -> b = amin * slope; + + } + else + if (h >= 225. && h < 315.) { + // clip by bmin + Lab -> b = bmin; + Lab -> a = bmin / slope; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "Invalid angle"); + return FALSE; + } + + } + + return TRUE; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmshalf.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmshalf.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmshalf.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmshalf.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,564 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// +#include "lcms2_internal.h" + +#ifndef CMS_NO_HALF_SUPPORT + +// This code is inspired in the paper "Fast Half Float Conversions" +// by Jeroen van der Zijp + +static const cmsUInt32Number Mantissa[2048] = { + +0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000, +0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, +0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000, +0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000, +0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000, +0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000, +0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000, +0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000, +0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000, +0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000, +0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000, +0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000, +0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000, +0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000, +0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000, +0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000, +0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000, +0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000, +0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000, +0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000, +0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000, +0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, +0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, +0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000, +0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, +0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000, +0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000, +0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, +0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000, +0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000, +0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, +0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000, +0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000, +0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000, +0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000, +0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000, +0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000, +0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, +0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000, +0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000, +0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, +0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000, +0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000, +0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, +0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000, +0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, +0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000, +0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000, +0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, +0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, +0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000, +0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000, +0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000, +0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000, +0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000, +0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000, +0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000, +0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000, +0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000, +0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000, +0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000, +0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000, +0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000, +0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000, +0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000, +0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000, +0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000, +0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000, +0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000, +0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000, +0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000, +0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000, +0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000, +0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000, +0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000, +0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000, +0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000, +0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000, +0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000, +0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000, +0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000, +0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000, +0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000, +0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000, +0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000, +0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000, +0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000, +0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000, +0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000, +0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000, +0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000, +0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000, +0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000, +0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000, +0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000, +0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000, +0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000, +0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000, +0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000, +0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000, +0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000, +0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000, +0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000, +0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000, +0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000, +0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000, +0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000, +0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000, +0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000, +0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000, +0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000, +0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000, +0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000, +0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000, +0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000, +0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000, +0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000, +0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000, +0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000, +0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000, +0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000, +0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000, +0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000, +0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000, +0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000, +0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000, +0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000, +0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000, +0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000, +0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000, +0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000, +0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000, +0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000, +0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000, +0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000, +0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000, +0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000, +0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000, +0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000, +0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000, +0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000, +0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000, +0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000, +0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000, +0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000, +0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000, +0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000, +0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000, +0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000, +0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000, +0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000, +0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000, +0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000, +0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000, +0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000, +0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000, +0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000, +0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000, +0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000, +0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000, +0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000, +0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000, +0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000, +0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000, +0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000, +0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000, +0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000, +0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000, +0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000, +0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000, +0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000, +0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000, +0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000, +0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000, +0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000, +0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000, +0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000, +0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, +0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000, +0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000, +0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000, +0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, +0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000, +0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000, +0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000, +0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000, +0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000, +0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000, +0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000, +0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000, +0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000, +0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000, +0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000, +0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, +0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000, +0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000, +0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000, +0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000, +0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000, +0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000, +0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000, +0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, +0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000, +0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000, +0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000, +0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000, +0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000, +0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000, +0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000, +0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000, +0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000, +0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000, +0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000, +0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, +0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000, +0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000, +0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000, +0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, +0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000, +0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000, +0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000, +0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000, +0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000, +0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000, +0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000, +0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, +0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000, +0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000, +0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000, +0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000, +0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000, +0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000, +0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000, +0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000, +0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000, +0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000, +0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000, +0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, +0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000, +0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000, +0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000, +0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, +0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000, +0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000, +0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000, +0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000, +0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000, +0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000, +0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000, +0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000, +0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000, +0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000, +0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000, +0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000, +0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000, +0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000, +0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000, +0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000, +0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000, +0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000, +0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000, +0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, +0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000, +0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000, +0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000, +0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, +0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000, +0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000, +0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000, +0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000, +0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000, +0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000, +0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000, +0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000, +0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000, +0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000, +0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000, +0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, +0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000, +0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000, +0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000, +0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000, +0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000, +0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000, +0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000, +0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, +0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000, +0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000, +0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000, +0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, +0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000, +0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000, +0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000, +0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000, +0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000, +0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000, +0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000, +0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000, +0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000, +0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000, +0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000, +0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, +0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000, +0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000, +0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000, +0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000, +0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000, +0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000, +0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000, +0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, +0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000, +0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000, +0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000, +0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000, +0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000, +0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000, +0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000, +0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000, +0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000, +0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000, +0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000, +0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, +0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000, +0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000, +0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000, +0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, +0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000, +0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000, +0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000, +0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000, +0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000, +0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000, +0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000, +0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000, +0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000, +0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000, +0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000, +0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000, +0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000, +0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000, +0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000, +0x387fc000, 0x387fe000 +}; + +static cmsUInt16Number Offset[64] = { +0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400 +}; + +static const cmsUInt32Number Exponent[64] = { +0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, +0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, +0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000, +0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000, +0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000, +0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000, +0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, +0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, +0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000, +0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000, +0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000 +}; + +static const cmsUInt16Number Base[512] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, +0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, +0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, +0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, +0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, +0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, +0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, +0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, +0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00 +}; + +static const cmsUInt8Number Shift[512] = { +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, +0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, +0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x0d +}; + +cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } out; + + int n = h >> 10; + + out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ]; + return out.flt; +} + +cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } in; + + cmsUInt32Number n, j; + + in.flt = flt; + n = in.num; + j = (n >> 23) & 0x1ff; + + return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ])); +} + +#endif diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsintrp.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsintrp.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsintrp.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsintrp.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1345 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// This module incorporates several interpolation routines, for 1 to 8 channels on input and +// up to 65535 channels on output. The user may change those by using the interpolation plug-in + +// Some people may want to compile as C++ with all warnings on, in this case make compiler silent +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# pragma warning( disable : 4365 ) +# endif +#endif + +// Interpolation routines by default +static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// This is the default factory +_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL }; + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + void* from; + + _cmsAssert(ctx != NULL); + + if (src != NULL) { + from = src ->chunks[InterpPlugin]; + } + else { + static _cmsInterpPluginChunkType InterpPluginChunk = { NULL }; + + from = &InterpPluginChunk; + } + + _cmsAssert(from != NULL); + ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType)); +} + + +// Main plug-in entry +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + if (Data == NULL) { + + ptr ->Interpolators = NULL; + return TRUE; + } + + // Set replacement functions + ptr ->Interpolators = Plugin ->InterpolatorsFactory; + return TRUE; +} + + +// Set the interpolation method +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p) +{ + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + p ->Interpolation.Lerp16 = NULL; + + // Invoke factory, possibly in the Plug-in + if (ptr ->Interpolators != NULL) + p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags); + + // If unsupported by the plug-in, go for the LittleCMS default. + // If happens only if an extern plug-in is being used + if (p ->Interpolation.Lerp16 == NULL) + p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags); + + // Check for valid interpolator (we just check one member of the union) + if (p ->Interpolation.Lerp16 == NULL) { + return FALSE; + } + + return TRUE; +} + + +// This function precalculates as many parameters as possible to speed up the interpolation. +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, + const cmsUInt32Number nSamples[], + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, + const void *Table, + cmsUInt32Number dwFlags) +{ + cmsInterpParams* p; + cmsUInt32Number i; + + // Check for maximum inputs + if (InputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + // Creates an empty object + p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams)); + if (p == NULL) return NULL; + + // Keep original parameters + p -> dwFlags = dwFlags; + p -> nInputs = InputChan; + p -> nOutputs = OutputChan; + p ->Table = Table; + p ->ContextID = ContextID; + + // Fill samples per input direction and domain (which is number of nodes minus one) + for (i=0; i < InputChan; i++) { + + p -> nSamples[i] = nSamples[i]; + p -> Domain[i] = nSamples[i] - 1; + } + + // Compute factors to apply to each component to index the grid array + p -> opta[0] = p -> nOutputs; + for (i=1; i < InputChan; i++) + p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; + + + if (!_cmsSetInterpolationRoutine(ContextID, p)) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); + _cmsFree(ContextID, p); + return NULL; + } + + // All seems ok + return p; +} + + +// This one is a wrapper on the anterior, but assuming all directions have same number of nodes +cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, + cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags) +{ + int i; + cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS]; + + // Fill the auxiliary array + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Samples[i] = nSamples; + + // Call the extended function + return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags); +} + + +// Free all associated memory +void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p) +{ + if (p != NULL) _cmsFree(p ->ContextID, p); +} + + +// Inline fixed point interpolation +cmsINLINE CMS_NO_SANITIZE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h) +{ + cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000; + dif = (dif >> 16) + l; + return (cmsUInt16Number) (dif); +} + + +// Linear interpolation (Fixed-point optimized) +static +void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) +{ + cmsUInt16Number y1, y0; + int cell0, rest; + int val3; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + + // if last value... + if (Value[0] == 0xffff) { + + Output[0] = LutTable[p -> Domain[0]]; + } + else + { + val3 = p->Domain[0] * Value[0]; + val3 = _cmsToFixedDomain(val3); // To fixed 15.16 + + cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits + rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits + + y0 = LutTable[cell0]; + y1 = LutTable[cell0 + 1]; + + Output[0] = LinearInterp(rest, y0, y1); + } +} + +// To prevent out of bounds indexing +cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) +{ + return ((v < 1.0e-9f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); +} + +// Floating-point version of 1D interpolation +static +void LinLerp1Dfloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + } + else + { + val2 *= p->Domain[0]; + + cell0 = (int)floor(val2); + cell1 = (int)ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + y0 = LutTable[cell0]; + y1 = LutTable[cell1]; + + Output[0] = y0 + (y1 - y0) * rest; + } +} + + + +// Eval gray LUT having only one input channel +static CMS_NO_SANITIZE +void Eval1Input(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p16) +{ + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, k1, rk, K0, K1; + int v; + cmsUInt32Number OutChan; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + + v = Input[0] * p16 -> Domain[0]; + fk = _cmsToFixedDomain(v); + + k0 = FIXED_TO_INT(fk); + rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); + + k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); + + K0 = p16 -> opta[0] * k0; + K1 = p16 -> opta[0] * k1; + + for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { + + Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); + } +} + + + +// Eval gray LUT having only one input channel +static +void Eval1InputFloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + cmsUInt32Number OutChan; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + + y0 = LutTable[p->Domain[0]]; + + for (OutChan = 0; OutChan < p->nOutputs; OutChan++) { + Output[OutChan] = y0; + } + } + else + { + val2 *= p->Domain[0]; + + cell0 = (int)floor(val2); + cell1 = (int)ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + cell0 *= p->opta[0]; + cell1 *= p->opta[0]; + + for (OutChan = 0; OutChan < p->nOutputs; OutChan++) { + + y0 = LutTable[cell0 + OutChan]; + y1 = LutTable[cell1 + OutChan]; + + Output[OutChan] = y0 + (y1 - y0) * rest; + } + } +} + +// Bilinear interpolation (16 bits) - cmsFloat32Number version +static +void BilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j) (LutTable[(i)+(j)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py; + int x0, y0, + X0, Y0, X1, Y1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, + d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + + x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; + y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; + + X0 = p -> opta[1] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(fx, d00, d10); + dx1 = LERP(fx, d01, d11); + + dxy = LERP(fy, dx0, dx1); + + Output[OutChan] = dxy; + } + + +# undef LERP +# undef DENS +} + +// Bilinear interpolation (16 bits) - optimized version +static CMS_NO_SANITIZE +void BilinearInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) + +{ +#define DENS(i,j) (LutTable[(i)+(j)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy; + CMSREGISTER int rx, ry; + int x0, y0; + CMSREGISTER int X0, X1, Y0, Y1; + + int d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + + X0 = p -> opta[1] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(rx, d00, d10); + dx1 = LERP(rx, d01, d11); + + dxy = LERP(ry, dx0, dx1); + + Output[OutChan] = (cmsUInt16Number) dxy; + } + + +# undef LERP +# undef DENS +} + + +// Trilinear interpolation (16 bits) - cmsFloat32Number version +static +void TrilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + int TotalOut, OutChan; + + cmsFloat32Number fx, fy, fz, + d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) floor(px); fx = px - (cmsFloat32Number) x0; // We need full floor funcionality here + y0 = (int) floor(py); fy = py - (cmsFloat32Number) y0; + z0 = (int) floor(pz); fz = pz - (cmsFloat32Number) z0; + + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(fx, d000, d100); + dx01 = LERP(fx, d001, d101); + dx10 = LERP(fx, d010, d110); + dx11 = LERP(fx, d011, d111); + + dxy0 = LERP(fy, dx00, dx10); + dxy1 = LERP(fy, dx01, dx11); + + dxyz = LERP(fz, dxy0, dxy1); + + Output[OutChan] = dxyz; + } + + +# undef LERP +# undef DENS +} + +// Trilinear interpolation (16 bits) - optimized version +static CMS_NO_SANITIZE +void TrilinearInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) + +{ +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy, fz; + CMSREGISTER int rx, ry, rz; + int x0, y0, z0; + CMSREGISTER int X0, X1, Y0, Y1, Z0, Z1; + int d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + z0 = FIXED_TO_INT(fz); + rz = FIXED_REST_TO_INT(fz); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(rx, d000, d100); + dx01 = LERP(rx, d001, d101); + dx10 = LERP(rx, d010, d110); + dx11 = LERP(rx, d011, d111); + + dxy0 = LERP(ry, dx00, dx10); + dxy1 = LERP(ry, dx01, dx11); + + dxyz = LERP(rz, dxy0, dxy1); + + Output[OutChan] = (cmsUInt16Number) dxyz; + } + + +# undef LERP +# undef DENS +} + + +// Tetrahedral interpolation, using Sakamoto algorithm. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void TetrahedralInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + cmsFloat32Number rx, ry, rz; + cmsFloat32Number c0, c1=0, c2=0, c3=0; + int OutChan, TotalOut; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) floor(px); rx = (px - (cmsFloat32Number) x0); // We need full floor functionality here + y0 = (int) floor(py); ry = (py - (cmsFloat32Number) y0); + z0 = (int) floor(pz); rz = (pz - (cmsFloat32Number) z0); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (fclamp(Input[1]) >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (fclamp(Input[2]) >= 1.0 ? 0 : p->opta[0]); + + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + // These are the 6 Tetrahedral + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz; + } + +} + +#undef DENS + +static CMS_NO_SANITIZE +void TetrahedralInterp16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + X0 = p -> opta[2] * x0; + X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + LutTable += X0+Y0+Z0; + + // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)) + // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16 + // This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16 + // at the cost of being off by one at 7fff and 17ffe. + + if (rx >= ry) { + if (ry >= rz) { + Y1 += X1; + Z1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c2; + c2 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (rz >= rx) { + X1 += Z1; + Y1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c1; + c1 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Z1 += X1; + Y1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c3; + c3 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } else { + if (rx >= rz) { + X1 += Y1; + Z1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c1; + c1 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (ry >= rz) { + Z1 += Y1; + X1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c3; + c3 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Y1 += Z1; + X1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c2; + c2 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } +} + + +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static CMS_NO_SANITIZE +void Eval4Inputs(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number i; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number OutChan; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + + + fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]); + fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]); + fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]); + fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]); + + k0 = FIXED_TO_INT(fk); + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rk = FIXED_REST_TO_INT(fk); + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + K0 = p16 -> opta[3] * k0; + K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]); + + X0 = p16 -> opta[2] * x0; + X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]); + + Y0 = p16 -> opta[1] * y0; + Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]); + + Z0 = p16 -> opta[0] * z0; + Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]); + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K0; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp1[OutChan] = (cmsUInt16Number)(c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } + + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K1; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp2[OutChan] = (cmsUInt16Number) (c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest))); + } + + + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} +#undef DENS + + +// For more that 3 inputs (i.e., CMYK) +// evaluate two 3-dimensional interpolations and then linearly interpolate between them. +static +void Eval4InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[3] * k0; + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[3]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + TetrahedralInterpFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + TetrahedralInterpFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) + { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + +#define EVAL_FNS(N,NM) static CMS_NO_SANITIZE \ +void Eval##N##Inputs(CMSREGISTER const cmsUInt16Number Input[], CMSREGISTER cmsUInt16Number Output[], CMSREGISTER const cmsInterpParams* p16)\ +{\ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;\ + cmsS15Fixed16Number fk;\ + cmsS15Fixed16Number k0, rk;\ + int K0, K1;\ + const cmsUInt16Number* T;\ + cmsUInt32Number i;\ + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\ + cmsInterpParams p1;\ +\ + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]);\ + k0 = FIXED_TO_INT(fk);\ + rk = FIXED_REST_TO_INT(fk);\ +\ + K0 = p16 -> opta[NM] * k0;\ + K1 = p16 -> opta[NM] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0));\ +\ + p1 = *p16;\ + memmove(&p1.Domain[0], &p16 ->Domain[1], NM*sizeof(cmsUInt32Number));\ +\ + T = LutTable + K0;\ + p1.Table = T;\ +\ + Eval##NM##Inputs(Input + 1, Tmp1, &p1);\ +\ + T = LutTable + K1;\ + p1.Table = T;\ +\ + Eval##NM##Inputs(Input + 1, Tmp2, &p1);\ +\ + for (i=0; i < p16 -> nOutputs; i++) {\ +\ + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]);\ + }\ +}\ +\ +static void Eval##N##InputsFloat(const cmsFloat32Number Input[], \ + cmsFloat32Number Output[],\ + const cmsInterpParams * p)\ +{\ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table;\ + cmsFloat32Number rest;\ + cmsFloat32Number pk;\ + int k0, K0, K1;\ + const cmsFloat32Number* T;\ + cmsUInt32Number i;\ + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS];\ + cmsInterpParams p1;\ +\ + pk = fclamp(Input[0]) * p->Domain[0];\ + k0 = _cmsQuickFloor(pk);\ + rest = pk - (cmsFloat32Number) k0;\ +\ + K0 = p -> opta[NM] * k0;\ + K1 = K0 + (fclamp(Input[0]) >= 1.0 ? 0 : p->opta[NM]);\ +\ + p1 = *p;\ + memmove(&p1.Domain[0], &p ->Domain[1], NM*sizeof(cmsUInt32Number));\ +\ + T = LutTable + K0;\ + p1.Table = T;\ +\ + Eval##NM##InputsFloat(Input + 1, Tmp1, &p1);\ +\ + T = LutTable + K1;\ + p1.Table = T;\ +\ + Eval##NM##InputsFloat(Input + 1, Tmp2, &p1);\ +\ + for (i=0; i < p -> nOutputs; i++) {\ +\ + cmsFloat32Number y0 = Tmp1[i];\ + cmsFloat32Number y1 = Tmp2[i];\ +\ + Output[i] = y0 + (y1 - y0) * rest;\ + }\ +} + + +/** +* Thanks to Carles Llopis for the templating idea +*/ +EVAL_FNS(5, 4) +EVAL_FNS(6, 5) +EVAL_FNS(7, 6) +EVAL_FNS(8, 7) +EVAL_FNS(9, 8) +EVAL_FNS(10, 9) +EVAL_FNS(11, 10) +EVAL_FNS(12, 11) +EVAL_FNS(13, 12) +EVAL_FNS(14, 13) +EVAL_FNS(15, 14) + + +// The default factory +static +cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags) +{ + + cmsInterpFunction Interpolation; + cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); + cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR); + + memset(&Interpolation, 0, sizeof(Interpolation)); + + // Safety check + if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS) + return Interpolation; + + switch (nInputChannels) { + + case 1: // Gray LUT / linear + + if (nOutputChannels == 1) { + + if (IsFloat) + Interpolation.LerpFloat = LinLerp1Dfloat; + else + Interpolation.Lerp16 = LinLerp1D; + + } + else { + + if (IsFloat) + Interpolation.LerpFloat = Eval1InputFloat; + else + Interpolation.Lerp16 = Eval1Input; + } + break; + + case 2: // Duotone + if (IsFloat) + Interpolation.LerpFloat = BilinearInterpFloat; + else + Interpolation.Lerp16 = BilinearInterp16; + break; + + case 3: // RGB et al + + if (IsTrilinear) { + + if (IsFloat) + Interpolation.LerpFloat = TrilinearInterpFloat; + else + Interpolation.Lerp16 = TrilinearInterp16; + } + else { + + if (IsFloat) + Interpolation.LerpFloat = TetrahedralInterpFloat; + else { + + Interpolation.Lerp16 = TetrahedralInterp16; + } + } + break; + + case 4: // CMYK lut + + if (IsFloat) + Interpolation.LerpFloat = Eval4InputsFloat; + else + Interpolation.Lerp16 = Eval4Inputs; + break; + + case 5: // 5 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval5InputsFloat; + else + Interpolation.Lerp16 = Eval5Inputs; + break; + + case 6: // 6 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval6InputsFloat; + else + Interpolation.Lerp16 = Eval6Inputs; + break; + + case 7: // 7 inks + if (IsFloat) + Interpolation.LerpFloat = Eval7InputsFloat; + else + Interpolation.Lerp16 = Eval7Inputs; + break; + + case 8: // 8 inks + if (IsFloat) + Interpolation.LerpFloat = Eval8InputsFloat; + else + Interpolation.Lerp16 = Eval8Inputs; + break; + + case 9: + if (IsFloat) + Interpolation.LerpFloat = Eval9InputsFloat; + else + Interpolation.Lerp16 = Eval9Inputs; + break; + + case 10: + if (IsFloat) + Interpolation.LerpFloat = Eval10InputsFloat; + else + Interpolation.Lerp16 = Eval10Inputs; + break; + + case 11: + if (IsFloat) + Interpolation.LerpFloat = Eval11InputsFloat; + else + Interpolation.Lerp16 = Eval11Inputs; + break; + + case 12: + if (IsFloat) + Interpolation.LerpFloat = Eval12InputsFloat; + else + Interpolation.Lerp16 = Eval12Inputs; + break; + + case 13: + if (IsFloat) + Interpolation.LerpFloat = Eval13InputsFloat; + else + Interpolation.Lerp16 = Eval13Inputs; + break; + + case 14: + if (IsFloat) + Interpolation.LerpFloat = Eval14InputsFloat; + else + Interpolation.Lerp16 = Eval14Inputs; + break; + + case 15: + if (IsFloat) + Interpolation.LerpFloat = Eval15InputsFloat; + else + Interpolation.Lerp16 = Eval15Inputs; + break; + + default: + Interpolation.Lerp16 = NULL; + } + + return Interpolation; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio0.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio0.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio0.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio0.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1979 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Generic I/O, tag dictionary management, profile struct + +// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, +// memory block or any storage. Each IOhandler provides implementations for read, +// write, seek and tell functions. LittleCMS code deals with IO across those objects. +// In this way, is easier to add support for new storage media. + +// NULL stream, for taking care of used space ------------------------------------- + +// NULL IOhandler basically does nothing but keep track on how many bytes have been +// written. This is handy when creating profiles, where the file size is needed in the +// header. Then, whole profile is serialized across NULL IOhandler and a second pass +// writes the bytes to the pertinent IOhandler. + +typedef struct { + cmsUInt32Number Pointer; // Points to current location +} FILENULL; + +static +cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + cmsUInt32Number len = size * count; + ResData -> Pointer += len; + return count; + + cmsUNUSED_PARAMETER(Buffer); +} + +static +cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer = offset; + return TRUE; +} + +static +cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + return ResData -> Pointer; +} + +static +cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer += size; + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; + + cmsUNUSED_PARAMETER(Ptr); +} + +static +cmsBool NULLClose(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// The NULL IOhandler creator +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) +{ + struct _cms_io_handler* iohandler = NULL; + FILENULL* fm = NULL; + + iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); + if (iohandler == NULL) return NULL; + + fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); + if (fm == NULL) goto Error; + + fm ->Pointer = 0; + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->ReportedSize = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = NULLRead; + iohandler ->Seek = NULLSeek; + iohandler ->Close = NULLClose; + iohandler ->Tell = NULLTell; + iohandler ->Write = NULLWrite; + + return iohandler; + +Error: + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; + +} + + +// Memory-based stream -------------------------------------------------------------- + +// Those functions implements an iohandler which takes a block of memory as storage medium. + +typedef struct { + cmsUInt8Number* Block; // Points to allocated memory + cmsUInt32Number Size; // Size of allocated memory + cmsUInt32Number Pointer; // Points to current location + int FreeBlockOnClose; // As title + +} FILEMEM; + +static +cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + cmsUInt8Number* Ptr; + cmsUInt32Number len = size * count; + + if (ResData -> Pointer + len > ResData -> Size){ + + len = (ResData -> Size - ResData -> Pointer); + cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); + return 0; + } + + Ptr = ResData -> Block; + Ptr += ResData -> Pointer; + memmove(Buffer, Ptr, len); + ResData -> Pointer += len; + + return count; +} + +// SEEK_CUR is assumed +static +cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (offset > ResData ->Size) { + cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); + return FALSE; + } + + ResData ->Pointer = offset; + return TRUE; +} + +// Tell for memory +static +cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return 0; + return ResData -> Pointer; +} + + +// Writes data to memory, also keeps used space for further reference. +static +cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return FALSE; // Housekeeping + + // Check for available space. Clip. + if (ResData->Pointer + size > ResData->Size) { + size = ResData ->Size - ResData->Pointer; + } + + if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing + + memmove(ResData ->Block + ResData ->Pointer, Ptr, size); + ResData ->Pointer += size; + + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; +} + + +static +cmsBool MemoryClose(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData ->FreeBlockOnClose) { + + if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); + } + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + + return TRUE; +} + +// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes +// a copy of the memory block for letting user to free the memory after invoking open profile. In write +// mode ("w"), Buffer points to the begin of memory block to be written. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILEMEM* fm = NULL; + + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + if (Buffer == NULL) { + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); + goto Error; + } + + fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); + if (fm ->Block == NULL) { + + _cmsFree(ContextID, fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", (long) size); + return NULL; + } + + + memmove(fm->Block, Buffer, size); + fm ->FreeBlockOnClose = TRUE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = size; + break; + + case 'w': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + fm ->Block = (cmsUInt8Number*) Buffer; + fm ->FreeBlockOnClose = FALSE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = 0; + break; + + default: + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = MemoryRead; + iohandler ->Seek = MemorySeek; + iohandler ->Close = MemoryClose; + iohandler ->Tell = MemoryTell; + iohandler ->Write = MemoryWrite; + + return iohandler; + +Error: + if (fm) _cmsFree(ContextID, fm); + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; +} + +// File-based stream ------------------------------------------------------- + +// Read count elements of size bytes each. Return number of elements read +static +cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); + + if (nReaded != count) { + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); + return 0; + } + + return nReaded; +} + +// Position file pointer in the file +static +cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { + + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); + return FALSE; + } + + return TRUE; +} + +// Returns file pointer position or 0 on error, which is also a valid position. +static +cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) +{ + long t = ftell((FILE*)iohandler ->stream); + if (t == -1L) { + cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file"); + return 0; + } + + return (cmsUInt32Number)t; +} + +// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error +static +cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) +{ + if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written + + iohandler->UsedSpace += size; + return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1); +} + +// Closes the file +static +cmsBool FileClose(cmsIOHANDLER* iohandler) +{ + if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// Create a iohandler for disk based files. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILE* fm = NULL; + cmsInt32Number fileLen; + + _cmsAssert(FileName != NULL); + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = fopen(FileName, "rb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); + return NULL; + } + fileLen = cmsfilelength(fm); + if (fileLen < 0) + { + fclose(fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName); + return NULL; + } + + iohandler -> ReportedSize = (cmsUInt32Number) fileLen; + break; + + case 'w': + fm = fopen(FileName, "wb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); + return NULL; + } + iohandler -> ReportedSize = 0; + break; + + default: + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + + // Keep track of the original file + strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); + iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + +// Create a iohandler for stream based files +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) +{ + cmsIOHANDLER* iohandler = NULL; + cmsInt32Number fileSize; + + fileSize = cmsfilelength(Stream); + if (fileSize < 0) + { + cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream"); + return NULL; + } + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + iohandler -> ContextID = ContextID; + iohandler -> stream = (void*) Stream; + iohandler -> UsedSpace = 0; + iohandler -> ReportedSize = (cmsUInt32Number) fileSize; + iohandler -> PhysicalFile[0] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + + + +// Close an open IO handler +cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) +{ + return io -> Close(io); +} + +// ------------------------------------------------------------------------------------------------------- + +cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile; + + if (Icc == NULL) return NULL; + return Icc->IOhandler; +} + +// Creates an empty structure holding all required parameters +cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) +{ + time_t now = time(NULL); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); + if (Icc == NULL) return NULL; + + Icc ->ContextID = ContextID; + + // Set it to empty + Icc -> TagCount = 0; + + // Set default version + Icc ->Version = 0x02100000; + + // Set creation date/time + memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); + + // Create a mutex if the user provided proper plugin. NULL otherwise + Icc ->UsrMutex = _cmsCreateMutex(ContextID); + + // Return the handle + return (cmsHPROFILE) Icc; +} + +cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (Icc == NULL) return NULL; + return Icc -> ContextID; +} + + +// Return the number of tags +cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + if (Icc == NULL) return -1; + + return (cmsInt32Number) Icc->TagCount; +} + +// Return the tag signature of a given tag number +cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available + if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check + + return Icc ->TagNames[n]; +} + + +static +int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) +{ + int i; + + for (i=0; i < (int) Profile -> TagCount; i++) { + + if (sig == Profile -> TagNames[i]) + return i; + } + + return -1; +} + +// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. +// If followlinks is turned on, then the position of the linked tag is returned +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) +{ + int n; + cmsTagSignature LinkedSig; + + do { + + // Search for given tag in ICC profile directory + n = SearchOneTag(Icc, sig); + if (n < 0) + return -1; // Not found + + if (!lFollowLinks) + return n; // Found, don't follow links + + // Is this a linked tag? + LinkedSig = Icc ->TagLinked[n]; + + // Yes, follow link + if (LinkedSig != (cmsTagSignature) 0) { + sig = LinkedSig; + } + + } while (LinkedSig != (cmsTagSignature) 0); + + return n; +} + +// Deletes a tag entry + +static +void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i) +{ + _cmsAssert(Icc != NULL); + _cmsAssert(i >= 0); + + + if (Icc -> TagPtrs[i] != NULL) { + + // Free previous version + if (Icc ->TagSaveAsRaw[i]) { + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + else { + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + Icc ->TagPtrs[i] = NULL; + } + } + + } +} + + +// Creates a new tag entry +static +cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) +{ + int i; + + // Search for the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Already exists? delete it + _cmsDeleteTagByPos(Icc, i); + *NewPos = i; + } + else { + + // No, make a new one + if (Icc -> TagCount >= MAX_TABLE_TAG) { + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); + return FALSE; + } + + *NewPos = (int) Icc ->TagCount; + Icc -> TagCount++; + } + + return TRUE; +} + + +// Check existence +cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; + return _cmsSearchTag(Icc, sig, FALSE) >= 0; +} + +// Enforces that the profile version is per. spec. +// Operates on the big endian bytes from the profile. +// Called before converting to platform endianness. +// Byte 0 is BCD major version, so max 9. +// Byte 1 is 2 BCD digits, one per nibble. +// Reserved bytes 2 & 3 must be 0. +static +cmsUInt32Number _validatedVersion(cmsUInt32Number DWord) +{ + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09; + temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0); + temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f); + if (temp1 > 0x90U) temp1 = 0x90U; + if (temp2 > 0x09U) temp2 = 0x09U; + *(pByte+1) = (cmsUInt8Number)(temp1 | temp2); + *(pByte+2) = (cmsUInt8Number)0; + *(pByte+3) = (cmsUInt8Number)0; + + return DWord; +} + +// Read profile header and validate it +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) +{ + cmsTagEntry Tag; + cmsICCHeader Header; + cmsUInt32Number i, j; + cmsUInt32Number HeaderSize; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsUInt32Number TagCount; + + + // Read the header + if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { + return FALSE; + } + + // Validate file as an ICC profile + if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { + cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); + return FALSE; + } + + // Adjust endianness of the used parameters + Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); + Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); + Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); + + Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); + Icc -> flags = _cmsAdjustEndianess32(Header.flags); + Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); + Icc -> model = _cmsAdjustEndianess32(Header.model); + Icc -> creator = _cmsAdjustEndianess32(Header.creator); + + _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); + Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version)); + + // Get size as reported in header + HeaderSize = _cmsAdjustEndianess32(Header.size); + + // Make sure HeaderSize is lower than profile size + if (HeaderSize >= Icc ->IOhandler ->ReportedSize) + HeaderSize = Icc ->IOhandler ->ReportedSize; + + + // Get creation date/time + _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); + + // The profile ID are 32 raw bytes + memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); + + + // Read tag directory + if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; + if (TagCount > MAX_TABLE_TAG) { + + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); + return FALSE; + } + + + // Read tag directory + Icc -> TagCount = 0; + for (i=0; i < TagCount; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; + + // Perform some sanity check. Offset + size should fall inside file. + if (Tag.offset + Tag.size > HeaderSize || + Tag.offset + Tag.size < Tag.offset) + continue; + + Icc -> TagNames[Icc ->TagCount] = Tag.sig; + Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; + Icc -> TagSizes[Icc ->TagCount] = Tag.size; + + // Search for links + for (j=0; j < Icc ->TagCount; j++) { + + if ((Icc ->TagOffsets[j] == Tag.offset) && + (Icc ->TagSizes[j] == Tag.size)) { + + Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + } + + } + + Icc ->TagCount++; + } + + return TRUE; +} + +// Saves profile header +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) +{ + cmsICCHeader Header; + cmsUInt32Number i; + cmsTagEntry Tag; + cmsUInt32Number Count; + + Header.size = _cmsAdjustEndianess32(UsedSpace); + Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); + Header.version = _cmsAdjustEndianess32(Icc ->Version); + + Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); + Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); + Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); + + // NOTE: in v4 Timestamp must be in UTC rather than in local time + _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); + + Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); + +#ifdef CMS_IS_WINDOWS_ + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); +#else + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); +#endif + + Header.flags = _cmsAdjustEndianess32(Icc -> flags); + Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); + Header.model = _cmsAdjustEndianess32(Icc -> model); + + _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); + + // Rendering intent in the header (for embedded profiles) + Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); + + // Illuminant is always D50 + Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); + Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); + Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); + + // Created by LittleCMS (that's me!) + Header.creator = _cmsAdjustEndianess32(lcmsSignature); + + memset(&Header.reserved, 0, sizeof(Header.reserved)); + + // Set profile ID. Endianness is always big endian + memmove(&Header.profileID, &Icc ->ProfileID, 16); + + // Dump the header + if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; + + // Saves Tag directory + + // Get true count + Count = 0; + for (i=0; i < Icc -> TagCount; i++) { + if (Icc ->TagNames[i] != (cmsTagSignature) 0) + Count++; + } + + // Store number of tags + if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; // It is just a placeholder + + Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]); + Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]); + Tag.size = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]); + + if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- Set/Get several struct members + + +cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> RenderingIntent; +} + +void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> RenderingIntent = RenderingIntent; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return (cmsUInt32Number) Icc -> flags; +} + +void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> flags = (cmsUInt32Number) Flags; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->manufacturer; +} + +void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> manufacturer = manufacturer; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->creator; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->model; +} + +void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> model = model; +} + +void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(ProfileID, Icc ->ProfileID.ID8, 16); +} + +void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> ProfileID, ProfileID, 16); +} + +cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Dest, &Icc ->Created, sizeof(struct tm)); + return TRUE; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> PCS; +} + +void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> PCS = pcs; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> ColorSpace; +} + +void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> ColorSpace = sig; +} + +cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> DeviceClass; +} + +void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> DeviceClass = sig; +} + +cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> Version; +} + +void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> Version = Version; +} + +// Get an hexadecimal number with same digits as v +static +cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) +{ + char Buff[100]; + int i, len; + cmsUInt32Number out; + + for (len=0; in > 0 && len < 100; len++) { + + Buff[len] = (char) (in % BaseIn); + in /= BaseIn; + } + + for (i=len-1, out=0; i >= 0; --i) { + out = out * BaseOut + Buff[i]; + } + + return out; +} + +void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + // 4.2 -> 0x4200000 + + Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; +} + +cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsUInt32Number n = Icc -> Version >> 16; + + return BaseToBase(n, 16, 10) / 100.0; +} +// -------------------------------------------------------------------------------------------------------------- + + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (write) { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +// Create profile from disk file +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'W' || *sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; + +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); +} + + +// Open from memory block +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty; + + hEmpty = cmsCreateProfilePlaceholder(ContextID); + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + // Ok, in this case const void* is casted to void* just because open IO handler + // shares read and writing modes. Don't abuse this feature! + NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (!_cmsReadHeader(NewIcc)) goto Error; + + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) +{ + return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); +} + + + +// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig +static +cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) +{ + cmsUInt8Number* Data; + cmsUInt32Number i; + cmsUInt32Number Begin; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature TypeBase; + cmsTagTypeSignature Type; + cmsTagTypeHandler* TypeHandler; + cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); + cmsTagTypeHandler LocalTypeHandler; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; + + // Linked tags are not written + if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; + + Icc -> TagOffsets[i] = Begin = io ->UsedSpace; + + Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; + + if (!Data) { + + // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. + // In this case a blind copy of the block data is performed + if (FileOrig != NULL && Icc -> TagOffsets[i]) { + + if (FileOrig->IOhandler != NULL) + { + cmsUInt32Number TagSize = FileOrig->TagSizes[i]; + cmsUInt32Number TagOffset = FileOrig->TagOffsets[i]; + void* Mem; + + if (!FileOrig->IOhandler->Seek(FileOrig->IOhandler, TagOffset)) return FALSE; + + Mem = _cmsMalloc(Icc->ContextID, TagSize); + if (Mem == NULL) return FALSE; + + if (FileOrig->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; + if (!io->Write(io, TagSize, Mem)) return FALSE; + _cmsFree(Icc->ContextID, Mem); + + Icc->TagSizes[i] = (io->UsedSpace - Begin); + + + // Align to 32 bit boundary. + if (!_cmsWriteAlignment(io)) + return FALSE; + } + } + + continue; + } + + + // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) + if (Icc ->TagSaveAsRaw[i]) { + + if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; + } + else { + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); + if (TagDescriptor == NULL) continue; // Unsupported, ignore it + + if (TagDescriptor ->DecideType != NULL) { + + Type = TagDescriptor ->DecideType(Version, Data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + + if (TypeHandler == NULL) { + cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); + continue; + } + + TypeBase = TypeHandler ->Signature; + if (!_cmsWriteTypeBase(io, TypeBase)) + return FALSE; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); + cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); + return FALSE; + } + } + + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + + return TRUE; +} + + +// Fill the offset and size fields for all linked tags +static +cmsBool SetLinks( _cmsICCPROFILE* Icc) +{ + cmsUInt32Number i; + + for (i=0; i < Icc -> TagCount; i++) { + + cmsTagSignature lnk = Icc ->TagLinked[i]; + if (lnk != (cmsTagSignature) 0) { + + int j = _cmsSearchTag(Icc, lnk, FALSE); + if (j >= 0) { + + Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; + Icc ->TagSizes[i] = Icc ->TagSizes[j]; + } + + } + } + + return TRUE; +} + +// Low-level save to IOHANDLER. It returns the number of bytes used to +// store the profile, or zero on error. io may be NULL and in this case +// no data is written--only sizes are calculated +cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + cmsIOHANDLER* PrevIO = NULL; + cmsUInt32Number UsedSpace; + cmsContext ContextID; + + _cmsAssert(hProfile != NULL); + + if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0; + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + ContextID = cmsGetProfileContextID(hProfile); + PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); + if (PrevIO == NULL) { + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + return 0; + } + + // Pass #1 does compute offsets + + if (!_cmsWriteHeader(Icc, 0)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + + UsedSpace = PrevIO ->UsedSpace; + + // Pass #2 does save to iohandler + + if (io != NULL) { + + Icc ->IOhandler = io; + if (!SetLinks(Icc)) goto Error; + if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + } + + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + if (!cmsCloseIOhandler(PrevIO)) + UsedSpace = 0; // As a error marker + + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return UsedSpace; + + +Error: + cmsCloseIOhandler(PrevIO); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + + return 0; +} + + +// Low-level save to disk. +cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); + cmsBool rc; + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + if (rc == FALSE) { // remove() is C99 per 7.19.4.1 + remove(FileName); // We have to IGNORE return value in this case + } + return rc; +} + +// Same as anterior, but for streams +cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) +{ + cmsBool rc; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + +// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only +cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + cmsBool rc; + cmsIOHANDLER* io; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + _cmsAssert(BytesNeeded != NULL); + + // Should we just calculate the needed space? + if (MemPtr == NULL) { + + *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); + return (*BytesNeeded == 0) ? FALSE : TRUE; + } + + // That is a real write operation + io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + + +// Closes a profile freeing any involved resources +cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsBool rc = TRUE; + cmsUInt32Number i; + + if (!Icc) return FALSE; + + // Was open in write mode? + if (Icc ->IsWrite) { + + Icc ->IsWrite = FALSE; // Assure no further writing + rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); + } + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc -> TagPtrs[i]) { + + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + } + else + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + } + + if (Icc ->IOhandler != NULL) { + rc &= cmsCloseIOhandler(Icc->IOhandler); + } + + _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); + + _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory + + return rc; +} + + +// ------------------------------------------------------------------------------------------------------------------- + + +// Returns TRUE if a given tag is supported by a plug-in +static +cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) +{ + cmsUInt32Number i, nMaxTypes; + + nMaxTypes = TagDescriptor->nSupportedTypes; + if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) + nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; + + for (i=0; i < nMaxTypes; i++) { + if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; + } + + return FALSE; +} + + +// That's the main read function +void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsIOHANDLER* io; + cmsTagTypeHandler* TypeHandler; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature BaseType; + cmsUInt32Number Offset, TagSize; + cmsUInt32Number ElemCount; + int n; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; + + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) goto Error; // Not found, return NULL + + + // If the element is already in memory, return the pointer + if (Icc -> TagPtrs[n]) { + + if (Icc->TagTypeHandlers[n] == NULL) goto Error; + + // Sanity check + BaseType = Icc->TagTypeHandlers[n]->Signature; + if (BaseType == 0) goto Error; + + TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); + if (TagDescriptor == NULL) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + } + + // We need to read it. Get the offset and size to the file + Offset = Icc -> TagOffsets[n]; + TagSize = Icc -> TagSizes[n]; + + if (TagSize < 8) goto Error; + + io = Icc ->IOhandler; + // Seek to its location + if (!io -> Seek(io, Offset)) + goto Error; + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + + // An unknown element was found. + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); + goto Error; // Unsupported. + } + + // if supported, get type and check if in list + BaseType = _cmsReadTypeBase(io); + if (BaseType == 0) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + TagSize -= 8; // Already read by the type base logic + + // Get type handler + TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); + if (TypeHandler == NULL) goto Error; + LocalTypeHandler = *TypeHandler; + + + // Read the tag + Icc -> TagTypeHandlers[n] = TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); + + // The tag type is supported, but something wrong happened and we cannot read the tag. + // let know the user about this (although it is just a warning) + if (Icc -> TagPtrs[n] == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); + goto Error; + } + + // This is a weird error that may be a symptom of something more serious, the number of + // stored item is actually less than the number of required elements. + if (ElemCount < TagDescriptor ->ElemCount) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", + String, TagDescriptor ->ElemCount, ElemCount); + goto Error; + } + + + // Return the data + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + + + // Return error and unlock tha data +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return NULL; +} + + +// Get true type of data +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler; + int n; + + // Search for given tag in ICC profile directory + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL + + // Get the handler. The true type is there + TypeHandler = Icc -> TagTypeHandlers[n]; + return TypeHandler ->Signature; +} + + +// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already +// in that list, the previous version is deleted. +cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsTagTypeSignature Type; + int i; + cmsFloat64Number Version; + char TypeString[5], SigString[5]; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + + // To delete tags. + if (data == NULL) { + + // Delete the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Use zero as a mark of deleted + _cmsDeleteTagByPos(Icc, i); + Icc ->TagNames[i] = (cmsTagSignature) 0; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + } + // Didn't find the tag + goto Error; + } + + if (!_cmsNewTag(Icc, sig, &i)) goto Error; + + // This is not raw + Icc ->TagSaveAsRaw[i] = FALSE; + + // This is not a link + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Get information about the TAG. + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL){ + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); + goto Error; + } + + + // Now we need to know which type to use. It depends on the version. + Version = cmsGetProfileVersion(hProfile); + + if (TagDescriptor ->DecideType != NULL) { + + // Let the tag descriptor to decide the type base on depending on + // the data. This is useful for example on parametric curves, where + // curves specified by a table cannot be saved as parametric and needs + // to be casted to single v2-curves, even on v4 profiles. + + Type = TagDescriptor ->DecideType(Version, data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + // Does the tag support this type? + if (!IsTypeSupported(TagDescriptor, Type)) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; + } + + // Does we have a handler for this type? + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + if (TypeHandler == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; // Should never happen + } + + + // Fill fields on icc structure + Icc ->TagTypeHandlers[i] = TypeHandler; + Icc ->TagNames[i] = sig; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); + + if (Icc ->TagPtrs[i] == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); + + goto Error; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + +} + +// Read and write raw data. The only way those function would work and keep consistence with normal read and write +// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained +// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where +// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows +// to write a tag as raw data and the read it as handled. + +cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + void *Object; + int i; + cmsIOHANDLER* MemIO; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsUInt32Number rc; + cmsUInt32Number Offset, TagSize; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, TRUE); + if (i < 0) goto Error; // Not found, + + // It is already read? + if (Icc -> TagPtrs[i] == NULL) { + + // No yet, get original position + Offset = Icc ->TagOffsets[i]; + TagSize = Icc ->TagSizes[i]; + + // read the data directly, don't keep copy + if (data != NULL) { + + if (BufferSize < TagSize) + TagSize = BufferSize; + + if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; + if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // The data has been already read, or written. But wait!, maybe the user chose to save as + // raw data. In this case, return the raw data directly + if (Icc ->TagSaveAsRaw[i]) { + + if (data != NULL) { + + TagSize = Icc ->TagSizes[i]; + if (BufferSize < TagSize) + TagSize = BufferSize; + + memmove(data, Icc ->TagPtrs[i], TagSize); + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // Already read, or previously set by cmsWriteTag(). We need to serialize that + // data to raw in order to maintain consistency. + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + Object = cmsReadTag(hProfile, sig); + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (Object == NULL) goto Error; + + // Now we need to serialize to a memory block: just use a memory iohandler + + if (data == NULL) { + MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); + } else{ + MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); + } + if (MemIO == NULL) goto Error; + + // Obtain type handling for the tag + TypeHandler = Icc ->TagTypeHandlers[i]; + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (TypeHandler == NULL) goto Error; + + // Serialize + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + + if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + // Get Size and close + rc = MemIO ->Tell(MemIO); + cmsCloseIOhandler(MemIO); // Ignore return code this time + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return rc; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return 0; +} + +// Similar to the anterior. This function allows to write directly to the ICC profile any data, without +// checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading +// it as cooked without serializing does result into an error. If that is what you want, you will need to dump +// the profile to memry or disk and then reopen it. +cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } + + // Mark the tag as being written as RAW + Icc ->TagSaveAsRaw[i] = TRUE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Keep a copy of the block + Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); + Icc ->TagSizes[i] = Size; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + + if (Icc->TagPtrs[i] == NULL) { + Icc->TagNames[i] = (cmsTagSignature) 0; + return FALSE; + } + return TRUE; +} + +// Using this function you can collapse several tag entries to the same block in the profile +cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } + + // Keep necessary information + Icc ->TagSaveAsRaw[i] = FALSE; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = dest; + + Icc ->TagPtrs[i] = NULL; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; +} + + +// Returns the tag linked to sig, in the case two tags are sharing same resource +cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, FALSE); + if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 + + return Icc -> TagLinked[i]; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio1.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio1.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsio1.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsio1.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1057 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Read tags using low-level functions, provides necessary glue code to adapt versions, etc. + +// LUT tags +static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual + cmsSigAToB1Tag, // Relative colorimetric + cmsSigAToB2Tag, // Saturation + cmsSigAToB1Tag }; // Absolute colorimetric + +static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual + cmsSigDToB1Tag, // Relative colorimetric + cmsSigDToB2Tag, // Saturation + cmsSigDToB3Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual + cmsSigBToA1Tag, // Relative colorimetric + cmsSigBToA2Tag, // Saturation + cmsSigBToA1Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual + cmsSigBToD1Tag, // Relative colorimetric + cmsSigBToD2Tag, // Saturation + cmsSigBToD3Tag }; // Absolute colorimetric + + +// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa +#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) +#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) + +// Several resources for gray conversions. +static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; +static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; +static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; +static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; + +// Get a media white point fixing some issues found in certain old profiles +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) +{ + cmsCIEXYZ* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + // If no wp, take D50 + if (Tag == NULL) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + } + + // All seems ok + *Dest = *Tag; + return TRUE; +} + + +// Chromatic adaptation matrix. Fix some issues as well +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile) +{ + cmsMAT3* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag); + + if (Tag != NULL) { + *Dest = *Tag; + return TRUE; + } + + // No CHAD available, default it to identity + _cmsMAT3identity(Dest); + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + + cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + if (White == NULL) { + + _cmsMAT3identity(Dest); + return TRUE; + } + + return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ()); + } + } + + return TRUE; +} + + +// Auxiliary, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper +static +cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) +{ + cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue; + + _cmsAssert(r != NULL); + + PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag); + PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag); + PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag); + + if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL) + return FALSE; + + _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X); + _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y); + _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z); + + return TRUE; +} + + +// Gray input pipeline +static +cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 1, 3); + if (Lut == NULL) + goto Error; + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + // In this case we implement the profile as an identity matrix plus 3 tone curves + cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; + cmsToneCurve* EmptyTab; + cmsToneCurve* LabCurves[3]; + + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + + if (EmptyTab == NULL) + goto Error; + + LabCurves[0] = GrayTRC; + LabCurves[1] = EmptyTab; + LabCurves[2] = EmptyTab; + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { + cmsFreeToneCurve(EmptyTab); + goto Error; + } + + cmsFreeToneCurve(EmptyTab); + + } + else { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// RGB Matrix shaper +static +cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsMAT3 Mat; + cmsToneCurve *Shapes[3]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + int i, j; + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so + // we need to adjust the output by a factor of (0x10000/0xffff) to put data in + // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Mat.v[i].n[j] *= InpAdj; + + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) + goto Error; + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) + goto Error; + } + + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + + if (Lut == NULL) return NULL; + + // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, + // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) + if ( spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if( PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + +// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc +// is adjusted here in order to create a LUT that takes care of all those details. +// We add intent = 0xffffffff as a way to read matrix shaper always, no matter of other LUT +cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsPipeline* Lut; + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) { + cmsFreeNamedColorList(nc); + return NULL; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { + cmsPipelineFree(Lut); + return NULL; + } + return Lut; + } + + // This is an attempt to reuse this function to retrieve the matrix-shaper as pipeline no + // matter other LUT are present and have precedence. Intent = 0xffffffff can be used for that. + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4, but the encoding range is no + // longer 0..1.0, so we need to add an stage depending on the color space + return _cmsReadFloatInputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = Device2PCS16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have now info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + + // We need to adjust data only for Lab16 on output + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // If the input is Lab, add also a conversion at the begin + if (cmsGetColorSpace(hProfile) == cmsSigLabData && + !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // Add a matrix for conversion V2 to V4 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut was not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayInputMatrixPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper + return BuildRGBInputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Gray output pipeline. +// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be +// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. +// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. + +static +cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC, *RevGrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + RevGrayTRC = cmsReverseToneCurve(GrayTRC); + if (RevGrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 1); + if (Lut == NULL) { + cmsFreeToneCurve(RevGrayTRC); + return NULL; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + } + else { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC))) + goto Error; + + cmsFreeToneCurve(RevGrayTRC); + return Lut; + +Error: + cmsFreeToneCurve(RevGrayTRC); + cmsPipelineFree(Lut); + return NULL; +} + + +static +cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsToneCurve *Shapes[3], *InvShapes[3]; + cmsMAT3 Mat, Inv; + int i, j; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) + return NULL; + + if (!_cmsMAT3inverse(&Mat, &Inv)) + return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so + // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of + // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Inv.v[i].n[j] *= OutpAdj; + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + InvShapes[0] = cmsReverseToneCurve(Shapes[0]); + InvShapes[1] = cmsReverseToneCurve(Shapes[1]); + InvShapes[2] = cmsReverseToneCurve(Shapes[2]); + + if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { + return NULL; + } + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes))) + goto Error; + } + + cmsFreeToneCurveTriple(InvShapes); + return Lut; +Error: + cmsFreeToneCurveTriple(InvShapes); + cmsPipelineFree(Lut); + return NULL; +} + + +// Change CLUT interpolation to trilinear +static +void ChangeInterpolationToTrilinear(cmsPipeline* Lut) +{ + cmsStage* Stage; + + for (Stage = cmsPipelineGetPtrToFirstStage(Lut); + Stage != NULL; + Stage = cmsStageNext(Stage)) { + + if (cmsStageType(Stage) == cmsSigCLutElemType) { + + _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; + + CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; + _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); + } + } +} + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, + // and since the formatter has already accommodated to 0..1.0, we should undo this change + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline + if ( dataSpace == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if (dataSpace == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// Create an output MPE LUT from agiven profile. Version mismatches are handled here +cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + if (Intent <= INTENT_ABSOLUTE_COLORIMETRIC) { + + tag16 = PCS2Device16[Intent]; + tagFloat = PCS2DeviceFloat[Intent]; + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4 + return _cmsReadFloatOutputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = PCS2Device16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // We need to adjust data only for Lab and Lut16 type + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // Add a matrix for conversion V4 to V2 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // If the output is Lab, add also a conversion at the end + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropriate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayOutputPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper, which only operates in XYZ space + return BuildRGBOutputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + if (spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if (PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The +// tag name here may default to AToB0 +cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsPipeline* Lut; + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16; + cmsTagSignature tagFloat; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + if (Intent > INTENT_ABSOLUTE_COLORIMETRIC) + return NULL; + + tag16 = Device2PCS16[Intent]; + tagFloat = Device2PCSFloat[Intent]; + + // On named color, take the appropriate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*)cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) + goto Error; + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) + goto Error; + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; + Error: + cmsPipelineFree(Lut); + cmsFreeNamedColorList(nc); + return NULL; + } + + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V + return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); + } + + tagFloat = Device2PCSFloat[0]; + if (cmsIsTag(hProfile, tagFloat)) { + + return cmsPipelineDup((cmsPipeline*)cmsReadTag(hProfile, tagFloat)); + } + + if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + tag16 = Device2PCS16[0]; + if (!cmsIsTag(hProfile, tag16)) return NULL; + } + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // Read the tag + Lut = (cmsPipeline*)cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // We need to adjust data for Lab16 on output + if (OriginalType != cmsSigLut16Type) return Lut; + + // Here it is possible to get Lab on both sides + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error2; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error2; + } + + return Lut; + +Error2: + cmsPipelineFree(Lut); + return NULL; +} + +// --------------------------------------------------------------------------------------------------------------- + +// Returns TRUE if the profile is implemented as matrix-shaper +cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile) +{ + switch (cmsGetColorSpace(hProfile)) { + + case cmsSigGrayData: + + return cmsIsTag(hProfile, cmsSigGrayTRCTag); + + case cmsSigRgbData: + + return (cmsIsTag(hProfile, cmsSigRedColorantTag) && + cmsIsTag(hProfile, cmsSigGreenColorantTag) && + cmsIsTag(hProfile, cmsSigBlueColorantTag) && + cmsIsTag(hProfile, cmsSigRedTRCTag) && + cmsIsTag(hProfile, cmsSigGreenTRCTag) && + cmsIsTag(hProfile, cmsSigBlueTRCTag)); + + default: + + return FALSE; + } +} + +// Returns TRUE if the intent is implemented as CLUT +cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + const cmsTagSignature* TagTable; + + // For devicelinks, the supported intent is that one stated in the header + if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { + return (cmsGetHeaderRenderingIntent(hProfile) == Intent); + } + + switch (UsedDirection) { + + case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break; + case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break; + + // For proofing, we need rel. colorimetric in output. Let's do some recursion + case LCMS_USED_AS_PROOF: + return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && + cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT); + + default: + cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection); + return FALSE; + } + + return cmsIsTag(hProfile, TagTable[Intent]); + +} + + +// Return info about supported intents +cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + + if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE; + + // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper + // does not fully support relative colorimetric because they cannot deal with non-zero black points, but + // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter + // the accuracy would be less than optimal in rel.col and v2 case. + + return cmsIsMatrixShaper(hProfile); +} + + +// --------------------------------------------------------------------------------------------------------------- + +// Read both, profile sequence description and profile sequence id if present. Then combine both to +// create qa unique structure holding both. Shame on ICC to store things in such complicated way. +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) +{ + cmsSEQ* ProfileSeq; + cmsSEQ* ProfileId; + cmsSEQ* NewSeq; + cmsUInt32Number i; + + // Take profile sequence description first + ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag); + + // Take profile sequence ID + ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag); + + if (ProfileSeq == NULL && ProfileId == NULL) return NULL; + + if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId); + if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq); + + // We have to mix both together. For that they must agree + if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq); + + NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); + + // Ok, proceed to the mixing + if (NewSeq != NULL) { + for (i=0; i < ProfileSeq ->n; i++) { + + memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); + } + } + return NewSeq; +} + +// Dump the contents of profile sequence in both tags (if v4 available) +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq) +{ + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE; + + if (cmsGetEncodedICCversion(hProfile) >= 0x4000000) { + + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE; + } + + return TRUE; +} + + +// Auxiliary, read and duplicate a MLU if found. +static +cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig) +{ + cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig); + if (mlu == NULL) return NULL; + + return cmsMLUdup(mlu); +} + +// Create a sequence description out of an array of profiles +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]) +{ + cmsUInt32Number i; + cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles); + + if (seq == NULL) return NULL; + + for (i=0; i < nProfiles; i++) { + + cmsPSEQDESC* ps = &seq ->seq[i]; + cmsHPROFILE h = hProfiles[i]; + cmsTechnologySignature* techpt; + + cmsGetHeaderAttributes(h, &ps ->attributes); + cmsGetHeaderProfileID(h, ps ->ProfileID.ID8); + ps ->deviceMfg = cmsGetHeaderManufacturer(h); + ps ->deviceModel = cmsGetHeaderModel(h); + + techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag); + if (techpt == NULL) + ps ->technology = (cmsTechnologySignature) 0; + else + ps ->technology = *techpt; + + ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag); + ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag); + ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag); + + } + + return seq; +} + +// ------------------------------------------------------------------------------------------------------------------- + + +static +const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info) +{ + cmsTagSignature sig; + + switch (Info) { + + case cmsInfoDescription: + sig = cmsSigProfileDescriptionTag; + break; + + case cmsInfoManufacturer: + sig = cmsSigDeviceMfgDescTag; + break; + + case cmsInfoModel: + sig = cmsSigDeviceModelDescTag; + break; + + case cmsInfoCopyright: + sig = cmsSigCopyrightTag; + break; + + default: return NULL; + } + + + return (cmsMLU*) cmsReadTag(hProfile, sig); +} + + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmslut.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmslut.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmslut.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmslut.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1866 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// Allocates an empty multi profile element +cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, + _cmsStageDupElemFn DupElemPtr, + _cmsStageFreeElemFn FreePtr, + void* Data) +{ + cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); + + if (ph == NULL) return NULL; + + + ph ->ContextID = ContextID; + + ph ->Type = Type; + ph ->Implements = Type; // By default, no clue on what is implementing + + ph ->InputChannels = InputChannels; + ph ->OutputChannels = OutputChannels; + ph ->EvalPtr = EvalPtr; + ph ->DupElemPtr = DupElemPtr; + ph ->FreePtr = FreePtr; + ph ->Data = Data; + + return ph; +} + + +static +void EvaluateIdentity(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); +} + + +cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, + cmsSigIdentityElemType, + nChannels, nChannels, + EvaluateIdentity, + NULL, + NULL, + NULL); + } + +// Conversion functions. From floating point to 16 bits +static +void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); + } +} + +// From 16 bits to floating point +static +void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = (cmsFloat32Number) In[i] / 65535.0F; + } +} + + +// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements +// that conform the LUT. It should be called with the LUT, the number of expected elements and +// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass +// the storage process. +cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...) +{ + va_list args; + cmsUInt32Number i; + cmsStage* mpe; + cmsStageSignature Type; + void** ElemPtr; + + // Make sure same number of elements + if (cmsPipelineStageCount(Lut) != n) return FALSE; + + va_start(args, n); + + // Iterate across asked types + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + // Get asked type. cmsStageSignature is promoted to int by compiler + Type = (cmsStageSignature)va_arg(args, int); + if (mpe ->Type != Type) { + + va_end(args); // Mismatch. We are done. + return FALSE; + } + mpe = mpe ->Next; + } + + // Found a combination, fill pointers if not NULL + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + ElemPtr = va_arg(args, void**); + if (ElemPtr != NULL) + *ElemPtr = mpe; + + mpe = mpe ->Next; + } + + va_end(args); + return TRUE; +} + +// Below there are implementations for several types of elements. Each type may be implemented by a +// evaluation function, a duplication function, a function to free resources and a constructor. + +// ************************************************************************************************* +// Type cmsSigCurveSetElemType (curves) +// ************************************************************************************************* + +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + + return Data ->TheCurves; +} + +static +void EvaluateCurves(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves == NULL) return; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]); + } +} + +static +void CurveSetElemTypeFree(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves != NULL) { + for (i=0; i < Data ->nCurves; i++) { + if (Data ->TheCurves[i] != NULL) + cmsFreeToneCurve(Data ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, Data ->TheCurves); + _cmsFree(mpe ->ContextID, Data); +} + + +static +void* CurveSetDup(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + _cmsStageToneCurvesData* NewElem; + cmsUInt32Number i; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) return NULL; + + NewElem ->nCurves = Data ->nCurves; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); + + if (NewElem ->TheCurves == NULL) goto Error; + + for (i=0; i < NewElem ->nCurves; i++) { + + // Duplicate each curve. It may fail. + NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]); + if (NewElem ->TheCurves[i] == NULL) goto Error; + + + } + return (void*) NewElem; + +Error: + + if (NewElem ->TheCurves != NULL) { + for (i=0; i < NewElem ->nCurves; i++) { + if (NewElem ->TheCurves[i]) + cmsFreeToneCurve(NewElem ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, NewElem ->TheCurves); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +// Curves == NULL forces identity curves +cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) +{ + cmsUInt32Number i; + _cmsStageToneCurvesData* NewElem; + cmsStage* NewMPE; + + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, + EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem ->nCurves = nChannels; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); + if (NewElem ->TheCurves == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + for (i=0; i < nChannels; i++) { + + if (Curves == NULL) { + NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); + } + else { + NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]); + } + + if (NewElem ->TheCurves[i] == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + } + + return NewMPE; +} + + +// Create a bunch of identity curves +cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels) +{ + cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + +// ************************************************************************************************* +// Type cmsSigMatrixElemType (Matrices) +// ************************************************************************************************* + + +// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used +static +void EvaluateMatrix(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsUInt32Number i, j; + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + cmsFloat64Number Tmp; + + // Input is already in 0..1.0 notation + for (i=0; i < mpe ->OutputChannels; i++) { + + Tmp = 0; + for (j=0; j < mpe->InputChannels; j++) { + Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; + } + + if (Data ->Offset != NULL) + Tmp += Data->Offset[i]; + + Out[i] = (cmsFloat32Number) Tmp; + } + + + // Output in 0..1.0 domain +} + + +// Duplicate a yet-existing matrix element +static +void* MatrixElemDup(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + _cmsStageMatrixData* NewElem; + cmsUInt32Number sz; + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + sz = mpe ->InputChannels * mpe ->OutputChannels; + + NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; + + if (Data ->Offset) + NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, + Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; + + return (void*) NewElem; +} + + +static +void MatrixElemTypeFree(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + if (Data == NULL) + return; + if (Data ->Double) + _cmsFree(mpe ->ContextID, Data ->Double); + + if (Data ->Offset) + _cmsFree(mpe ->ContextID, Data ->Offset); + + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + + +cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, + const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) +{ + cmsUInt32Number i, n; + _cmsStageMatrixData* NewElem; + cmsStage* NewMPE; + + n = Rows * Cols; + + // Check for overflow + if (n == 0) return NULL; + if (n >= UINT_MAX / Cols) return NULL; + if (n >= UINT_MAX / Rows) return NULL; + if (n < Rows || n < Cols) return NULL; + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, + EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) goto Error; + NewMPE->Data = (void*)NewElem; + + NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); + if (NewElem->Double == NULL) goto Error; + + for (i=0; i < n; i++) { + NewElem ->Double[i] = Matrix[i]; + } + + if (Offset != NULL) { + + NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); + if (NewElem->Offset == NULL) goto Error; + + for (i=0; i < Rows; i++) { + NewElem ->Offset[i] = Offset[i]; + } + } + + return NewMPE; + +Error: + cmsStageFree(NewMPE); + return NULL; +} + + +// ************************************************************************************************* +// Type cmsSigCLutElemType +// ************************************************************************************************* + + +// Evaluate in true floating point +static +void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params); +} + + +// Convert to 16 bits, evaluate, and back to floating point +static +void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; + + _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); + _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); + + FromFloatTo16(In, In16, mpe ->InputChannels); + Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params); + From16ToFloat(Out16, Out, mpe ->OutputChannels); +} + + +// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes +static +cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) +{ + cmsUInt32Number rv, dim; + + _cmsAssert(Dims != NULL); + + for (rv = 1; b > 0; b--) { + + dim = Dims[b-1]; + if (dim == 0) return 0; // Error + + rv *= dim; + + // Check for overflow + if (rv > UINT_MAX / dim) return 0; + } + + return rv; +} + +static +void* CLUTElemDup(cmsStage* mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + _cmsStageCLutData* NewElem; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) return NULL; + + NewElem ->nEntries = Data ->nEntries; + NewElem ->HasFloatValues = Data ->HasFloatValues; + + if (Data ->Tab.T) { + + if (Data ->HasFloatValues) { + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) + goto Error; + } else { + NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) + goto Error; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID, + Data ->Params ->nSamples, + Data ->Params ->nInputs, + Data ->Params ->nOutputs, + NewElem ->Tab.T, + Data ->Params ->dwFlags); + if (NewElem->Params != NULL) + return (void*) NewElem; + Error: + if (NewElem->Tab.T) + // This works for both types + _cmsFree(mpe ->ContextID, NewElem -> Tab.T); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +static +void CLutElemTypeFree(cmsStage* mpe) +{ + + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + // Already empty + if (Data == NULL) return; + + // This works for both types + if (Data -> Tab.T) + _cmsFree(mpe ->ContextID, Data -> Tab.T); + + _cmsFreeInterpParams(Data ->Params); + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + +// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different +// granularity on each dimension. +cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, + const cmsUInt32Number clutPoints[], + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); + + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = FALSE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + + NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.T[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + +cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsFloat32Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + // There is a potential integer overflow on conputing n and nEntries. + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = TRUE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.TFloat[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + + +static +int IdentitySampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) +{ + int nChan = *(int*) Cargo; + int i; + + for (i=0; i < nChan; i++) + Out[i] = In[i]; + + return 1; +} + +// Creates an MPE that just copies input to output +cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsStage* mpe ; + int i; + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = 2; + + mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); + if (mpe == NULL) return NULL; + + if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) { + cmsStageFree(mpe); + return NULL; + } + + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + + +// Quantize a value 0 <= i < MaxSamples to 0..0xffff +cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples) +{ + cmsFloat64Number x; + + x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); + return _cmsQuickSaturateWord(x); +} + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut; + + if (mpe == NULL) return FALSE; + + clut = (_cmsStageCLutData*) mpe->Data; + + if (clut == NULL) return FALSE; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + memset(In, 0, sizeof(In)); + memset(Out, 0, sizeof(Out)); + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int)nInputs - 1; t >= 0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); + } + + if (clut ->Tab.T != NULL) { + for (t = 0; t < (int)nOutputs; t++) + Out[t] = clut->Tab.T[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.T != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.T[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + +// Same as anterior, but for floating point +cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, index, rest; + cmsUInt32Number nTotalPoints; + cmsUInt32Number nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < (int)nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); + } + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + Out[t] = clut->Tab.TFloat[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < (int) nOutputs; t++) + clut->Tab.TFloat[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo) +{ + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsUInt16Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo) +{ + int i, t, rest; + cmsUInt32Number nTotalPoints; + cmsFloat32Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < (int) nTotalPoints; i++) { + + rest = i; + for (t = (int) nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +// ******************************************************************************** +// Type cmsSigLab2XYZElemType +// ******************************************************************************** + + +static +void EvaluateLab2XYZ(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // V4 rules + Lab.L = In[0] * 100.0; + Lab.a = In[1] * 255.0 - 128.0; + Lab.b = In[2] * 255.0 - 128.0; + + cmsLab2XYZ(NULL, &XYZ, &Lab); + + // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff + // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) + + Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); + Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); + Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); + return; + + cmsUNUSED_PARAMETER(mpe); +} + + +// No dup or free routines needed, as the structure has no pointers in it. +cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); +} + +// ******************************************************************************** + +// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable +// number of gridpoints that would make exact match. However, a prelinearization +// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. +// Almost all what we need but unfortunately, the rest of entries should be scaled by +// (255*257/256) and this is not exact. + +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) +{ + cmsStage* mpe; + cmsToneCurve* LabTable[3]; + int i, j; + + LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + + for (j=0; j < 3; j++) { + + if (LabTable[j] == NULL) { + cmsFreeToneCurveTriple(LabTable); + return NULL; + } + + // We need to map * (0xffff / 0xff00), that's same as (257 / 256) + // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); + for (i=0; i < 257; i++) { + + LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); + } + + LabTable[j] ->Table16[257] = 0xffff; + } + + mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); + cmsFreeToneCurveTriple(LabTable); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + +// ******************************************************************************** + +// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles +cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID) +{ + static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, + 0, 65535.0/65280.0, 0, + 0, 0, 65535.0/65280.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + + +// Reverse direction +cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID) +{ + static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, + 0, 65280.0/65535.0, 0, + 0, 0, 65280.0/65535.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV4toV2; + return mpe; +} + + +// To Lab to float. Note that the MPE gives numbers in normal Lab range +// and we need 0..1.0 range for the formatters +// L* : 0...100 => 0...1.0 (L* / 100) +// ab* : -128..+127 to 0..1 ((ab* + 128) / 255) + +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 1.0/100.0, 0, 0, + 0, 1.0/255.0, 0, + 0, 0, 1.0/255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + 128.0/255.0, + 128.0/255.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLab2FloatPCS; + return mpe; +} + +// Fom XYZ to floating point PCS +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) +{ +#define n (32768.0/65535.0) + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigXYZ2FloatPCS; + return mpe; +} + +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 100.0, 0, 0, + 0, 255.0, 0, + 0, 0, 255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + -128.0, + -128.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2Lab; + return mpe; +} + +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) +{ +#define n (65535.0/32768.0) + + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2XYZ; + return mpe; +} + +// Clips values smaller than zero +static +void Clipper(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsUInt32Number i; + for (i = 0; i < mpe->InputChannels; i++) { + + cmsFloat32Number n = In[i]; + Out[i] = n < 0 ? 0 : n; + } +} + +cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType, + nChannels, nChannels, Clipper, NULL, NULL, NULL); +} + +// ******************************************************************************** +// Type cmsSigXYZ2LabElemType +// ******************************************************************************** + +static +void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // From 0..1.0 to XYZ + + XYZ.X = In[0] * XYZadj; + XYZ.Y = In[1] * XYZadj; + XYZ.Z = In[2] * XYZadj; + + cmsXYZ2Lab(NULL, &Lab, &XYZ); + + // From V4 Lab to 0..1.0 + + Out[0] = (cmsFloat32Number) (Lab.L / 100.0); + Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); + Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); + return; + + cmsUNUSED_PARAMETER(mpe); +} + +cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); + +} + +// ******************************************************************************** + +// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray + +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) +{ + cmsToneCurve* LabTable[3]; + cmsFloat64Number Params[1] = {2.4} ; + + LabTable[0] = cmsBuildGamma(ContextID, 1.0); + LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); + LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); + + return cmsStageAllocToneCurves(ContextID, 3, LabTable); +} + + +// Free a single MPE +void CMSEXPORT cmsStageFree(cmsStage* mpe) +{ + if (mpe ->FreePtr) + mpe ->FreePtr(mpe); + + _cmsFree(mpe ->ContextID, mpe); +} + + +cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe) +{ + return mpe ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe) +{ + return mpe ->OutputChannels; +} + +cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe) +{ + return mpe -> Type; +} + +void* CMSEXPORT cmsStageData(const cmsStage* mpe) +{ + return mpe -> Data; +} + +cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) +{ + return mpe -> Next; +} + + +// Duplicates an MPE +cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) +{ + cmsStage* NewMPE; + + if (mpe == NULL) return NULL; + NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID, + mpe ->Type, + mpe ->InputChannels, + mpe ->OutputChannels, + mpe ->EvalPtr, + mpe ->DupElemPtr, + mpe ->FreePtr, + NULL); + if (NewMPE == NULL) return NULL; + + NewMPE ->Implements = mpe ->Implements; + + if (mpe ->DupElemPtr) { + + NewMPE ->Data = mpe ->DupElemPtr(mpe); + + if (NewMPE->Data == NULL) { + + cmsStageFree(NewMPE); + return NULL; + } + + } else { + + NewMPE ->Data = NULL; + } + + return NewMPE; +} + + +// *********************************************************************************************************** + +// This function sets up the channel count +static +cmsBool BlessLUT(cmsPipeline* lut) +{ + // We can set the input/output channels only if we have elements. + if (lut ->Elements != NULL) { + + cmsStage* prev; + cmsStage* next; + cmsStage* First; + cmsStage* Last; + + First = cmsPipelineGetPtrToFirstStage(lut); + Last = cmsPipelineGetPtrToLastStage(lut); + + if (First == NULL || Last == NULL) return FALSE; + + lut->InputChannels = First->InputChannels; + lut->OutputChannels = Last->OutputChannels; + + // Check chain consistency + prev = First; + next = prev->Next; + + while (next != NULL) + { + if (next->InputChannels != prev->OutputChannels) + return FALSE; + + next = next->Next; + prev = prev->Next; + } +} + + return TRUE; +} + + +// Default to evaluate the LUT on 16 bit-basis. Precision is retained. +static +void _LUTeval16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + + FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); +} + + + +// Does evaluate the LUT on cmsFloat32Number-basis. +static +void _LUTevalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS]; + int Phase = 0, NextPhase; + + memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); +} + + +// LUT Creation & Destruction +cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsPipeline* NewLUT; + + // A value of zero in channels is allowed as placeholder + if (InputChannels >= cmsMAXCHANNELS || + OutputChannels >= cmsMAXCHANNELS) return NULL; + + NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); + if (NewLUT == NULL) return NULL; + + NewLUT -> InputChannels = InputChannels; + NewLUT -> OutputChannels = OutputChannels; + + NewLUT ->Eval16Fn = _LUTeval16; + NewLUT ->EvalFloatFn = _LUTevalFloat; + NewLUT ->DupDataFn = NULL; + NewLUT ->FreeDataFn = NULL; + NewLUT ->Data = NewLUT; + NewLUT ->ContextID = ContextID; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + +cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->ContextID; +} + +cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->OutputChannels; +} + +// Free a profile elements LUT +void CMSEXPORT cmsPipelineFree(cmsPipeline* lut) +{ + cmsStage *mpe, *Next; + + if (lut == NULL) return; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = Next) { + + Next = mpe ->Next; + cmsStageFree(mpe); + } + + if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data); + + _cmsFree(lut ->ContextID, lut); +} + + +// Default to evaluate the LUT on 16 bit-basis. +void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->Eval16Fn(In, Out, lut->Data); +} + + +// Does evaluate the LUT on cmsFloat32Number-basis. +void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->EvalFloatFn(In, Out, lut); +} + + + +// Duplicates a LUT +cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) +{ + cmsPipeline* NewLUT; + cmsStage *NewMPE, *Anterior = NULL, *mpe; + cmsBool First = TRUE; + + if (lut == NULL) return NULL; + + NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); + if (NewLUT == NULL) return NULL; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NewMPE = cmsStageDup(mpe); + + if (NewMPE == NULL) { + cmsPipelineFree(NewLUT); + return NULL; + } + + if (First) { + NewLUT ->Elements = NewMPE; + First = FALSE; + } + else { + if (Anterior != NULL) + Anterior ->Next = NewMPE; + } + + Anterior = NewMPE; + } + + NewLUT ->Eval16Fn = lut ->Eval16Fn; + NewLUT ->EvalFloatFn = lut ->EvalFloatFn; + NewLUT ->DupDataFn = lut ->DupDataFn; + NewLUT ->FreeDataFn = lut ->FreeDataFn; + + if (NewLUT ->DupDataFn != NULL) + NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); + + + NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(lut->ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + + +int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) +{ + cmsStage* Anterior = NULL, *pt; + + if (lut == NULL || mpe == NULL) + return FALSE; + + switch (loc) { + + case cmsAT_BEGIN: + mpe ->Next = lut ->Elements; + lut ->Elements = mpe; + break; + + case cmsAT_END: + + if (lut ->Elements == NULL) + lut ->Elements = mpe; + else { + + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) Anterior = pt; + + Anterior ->Next = mpe; + mpe ->Next = NULL; + } + break; + default:; + return FALSE; + } + + return BlessLUT(lut); +} + +// Unlink an element and return the pointer to it +void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) +{ + cmsStage *Anterior, *pt, *Last; + cmsStage *Unlinked = NULL; + + + // If empty LUT, there is nothing to remove + if (lut ->Elements == NULL) { + if (mpe) *mpe = NULL; + return; + } + + // On depending on the strategy... + switch (loc) { + + case cmsAT_BEGIN: + { + cmsStage* elem = lut ->Elements; + + lut ->Elements = elem -> Next; + elem ->Next = NULL; + Unlinked = elem; + + } + break; + + case cmsAT_END: + Anterior = Last = NULL; + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) { + Anterior = Last; + Last = pt; + } + + Unlinked = Last; // Next already points to NULL + + // Truncate the chain + if (Anterior) + Anterior ->Next = NULL; + else + lut ->Elements = NULL; + break; + default:; + } + + if (mpe) + *mpe = Unlinked; + else + cmsStageFree(Unlinked); + + // May fail, but we ignore it + BlessLUT(lut); +} + + +// Concatenate two LUT into a new single one +cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) +{ + cmsStage* mpe; + + // If both LUTS does not have elements, we need to inherit + // the number of channels + if (l1 ->Elements == NULL && l2 ->Elements == NULL) { + l1 ->InputChannels = l2 ->InputChannels; + l1 ->OutputChannels = l2 ->OutputChannels; + } + + // Cat second + for (mpe = l2 ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + // We have to dup each element + if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe))) + return FALSE; + } + + return BlessLUT(l1); +} + + +cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On) +{ + cmsBool Anterior = lut ->SaveAs8Bits; + + lut ->SaveAs8Bits = On; + return Anterior; +} + + +cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut) +{ + return lut ->Elements; +} + +cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut) +{ + cmsStage *mpe, *Anterior = NULL; + + for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + Anterior = mpe; + + return Anterior; +} + +cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut) +{ + cmsStage *mpe; + cmsUInt32Number n; + + for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + n++; + + return n; +} + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. +void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsPipelineEval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn) +{ + + Lut ->Eval16Fn = Eval16; + Lut ->DupDataFn = DupPrivateDataFn; + Lut ->FreeDataFn = FreePrivateDataFn; + Lut ->Data = PrivateData; +} + + +// ----------------------------------------------------------- Reverse interpolation +// Here's how it goes. The derivative Df(x) of the function f is the linear +// transformation that best approximates f near the point x. It can be represented +// by a matrix A whose entries are the partial derivatives of the components of f +// with respect to all the coordinates. This is know as the Jacobian +// +// The best linear approximation to f is given by the matrix equation: +// +// y-y0 = A (x-x0) +// +// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this +// linear approximation will give a "better guess" for the zero of f. Thus let y=0, +// and since y0=f(x0) one can solve the above equation for x. This leads to the +// Newton's method formula: +// +// xn+1 = xn - A-1 f(xn) +// +// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the +// fashion described above. Iterating this will give better and better approximations +// if you have a "good enough" initial guess. + + +#define JACOBIAN_EPSILON 0.001f +#define INVERSION_MAX_ITERATIONS 30 + +// Increment with reflexion on boundary +static +void IncDelta(cmsFloat32Number *Val) +{ + if (*Val < (1.0 - JACOBIAN_EPSILON)) + + *Val += JACOBIAN_EPSILON; + + else + *Val -= JACOBIAN_EPSILON; + +} + + + +// Euclidean distance between two vectors of n elements each one +static +cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) +{ + cmsFloat32Number sum = 0; + int i; + + for (i=0; i < n; i++) { + cmsFloat32Number dif = b[i] - a[i]; + sum += dif * dif; + } + + return sqrtf(sum); +} + + +// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method +// +// x1 <- x - [J(x)]^-1 * f(x) +// +// lut: The LUT on where to do the search +// Target: LabK, 3 values of Lab plus destination K which is fixed +// Result: The obtained CMYK +// Hint: Location where begin the search + +cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], + cmsFloat32Number Result[], + cmsFloat32Number Hint[], + const cmsPipeline* lut) +{ + cmsUInt32Number i, j; + cmsFloat64Number error, LastError = 1E20; + cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; + cmsVEC3 tmp, tmp2; + cmsMAT3 Jacobian; + + // Only 3->3 and 4->3 are supported + if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; + if (lut ->OutputChannels != 3) return FALSE; + + // Take the hint as starting point if specified + if (Hint == NULL) { + + // Begin at any point, we choose 1/3 of CMY axis + x[0] = x[1] = x[2] = 0.3f; + } + else { + + // Only copy 3 channels from hint... + for (j=0; j < 3; j++) + x[j] = Hint[j]; + } + + // If Lut is 4-dimensions, then grab target[3], which is fixed + if (lut ->InputChannels == 4) { + x[3] = Target[3]; + } + else x[3] = 0; // To keep lint happy + + + // Iterate + for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { + + // Get beginning fx + cmsPipelineEvalFloat(x, fx, lut); + + // Compute error + error = EuclideanDistance(fx, Target, 3); + + // If not convergent, return last safe value + if (error >= LastError) + break; + + // Keep latest values + LastError = error; + for (j=0; j < lut ->InputChannels; j++) + Result[j] = x[j]; + + // Found an exact match? + if (error <= 0) + break; + + // Obtain slope (the Jacobian) + for (j = 0; j < 3; j++) { + + xd[0] = x[0]; + xd[1] = x[1]; + xd[2] = x[2]; + xd[3] = x[3]; // Keep fixed channel + + IncDelta(&xd[j]); + + cmsPipelineEvalFloat(xd, fxd, lut); + + Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); + Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); + Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); + } + + // Solve system + tmp2.n[0] = fx[0] - Target[0]; + tmp2.n[1] = fx[1] - Target[1]; + tmp2.n[2] = fx[2] - Target[2]; + + if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2)) + return FALSE; + + // Move our guess + x[0] -= (cmsFloat32Number) tmp.n[0]; + x[1] -= (cmsFloat32Number) tmp.n[1]; + x[2] -= (cmsFloat32Number) tmp.n[2]; + + // Some clipping.... + for (j=0; j < 3; j++) { + if (x[j] < 0) x[j] = 0; + else + if (x[j] > 1.0) x[j] = 1.0; + } + } + + return TRUE; +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmd5.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmd5.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmd5.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmd5.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,342 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- + + +#include "lcms2_internal.h" + +#ifdef CMS_USE_BIG_ENDIAN + +static +void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs) +{ + do { + + cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf); + *(cmsUInt32Number *) buf = t; + buf += sizeof(cmsUInt32Number); + + } while (--longs); + +} + +#else +#define byteReverse(buf, len) +#endif + + +typedef struct { + + cmsUInt32Number buf[4]; + cmsUInt32Number bits[2]; + cmsUInt8Number in[64]; + cmsContext ContextID; + +} _cmsMD5; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + + +static +void cmsMD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16]) +{ + CMSREGISTER cmsUInt32Number a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +// Create a MD5 object + +cmsHANDLE CMSEXPORT cmsMD5alloc(cmsContext ContextID) +{ + _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5)); + if (ctx == NULL) return NULL; + + ctx ->ContextID = ContextID; + + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; + + return (cmsHANDLE) ctx; +} + +void CMSEXPORT cmsMD5add(cmsHANDLE Handle, const cmsUInt8Number* buf, cmsUInt32Number len) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + (len << 3)) < t) + ctx->bits[1]++; + + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + + cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + + memmove(p, buf, t); + byteReverse(ctx->in, 16); + + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += 64; + len -= 64; + } + + memmove(ctx->in, buf, len); +} + +// Destroy the object and return the checksum +void CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number count; + cmsUInt8Number *p; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + + count = 64 - 1 - count; + + if (count < 8) { + + memset(p, 0, count); + byteReverse(ctx->in, 16); + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0]; + ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1]; + + cmsMD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + byteReverse((cmsUInt8Number *) ctx->buf, 4); + memmove(ProfileID ->ID8, ctx->buf, 16); + + _cmsFree(ctx ->ContextID, ctx); +} + + + +// Assuming io points to an ICC profile, compute and store MD5 checksum +// In the header, rendering intentent, attributes and ID should be set to zero +// before computing MD5 checksum (per 6.1.13 in ICC spec) + +cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) +{ + cmsContext ContextID; + cmsUInt32Number BytesNeeded; + cmsUInt8Number* Mem = NULL; + cmsHANDLE MD5 = NULL; + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + + _cmsAssert(hProfile != NULL); + + ContextID = cmsGetProfileContextID(hProfile); + + // Save a copy of the profile header + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + // Set RI, attributes and ID + memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); + Icc ->RenderingIntent = 0; + memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); + + // Compute needed storage + if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error; + + // Allocate memory + Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded); + if (Mem == NULL) goto Error; + + // Save to temporary storage + if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error; + + // Create MD5 object + MD5 = cmsMD5alloc(ContextID); + if (MD5 == NULL) goto Error; + + // Add all bytes + cmsMD5add(MD5, Mem, BytesNeeded); + + // Temp storage is no longer needed + _cmsFree(ContextID, Mem); + + // Restore header + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + + // And store the ID + cmsMD5finish(&Icc ->ProfileID, MD5); + return TRUE; + +Error: + + // Free resources as something went wrong + // "MD5" cannot be other than NULL here, so no need to free it + if (Mem != NULL) _cmsFree(ContextID, Mem); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + return FALSE; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmtrx.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmtrx.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsmtrx.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsmtrx.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,205 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;} + + +// Initiate a vector +void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z) +{ + r -> n[VX] = x; + r -> n[VY] = y; + r -> n[VZ] = z; +} + +// Vector subtraction +void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b) +{ + r -> n[VX] = a -> n[VX] - b -> n[VX]; + r -> n[VY] = a -> n[VY] - b -> n[VY]; + r -> n[VZ] = a -> n[VZ] - b -> n[VZ]; +} + +// Vector cross product +void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v) +{ + r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ]; + r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX]; + r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY]; +} + +// Vector dot product +cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v) +{ + return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ]; +} + +// Euclidean length +cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a) +{ + return sqrt(a ->n[VX] * a ->n[VX] + + a ->n[VY] * a ->n[VY] + + a ->n[VZ] * a ->n[VZ]); +} + +// Euclidean distance +cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b) +{ + cmsFloat64Number d1 = a ->n[VX] - b ->n[VX]; + cmsFloat64Number d2 = a ->n[VY] - b ->n[VY]; + cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ]; + + return sqrt(d1*d1 + d2*d2 + d3*d3); +} + + + +// 3x3 Identity +void CMSEXPORT _cmsMAT3identity(cmsMAT3* a) +{ + _cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0); + _cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0); + _cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0); +} + +static +cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < (1.0 / 65535.0); +} + + +cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} + + +// Multiply two matrices +void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b) +{ +#define ROWCOL(i, j) \ + a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j] + + _cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); + _cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); + _cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); + +#undef ROWCOL //(i, j) +} + + + +// Inverse of a matrix b = a^(-1) +cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b) +{ + cmsFloat64Number det, c0, c1, c2; + + c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1]; + c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0]; + c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0]; + + det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2; + + if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert + + b -> v[0].n[0] = c0/det; + b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det; + b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det; + b -> v[1].n[0] = c1/det; + b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det; + b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det; + b -> v[2].n[0] = c2/det; + b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det; + b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det; + + return TRUE; +} + + +// Solve a system in the form Ax = b +cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b) +{ + cmsMAT3 m, a_1; + + memmove(&m, a, sizeof(cmsMAT3)); + + if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix + + _cmsMAT3eval(x, &a_1, b); + return TRUE; +} + +// Evaluate a vector across a matrix +void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v) +{ + r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ]; + r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ]; + r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ]; +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsnamed.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsnamed.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsnamed.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsnamed.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1009 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Multilocalized unicode objects. That is an attempt to encapsulate i18n. + + +// Allocates an empty multi localizad unicode object +cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) +{ + cmsMLU* mlu; + + // nItems should be positive if given + if (nItems <= 0) nItems = 2; + + // Create the container + mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); + if (mlu == NULL) return NULL; + + mlu ->ContextID = ContextID; + + // Create entry array + mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); + if (mlu ->Entries == NULL) { + _cmsFree(ContextID, mlu); + return NULL; + } + + // Ok, keep indexes up to date + mlu ->AllocatedEntries = nItems; + mlu ->UsedEntries = 0; + + return mlu; +} + + +// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two. +static +cmsBool GrowMLUpool(cmsMLU* mlu) +{ + cmsUInt32Number size; + void *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + if (mlu ->PoolSize == 0) + size = 256; + else + size = mlu ->PoolSize * 2; + + // Check for overflow + if (size < mlu ->PoolSize) return FALSE; + + // Reallocate the pool + NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); + if (NewPtr == NULL) return FALSE; + + + mlu ->MemPool = NewPtr; + mlu ->PoolSize = size; + + return TRUE; +} + + +// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two. +static +cmsBool GrowMLUtable(cmsMLU* mlu) +{ + cmsUInt32Number AllocatedEntries; + _cmsMLUentry *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + AllocatedEntries = mlu ->AllocatedEntries * 2; + + // Check for overflow + if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; + + // Reallocate the memory + NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); + if (NewPtr == NULL) return FALSE; + + mlu ->Entries = NewPtr; + mlu ->AllocatedEntries = AllocatedEntries; + + return TRUE; +} + + +// Search for a specific entry in the structure. Language and Country are used. +static +int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number i; + + // Sanity check + if (mlu == NULL) return -1; + + // Iterate whole table + for (i=0; i < mlu ->UsedEntries; i++) { + + if (mlu ->Entries[i].Country == CountryCode && + mlu ->Entries[i].Language == LanguageCode) return (int) i; + } + + // Not found + return -1; +} + +// Add a block of characters to the intended MLU. Language and country are specified. +// Only one entry for Language/country pair is allowed. +static +cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number Offset; + cmsUInt8Number* Ptr; + + // Sanity check + if (mlu == NULL) return FALSE; + + // Is there any room available? + if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { + if (!GrowMLUtable(mlu)) return FALSE; + } + + // Only one ASCII string + if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed! + + // Check for size + while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { + + if (!GrowMLUpool(mlu)) return FALSE; + } + + Offset = mlu ->PoolUsed; + + Ptr = (cmsUInt8Number*) mlu ->MemPool; + if (Ptr == NULL) return FALSE; + + // Set the entry + memmove(Ptr + Offset, Block, size); + mlu ->PoolUsed += size; + + mlu ->Entries[mlu ->UsedEntries].StrW = Offset; + mlu ->Entries[mlu ->UsedEntries].Len = size; + mlu ->Entries[mlu ->UsedEntries].Country = CountryCode; + mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode; + mlu ->UsedEntries++; + + return TRUE; +} + +// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some +// compilers don't properly align beginning of strings +static +cmsUInt16Number strTo16(const char str[3]) +{ + const cmsUInt8Number* ptr8; + cmsUInt16Number n; + + // For non-existent strings + if (str == NULL) return 0; + ptr8 = (const cmsUInt8Number*)str; + n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]); + + return n; +} + +static +void strFrom16(char str[3], cmsUInt16Number n) +{ + str[0] = (char)(n >> 8); + str[1] = (char)n; + str[2] = (char)0; + +} + +// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61) +// In the case the user explicitely sets an empty string, we force a \0 +cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString) +{ + cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString); + wchar_t* WStr; + cmsBool rc; + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + if (mlu == NULL) return FALSE; + + // len == 0 would prevent operation, so we set a empty string pointing to zero + if (len == 0) + { + len = 1; + } + + WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t)); + if (WStr == NULL) return FALSE; + + for (i=0; i < len; i++) + WStr[i] = (wchar_t) ASCIIString[i]; + + rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry); + + _cmsFree(mlu ->ContextID, WStr); + return rc; + +} + +// We don't need any wcs support library +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +// Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61) +cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString) +{ + cmsUInt16Number Lang = strTo16(Language); + cmsUInt16Number Cntry = strTo16(Country); + cmsUInt32Number len; + + if (mlu == NULL) return FALSE; + if (WideString == NULL) return FALSE; + + len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t); + if (len == 0) + len = sizeof(wchar_t); + + return AddMLUBlock(mlu, len, WideString, Lang, Cntry); +} + +// Duplicating a MLU is as easy as copying all members +cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) +{ + cmsMLU* NewMlu = NULL; + + // Duplicating a NULL obtains a NULL + if (mlu == NULL) return NULL; + + NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); + if (NewMlu == NULL) return NULL; + + // Should never happen + if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) + goto Error; + + // Sanitize... + if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; + + memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); + NewMlu ->UsedEntries = mlu ->UsedEntries; + + // The MLU may be empty + if (mlu ->PoolUsed == 0) { + NewMlu ->MemPool = NULL; + } + else { + // It is not empty + NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); + if (NewMlu ->MemPool == NULL) goto Error; + } + + NewMlu ->PoolSize = mlu ->PoolUsed; + + if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error; + + memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed); + NewMlu ->PoolUsed = mlu ->PoolUsed; + + return NewMlu; + +Error: + + if (NewMlu != NULL) cmsMLUfree(NewMlu); + return NULL; +} + +// Free any used memory +void CMSEXPORT cmsMLUfree(cmsMLU* mlu) +{ + if (mlu) { + + if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); + if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); + + _cmsFree(mlu ->ContextID, mlu); + } +} + + +// The algorithm first searches for an exact match of country and language, if not found it uses +// the Language. If none is found, first entry is used instead. +static +const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, + cmsUInt32Number *len, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, + cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) +{ + cmsUInt32Number i; + int Best = -1; + _cmsMLUentry* v; + + if (mlu == NULL) return NULL; + + if (mlu -> AllocatedEntries <= 0) return NULL; + + for (i=0; i < mlu ->UsedEntries; i++) { + + v = mlu ->Entries + i; + + if (v -> Language == LanguageCode) { + + if (Best == -1) Best = (int) i; + + if (v -> Country == CountryCode) { + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match + } + } + } + + // No string found. Return First one + if (Best == -1) + Best = 0; + + v = mlu ->Entries + Best; + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); +} + + +// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len +cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + cmsUInt32Number ASCIIlen, i; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + // Get WideChar + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + ASCIIlen = StrLen / sizeof(wchar_t); + + // Maybe we want only to know the len? + if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < ASCIIlen + 1) + ASCIIlen = BufferSize - 1; + + // Precess each character + for (i=0; i < ASCIIlen; i++) { + + if (Wide[i] == 0) + Buffer[i] = 0; + else + Buffer[i] = (char) Wide[i]; + } + + // We put a termination "\0" + Buffer[ASCIIlen] = 0; + return ASCIIlen + 1; +} + +// Obtain a wide representation of the MLU, on depending on current locale settings +cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + // Maybe we want only to know the len? + if (Buffer == NULL) return StrLen + sizeof(wchar_t); + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < StrLen + sizeof(wchar_t)) + StrLen = BufferSize - + sizeof(wchar_t); + + memmove(Buffer, Wide, StrLen); + Buffer[StrLen / sizeof(wchar_t)] = 0; + + return StrLen + sizeof(wchar_t); +} + + +// Get also the language and country +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]) +{ + const wchar_t *Wide; + + cmsUInt16Number Lang = strTo16(LanguageCode); + cmsUInt16Number Cntry = strTo16(CountryCode); + cmsUInt16Number ObtLang, ObtCode; + + // Sanitize + if (mlu == NULL) return FALSE; + + Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); + if (Wide == NULL) return FALSE; + + // Get used language and code + strFrom16(ObtainedLanguage, ObtLang); + strFrom16(ObtainedCountry, ObtCode); + + return TRUE; +} + + + +// Get the number of translations in the MLU object +cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu) +{ + if (mlu == NULL) return 0; + return mlu->UsedEntries; +} + +// Get the language and country codes for a specific MLU index +cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]) +{ + _cmsMLUentry *entry; + + if (mlu == NULL) return FALSE; + + if (idx >= mlu->UsedEntries) return FALSE; + + entry = &mlu->Entries[idx]; + + strFrom16(LanguageCode, entry->Language); + strFrom16(CountryCode, entry->Country); + + return TRUE; +} + + +// Named color lists -------------------------------------------------------------------------------------------- + +// Grow the list to keep at least NumElements +static +cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) +{ + cmsUInt32Number size; + _cmsNAMEDCOLOR * NewPtr; + + if (v == NULL) return FALSE; + + if (v ->Allocated == 0) + size = 64; // Initial guess + else + size = v ->Allocated * 2; + + // Keep a maximum color lists can grow, 100K entries seems reasonable + if (size > 1024 * 100) { + _cmsFree(v->ContextID, (void*) v->List); + v->List = NULL; + return FALSE; + } + + NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR)); + if (NewPtr == NULL) + return FALSE; + + v ->List = NewPtr; + v ->Allocated = size; + return TRUE; +} + +// Allocate a list for n elements +cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) +{ + cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); + + if (v == NULL) return NULL; + + v ->List = NULL; + v ->nColors = 0; + v ->ContextID = ContextID; + + while (v -> Allocated < n) { + if (!GrowNamedColorList(v)) { + cmsFreeNamedColorList(v); + return NULL; + } + } + + strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); + strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); + v->Prefix[32] = v->Suffix[32] = 0; + + v -> ColorantCount = ColorantCount; + + return v; +} + +// Free a list +void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) +{ + if (v == NULL) return; + if (v ->List) _cmsFree(v ->ContextID, v ->List); + _cmsFree(v ->ContextID, v); +} + +cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) +{ + cmsNAMEDCOLORLIST* NewNC; + + if (v == NULL) return NULL; + + NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix); + if (NewNC == NULL) return NULL; + + // For really large tables we need this + while (NewNC ->Allocated < v ->Allocated){ + if (!GrowNamedColorList(NewNC)) + { + cmsFreeNamedColorList(NewNC); + return NULL; + } + } + + memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); + memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); + NewNC ->ColorantCount = v ->ColorantCount; + memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); + NewNC ->nColors = v ->nColors; + return NewNC; +} + + +// Append a color to a list. List pointer may change if reallocated +cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, + const char* Name, + cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) +{ + cmsUInt32Number i; + + if (NamedColorList == NULL) return FALSE; + + if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { + if (!GrowNamedColorList(NamedColorList)) return FALSE; + } + + for (i=0; i < NamedColorList ->ColorantCount; i++) + NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i]; + + for (i=0; i < 3; i++) + NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i]; + + if (Name != NULL) { + + strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); + NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; + + } + else + NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; + + + NamedColorList ->nColors++; + return TRUE; +} + +// Returns number of elements +cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) +{ + if (NamedColorList == NULL) return 0; + return NamedColorList ->nColors; +} + +// Info aboout a given color +cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant) +{ + if (NamedColorList == NULL) return FALSE; + + if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; + + // strcpy instead of strncpy because many apps are using small buffers + if (Name) strcpy(Name, NamedColorList->List[nColor].Name); + if (Prefix) strcpy(Prefix, NamedColorList->Prefix); + if (Suffix) strcpy(Suffix, NamedColorList->Suffix); + if (PCS) + memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); + + if (Colorant) + memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, + sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); + + + return TRUE; +} + +// Search for a given color name (no prefix or suffix) +cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) +{ + cmsUInt32Number i; + cmsUInt32Number n; + + if (NamedColorList == NULL) return -1; + n = cmsNamedColorCount(NamedColorList); + for (i=0; i < n; i++) { + if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) + return (cmsInt32Number) i; + } + + return -1; +} + +// MPE support ----------------------------------------------------------------------------------------------------------------- + +static +void FreeNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsFreeNamedColorList(List); +} + +static +void* DupNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + return cmsDupNamedColorList(List); +} + +static +void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + Out[0] = Out[1] = Out[2] = 0.0f; + } + else { + + // Named color always uses Lab + Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); + Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); + Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); + } +} + +static +void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + cmsUInt32Number j; + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); + for (j = 0; j < NamedColorList->ColorantCount; j++) + Out[j] = 0.0f; + + } + else { + for (j=0; j < NamedColorList ->ColorantCount; j++) + Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); + } +} + + +// Named color lookup element +cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) +{ + return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, + cmsSigNamedColorElemType, + 1, UsePCS ? 3 : NamedColorList ->ColorantCount, + UsePCS ? EvalNamedColorPCS : EvalNamedColor, + DupNamedColorList, + FreeNamedColorList, + cmsDupNamedColorList(NamedColorList)); + +} + + +// Retrieve the named color list from a transform. Should be first element in the LUT +cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsStage* mpe = v ->Lut->Elements; + + if (mpe ->Type != cmsSigNamedColorElemType) return NULL; + return (cmsNAMEDCOLORLIST*) mpe ->Data; +} + + +// Profile sequence description routines ------------------------------------------------------------------------------------- + +cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) +{ + cmsSEQ* Seq; + cmsUInt32Number i; + + if (n == 0) return NULL; + + // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked + // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! + if (n > 255) return NULL; + + Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); + if (Seq == NULL) return NULL; + + Seq -> ContextID = ContextID; + Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); + Seq -> n = n; + + if (Seq -> seq == NULL) { + _cmsFree(ContextID, Seq); + return NULL; + } + + for (i=0; i < n; i++) { + Seq -> seq[i].Manufacturer = NULL; + Seq -> seq[i].Model = NULL; + Seq -> seq[i].Description = NULL; + } + + return Seq; +} + +void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) +{ + cmsUInt32Number i; + + for (i=0; i < pseq ->n; i++) { + if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); + if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); + if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); + } + + if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); + _cmsFree(pseq -> ContextID, pseq); +} + +cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) +{ + cmsSEQ *NewSeq; + cmsUInt32Number i; + + if (pseq == NULL) + return NULL; + + NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); + if (NewSeq == NULL) return NULL; + + + NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); + if (NewSeq ->seq == NULL) goto Error; + + NewSeq -> ContextID = pseq ->ContextID; + NewSeq -> n = pseq ->n; + + for (i=0; i < pseq->n; i++) { + + memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); + + NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; + NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; + memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].technology = pseq ->seq[i].technology; + + NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); + NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); + NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); + + } + + return NewSeq; + +Error: + + cmsFreeProfileSequenceDescription(NewSeq); + return NULL; +} + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +// Dictionaries are just very simple linked lists + + +typedef struct _cmsDICT_struct { + cmsDICTentry* head; + cmsContext ContextID; +} _cmsDICT; + + +// Allocate an empty dictionary +cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) +{ + _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); + if (dict == NULL) return NULL; + + dict ->ContextID = ContextID; + return (cmsHANDLE) dict; + +} + +// Dispose resources +void CMSEXPORT cmsDictFree(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry, *next; + + _cmsAssert(dict != NULL); + + // Walk the list freeing all nodes + entry = dict ->head; + while (entry != NULL) { + + if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); + if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); + if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); + if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); + + // Don't fall in the habitual trap... + next = entry ->Next; + _cmsFree(dict ->ContextID, entry); + + entry = next; + } + + _cmsFree(dict ->ContextID, dict); +} + + +// Duplicate a wide char string +static +wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) +{ + if (ptr == NULL) return NULL; + return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); +} + +// Add a new entry to the linked list +cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry; + + _cmsAssert(dict != NULL); + _cmsAssert(Name != NULL); + + entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); + if (entry == NULL) return FALSE; + + entry ->DisplayName = cmsMLUdup(DisplayName); + entry ->DisplayValue = cmsMLUdup(DisplayValue); + entry ->Name = DupWcs(dict ->ContextID, Name); + entry ->Value = DupWcs(dict ->ContextID, Value); + + entry ->Next = dict ->head; + dict ->head = entry; + + return TRUE; +} + + +// Duplicates an existing dictionary +cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) +{ + _cmsDICT* old_dict = (_cmsDICT*) hDict; + cmsHANDLE hNew; + cmsDICTentry *entry; + + _cmsAssert(old_dict != NULL); + + hNew = cmsDictAlloc(old_dict ->ContextID); + if (hNew == NULL) return NULL; + + // Walk the list freeing all nodes + entry = old_dict ->head; + while (entry != NULL) { + + if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { + + cmsDictFree(hNew); + return NULL; + } + + entry = entry -> Next; + } + + return hNew; +} + +// Get a pointer to the linked list +const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + + if (dict == NULL) return NULL; + return dict ->head; +} + +// Helper For external languages +const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) +{ + if (e == NULL) return NULL; + return e ->Next; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsopt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsopt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsopt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsopt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,2005 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +//---------------------------------------------------------------------------------- + +// Optimization for 8 bits, Shaper-CLUT (3 inputs only) +typedef struct { + + cmsContext ContextID; + + const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. + + cmsUInt16Number rx[256], ry[256], rz[256]; + cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data + + +} Prelin8Data; + + +// Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) +typedef struct { + + cmsContext ContextID; + + // Number of channels + cmsUInt32Number nInputs; + cmsUInt32Number nOutputs; + + _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance + cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; + + _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid + const cmsInterpParams* CLUTparams; // (not-owned pointer) + + + _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) + cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) + + +} Prelin16Data; + + +// Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed + +typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! + +#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) + +typedef struct { + + cmsContext ContextID; + + cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) + cmsS1Fixed14Number Shaper1G[256]; + cmsS1Fixed14Number Shaper1B[256]; + + cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) + cmsS1Fixed14Number Off[3]; + + cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 + cmsUInt16Number Shaper2G[16385]; + cmsUInt16Number Shaper2B[16385]; + +} MatShaper8Data; + +// Curves, optimization is shared between 8 and 16 bits +typedef struct { + + cmsContext ContextID; + + cmsUInt32Number nCurves; // Number of curves + cmsUInt32Number nElements; // Elements in curves + cmsUInt16Number** Curves; // Points to a dynamically allocated array + +} Curves16Data; + + +// Simple optimizations ---------------------------------------------------------------------------------------------------------- + + +// Remove an element in linked chain +static +void _RemoveElement(cmsStage** head) +{ + cmsStage* mpe = *head; + cmsStage* next = mpe ->Next; + *head = next; + cmsStageFree(mpe); +} + +// Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. +static +cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) +{ + cmsStage** pt = &Lut ->Elements; + cmsBool AnyOpt = FALSE; + + while (*pt != NULL) { + + if ((*pt) ->Implements == UnaryOp) { + _RemoveElement(pt); + AnyOpt = TRUE; + } + else + pt = &((*pt) -> Next); + } + + return AnyOpt; +} + +// Same, but only if two adjacent elements are found +static +cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut ->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1) -> Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { + _RemoveElement(pt2); + _RemoveElement(pt1); + AnyOpt = TRUE; + } + else + pt1 = &((*pt1) -> Next); + } + + return AnyOpt; +} + + +static +cmsBool CloseEnoughFloat(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < 0.00001f; +} + +static +cmsBool isFloatMatrixIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + if (!CloseEnoughFloat(a->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} +// if two adjacent matrices are found, multiply them. +static +cmsBool _MultiplyMatrix(cmsPipeline* Lut) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsStage* chain; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1)->Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1)->Implements == cmsSigMatrixElemType && (*pt2)->Implements == cmsSigMatrixElemType) { + + // Get both matrices + _cmsStageMatrixData* m1 = (_cmsStageMatrixData*) cmsStageData(*pt1); + _cmsStageMatrixData* m2 = (_cmsStageMatrixData*) cmsStageData(*pt2); + cmsMAT3 res; + + // Input offset and output offset should be zero to use this optimization + if (m1->Offset != NULL || m2 ->Offset != NULL || + cmsStageInputChannels(*pt1) != 3 || cmsStageOutputChannels(*pt1) != 3 || + cmsStageInputChannels(*pt2) != 3 || cmsStageOutputChannels(*pt2) != 3) + return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double); + + // Get the next in chain after the matrices + chain = (*pt2)->Next; + + // Remove both matrices + _RemoveElement(pt2); + _RemoveElement(pt1); + + // Now what if the result is a plain identity? + if (!isFloatMatrixIdentity(&res)) { + + // We can not get rid of full matrix + cmsStage* Multmat = cmsStageAllocMatrix(Lut->ContextID, 3, 3, (const cmsFloat64Number*) &res, NULL); + if (Multmat == NULL) return FALSE; // Should never happen + + // Recover the chain + Multmat->Next = chain; + *pt1 = Multmat; + } + + AnyOpt = TRUE; + } + else + pt1 = &((*pt1)->Next); + } + + return AnyOpt; +} + + +// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed +// by a v4 to v2 and vice-versa. The elements are then discarded. +static +cmsBool PreOptimize(cmsPipeline* Lut) +{ + cmsBool AnyOpt = FALSE, Opt; + + do { + + Opt = FALSE; + + // Remove all identities + Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); + + // Remove XYZ2Lab followed by Lab2XYZ + Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); + + // Remove Lab2XYZ followed by XYZ2Lab + Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); + + // Remove V4 to V2 followed by V2 to V4 + Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); + + // Remove V2 to V4 followed by V4 to V2 + Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); + + // Simplify matrix. + Opt |= _MultiplyMatrix(Lut); + + if (Opt) AnyOpt = TRUE; + + } while (Opt); + + return AnyOpt; +} + +static +void Eval16nop1D(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const struct _cms_interp_struc* p) +{ + Output[0] = Input[0]; + + cmsUNUSED_PARAMETER(p); +} + +static +void PrelinEval16(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const void* D) +{ + Prelin16Data* p16 = (Prelin16Data*) D; + cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; + cmsUInt16Number StageDEF[cmsMAXCHANNELS]; + cmsUInt32Number i; + + for (i=0; i < p16 ->nInputs; i++) { + + p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); + } + + p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); + + for (i=0; i < p16 ->nOutputs; i++) { + + p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); + } +} + + +static +void PrelinOpt16free(cmsContext ContextID, void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + + _cmsFree(ContextID, p16 ->EvalCurveOut16); + _cmsFree(ContextID, p16 ->ParamsCurveOut16); + + _cmsFree(ContextID, p16); +} + +static +void* Prelin16dup(cmsContext ContextID, const void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + Prelin16Data* Duped = (Prelin16Data*) _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); + + if (Duped == NULL) return NULL; + + Duped->EvalCurveOut16 = (_cmsInterpFn16*) _cmsDupMem(ContextID, p16->EvalCurveOut16, p16->nOutputs * sizeof(_cmsInterpFn16)); + Duped->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16->ParamsCurveOut16, p16->nOutputs * sizeof(cmsInterpParams*)); + + return Duped; +} + + +static +Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, + const cmsInterpParams* ColorMap, + cmsUInt32Number nInputs, cmsToneCurve** In, + cmsUInt32Number nOutputs, cmsToneCurve** Out ) +{ + cmsUInt32Number i; + Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data)); + if (p16 == NULL) return NULL; + + p16 ->nInputs = nInputs; + p16 ->nOutputs = nOutputs; + + + for (i=0; i < nInputs; i++) { + + if (In == NULL) { + p16 -> ParamsCurveIn16[i] = NULL; + p16 -> EvalCurveIn16[i] = Eval16nop1D; + + } + else { + p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; + p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; + } + } + + p16 ->CLUTparams = ColorMap; + p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; + + + p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); + if (p16->EvalCurveOut16 == NULL) + { + _cmsFree(ContextID, p16); + return NULL; + } + + p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); + if (p16->ParamsCurveOut16 == NULL) + { + + _cmsFree(ContextID, p16->EvalCurveOut16); + _cmsFree(ContextID, p16); + return NULL; + } + + for (i=0; i < nOutputs; i++) { + + if (Out == NULL) { + p16 ->ParamsCurveOut16[i] = NULL; + p16 -> EvalCurveOut16[i] = Eval16nop1D; + } + else { + + p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; + p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; + } + } + + return p16; +} + + + +// Resampling --------------------------------------------------------------------------------- + +#define PRELINEARIZATION_POINTS 4096 + +// Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for +// almost any transform. We use floating point precision and then convert from floating point to 16 bits. +static +cmsInt32Number XFormSampler16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER void* Cargo) +{ + cmsPipeline* Lut = (cmsPipeline*) Cargo; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i; + + _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); + _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); + + // From 16 bit to floating point + for (i=0; i < Lut ->InputChannels; i++) + InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Evaluate in floating point + cmsPipelineEvalFloat(InFloat, OutFloat, Lut); + + // Back to 16 bits representation + for (i=0; i < Lut ->OutputChannels; i++) + Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); + + // Always succeed + return TRUE; +} + +// Try to see if the curves of a given MPE are linear +static +cmsBool AllCurvesAreLinear(cmsStage* mpe) +{ + cmsToneCurve** Curves; + cmsUInt32Number i, n; + + Curves = _cmsStageGetPtrToCurveSet(mpe); + if (Curves == NULL) return FALSE; + + n = cmsStageOutputChannels(mpe); + + for (i=0; i < n; i++) { + if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; + } + + return TRUE; +} + +// This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose +// is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels +static +cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], + cmsUInt32Number nChannelsOut, cmsUInt32Number nChannelsIn) +{ + _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; + cmsInterpParams* p16 = Grid ->Params; + cmsFloat64Number px, py, pz, pw; + int x0, y0, z0, w0; + int i, index; + + if (CLUT -> Type != cmsSigCLutElemType) { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); + return FALSE; + } + + if (nChannelsIn == 4) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + w0 = (int) floor(pw); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0) || + ((pw - w0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[3] * x0 + + (int) p16 -> opta[2] * y0 + + (int) p16 -> opta[1] * z0 + + (int) p16 -> opta[0] * w0; + } + else + if (nChannelsIn == 3) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[2] * x0 + + (int) p16 -> opta[1] * y0 + + (int) p16 -> opta[0] * z0; + } + else + if (nChannelsIn == 1) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + + x0 = (int) floor(px); + + if (((px - x0) != 0)) return FALSE; // Not on exact node + + index = (int) p16 -> opta[0] * x0; + } + else { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return FALSE; + } + + for (i = 0; i < (int) nChannelsOut; i++) + Grid->Tab.T[index + i] = Value[i]; + + return TRUE; +} + +// Auxiliary, to see if two values are equal or very different +static +cmsBool WhitesAreEqual(cmsUInt32Number n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + + if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremely different that the fixup should be avoided + if (White1[i] != White2[i]) return FALSE; + } + return TRUE; +} + + +// Locate the node for the white point and fix it to pure white in order to avoid scum dot. +static +cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) +{ + cmsUInt16Number *WhitePointIn, *WhitePointOut; + cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; + cmsUInt32Number i, nOuts, nIns; + cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; + + if (!_cmsEndPointsBySpace(EntryColorSpace, + &WhitePointIn, NULL, &nIns)) return FALSE; + + if (!_cmsEndPointsBySpace(ExitColorSpace, + &WhitePointOut, NULL, &nOuts)) return FALSE; + + // It needs to be fixed? + if (Lut ->InputChannels != nIns) return FALSE; + if (Lut ->OutputChannels != nOuts) return FALSE; + + cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); + + if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match + + // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) + return FALSE; + + // We need to interpolate white points of both, pre and post curves + if (PreLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); + + for (i=0; i < nIns; i++) { + WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); + } + } + else { + for (i=0; i < nIns; i++) + WhiteIn[i] = WhitePointIn[i]; + } + + // If any post-linearization, we need to find how is represented white before the curve, do + // a reverse interpolation in this case. + if (PostLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); + + for (i=0; i < nOuts; i++) { + + cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); + if (InversePostLin == NULL) { + WhiteOut[i] = WhitePointOut[i]; + + } else { + + WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); + cmsFreeToneCurve(InversePostLin); + } + } + } + else { + for (i=0; i < nOuts; i++) + WhiteOut[i] = WhitePointOut[i]; + } + + // Ok, proceed with patching. May fail and we don't care if it fails + PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// This function creates simple LUT from complex ones. The generated LUT has an optional set of +// prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. +// These curves have to exist in the original LUT in order to be used in the simplified output. +// Caller may also use the flags to allow this feature. +// LUTS with all curves will be simplified to a single curve. Parametric curves are lost. +// This function should be used on 16-bits LUTS only, as floating point losses precision when simplified +// ----------------------------------------------------------------------------------------------------------------------------------------------- + +static +cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* Src = NULL; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* CLUT; + cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; + cmsUInt32Number nGridPoints; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage *NewPreLin = NULL; + cmsStage *NewPostLin = NULL; + _cmsStageCLutData* DataCLUT; + cmsToneCurve** DataSetIn; + cmsToneCurve** DataSetOut; + Prelin16Data* p16; + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // For empty LUTs, 2 points are enough + if (cmsPipelineStageCount(*Lut) == 0) + nGridPoints = 2; + + Src = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Prelinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { + + // Get a pointer to the prelinearization element + cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); + + // Check if suitable + if (PreLin && PreLin ->Type == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PreLin)) { + + // All seems ok, proceed. + NewPreLin = cmsStageDup(PreLin); + if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) + goto Error; + + // Remove prelinearization. Since we have duplicated the curve + // in destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); + } + } + } + + // Allocate the CLUT + CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); + if (CLUT == NULL) goto Error; + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { + goto Error; + } + + // Postlinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { + + // Get a pointer to the postlinearization if present + cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); + + // Check if suitable + if (PostLin && cmsStageType(PostLin) == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PostLin)) { + + // All seems ok, proceed. + NewPostLin = cmsStageDup(PostLin); + if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) + goto Error; + + // In destination LUT, the sampling should be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); + } + } + } + + // Now its time to do the sampling. We have to ignore pre/post linearization + // The source LUT without pre/post curves is passed as parameter. + if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { +Error: + // Ops, something went wrong, Restore stages + if (KeepPreLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { + _cmsAssert(0); // This never happens + } + } + if (KeepPostLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { + _cmsAssert(0); // This never happens + } + } + cmsPipelineFree(Dest); + return FALSE; + } + + // Done. + + if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); + if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); + cmsPipelineFree(Src); + + DataCLUT = (_cmsStageCLutData*) CLUT ->Data; + + if (NewPreLin == NULL) DataSetIn = NULL; + else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; + + if (NewPostLin == NULL) DataSetOut = NULL; + else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; + + + if (DataSetIn == NULL && DataSetOut == NULL) { + + _cmsPipelineSetOptimizationParameters(Dest, (_cmsPipelineEval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); + } + else { + + p16 = PrelinOpt16alloc(Dest ->ContextID, + DataCLUT ->Params, + Dest ->InputChannels, + DataSetIn, + Dest ->OutputChannels, + DataSetOut); + + _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + } + + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); + } + + *Lut = Dest; + return TRUE; + + cmsUNUSED_PARAMETER(Intent); +} + + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on +// Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works +// for RGB transforms. See the paper for more details +// ----------------------------------------------------------------------------------------------------------------------------------------------- + + +// Normalize endpoints by slope limiting max and min. This assures endpoints as well. +// Descending curves are handled as well. +static +void SlopeLimiting(cmsToneCurve* g) +{ + int BeginVal, EndVal; + int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% + int AtEnd = (int) g ->nEntries - AtBegin - 1; // And 98% + cmsFloat64Number Val, Slope, beta; + int i; + + if (cmsIsToneCurveDescending(g)) { + BeginVal = 0xffff; EndVal = 0; + } + else { + BeginVal = 0; EndVal = 0xffff; + } + + // Compute slope and offset for begin of curve + Val = g ->Table16[AtBegin]; + Slope = (Val - BeginVal) / AtBegin; + beta = Val - Slope * AtBegin; + + for (i=0; i < AtBegin; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); + + // Compute slope and offset for the end + Val = g ->Table16[AtEnd]; + Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases + beta = Val - Slope * AtEnd; + + for (i = AtEnd; i < (int) g ->nEntries; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); +} + + +// Precomputes tables for 8-bit on input devicelink. +static +Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) +{ + int i; + cmsUInt16Number Input[3]; + cmsS15Fixed16Number v1, v2, v3; + Prelin8Data* p8; + + p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data)); + if (p8 == NULL) return NULL; + + // Since this only works for 8 bit input, values comes always as x * 257, + // we can safely take msb byte (x << 8 + x) + + for (i=0; i < 256; i++) { + + if (G != NULL) { + + // Get 16-bit representation + Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); + Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); + Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); + } + else { + Input[0] = FROM_8_TO_16(i); + Input[1] = FROM_8_TO_16(i); + Input[2] = FROM_8_TO_16(i); + } + + + // Move to 0..1.0 in fixed domain + v1 = _cmsToFixedDomain((int) (Input[0] * p -> Domain[0])); + v2 = _cmsToFixedDomain((int) (Input[1] * p -> Domain[1])); + v3 = _cmsToFixedDomain((int) (Input[2] * p -> Domain[2])); + + // Store the precalculated table of nodes + p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); + p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); + p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); + + // Store the precalculated table of offsets + p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); + p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); + p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); + } + + p8 ->ContextID = ContextID; + p8 ->p = p; + + return p8; +} + +static +void Prelin8free(cmsContext ContextID, void* ptr) +{ + _cmsFree(ContextID, ptr); +} + +static +void* Prelin8dup(cmsContext ContextID, const void* ptr) +{ + return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); +} + + + +// A optimized interpolation for 8-bit input. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static CMS_NO_SANITIZE +void PrelinEval8(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const void* D) +{ + + cmsUInt8Number r, g, b; + cmsS15Fixed16Number rx, ry, rz; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + int OutChan; + CMSREGISTER cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + Prelin8Data* p8 = (Prelin8Data*) D; + CMSREGISTER const cmsInterpParams* p = p8 ->p; + int TotalOut = (int) p -> nOutputs; + const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table; + + r = (cmsUInt8Number) (Input[0] >> 8); + g = (cmsUInt8Number) (Input[1] >> 8); + b = (cmsUInt8Number) (Input[2] >> 8); + + X0 = (cmsS15Fixed16Number) p8->X0[r]; + Y0 = (cmsS15Fixed16Number) p8->Y0[g]; + Z0 = (cmsS15Fixed16Number) p8->Z0[b]; + + rx = p8 ->rx[r]; + ry = p8 ->ry[g]; + rz = p8 ->rz[b]; + + X1 = X0 + (cmsS15Fixed16Number)((rx == 0) ? 0 : p ->opta[2]); + Y1 = Y0 + (cmsS15Fixed16Number)((ry == 0) ? 0 : p ->opta[1]); + Z1 = Z0 + (cmsS15Fixed16Number)((rz == 0) ? 0 : p ->opta[0]); + + + // These are the 6 Tetrahedral + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (rx >= rz && rz >= ry) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + } + else + if (rz >= rx && rx >= ry) + { + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else + if (ry >= rx && rx >= rz) + { + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (ry >= rz && rz >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + } + else + if (rz >= ry && ry >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16)); + + } +} + +#undef DENS + + +// Curves that contain wide empty areas are not optimizeable +static +cmsBool IsDegenerated(const cmsToneCurve* g) +{ + cmsUInt32Number i, Zeros = 0, Poles = 0; + cmsUInt32Number nEntries = g ->nEntries; + + for (i=0; i < nEntries; i++) { + + if (g ->Table16[i] == 0x0000) Zeros++; + if (g ->Table16[i] == 0xffff) Poles++; + } + + if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables + if (Zeros > (nEntries / 20)) return TRUE; // Degenerated, many zeros + if (Poles > (nEntries / 20)) return TRUE; // Degenerated, many poles + + return FALSE; +} + +// -------------------------------------------------------------------------------------------------------------- +// We need xput over here + +static +cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* OriginalLut; + cmsUInt32Number nGridPoints; + cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; + cmsUInt32Number t, i; + cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; + cmsBool lIsSuitable, lIsLinear; + cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; + cmsStage* OptimizedCLUTmpe; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage* OptimizedPrelinMpe; + cmsStage* mpe; + cmsToneCurve** OptimizedPrelinCurves; + _cmsStageCLutData* OptimizedPrelinCLUT; + + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only on chunky RGB + if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*InputFormat)) return FALSE; + + if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; + if (T_PLANAR(*OutputFormat)) return FALSE; + + // On 16 bits, user has to specify the feature + if (!_cmsFormatterIs8bit(*InputFormat)) { + if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; + } + + OriginalLut = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); + + // Color space must be specified + if (ColorSpace == (cmsColorSpaceSignature)0 || + OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; + + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // Empty gamma containers + memset(Trans, 0, sizeof(Trans)); + memset(TransReverse, 0, sizeof(TransReverse)); + + // If the last stage of the original lut are curves, and those curves are + // degenerated, it is likely the transform is squeezing and clipping + // the output from previous CLUT. We cannot optimize this case + { + cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut); + + if (last == NULL) goto Error; + if (cmsStageType(last) == cmsSigCurveSetElemType) { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last); + for (i = 0; i < Data->nCurves; i++) { + if (IsDegenerated(Data->TheCurves[i])) + goto Error; + } + } + } + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (Trans[t] == NULL) goto Error; + } + + // Populate the curves + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + // Feed input with a gray ramp + for (t=0; t < OriginalLut ->InputChannels; t++) + In[t] = v; + + // Evaluate the gray value + cmsPipelineEvalFloat(In, Out, OriginalLut); + + // Store result in curve + for (t=0; t < OriginalLut ->InputChannels; t++) + Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); + } + + // Slope-limit the obtained curves + for (t = 0; t < OriginalLut ->InputChannels; t++) + SlopeLimiting(Trans[t]); + + // Check for validity + lIsSuitable = TRUE; + lIsLinear = TRUE; + for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { + + // Exclude if already linear + if (!cmsIsToneCurveLinear(Trans[t])) + lIsLinear = FALSE; + + // Exclude if non-monotonic + if (!cmsIsToneCurveMonotonic(Trans[t])) + lIsSuitable = FALSE; + + if (IsDegenerated(Trans[t])) + lIsSuitable = FALSE; + } + + // If it is not suitable, just quit + if (!lIsSuitable) goto Error; + + // Invert curves if possible + for (t = 0; t < OriginalLut ->InputChannels; t++) { + TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); + if (TransReverse[t] == NULL) goto Error; + } + + // Now inset the reversed curves at the begin of transform + LutPlusCurves = cmsPipelineDup(OriginalLut); + if (LutPlusCurves == NULL) goto Error; + + if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) + goto Error; + + // Create the result LUT + OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); + if (OptimizedLUT == NULL) goto Error; + + OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); + + // Create and insert the curves at the beginning + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) + goto Error; + + // Allocate the CLUT for result + OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) + goto Error; + + // Resample the LUT + if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; + + // Free resources + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + cmsPipelineFree(LutPlusCurves); + + + OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); + OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; + + // Set the evaluator if 8-bit + if (_cmsFormatterIs8bit(*InputFormat)) { + + Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + OptimizedPrelinCurves); + if (p8 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); + + } + else + { + Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + 3, OptimizedPrelinCurves, 3, NULL); + if (p16 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + + } + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { + + return FALSE; + } + } + + // And return the obtained LUT + + cmsPipelineFree(OriginalLut); + *Lut = OptimizedLUT; + return TRUE; + +Error: + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); + if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); + + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(lIsLinear); +} + + +// Curves optimizer ------------------------------------------------------------------------------------------------------------------ + +static +void CurvesFree(cmsContext ContextID, void* ptr) +{ + Curves16Data* Data = (Curves16Data*) ptr; + cmsUInt32Number i; + + for (i=0; i < Data -> nCurves; i++) { + + _cmsFree(ContextID, Data ->Curves[i]); + } + + _cmsFree(ContextID, Data ->Curves); + _cmsFree(ContextID, ptr); +} + +static +void* CurvesDup(cmsContext ContextID, const void* ptr) +{ + Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); + cmsUInt32Number i; + + if (Data == NULL) return NULL; + + Data->Curves = (cmsUInt16Number**) _cmsDupMem(ContextID, Data->Curves, Data->nCurves * sizeof(cmsUInt16Number*)); + + for (i=0; i < Data -> nCurves; i++) { + Data->Curves[i] = (cmsUInt16Number*) _cmsDupMem(ContextID, Data->Curves[i], Data->nElements * sizeof(cmsUInt16Number)); + } + + return (void*) Data; +} + +// Precomputes tables for 8-bit on input devicelink. +static +Curves16Data* CurvesAlloc(cmsContext ContextID, cmsUInt32Number nCurves, cmsUInt32Number nElements, cmsToneCurve** G) +{ + cmsUInt32Number i, j; + Curves16Data* c16; + + c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data)); + if (c16 == NULL) return NULL; + + c16 ->nCurves = nCurves; + c16 ->nElements = nElements; + + c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); + if (c16->Curves == NULL) { + _cmsFree(ContextID, c16); + return NULL; + } + + for (i=0; i < nCurves; i++) { + + c16->Curves[i] = (cmsUInt16Number*) _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); + + if (c16->Curves[i] == NULL) { + + for (j=0; j < i; j++) { + _cmsFree(ContextID, c16->Curves[j]); + } + _cmsFree(ContextID, c16->Curves); + _cmsFree(ContextID, c16); + return NULL; + } + + if (nElements == 256U) { + + for (j=0; j < nElements; j++) { + + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); + } + } + else { + + for (j=0; j < nElements; j++) { + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); + } + } + } + + return c16; +} + +static +void FastEvaluateCurves8(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + int x; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + + x = (In[i] >> 8); + Out[i] = Data -> Curves[i][x]; + } +} + + +static +void FastEvaluateCurves16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + cmsUInt32Number i; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = Data -> Curves[i][In[i]]; + } +} + + +static +void FastIdentity16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + cmsPipeline* Lut = (cmsPipeline*) D; + cmsUInt32Number i; + + for (i=0; i < Lut ->InputChannels; i++) { + Out[i] = In[i]; + } +} + + +// If the target LUT holds only curves, the optimization procedure is to join all those +// curves together. That only works on curves and does not work on matrices. +static +cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsToneCurve** GammaTables = NULL; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i, j; + cmsPipeline* Src = *Lut; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* ObtainedCurves = NULL; + + + // This is a lossy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only curves in this LUT? + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (Dest == NULL) return FALSE; + + // Create target curves + GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (GammaTables[i] == NULL) goto Error; + } + + // Compute 16 bit result by using floating point + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + for (j=0; j < Src ->InputChannels; j++) + InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + cmsPipelineEvalFloat(InFloat, OutFloat, Src); + + for (j=0; j < Src ->InputChannels; j++) + GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); + } + + ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); + if (ObtainedCurves == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + cmsFreeToneCurve(GammaTables[i]); + GammaTables[i] = NULL; + } + + if (GammaTables != NULL) { + _cmsFree(Src->ContextID, GammaTables); + GammaTables = NULL; + } + + // Maybe the curves are linear at the end + if (!AllCurvesAreLinear(ObtainedCurves)) { + _cmsStageToneCurvesData* Data; + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) + goto Error; + Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); + ObtainedCurves = NULL; + + // If the curves are to be applied in 8 bits, we can save memory + if (_cmsFormatterIs8bit(*InputFormat)) { + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); + + } + else { + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); + } + } + else { + + // LUT optimizes to nothing. Set the identity LUT + cmsStageFree(ObtainedCurves); + ObtainedCurves = NULL; + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) + goto Error; + + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); + } + + // We are done. + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; + +Error: + + if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); + if (GammaTables != NULL) { + for (i=0; i < Src ->InputChannels; i++) { + if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(Src ->ContextID, GammaTables); + } + + if (Dest != NULL) cmsPipelineFree(Dest); + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(InputFormat); + cmsUNUSED_PARAMETER(OutputFormat); + cmsUNUSED_PARAMETER(dwFlags); +} + +// ------------------------------------------------------------------------------------------------------------------------------------- +// LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles + + +static +void FreeMatShaper(cmsContext ContextID, void* Data) +{ + if (Data != NULL) _cmsFree(ContextID, Data); +} + +static +void* DupMatShaper(cmsContext ContextID, const void* Data) +{ + return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); +} + + +// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point +// to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, +// in total about 50K, and the performance boost is huge! +static +void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER const void* D) +{ + MatShaper8Data* p = (MatShaper8Data*) D; + cmsS1Fixed14Number l1, l2, l3, r, g, b; + cmsUInt32Number ri, gi, bi; + + // In this case (and only in this case!) we can use this simplification since + // In[] is assured to come from a 8 bit number. (a << 8 | a) + ri = In[0] & 0xFFU; + gi = In[1] & 0xFFU; + bi = In[2] & 0xFFU; + + // Across first shaper, which also converts to 1.14 fixed point + r = p->Shaper1R[ri]; + g = p->Shaper1G[gi]; + b = p->Shaper1B[bi]; + + // Evaluate the matrix in 1.14 fixed point + l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; + l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; + l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; + + // Now we have to clip to 0..1.0 range + ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1); + gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384U : (cmsUInt32Number) l2); + bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384U : (cmsUInt32Number) l3); + + // And across second shaper, + Out[0] = p->Shaper2R[ri]; + Out[1] = p->Shaper2G[gi]; + Out[2] = p->Shaper2B[bi]; + +} + +// This table converts from 8 bits to 1.14 after applying the curve +static +void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) +{ + int i; + cmsFloat32Number R, y; + + for (i=0; i < 256; i++) { + + R = (cmsFloat32Number) (i / 255.0); + y = cmsEvalToneCurveFloat(Curve, R); + + if (y < 131072.0) + Table[i] = DOUBLE_TO_1FIXED14(y); + else + Table[i] = 0x7fffffff; + } +} + +// This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve +static +void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) +{ + int i; + cmsFloat32Number R, Val; + + for (i=0; i < 16385; i++) { + + R = (cmsFloat32Number) (i / 16384.0); + Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 + + if (Val < 0) + Val = 0; + + if (Val > 1.0) + Val = 1.0; + + if (Is8BitsOutput) { + + // If 8 bits output, we can optimize further by computing the / 257 part. + // first we compute the resulting byte and then we store the byte times + // 257. This quantization allows to round very quick by doing a >> 8, but + // since the low byte is always equal to msb, we can do a & 0xff and this works! + cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); + cmsUInt8Number b = FROM_16_TO_8(w); + + Table[i] = FROM_8_TO_16(b); + } + else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); + } +} + +// Compute the matrix-shaper structure +static +cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) +{ + MatShaper8Data* p; + int i, j; + cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); + + // Allocate a big chuck of memory to store precomputed tables + p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); + if (p == NULL) return FALSE; + + p -> ContextID = Dest -> ContextID; + + // Precompute tables + FillFirstShaper(p ->Shaper1R, Curve1[0]); + FillFirstShaper(p ->Shaper1G, Curve1[1]); + FillFirstShaper(p ->Shaper1B, Curve1[2]); + + FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); + FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); + FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); + + // Convert matrix to nFixed14. Note that those values may take more than 16 bits + for (i=0; i < 3; i++) { + for (j=0; j < 3; j++) { + p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); + } + } + + for (i=0; i < 3; i++) { + + if (Off == NULL) { + p ->Off[i] = 0; + } + else { + p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); + } + } + + // Mark as optimized for faster formatter + if (Is8Bits) + *OutputFormat |= OPTIMIZED_SH(1); + + // Fill function pointers + _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); + return TRUE; +} + +// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! +static +cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsStage* Curve1, *Curve2; + cmsStage* Matrix1, *Matrix2; + cmsMAT3 res; + cmsBool IdentityMat; + cmsPipeline* Dest, *Src; + cmsFloat64Number* Offset; + + // Only works on RGB to RGB + if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; + + // Only works on 8 bit input + if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; + + // Seems suitable, proceed + Src = *Lut; + + // Check for: + // + // shaper-matrix-matrix-shaper + // shaper-matrix-shaper + // + // Both of those constructs are possible (first because abs. colorimetric). + // additionally, In the first case, the input matrix offset should be zero. + + IdentityMat = FALSE; + if (cmsPipelineCheckAndRetreiveStages(Src, 4, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Matrix2, &Curve2)) { + + // Get both matrices + _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1); + _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2); + + // Input offset should be zero + if (Data1->Offset != NULL) return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*)Data2->Double, (cmsMAT3*)Data1->Double); + + // Only 2nd matrix has offset, or it is zero + Offset = Data2->Offset; + + // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + + } + else { + + if (cmsPipelineCheckAndRetreiveStages(Src, 3, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Curve2)) { + + _cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1); + + // Copy the matrix to our result + memcpy(&res, Data->Double, sizeof(res)); + + // Preserve the Odffset (may be NULL as a zero offset) + Offset = Data->Offset; + + if (_cmsMAT3isIdentity(&res) && Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + } + else + return FALSE; // Not optimizeable this time + + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Assamble the new LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) + goto Error; + + if (!IdentityMat) { + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest->ContextID, 3, 3, (const cmsFloat64Number*)&res, Offset))) + goto Error; + } + + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) + goto Error; + + // If identity on matrix, we can further optimize the curves, so call the join curves routine + if (IdentityMat) { + + OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); + } + else { + _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); + _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); + + // In this particular optimization, cache does not help as it takes more time to deal with + // the cache that with the pixel handling + *dwFlags |= cmsFLAGS_NOCACHE; + + // Setup the optimizarion routines + SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat); + } + + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; +Error: + // Leave Src unchanged + cmsPipelineFree(Dest); + return FALSE; +} + + +// ------------------------------------------------------------------------------------------------------------------------------------- +// Optimization plug-ins + +// List of optimizations +typedef struct _cmsOptimizationCollection_st { + + _cmsOPToptimizeFn OptimizePtr; + + struct _cmsOptimizationCollection_st *Next; + +} _cmsOptimizationCollection; + + +// The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling +static _cmsOptimizationCollection DefaultOptimization[] = { + + { OptimizeByJoiningCurves, &DefaultOptimization[1] }, + { OptimizeMatrixShaper, &DefaultOptimization[2] }, + { OptimizeByComputingLinearization, &DefaultOptimization[3] }, + { OptimizeByResampling, NULL } +}; + +// The linked list head +_cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginOptimizationList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsOptimizationPluginChunkType newHead = { NULL }; + _cmsOptimizationCollection* entry; + _cmsOptimizationCollection* Anterior = NULL; + _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin]; + + _cmsAssert(ctx != NULL); + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->OptimizationCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.OptimizationCollection == NULL) + newHead.OptimizationCollection = newEntry; + } + + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType)); +} + +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginOptimizationList(ctx, src); + } + else { + static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL }; + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType)); + } +} + + +// Register new ways to optimize +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); + _cmsOptimizationCollection* fl; + + if (Data == NULL) { + + ctx->OptimizationCollection = NULL; + return TRUE; + } + + // Optimizer callback is required + if (Plugin ->OptimizePtr == NULL) return FALSE; + + fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->OptimizePtr = Plugin ->OptimizePtr; + + // Keep linked list + fl ->Next = ctx->OptimizationCollection; + + // Set the head + ctx ->OptimizationCollection = fl; + + // All is ok + return TRUE; +} + +// The entry point for LUT optimization +cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** PtrLut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags) +{ + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); + _cmsOptimizationCollection* Opts; + cmsBool AnySuccess = FALSE; + + // A CLUT is being asked, so force this specific optimization + if (*dwFlags & cmsFLAGS_FORCE_CLUT) { + + PreOptimize(*PtrLut); + return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags); + } + + // Anything to optimize? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Try to get rid of identities and trivial conversions. + AnySuccess = PreOptimize(*PtrLut); + + // After removal do we end with an identity? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Do not optimize, keep all precision + if (*dwFlags & cmsFLAGS_NOOPTIMIZE) + return FALSE; + + // Try plug-in optimizations + for (Opts = ctx->OptimizationCollection; + Opts != NULL; + Opts = Opts ->Next) { + + // If one schema succeeded, we are done + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; // Optimized! + } + } + + // Try built-in optimizations + for (Opts = DefaultOptimization; + Opts != NULL; + Opts = Opts ->Next) { + + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; + } + } + + // Only simple optimizations succeeded + return AnySuccess; +} + + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmspack.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmspack.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmspack.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmspack.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,3462 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// This module handles all formats supported by lcms. There are two flavors, 16 bits and +// floating point. Floating point is supported only in a subset, those formats holding +// cmsFloat32Number (4 bytes per component) and double (marked as 0 bytes per component +// as special case) + +// --------------------------------------------------------------------------- + + +// This macro return words stored as big endian +#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) + +// These macros handles reversing (negative) +#define REVERSE_FLAVOR_8(x) ((cmsUInt8Number) (0xff-(x))) +#define REVERSE_FLAVOR_16(x) ((cmsUInt16Number)(0xffff-(x))) + +// * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256 +cmsINLINE cmsUInt16Number FomLabV2ToLabV4(cmsUInt16Number x) +{ + int a = (x << 8 | x) >> 8; // * 257 / 256 + if ( a > 0xffff) return 0xffff; + return (cmsUInt16Number) a; +} + +// * 0xf00 / 0xffff = * 256 / 257 +cmsINLINE cmsUInt16Number FomLabV4ToLabV2(cmsUInt16Number x) +{ + return (cmsUInt16Number) (((x << 8) + 0x80) / 257); +} + + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatter16 Frm; + +} cmsFormatters16; + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatterFloat Frm; + +} cmsFormattersFloat; + + +#define ANYSPACE COLORSPACE_SH(31) +#define ANYCHANNELS CHANNELS_SH(15) +#define ANYEXTRA EXTRA_SH(7) +#define ANYPLANAR PLANAR_SH(1) +#define ANYENDIAN ENDIAN16_SH(1) +#define ANYSWAP DOSWAP_SH(1) +#define ANYSWAPFIRST SWAPFIRST_SH(1) +#define ANYFLAVOR FLAVOR_SH(1) + + +// Suppress waning about info never being used + +#ifdef _MSC_VER +#pragma warning(disable : 4100) +#endif + +// Unpacking routines (16 bits) ---------------------------------------------------------------------------------------- + + +// Does almost everything but is slow +static +cmsUInt8Number* UnrollChunkyBytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number v; + cmsUInt32Number i; + + if (ExtraFirst) { + accum += Extra; + } + + for (i=0; i < nChan; i++) { + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_8_TO_16(*accum); + v = Reverse ? REVERSE_FLAVOR_16(v) : v; + wIn[index] = v; + accum++; + } + + if (!ExtraFirst) { + accum += Extra; + } + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); + +} + +// Extra channels are just ignored because come in the next planes +static +cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = accum; + + if (DoSwap ^ SwapFirst) { + accum += T_EXTRA(info -> InputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = FROM_8_TO_16(*accum); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + accum += Stride; + } + + return (Init + 1); +} + +// Special cases, provided for performance +static +cmsUInt8Number* Unroll4Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesReverse(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C + wIn[1] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M + wIn[2] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y + wIn[3] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4BytesSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // K + wIn[1] = FROM_8_TO_16(*accum); accum++; // Y + wIn[0] = FROM_8_TO_16(*accum); accum++; // M + wIn[3] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1Swap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + accum++; // A + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + accum++; // A + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// BRG +static +cmsUInt8Number* Unroll3BytesSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_8(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollALabV2_8(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // L + wIn[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // a + wIn[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// for duplex +static +cmsUInt8Number* Unroll2Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // ch1 + wIn[1] = FROM_8_TO_16(*accum); accum++; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + + + +// Monochrome duplicates L into RGB for null-transforms +static +cmsUInt8Number* Unroll1Byte(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Unroll1ByteSkip1(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 1; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteSkip2(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteReversed(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(FROM_8_TO_16(*accum)); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* UnrollAnyWords(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number i; + + if (ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollPlanarWords(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = accum; + + if (DoSwap) { + accum += T_EXTRA(info -> InputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += Stride; + } + + return (Init + sizeof(cmsUInt16Number)); +} + + +static +cmsUInt8Number* Unroll4Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsReverse(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // C + wIn[1] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // M + wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // Y + wIn[3] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4WordsSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1Swap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1Word(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordReversed(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordSkip3(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; + + accum += 8; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll2Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // ch1 + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// This is a conversion of Lab double to 16 bits +static +cmsUInt8Number* UnrollLabDoubleTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIELab Lab; + cmsUInt8Number* pos_L; + cmsUInt8Number* pos_a; + cmsUInt8Number* pos_b; + + pos_L = accum; + pos_a = accum + Stride; + pos_b = accum + Stride * 2; + + Lab.L = *(cmsFloat64Number*) pos_L; + Lab.a = *(cmsFloat64Number*) pos_a; + Lab.b = *(cmsFloat64Number*) pos_b; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat64Number); + } + else { + + cmsFloat2LabEncoded(wIn, (cmsCIELab*) accum); + accum += sizeof(cmsCIELab) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + return accum; + } +} + + +// This is a conversion of Lab float to 16 bits +static +cmsUInt8Number* UnrollLabFloatTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsCIELab Lab; + + if (T_PLANAR(info -> InputFormat)) { + + cmsUInt8Number* pos_L; + cmsUInt8Number* pos_a; + cmsUInt8Number* pos_b; + + pos_L = accum; + pos_a = accum + Stride; + pos_b = accum + Stride * 2; + + Lab.L = *(cmsFloat32Number*)pos_L; + Lab.a = *(cmsFloat32Number*)pos_a; + Lab.b = *(cmsFloat32Number*)pos_b; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat32Number); + } + else { + + Lab.L = ((cmsFloat32Number*) accum)[0]; + Lab.a = ((cmsFloat32Number*) accum)[1]; + Lab.b = ((cmsFloat32Number*) accum)[2]; + + cmsFloat2LabEncoded(wIn, &Lab); + accum += (3 + T_EXTRA(info ->InputFormat)) * sizeof(cmsFloat32Number); + return accum; + } +} + +// This is a conversion of XYZ double to 16 bits +static +cmsUInt8Number* UnrollXYZDoubleTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIEXYZ XYZ; + cmsUInt8Number* pos_X; + cmsUInt8Number* pos_Y; + cmsUInt8Number* pos_Z; + + pos_X = accum; + pos_Y = accum + Stride; + pos_Z = accum + Stride * 2; + + XYZ.X = *(cmsFloat64Number*)pos_X; + XYZ.Y = *(cmsFloat64Number*)pos_Y; + XYZ.Z = *(cmsFloat64Number*)pos_Z; + + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat64Number); + + } + + else { + cmsFloat2XYZEncoded(wIn, (cmsCIEXYZ*) accum); + accum += sizeof(cmsCIEXYZ) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + + return accum; + } +} + +// This is a conversion of XYZ float to 16 bits +static +cmsUInt8Number* UnrollXYZFloatTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsCIEXYZ XYZ; + cmsUInt8Number* pos_X; + cmsUInt8Number* pos_Y; + cmsUInt8Number* pos_Z; + + pos_X = accum; + pos_Y = accum + Stride; + pos_Z = accum + Stride * 2; + + XYZ.X = *(cmsFloat32Number*)pos_X; + XYZ.Y = *(cmsFloat32Number*)pos_Y; + XYZ.Z = *(cmsFloat32Number*)pos_Z; + + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat32Number); + + } + + else { + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + cmsCIEXYZ XYZ; + + XYZ.X = Pt[0]; + XYZ.Y = Pt[1]; + XYZ.Z = Pt[2]; + cmsFloat2XYZEncoded(wIn, &XYZ); + + accum += 3 * sizeof(cmsFloat32Number) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat32Number); + + return accum; + } +} + +// Check if space is marked as ink +cmsINLINE cmsBool IsInkSpace(cmsUInt32Number Type) +{ + switch (T_COLORSPACE(Type)) { + + case PT_CMY: + case PT_CMYK: + case PT_MCH5: + case PT_MCH6: + case PT_MCH7: + case PT_MCH8: + case PT_MCH9: + case PT_MCH10: + case PT_MCH11: + case PT_MCH12: + case PT_MCH13: + case PT_MCH14: + case PT_MCH15: return TRUE; + + default: return FALSE; + } +} + +// Return the size in bytes of a given formatter +static +cmsUInt32Number PixelSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field is zero + if (fmt_bytes == 0) + return sizeof(cmsUInt64Number); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + +// Inks does come in percentage, remaining cases are between 0..1.0, again to 16 bits +static +cmsUInt8Number* UnrollDoubleTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + cmsUInt16Number vi; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); +} + + + +static +cmsUInt8Number* UnrollFloatTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt16Number vi; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); +} + + + + +// For 1 channel, we need to duplicate data (it comes in 0..1.0 range) +static +cmsUInt8Number* UnrollDouble1Chan(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsFloat64Number* Inks = (cmsFloat64Number*) accum; + + wIn[0] = wIn[1] = wIn[2] = _cmsQuickSaturateWord(Inks[0] * 65535.0); + + return accum + sizeof(cmsFloat64Number); + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +//------------------------------------------------------------------------------------------------------------------- + +// For anything going from cmsFloat32Number +static +cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); +} + +// For anything going from double + +static +cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + cmsUInt32Number i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v); + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); +} + + + +// From Lab double to cmsFloat32Number +static +cmsUInt8Number* UnrollLabDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + +// From Lab double to cmsFloat32Number +static +cmsUInt8Number* UnrollLabFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + + + +// 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF) +static +cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + +static +cmsUInt8Number* UnrollXYZFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + Stride /= PixelSize(info->InputFormat); + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + + + +// Packing routines ----------------------------------------------------------------------------------------------------------- + + +// Generic chunky for byte + +static +cmsUInt8Number* PackAnyBytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt8Number* swap1; + cmsUInt8Number v = 0; + cmsUInt32Number i; + + swap1 = output; + + if (ExtraFirst) { + output += Extra; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_16_TO_8(wOut[index]); + + if (Reverse) + v = REVERSE_FLAVOR_8(v); + + *output++ = v; + } + + if (!ExtraFirst) { + output += Extra; + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, nChan-1); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); +} + + + +static +cmsUInt8Number* PackAnyWords(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number* swap1; + cmsUInt16Number v = 0; + cmsUInt32Number i; + + swap1 = (cmsUInt16Number*) output; + + if (ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + + output += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = output; + + + if (DoSwap ^ SwapFirst) { + output += T_EXTRA(info -> OutputFormat) * Stride; + } + + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt8Number v = FROM_16_TO_8(wOut[index]); + + *(cmsUInt8Number*) output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v); + output += Stride; + } + + return (Init + 1); + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarWords(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat); + cmsUInt32Number i; + cmsUInt8Number* Init = output; + cmsUInt16Number v; + + if (DoSwap) { + output += T_EXTRA(info -> OutputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + output += Stride; + } + + return (Init + sizeof(cmsUInt16Number)); +} + +// CMYKcm (unrolled for speed) + +static +cmsUInt8Number* Pack6Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[5]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm + +static +cmsUInt8Number* Pack6BytesSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[5]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYKcm +static +cmsUInt8Number* Pack6Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm +static +cmsUInt8Number* Pack6WordsSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesReverse(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[0])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[1])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[2])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[3])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4BytesSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4BytesSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesSwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4WordsReverse(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4WordsSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYK +static +cmsUInt8Number* Pack4WordsBigEndian(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackLabV2_8(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackALabV2_8(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackLabV2_16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[0]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[1]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[2]); + output += 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3Bytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesOptimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwapOptimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3Words(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsSwap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsBigEndian(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Optimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[0] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[2] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Swap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFFU); + *output++ = (wOut[1] & 0xFFU); + *output++ = (wOut[0] & 0xFFU); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1Swap(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + + +static +cmsUInt8Number* Pack1Byte(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteReversed(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(REVERSE_FLAVOR_16(wOut[0])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteSkip1(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1Word(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1WordReversed(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordBigEndian(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1WordSkip1(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 4; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordSkip1SwapFirst(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + output += 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// Unencoded Float values -- don't try optimize speed +static +cmsUInt8Number* PackLabDoubleFrom16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + + if (T_PLANAR(info -> OutputFormat)) { + + cmsCIELab Lab; + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsLabEncoded2Float(&Lab, wOut); + + Out[0] = Lab.L; + Out[Stride] = Lab.a; + Out[Stride*2] = Lab.b; + + return output + sizeof(cmsFloat64Number); + } + else { + + cmsLabEncoded2Float((cmsCIELab*) output, wOut); + return output + (sizeof(cmsCIELab) + T_EXTRA(info ->OutputFormat) * sizeof(cmsFloat64Number)); + } +} + + +static +cmsUInt8Number* PackLabFloatFrom16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsCIELab Lab; + cmsLabEncoded2Float(&Lab, wOut); + + if (T_PLANAR(info -> OutputFormat)) { + + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + Stride /= PixelSize(info->OutputFormat); + + Out[0] = (cmsFloat32Number)Lab.L; + Out[Stride] = (cmsFloat32Number)Lab.a; + Out[Stride*2] = (cmsFloat32Number)Lab.b; + + return output + sizeof(cmsFloat32Number); + } + else { + + ((cmsFloat32Number*) output)[0] = (cmsFloat32Number) Lab.L; + ((cmsFloat32Number*) output)[1] = (cmsFloat32Number) Lab.a; + ((cmsFloat32Number*) output)[2] = (cmsFloat32Number) Lab.b; + + return output + (3 + T_EXTRA(info ->OutputFormat)) * sizeof(cmsFloat32Number); + } +} + +static +cmsUInt8Number* PackXYZDoubleFrom16(CMSREGISTER _cmsTRANSFORM* Info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + if (T_PLANAR(Info -> OutputFormat)) { + + cmsCIEXYZ XYZ; + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = XYZ.X; + Out[Stride] = XYZ.Y; + Out[Stride*2] = XYZ.Z; + + return output + sizeof(cmsFloat64Number); + + } + else { + + cmsXYZEncoded2Float((cmsCIEXYZ*) output, wOut); + + return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } +} + +static +cmsUInt8Number* PackXYZFloatFrom16(CMSREGISTER _cmsTRANSFORM* Info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + if (T_PLANAR(Info -> OutputFormat)) { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) XYZ.X; + Out[Stride] = (cmsFloat32Number) XYZ.Y; + Out[Stride*2] = (cmsFloat32Number) XYZ.Z; + + return output + sizeof(cmsFloat32Number); + + } + else { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Out[0] = (cmsFloat32Number) XYZ.X; + Out[1] = (cmsFloat32Number) XYZ.Y; + Out[2] = (cmsFloat32Number) XYZ.Z; + + return output + (3 * sizeof(cmsFloat32Number) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } +} + +static +cmsUInt8Number* PackDoubleFrom16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info -> OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*) output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number) wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*) output)[(i + start) * Stride]= v; + else + ((cmsFloat64Number*) output)[i + start] = v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat64Number); + +} + + +static +cmsUInt8Number* PackFloatFrom16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat32Number* swap1 = (cmsFloat32Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number)wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*)output)[(i + start) * Stride] = (cmsFloat32Number)v; + else + ((cmsFloat32Number*)output)[i + start] = (cmsFloat32Number)v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number)v; + } + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat32Number); +} + + + +// -------------------------------------------------------------------------------------------------------- + +static +cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0; + cmsFloat32Number* swap1 = (cmsFloat32Number*)output; + cmsFloat64Number v = 0; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*)output)[(i + start)* Stride] = (cmsFloat32Number)v; + else + ((cmsFloat32Number*)output)[i + start] = (cmsFloat32Number)v; + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number)v; + } + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat32Number); +} + +static +cmsUInt8Number* PackDoublesFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info->OutputFormat) ? 100.0 : 1.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*)output)[(i + start) * Stride] = v; + else + ((cmsFloat64Number*)output)[i + start] = v; + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + (nChan + Extra) * sizeof(cmsFloat64Number); + +} + + + + + +static +cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat32Number); + } + else { + + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } + +} + + +static +cmsUInt8Number* PackLabDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } + +} + + +// From 0..1 range to 0..MAX_ENCODEABLE_XYZ +static +cmsUInt8Number* PackXYZFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat32Number); + } + else { + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } + +} + +// Same, but convert to double +static +cmsUInt8Number* PackXYZDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Stride /= PixelSize(Info->OutputFormat); + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } + +} + + +// ---------------------------------------------------------------------------------------------------------------- + +#ifndef CMS_NO_HALF_SUPPORT + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfTo16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F; + + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + if (Reverse) v = maximum - v; + + wIn[index] = _cmsQuickSaturateWord((cmsFloat64Number) v * maximum); + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + + +static +cmsUInt8Number* PackHalfFrom16(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 655.35F : 65535.0F; + cmsFloat32Number v = 0; + cmsUInt16Number* swap1 = (cmsUInt16Number*)output; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat32Number)wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*)output)[(i + start) * Stride] = _cmsFloat2Half(v); + else + ((cmsUInt16Number*)output)[i + start] = _cmsFloat2Half(v); + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsUInt16Number)); + *swap1 = _cmsFloat2Half(v); + } + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + (nChan + Extra) * sizeof(cmsUInt16Number); +} + + + +static +cmsUInt8Number* PackHalfFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Planar = T_PLANAR(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info->OutputFormat) ? 100.0F : 1.0F; + cmsUInt16Number* swap1 = (cmsUInt16Number*)output; + cmsFloat32Number v = 0; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->OutputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*)output)[(i + start)* Stride] = _cmsFloat2Half(v); + else + ((cmsUInt16Number*)output)[i + start] = _cmsFloat2Half(v); + } + + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan - 1)* sizeof(cmsUInt16Number)); + *swap1 = (cmsUInt16Number)_cmsFloat2Half(v); + } + + if (T_PLANAR(info->OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + (nChan + Extra)* sizeof(cmsUInt16Number); +} + +#endif + +// ---------------------------------------------------------------------------------------------------------------- + + +static const cmsFormatters16 InputFormatters16[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleTo16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleTo16}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatTo16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatTo16}, + { TYPE_GRAY_DBL, 0, UnrollDouble1Chan}, + { FLOAT_SH(1)|BYTES_SH(0), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollDoubleTo16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollFloatTo16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYEXTRA|ANYSWAP|ANYSPACE, UnrollHalfTo16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Unroll1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Unroll1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(2), ANYSPACE, Unroll1ByteSkip2}, + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll1ByteReversed}, + { COLORSPACE_SH(PT_MCH2)|CHANNELS_SH(2)|BYTES_SH(1), 0, Unroll2Bytes}, + + { TYPE_LabV2_8, 0, UnrollLabV2_8 }, + { TYPE_ALabV2_8, 0, UnrollALabV2_8 }, + { TYPE_LabV2_16, 0, UnrollLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Unroll3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSwap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSkip1Swap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3BytesSkip1SwapFirst}, + + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Unroll3BytesSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Unroll4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapSwapFirst}, + + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST| + ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Unroll1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3), ANYSPACE, Unroll1WordSkip3}, + + { CHANNELS_SH(2)|BYTES_SH(2), ANYSPACE, Unroll2Words}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Unroll3Words}, + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Unroll4Words}, + + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3WordsSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSkip1Swap}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapSwapFirst}, + + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollAnyWords}, +}; + + + +static const cmsFormattersFloat InputFormattersFloat[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleToFloat}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatToFloat}, + + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleToFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, +#endif +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockInputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsFormatter fr; + + switch (dwFlags) { + + case CMS_PACK_FLAGS_16BITS: { + for (i=0; i < sizeof(InputFormatters16) / sizeof(cmsFormatters16); i++) { + const cmsFormatters16* f = InputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + for (i=0; i < sizeof(InputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + const cmsFormattersFloat* f = InputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; +} + +static const cmsFormatters16 OutputFormatters16[] = { + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFrom16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFrom16}, + + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFrom16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFrom16}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackDoubleFrom16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackFloatFrom16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackHalfFrom16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Pack1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1ByteSkip1SwapFirst}, + + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack1ByteReversed}, + + { TYPE_LabV2_8, 0, PackLabV2_8 }, + { TYPE_ALabV2_8, 0, PackALabV2_8 }, + { TYPE_LabV2_16, 0, PackLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesAndSkip1Optimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesSwapOptimized}, + + + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Pack3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3BytesAndSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3BytesSwap}, + { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, + { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Pack4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapSwapFirst}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes}, + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Pack1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack1WordSkip1}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1WordSkip1SwapFirst}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack1WordBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Pack3Words}, + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack3WordsBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack3WordsAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3WordsAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3WordsAndSkip1SwapFirst}, + + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3WordsAndSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Pack4Words}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack4WordsBigEndian}, + + { CHANNELS_SH(6)|BYTES_SH(2), ANYSPACE, Pack6Words}, + { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack6WordsSwap}, + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords} + +}; + + +static const cmsFormattersFloat OutputFormattersFloat[] = { + // Type Mask Function + // ---------------------------- --------------------------------------------------- ---------------------------- + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFromFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFromFloat}, + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFromFloat}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFromFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackFloatsFromFloat }, + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackDoublesFromFloat }, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackHalfFromFloat }, +#endif + +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockOutputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsFormatter fr; + + // Optimization is only a hint + dwInput &= ~OPTIMIZED_SH(1); + + switch (dwFlags) + { + + case CMS_PACK_FLAGS_16BITS: { + + for (i=0; i < sizeof(OutputFormatters16) / sizeof(cmsFormatters16); i++) { + const cmsFormatters16* f = OutputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + + for (i=0; i < sizeof(OutputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + const cmsFormattersFloat* f = OutputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; +} + + +typedef struct _cms_formatters_factory_list { + + cmsFormatterFactory Factory; + struct _cms_formatters_factory_list *Next; + +} cmsFormattersFactoryList; + +_cmsFormattersPluginChunkType _cmsFormattersPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupFormatterFactoryList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsFormattersPluginChunkType newHead = { NULL }; + cmsFormattersFactoryList* entry; + cmsFormattersFactoryList* Anterior = NULL; + _cmsFormattersPluginChunkType* head = (_cmsFormattersPluginChunkType*) src->chunks[FormattersPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->FactoryList; + entry != NULL; + entry = entry ->Next) { + + cmsFormattersFactoryList *newEntry = ( cmsFormattersFactoryList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsFormattersFactoryList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.FactoryList == NULL) + newHead.FactoryList = newEntry; + } + + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsFormattersPluginChunkType)); +} + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate the LIST + DupFormatterFactoryList(ctx, src); + } + else { + static _cmsFormattersPluginChunkType FormattersPluginChunk = { NULL }; + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx ->MemPool, &FormattersPluginChunk, sizeof(_cmsFormattersPluginChunkType)); + } +} + + + +// Formatters management +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); + cmsPluginFormatters* Plugin = (cmsPluginFormatters*) Data; + cmsFormattersFactoryList* fl ; + + // Reset to built-in defaults + if (Data == NULL) { + + ctx ->FactoryList = NULL; + return TRUE; + } + + fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(ContextID, sizeof(cmsFormattersFactoryList)); + if (fl == NULL) return FALSE; + + fl ->Factory = Plugin ->FormattersFactory; + + fl ->Next = ctx -> FactoryList; + ctx ->FactoryList = fl; + + return TRUE; +} + +cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags) +{ + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); + cmsFormattersFactoryList* f; + + for (f =ctx->FactoryList; f != NULL; f = f ->Next) { + + cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); + if (fn.Fmt16 != NULL) return fn; + } + + // Revert to default + if (Dir == cmsFormatterInput) + return _cmsGetStockInputFormatter(Type, dwFlags); + else + return _cmsGetStockOutputFormatter(Type, dwFlags); +} + + +// Return whatever given formatter refers to float values +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type) +{ + return T_FLOAT(Type) ? TRUE : FALSE; +} + +// Return whatever given formatter refers to 8 bits +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type) +{ + cmsUInt32Number Bytes = T_BYTES(Type); + + return (Bytes == 1); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetColorSpace(hProfile); + cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1U : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + + cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1U : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmspcs.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmspcs.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmspcs.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmspcs.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,969 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// inter PCS conversions XYZ <-> CIE L* a* b* +/* + + + CIE 15:2004 CIELab is defined as: + + L* = 116*f(Y/Yn) - 16 0 <= L* <= 100 + a* = 500*[f(X/Xn) - f(Y/Yn)] + b* = 200*[f(Y/Yn) - f(Z/Zn)] + + and + + f(t) = t^(1/3) 1 >= t > (24/116)^3 + (841/108)*t + (16/116) 0 <= t <= (24/116)^3 + + + Reverse transform is: + + X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116) + = Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116) + + + + PCS in Lab2 is encoded as: + + 8 bit Lab PCS: + + L* 0..100 into a 0..ff byte. + a* t + 128 range is -128.0 +127.0 + b* + + 16 bit Lab PCS: + + L* 0..100 into a 0..ff00 word. + a* t + 128 range is -128.0 +127.9961 + b* + + + +Interchange Space Component Actual Range Encoded Range +CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff +CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff +CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff + +Version 2,3 +----------- + +CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00 +CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff +CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff + + +Version 4 +--------- + +CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff +CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff +CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff + +*/ + +// Conversions +void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source) +{ + cmsFloat64Number ISum; + + ISum = 1./(Source -> X + Source -> Y + Source -> Z); + + Dest -> x = (Source -> X) * ISum; + Dest -> y = (Source -> Y) * ISum; + Dest -> Y = Source -> Y; +} + +void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source) +{ + Dest -> X = (Source -> x / Source -> y) * Source -> Y; + Dest -> Y = Source -> Y; + Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y; +} + +/* + The break point (24/116)^3 = (6/29)^3 is a very small amount of tristimulus + primary (0.008856). Generally, this only happens for + nearly ideal blacks and for some orange / amber colors in transmission mode. + For example, the Z value of the orange turn indicator lamp lens on an + automobile will often be below this value. But the Z does not + contribute to the perceived color directly. +*/ + +static +cmsFloat64Number f(cmsFloat64Number t) +{ + const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0); + + if (t <= Limit) + return (841.0/108.0) * t + (16.0/116.0); + else + return pow(t, 1.0/3.0); +} + +static +cmsFloat64Number f_1(cmsFloat64Number t) +{ + const cmsFloat64Number Limit = (24.0/116.0); + + if (t <= Limit) { + return (108.0/841.0) * (t - (16.0/116.0)); + } + + return t * t * t; +} + + +// Standard XYZ to Lab. it can handle negative XZY numbers in some cases +void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz) +{ + cmsFloat64Number fx, fy, fz; + + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + fx = f(xyz->X / WhitePoint->X); + fy = f(xyz->Y / WhitePoint->Y); + fz = f(xyz->Z / WhitePoint->Z); + + Lab->L = 116.0*fy - 16.0; + Lab->a = 500.0*(fx - fy); + Lab->b = 200.0*(fy - fz); +} + + +// Standard XYZ to Lab. It can return negative XYZ in some cases +void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab) +{ + cmsFloat64Number x, y, z; + + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + y = (Lab-> L + 16.0) / 116.0; + x = y + 0.002 * Lab -> a; + z = y - 0.005 * Lab -> b; + + xyz -> X = f_1(x) * WhitePoint -> X; + xyz -> Y = f_1(y) * WhitePoint -> Y; + xyz -> Z = f_1(z) * WhitePoint -> Z; + +} + +static +cmsFloat64Number L2float2(cmsUInt16Number v) +{ + return (cmsFloat64Number) v / 652.800; +} + +// the a/b part +static +cmsFloat64Number ab2float2(cmsUInt16Number v) +{ + return ((cmsFloat64Number) v / 256.0) - 128.0; +} + +static +cmsUInt16Number L2Fix2(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 652.8); +} + +static +cmsUInt16Number ab2Fix2(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 256.0); +} + + +static +cmsFloat64Number L2float4(cmsUInt16Number v) +{ + return (cmsFloat64Number) v / 655.35; +} + +// the a/b part +static +cmsFloat64Number ab2float4(cmsUInt16Number v) +{ + return ((cmsFloat64Number) v / 257.0) - 128.0; +} + + +void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) +{ + Lab->L = L2float2(wLab[0]); + Lab->a = ab2float2(wLab[1]); + Lab->b = ab2float2(wLab[2]); +} + + +void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) +{ + Lab->L = L2float4(wLab[0]); + Lab->a = ab2float4(wLab[1]); + Lab->b = ab2float4(wLab[2]); +} + +static +cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L) +{ + const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00; + + if (L < 0) L = 0; + if (L > L_max) L = L_max; + + return L; +} + + +static +cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab) +{ + if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2; + if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2; + + return ab; +} + +void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* fLab) +{ + cmsCIELab Lab; + + Lab.L = Clamp_L_doubleV2(fLab ->L); + Lab.a = Clamp_ab_doubleV2(fLab ->a); + Lab.b = Clamp_ab_doubleV2(fLab ->b); + + wLab[0] = L2Fix2(Lab.L); + wLab[1] = ab2Fix2(Lab.a); + wLab[2] = ab2Fix2(Lab.b); +} + + +static +cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L) +{ + if (L < 0) L = 0; + if (L > 100.0) L = 100.0; + + return L; +} + +static +cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab) +{ + if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4; + if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4; + + return ab; +} + +static +cmsUInt16Number L2Fix4(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 655.35); +} + +static +cmsUInt16Number ab2Fix4(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 257.0); +} + +void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* fLab) +{ + cmsCIELab Lab; + + Lab.L = Clamp_L_doubleV4(fLab ->L); + Lab.a = Clamp_ab_doubleV4(fLab ->a); + Lab.b = Clamp_ab_doubleV4(fLab ->b); + + wLab[0] = L2Fix4(Lab.L); + wLab[1] = ab2Fix4(Lab.a); + wLab[2] = ab2Fix4(Lab.b); +} + +// Auxiliary: convert to Radians +static +cmsFloat64Number RADIANS(cmsFloat64Number deg) +{ + return (deg * M_PI) / 180.; +} + + +// Auxiliary: atan2 but operating in degrees and returning 0 if a==b==0 +static +cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b) +{ + cmsFloat64Number h; + + if (a == 0 && b == 0) + h = 0; + else + h = atan2(a, b); + + h *= (180. / M_PI); + + while (h > 360.) + h -= 360.; + + while ( h < 0) + h += 360.; + + return h; +} + + +// Auxiliary: Square +static +cmsFloat64Number Sqr(cmsFloat64Number v) +{ + return v * v; +} +// From cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLab2LCh(cmsCIELCh* LCh, const cmsCIELab* Lab) +{ + LCh -> L = Lab -> L; + LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5); + LCh -> h = atan2deg(Lab ->b, Lab ->a); +} + + +// To cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh) +{ + cmsFloat64Number h = (LCh -> h * M_PI) / 180.0; + + Lab -> L = LCh -> L; + Lab -> a = LCh -> C * cos(h); + Lab -> b = LCh -> C * sin(h); +} + +// In XYZ All 3 components are encoded using 1.15 fixed point +static +cmsUInt16Number XYZ2Fix(cmsFloat64Number d) +{ + return _cmsQuickSaturateWord(d * 32768.0); +} + +void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ) +{ + cmsCIEXYZ xyz; + + xyz.X = fXYZ -> X; + xyz.Y = fXYZ -> Y; + xyz.Z = fXYZ -> Z; + + // Clamp to encodeable values. + if (xyz.Y <= 0) { + + xyz.X = 0; + xyz.Y = 0; + xyz.Z = 0; + } + + if (xyz.X > MAX_ENCODEABLE_XYZ) + xyz.X = MAX_ENCODEABLE_XYZ; + + if (xyz.X < 0) + xyz.X = 0; + + if (xyz.Y > MAX_ENCODEABLE_XYZ) + xyz.Y = MAX_ENCODEABLE_XYZ; + + if (xyz.Y < 0) + xyz.Y = 0; + + if (xyz.Z > MAX_ENCODEABLE_XYZ) + xyz.Z = MAX_ENCODEABLE_XYZ; + + if (xyz.Z < 0) + xyz.Z = 0; + + + XYZ[0] = XYZ2Fix(xyz.X); + XYZ[1] = XYZ2Fix(xyz.Y); + XYZ[2] = XYZ2Fix(xyz.Z); +} + + +// To convert from Fixed 1.15 point to cmsFloat64Number +static +cmsFloat64Number XYZ2float(cmsUInt16Number v) +{ + cmsS15Fixed16Number fix32; + + // From 1.15 to 15.16 + fix32 = v << 1; + + // From fixed 15.16 to cmsFloat64Number + return _cms15Fixed16toDouble(fix32); +} + + +void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3]) +{ + fXYZ -> X = XYZ2float(XYZ[0]); + fXYZ -> Y = XYZ2float(XYZ[1]); + fXYZ -> Z = XYZ2float(XYZ[2]); +} + + +// Returns dE on two Lab values +cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number dL, da, db; + + dL = fabs(Lab1 -> L - Lab2 -> L); + da = fabs(Lab1 -> a - Lab2 -> a); + db = fabs(Lab1 -> b - Lab2 -> b); + + return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5); +} + + +// Return the CIE94 Delta E +cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsCIELCh LCh1, LCh2; + cmsFloat64Number dE, dL, dC, dh, dhsq; + cmsFloat64Number c12, sc, sh; + + dL = fabs(Lab1 ->L - Lab2 ->L); + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + dC = fabs(LCh1.C - LCh2.C); + dE = cmsDeltaE(Lab1, Lab2); + + dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC); + if (dhsq < 0) + dh = 0; + else + dh = pow(dhsq, 0.5); + + c12 = sqrt(LCh1.C * LCh2.C); + + sc = 1.0 + (0.048 * c12); + sh = 1.0 + (0.014 * c12); + + return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh)); +} + + +// Auxiliary +static +cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab) +{ + cmsFloat64Number yt; + + if (Lab->L > 7.996969) + yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100; + else + yt = 100 * (Lab->L / 903.3); + + return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6); +} + + + +// bfd - gets BFD(1:1) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL, + deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd; + cmsCIELCh LCh1, LCh2; + + + lbfd1 = ComputeLBFD(Lab1); + lbfd2 = ComputeLBFD(Lab2); + deltaL = lbfd2 - lbfd1; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + deltaC = LCh2.C - LCh1.C; + AveC = (LCh1.C+LCh2.C)/2; + Aveh = (LCh1.h+LCh2.h)/2; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC))) + deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC)); + else + deltah =0; + + + dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521; + g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000)); + t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))- + 0.040*cos((2*Aveh-136)/(180/M_PI))+ + 0.070*cos((3*Aveh-31)/(180/M_PI))+ + 0.049*cos((4*Aveh+114)/(180/M_PI))- + 0.015*cos((5*Aveh-103)/(180/M_PI))); + + dh = dc*(g*t+1-g); + rh = -0.260*cos((Aveh-308)/(180/M_PI))- + 0.379*cos((2*Aveh-160)/(180/M_PI))- + 0.636*cos((3*Aveh+254)/(180/M_PI))+ + 0.226*cos((4*Aveh+140)/(180/M_PI))- + 0.194*cos((5*Aveh+280)/(180/M_PI)); + + rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000)); + rt = rh*rc; + + bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh))); + + return bfd; +} + + +// cmc - CMC(l:c) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c) +{ + cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc; + cmsCIELCh LCh1, LCh2; + + if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + + dL = Lab2->L-Lab1->L; + dC = LCh2.C-LCh1.C; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) + dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC)); + else + dh =0; + + if ((LCh1.h > 164) && (LCh1.h < 345)) + t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI)))); + else + t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI)))); + + sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638; + sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L); + + if (Lab1->L<16) + sl = 0.511; + + f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900)); + sh = sc*(t*f+1-f); + cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh)); + + return cmc; +} + +// dE2000 The weightings KL, KC and KH can be modified to reflect the relative +// importance of lightness, chroma and hue in different industrial applications +cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, + cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh) +{ + cmsFloat64Number L1 = Lab1->L; + cmsFloat64Number a1 = Lab1->a; + cmsFloat64Number b1 = Lab1->b; + cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) ); + + cmsFloat64Number Ls = Lab2 ->L; + cmsFloat64Number as = Lab2 ->a; + cmsFloat64Number bs = Lab2 ->b; + cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) ); + + cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) )); + + cmsFloat64Number a_p = (1 + G ) * a1; + cmsFloat64Number b_p = b1; + cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p)); + cmsFloat64Number h_p = atan2deg(b_p, a_p); + + + cmsFloat64Number a_ps = (1 + G) * as; + cmsFloat64Number b_ps = bs; + cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps)); + cmsFloat64Number h_ps = atan2deg(b_ps, a_ps); + + cmsFloat64Number meanC_p =(C_p + C_ps) / 2; + + cmsFloat64Number hps_plus_hp = h_ps + h_p; + cmsFloat64Number hps_minus_hp = h_ps - h_p; + + cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 : + (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 : + (hps_plus_hp - 360)/2; + + cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) : + (hps_minus_hp) > 180 ? (hps_minus_hp - 360) : + (hps_minus_hp); + cmsFloat64Number delta_L = (Ls - L1); + cmsFloat64Number delta_C = (C_ps - C_p ); + + + cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2); + + cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30)) + + 0.24 * cos(RADIANS(2*meanh_p)) + + 0.32 * cos(RADIANS(3*meanh_p + 6)) + - 0.2 * cos(RADIANS(4*meanh_p - 63)); + + cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) ); + + cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2; + cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T; + + cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25))); + + cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0))); + + cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc; + + cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) + + Sqr(delta_C/(Sc * Kc)) + + Sqr(delta_H/(Sh * Kh)) + + Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh))); + + return deltaE00; +} + +// This function returns a number of gridpoints to be used as LUT table. It assumes same number +// of gripdpoints in all dimensions. Flags may override the choice. +cmsUInt32Number CMSEXPORT _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags) +{ + cmsUInt32Number nChannels; + + // Already specified? + if (dwFlags & 0x00FF0000) { + // Yes, grab'em + return (dwFlags >> 16) & 0xFF; + } + + nChannels = cmsChannelsOf(Colorspace); + + // HighResPrecalc is maximum resolution + if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { + + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) // 23 for CMYK + return 23; + + return 49; // 49 for RGB and others + } + + + // LowResPrecal is lower resolution + if (dwFlags & cmsFLAGS_LOWRESPRECALC) { + + if (nChannels > 4) + return 6; // 6 for more than 4 channels + + if (nChannels == 1) + return 33; // For monochrome + + return 17; // 17 for remaining + } + + // Default values + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) + return 17; // 17 for CMYK + + return 33; // 33 for RGB +} + + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs) +{ + // Only most common spaces + + static cmsUInt16Number RGBblack[4] = { 0, 0, 0 }; + static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink + static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 }; + static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding + static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 }; + static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 }; + static cmsUInt16Number Grayblack[4] = { 0 }; + static cmsUInt16Number GrayWhite[4] = { 0xffff }; + + switch (Space) { + + case cmsSigGrayData: if (White) *White = GrayWhite; + if (Black) *Black = Grayblack; + if (nOutputs) *nOutputs = 1; + return TRUE; + + case cmsSigRgbData: if (White) *White = RGBwhite; + if (Black) *Black = RGBblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigLabData: if (White) *White = LABwhite; + if (Black) *Black = LABblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigCmykData: if (White) *White = CMYKwhite; + if (Black) *Black = CMYKblack; + if (nOutputs) *nOutputs = 4; + return TRUE; + + case cmsSigCmyData: if (White) *White = CMYwhite; + if (Black) *Black = CMYblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + default:; + } + + return FALSE; +} + + + +// Several utilities ------------------------------------------------------- + +// Translate from our colorspace to ICC representation + +cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation) +{ + switch (OurNotation) { + + case 1: + case PT_GRAY: return cmsSigGrayData; + + case 2: + case PT_RGB: return cmsSigRgbData; + + case PT_CMY: return cmsSigCmyData; + case PT_CMYK: return cmsSigCmykData; + case PT_YCbCr:return cmsSigYCbCrData; + case PT_YUV: return cmsSigLuvData; + case PT_XYZ: return cmsSigXYZData; + + case PT_LabV2: + case PT_Lab: return cmsSigLabData; + + case PT_YUVK: return cmsSigLuvKData; + case PT_HSV: return cmsSigHsvData; + case PT_HLS: return cmsSigHlsData; + case PT_Yxy: return cmsSigYxyData; + + case PT_MCH1: return cmsSigMCH1Data; + case PT_MCH2: return cmsSigMCH2Data; + case PT_MCH3: return cmsSigMCH3Data; + case PT_MCH4: return cmsSigMCH4Data; + case PT_MCH5: return cmsSigMCH5Data; + case PT_MCH6: return cmsSigMCH6Data; + case PT_MCH7: return cmsSigMCH7Data; + case PT_MCH8: return cmsSigMCH8Data; + + case PT_MCH9: return cmsSigMCH9Data; + case PT_MCH10: return cmsSigMCHAData; + case PT_MCH11: return cmsSigMCHBData; + case PT_MCH12: return cmsSigMCHCData; + case PT_MCH13: return cmsSigMCHDData; + case PT_MCH14: return cmsSigMCHEData; + case PT_MCH15: return cmsSigMCHFData; + + default: return (cmsColorSpaceSignature) 0; + } +} + + +int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace) +{ + switch (ProfileSpace) { + + case cmsSigGrayData: return PT_GRAY; + case cmsSigRgbData: return PT_RGB; + case cmsSigCmyData: return PT_CMY; + case cmsSigCmykData: return PT_CMYK; + case cmsSigYCbCrData:return PT_YCbCr; + case cmsSigLuvData: return PT_YUV; + case cmsSigXYZData: return PT_XYZ; + case cmsSigLabData: return PT_Lab; + case cmsSigLuvKData: return PT_YUVK; + case cmsSigHsvData: return PT_HSV; + case cmsSigHlsData: return PT_HLS; + case cmsSigYxyData: return PT_Yxy; + + case cmsSig1colorData: + case cmsSigMCH1Data: return PT_MCH1; + + case cmsSig2colorData: + case cmsSigMCH2Data: return PT_MCH2; + + case cmsSig3colorData: + case cmsSigMCH3Data: return PT_MCH3; + + case cmsSig4colorData: + case cmsSigMCH4Data: return PT_MCH4; + + case cmsSig5colorData: + case cmsSigMCH5Data: return PT_MCH5; + + case cmsSig6colorData: + case cmsSigMCH6Data: return PT_MCH6; + + case cmsSigMCH7Data: + case cmsSig7colorData:return PT_MCH7; + + case cmsSigMCH8Data: + case cmsSig8colorData:return PT_MCH8; + + case cmsSigMCH9Data: + case cmsSig9colorData:return PT_MCH9; + + case cmsSigMCHAData: + case cmsSig10colorData:return PT_MCH10; + + case cmsSigMCHBData: + case cmsSig11colorData:return PT_MCH11; + + case cmsSigMCHCData: + case cmsSig12colorData:return PT_MCH12; + + case cmsSigMCHDData: + case cmsSig13colorData:return PT_MCH13; + + case cmsSigMCHEData: + case cmsSig14colorData:return PT_MCH14; + + case cmsSigMCHFData: + case cmsSig15colorData:return PT_MCH15; + + default: return (cmsColorSpaceSignature) 0; + } +} + + +cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) +{ + switch (ColorSpace) { + + case cmsSigMCH1Data: + case cmsSig1colorData: + case cmsSigGrayData: return 1; + + case cmsSigMCH2Data: + case cmsSig2colorData: return 2; + + case cmsSigXYZData: + case cmsSigLabData: + case cmsSigLuvData: + case cmsSigYCbCrData: + case cmsSigYxyData: + case cmsSigRgbData: + case cmsSigHsvData: + case cmsSigHlsData: + case cmsSigCmyData: + case cmsSigMCH3Data: + case cmsSig3colorData: return 3; + + case cmsSigLuvKData: + case cmsSigCmykData: + case cmsSigMCH4Data: + case cmsSig4colorData: return 4; + + case cmsSigMCH5Data: + case cmsSig5colorData: return 5; + + case cmsSigMCH6Data: + case cmsSig6colorData: return 6; + + case cmsSigMCH7Data: + case cmsSig7colorData: return 7; + + case cmsSigMCH8Data: + case cmsSig8colorData: return 8; + + case cmsSigMCH9Data: + case cmsSig9colorData: return 9; + + case cmsSigMCHAData: + case cmsSig10colorData: return 10; + + case cmsSigMCHBData: + case cmsSig11colorData: return 11; + + case cmsSigMCHCData: + case cmsSig12colorData: return 12; + + case cmsSigMCHDData: + case cmsSig13colorData: return 13; + + case cmsSigMCHEData: + case cmsSig14colorData: return 14; + + case cmsSigMCHFData: + case cmsSig15colorData: return 15; + + default: return 3; + } +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsplugin.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsplugin.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsplugin.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsplugin.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1020 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// ---------------------------------------------------------------------------------- +// Encoding & Decoding support functions +// ---------------------------------------------------------------------------------- + +// Little-Endian to Big-Endian + +// Adjust a word value after being read/ before being written from/to an ICC profile +cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &Word; + cmsUInt8Number tmp; + + tmp = pByte[0]; + pByte[0] = pByte[1]; + pByte[1] = tmp; +#endif + + return Word; +} + + +// Transports to properly encoded values - note that icc profiles does use big endian notation. + +// 1 2 3 4 +// 4 3 2 1 + +cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + temp1 = *pByte++; + temp2 = *pByte++; + *(pByte-1) = *pByte; + *pByte++ = temp2; + *(pByte-3) = *pByte; + *pByte = temp1; +#endif + return DWord; +} + +// 1 2 3 4 5 6 7 8 +// 8 7 6 5 4 3 2 1 + +void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord) +{ + +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pIn = (cmsUInt8Number*) QWord; + cmsUInt8Number* pOut = (cmsUInt8Number*) Result; + + _cmsAssert(Result != NULL); + + pOut[7] = pIn[0]; + pOut[6] = pIn[1]; + pOut[5] = pIn[2]; + pOut[4] = pIn[3]; + pOut[3] = pIn[4]; + pOut[2] = pIn[5]; + pOut[1] = pIn[6]; + pOut[0] = pIn[7]; + +#else + _cmsAssert(Result != NULL); + +# ifdef CMS_DONT_USE_INT64 + (*Result)[0] = (*QWord)[0]; + (*Result)[1] = (*QWord)[1]; +# else + *Result = *QWord; +# endif +#endif +} + +// Auxiliary -- read 8, 16 and 32-bit numbers +cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n) +{ + cmsUInt8Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = tmp; + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess16(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + if (!_cmsReadUInt16Number(io, Array + i)) return FALSE; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess32(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + + tmp = _cmsAdjustEndianess32(tmp); + *n = *(cmsFloat32Number*)(void*)&tmp; + + // Safeguard which covers against absurd values + if (*n > 1E+20 || *n < -1E+20) return FALSE; + + #if defined(_MSC_VER) && _MSC_VER < 1800 + return TRUE; + #elif defined (__BORLANDC__) + return TRUE; + #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L) + return TRUE; + #else + + // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards) + return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL)); + #endif + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1) + return FALSE; + + if (n != NULL) { + + _cmsAdjustEndianess64(n, &tmp); + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp)); + } + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + + if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE; + + if (XYZ != NULL) { + + XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X)); + XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y)); + XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z)); + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n) +{ + _cmsAssert(io != NULL); + + if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess16(n); + if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(Array != NULL); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE; + } + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32(n); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = *(cmsUInt32Number*) (void*) &n; + tmp = _cmsAdjustEndianess32(tmp); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + _cmsAdjustEndianess64(&tmp, n); + if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n)); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + _cmsAssert(XYZ != NULL); + + xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X)); + xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y)); + xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z)); + + return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz); +} + +// from Fixed point 8.8 to double +cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8) +{ + cmsUInt8Number msb, lsb; + + lsb = (cmsUInt8Number) (fixed8 & 0xff); + msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff); + + return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0)); +} + +cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val) +{ + cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val); + return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF); +} + +// from Fixed point 15.16 to double +cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32) +{ + cmsFloat64Number floater, sign, mid; + int Whole, FracPart; + + sign = (fix32 < 0 ? -1 : 1); + fix32 = abs(fix32); + + Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff; + FracPart = (cmsUInt16Number)(fix32 & 0xffff); + + mid = (cmsFloat64Number) FracPart / 65536.0; + floater = (cmsFloat64Number) Whole + mid; + + return sign * floater; +} + +// from double to Fixed point 15.16 +cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v) +{ + return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5)); +} + +// Date/Time functions + +void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest) +{ + + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds); + Dest->tm_min = _cmsAdjustEndianess16(Source->minutes); + Dest->tm_hour = _cmsAdjustEndianess16(Source->hours); + Dest->tm_mday = _cmsAdjustEndianess16(Source->day); + Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1; + Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900; + Dest->tm_wday = -1; + Dest->tm_yday = -1; + Dest->tm_isdst = 0; +} + +void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source) +{ + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec); + Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min); + Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour); + Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday); + Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1)); + Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900)); +} + +// Read base and return type base +cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1) + return (cmsTagTypeSignature) 0; + + return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig); +} + +// Setup base marker +cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig); + memset(&Base.reserved, 0, sizeof(Base.reserved)); + return io -> Write(io, sizeof(_cmsTagBase), &Base); +} + +cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1); +} + +cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + memset(Buffer, 0, BytesToNextAlignedPos); + return io -> Write(io, BytesToNextAlignedPos, Buffer); +} + + +// To deal with text streams. 2K at most +cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...) +{ + va_list args; + int len; + cmsUInt8Number Buffer[2048]; + cmsBool rc; + cmsUInt8Number* ptr; + + _cmsAssert(io != NULL); + _cmsAssert(frm != NULL); + + va_start(args, frm); + + len = vsnprintf((char*) Buffer, 2047, frm, args); + if (len < 0) { + va_end(args); + return FALSE; // Truncated, which is a fatal error for us + } + + // setlocale may be active, no commas are needed in PS generator + // and PS generator is our only client + for (ptr = Buffer; *ptr; ptr++) + { + if (*ptr == ',') *ptr = '.'; + } + + rc = io ->Write(io, (cmsUInt32Number) len, Buffer); + + va_end(args); + + return rc; +} + + +// Plugin memory management ------------------------------------------------------------------------------------------------- + +// Specialized malloc for plug-ins, that is freed upon exit. +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + struct _cmsContext_struct* ctx = _cmsGetContext(ContextID); + + if (ctx ->MemPool == NULL) { + + if (ContextID == NULL) { + + ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024); + if (ctx->MemPool == NULL) return NULL; + } + else { + cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context"); + return NULL; + } + } + + return _cmsSubAlloc(ctx->MemPool, size); +} + + +// Main plug-in dispatcher +cmsBool CMSEXPORT cmsPlugin(void* Plug_in) +{ + return cmsPluginTHR(NULL, Plug_in); +} + +cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) Plug_in; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic != cmsPluginMagicNumber) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); + return FALSE; + } + + if (Plugin ->ExpectedVersion > LCMS_VERSION) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", + Plugin ->ExpectedVersion, LCMS_VERSION); + return FALSE; + } + + switch (Plugin -> Type) { + + case cmsPluginMemHandlerSig: + if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginInterpolationSig: + if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagTypeSig: + if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagSig: + if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginFormattersSig: + if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginRenderingIntentSig: + if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginParametricCurveSig: + if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMultiProcessElementSig: + if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginOptimizationSig: + if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTransformSig: + if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMutexSig: + if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; + break; + + default: + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); + return FALSE; + } + } + + // Keep a reference to the plug-in + return TRUE; +} + + +// Revert all plug-ins to default +void CMSEXPORT cmsUnregisterPlugins(void) +{ + cmsUnregisterPluginsTHR(NULL); +} + + +// The Global storage for system context. This is the one and only global variable +// pointers structure. All global vars are referenced here. +static struct _cmsContext_struct globalContext = { + + NULL, // Not in the linked list + NULL, // No suballocator + { + NULL, // UserPtr, + &_cmsLogErrorChunk, // Logger, + &_cmsAlarmCodesChunk, // AlarmCodes, + &_cmsAdaptationStateChunk, // AdaptationState, + &_cmsMemPluginChunk, // MemPlugin, + &_cmsInterpPluginChunk, // InterpPlugin, + &_cmsCurvesPluginChunk, // CurvesPlugin, + &_cmsFormattersPluginChunk, // FormattersPlugin, + &_cmsTagTypePluginChunk, // TagTypePlugin, + &_cmsTagPluginChunk, // TagPlugin, + &_cmsIntentsPluginChunk, // IntentPlugin, + &_cmsMPETypePluginChunk, // MPEPlugin, + &_cmsOptimizationPluginChunk, // OptimizationPlugin, + &_cmsTransformPluginChunk, // TransformPlugin, + &_cmsMutexPluginChunk // MutexPlugin + }, + + { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0 +}; + + +// The context pool (linked list head) +static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER; +static struct _cmsContext_struct* _cmsContextPoolHead = NULL; + +// Internal, get associated pointer, with guessing. Never returns NULL. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) +{ + struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct* ctx; + + + // On 0, use global settings + if (id == NULL) + return &globalContext; + + // Search + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + for (ctx = _cmsContextPoolHead; + ctx != NULL; + ctx = ctx ->Next) { + + // Found it? + if (id == ctx) + { + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + return ctx; // New-style context + } + } + + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + return &globalContext; +} + + +// Internal: get the memory area associanted with each context client +// Returns the block assigned to the specific zone. Never return NULL. +void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc) +{ + struct _cmsContext_struct* ctx; + void *ptr; + + if ((int) mc < 0 || mc >= MemoryClientMax) { + + cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption"); + + // This is catastrophic. Should never reach here + _cmsAssert(0); + + // Reverts to global context + return globalContext.chunks[UserPtr]; + } + + ctx = _cmsGetContext(ContextID); + ptr = ctx ->chunks[mc]; + + if (ptr != NULL) + return ptr; + + // A null ptr means no special settings for that context, and this + // reverts to Context0 globals + return globalContext.chunks[mc]; +} + + +// This function returns the given context its default pristine state, +// as no plug-ins were declared. There is no way to unregister a single +// plug-in, as a single call to cmsPluginTHR() function may register +// many different plug-ins simultaneously, then there is no way to +// identify which plug-in to unregister. +void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID) +{ + _cmsRegisterMemHandlerPlugin(ContextID, NULL); + _cmsRegisterInterpPlugin(ContextID, NULL); + _cmsRegisterTagTypePlugin(ContextID, NULL); + _cmsRegisterTagPlugin(ContextID, NULL); + _cmsRegisterFormattersPlugin(ContextID, NULL); + _cmsRegisterRenderingIntentPlugin(ContextID, NULL); + _cmsRegisterParametricCurvesPlugin(ContextID, NULL); + _cmsRegisterMultiProcessElementPlugin(ContextID, NULL); + _cmsRegisterOptimizationPlugin(ContextID, NULL); + _cmsRegisterTransformPlugin(ContextID, NULL); + _cmsRegisterMutexPlugin(ContextID, NULL); +} + + +// Returns the memory manager plug-in, if any, from the Plug-in bundle +static +cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) PluginBundle; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic == cmsPluginMagicNumber && + Plugin -> ExpectedVersion <= LCMS_VERSION && + Plugin -> Type == cmsPluginMemHandlerSig) { + + // Found! + return (cmsPluginMemHandler*) Plugin; + } + } + + // Nope, revert to defaults + return NULL; +} + + +// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData) +{ + struct _cmsContext_struct* ctx; + struct _cmsContext_struct fakeContext; + + // See the comments regarding locking in lcms2_internal.h + // for an explanation of why we need the following code. +#ifndef CMS_NO_PTHREADS +#ifdef CMS_IS_WINDOWS_ +#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT + { + static HANDLE _cmsWindowsInitMutex = NULL; + static volatile HANDLE* mutex = &_cmsWindowsInitMutex; + + if (*mutex == NULL) + { + HANDLE p = CreateMutex(NULL, FALSE, NULL); + if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL) + CloseHandle(p); + } + if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) + return NULL; + if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL) + InitializeCriticalSection(&_cmsContextPoolHeadMutex); + if (*mutex == NULL || !ReleaseMutex(*mutex)) + return NULL; + } +#endif +#endif +#endif + + _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager); + + fakeContext.chunks[UserPtr] = UserData; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + // Create the context structure. + ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened! + + // Init the structure and the memory manager + memset(ctx, 0, sizeof(struct _cmsContext_struct)); + + // Keep memory manager + memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk)); + + // Maintain the linked list (with proper locking) + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + ctx ->chunks[UserPtr] = UserData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + // Now we can allocate the pool by using default memory manager + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); // default size about 22 pointers + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + _cmsAllocLogErrorChunk(ctx, NULL); + _cmsAllocAlarmCodesChunk(ctx, NULL); + _cmsAllocAdaptationStateChunk(ctx, NULL); + _cmsAllocMemPluginChunk(ctx, NULL); + _cmsAllocInterpPluginChunk(ctx, NULL); + _cmsAllocCurvesPluginChunk(ctx, NULL); + _cmsAllocFormattersPluginChunk(ctx, NULL); + _cmsAllocTagTypePluginChunk(ctx, NULL); + _cmsAllocMPETypePluginChunk(ctx, NULL); + _cmsAllocTagPluginChunk(ctx, NULL); + _cmsAllocIntentsPluginChunk(ctx, NULL); + _cmsAllocOptimizationPluginChunk(ctx, NULL); + _cmsAllocTransformPluginChunk(ctx, NULL); + _cmsAllocMutexPluginChunk(ctx, NULL); + + // Setup the plug-ins + if (!cmsPluginTHR(ctx, Plugin)) { + + cmsDeleteContext(ctx); + return NULL; + } + + return (cmsContext) ctx; +} + +// Duplicates a context with all associated plug-ins. +// Caller may specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) +{ + int i; + struct _cmsContext_struct* ctx; + const struct _cmsContext_struct* src = _cmsGetContext(ContextID); + + void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr]; + + + ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened + + // Setup default memory allocators + memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + // Maintain the linked list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + ctx ->chunks[UserPtr] = userData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + // Allocate all required chunks. + _cmsAllocLogErrorChunk(ctx, src); + _cmsAllocAlarmCodesChunk(ctx, src); + _cmsAllocAdaptationStateChunk(ctx, src); + _cmsAllocMemPluginChunk(ctx, src); + _cmsAllocInterpPluginChunk(ctx, src); + _cmsAllocCurvesPluginChunk(ctx, src); + _cmsAllocFormattersPluginChunk(ctx, src); + _cmsAllocTagTypePluginChunk(ctx, src); + _cmsAllocMPETypePluginChunk(ctx, src); + _cmsAllocTagPluginChunk(ctx, src); + _cmsAllocIntentsPluginChunk(ctx, src); + _cmsAllocOptimizationPluginChunk(ctx, src); + _cmsAllocTransformPluginChunk(ctx, src); + _cmsAllocMutexPluginChunk(ctx, src); + + // Make sure no one failed + for (i=Logger; i < MemoryClientMax; i++) { + + if (src ->chunks[i] == NULL) { + cmsDeleteContext((cmsContext) ctx); + return NULL; + } + } + + return (cmsContext) ctx; +} + + +// Frees any resources associated with the given context, +// and destroys the context placeholder. +// The ContextID can no longer be used in any THR operation. +void CMSEXPORT cmsDeleteContext(cmsContext ContextID) +{ + if (ContextID != NULL) { + + struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct fakeContext; + struct _cmsContext_struct* prev; + + memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr]; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + // Get rid of plugins + cmsUnregisterPluginsTHR(ContextID); + + // Since all memory is allocated in the private pool, all what we need to do is destroy the pool + if (ctx -> MemPool != NULL) + _cmsSubAllocDestroy(ctx ->MemPool); + ctx -> MemPool = NULL; + + // Maintain list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + if (_cmsContextPoolHead == ctx) { + + _cmsContextPoolHead = ctx->Next; + } + else { + + // Search for previous + for (prev = _cmsContextPoolHead; + prev != NULL; + prev = prev ->Next) + { + if (prev -> Next == ctx) { + prev -> Next = ctx ->Next; + break; + } + } + } + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + // free the memory block itself + _cmsFree(&fakeContext, ctx); + } +} + +// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation +void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID) +{ + return _cmsContextGetClientChunk(ContextID, UserPtr); +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsps2.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsps2.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsps2.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsps2.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1647 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// PostScript ColorRenderingDictionary and ColorSpaceArray + + +#define MAXPSCOLS 60 // Columns on tables + +/* + Implementation + -------------- + + PostScript does use XYZ as its internal PCS. But since PostScript + interpolation tables are limited to 8 bits, I use Lab as a way to + improve the accuracy, favoring perceptual results. So, for the creation + of each CRD, CSA the profiles are converted to Lab via a device + link between profile -> Lab or Lab -> profile. The PS code necessary to + convert Lab <-> XYZ is also included. + + + + Color Space Arrays (CSA) + ================================================================================== + + In order to obtain precision, code chooses between three ways to implement + the device -> XYZ transform. These cases identifies monochrome profiles (often + implemented as a set of curves), matrix-shaper and Pipeline-based. + + Monochrome + ----------- + + This is implemented as /CIEBasedA CSA. The prelinearization curve is + placed into /DecodeA section, and matrix equals to D50. Since here is + no interpolation tables, I do the conversion directly to XYZ + + NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT + flag is forced on such profiles. + + [ /CIEBasedA + << + /DecodeA { transfer function } bind + /MatrixA [D50] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + >> + ] + + On simpler profiles, the PCS is already XYZ, so no conversion is required. + + + Matrix-shaper based + ------------------- + + This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the + profile implementation. Since here there are no interpolation tables, I do + the conversion directly to XYZ + + + + [ /CIEBasedABC + << + /DecodeABC [ {transfer1} {transfer2} {transfer3} ] + /MatrixABC [Matrix] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] + /DecodeLMN [ { / 2} dup dup ] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + >> + ] + + + CLUT based + ---------- + + Lab is used in such cases. + + [ /CIEBasedDEF + << + /DecodeDEF [ ] + /Table [ p p p [<...>]] + /RangeABC [ 0 1 0 1 0 1] + /DecodeABC[ ] + /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ] + % -128/500 1+127/500 0 1 -127/200 1+128/200 + /MatrixABC [ 1 1 1 1 0 0 0 0 -1] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + ] + + + Color Rendering Dictionaries (CRD) + ================================== + These are always implemented as CLUT, and always are using Lab. Since CRD are expected to + be used as resources, the code adds the definition as well. + + << + /ColorRenderingType 1 + /WhitePoint [ D50 ] + /BlackPoint [BP] + /MatrixPQR [ Bradford ] + /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ] + /TransformPQR [ + {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind + {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind + {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind + ] + /MatrixABC <...> + /EncodeABC <...> + /RangeABC <.. used for XYZ -> Lab> + /EncodeLMN + /RenderTable [ p p p [<...>]] + + /RenderingIntent (Perceptual) + >> + /Current exch /ColorRendering defineresource pop + + + The following stages are used to convert from XYZ to Lab + -------------------------------------------------------- + + Input is given at LMN stage on X, Y, Z + + Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn) + + /EncodeLMN [ + + { 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + { 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + { 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + + ] + + + MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn) + + | 0 1 0| + | 1 -1 0| + | 0 1 -1| + + /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ] + + EncodeABC finally gives Lab values. + + /EncodeABC [ + { 116 mul 16 sub 100 div } bind + { 500 mul 128 add 255 div } bind + { 200 mul 128 add 255 div } bind + ] + + The following stages are used to convert Lab to XYZ + ---------------------------------------------------- + + /RangeABC [ 0 1 0 1 0 1] + /DecodeABC [ { 100 mul 16 add 116 div } bind + { 255 mul 128 sub 500 div } bind + { 255 mul 128 sub 200 div } bind + ] + + /MatrixABC [ 1 1 1 1 0 0 0 0 -1] + /DecodeLMN [ + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind + ] + + +*/ + +/* + + PostScript algorithms discussion. + ========================================================================================================= + + 1D interpolation algorithm + + + 1D interpolation (float) + ------------------------ + + val2 = Domain * Value; + + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + rest = val2 - cell0; + + y0 = LutTable[cell0] ; + y1 = LutTable[cell1] ; + + y = y0 + (y1 - y0) * rest; + + + + PostScript code Stack + ================================================ + + { % v + + [array] % v tab + dup % v tab tab + length 1 sub % v tab dom + + 3 -1 roll % tab dom v + + mul % tab val2 + dup % tab val2 val2 + dup % tab val2 val2 val2 + floor cvi % tab val2 val2 cell0 + exch % tab val2 cell0 val2 + ceiling cvi % tab val2 cell0 cell1 + + 3 index % tab val2 cell0 cell1 tab + exch % tab val2 cell0 tab cell1 + get % tab val2 cell0 y1 + + 4 -1 roll % val2 cell0 y1 tab + 3 -1 roll % val2 y1 tab cell0 + get % val2 y1 y0 + + dup % val2 y1 y0 y0 + 3 1 roll % val2 y0 y1 y0 + + sub % val2 y0 (y1-y0) + 3 -1 roll % y0 (y1-y0) val2 + dup % y0 (y1-y0) val2 val2 + floor cvi % y0 (y1-y0) val2 floor(val2) + sub % y0 (y1-y0) rest + mul % y0 t1 + add % y + 65535 div % result + + } bind + + +*/ + + +// This struct holds the memory block currently being write +typedef struct { + _cmsStageCLutData* Pipeline; + cmsIOHANDLER* m; + + int FirstComponent; + int SecondComponent; + + const char* PreMaj; + const char* PostMaj; + const char* PreMin; + const char* PostMin; + + int FixWhite; // Force mapping of pure white + + cmsColorSpaceSignature ColorSpace; // ColorSpace of profile + + +} cmsPsSamplerCargo; + +static int _cmsPSActualColumn = 0; + + +// Convert to byte +static +cmsUInt8Number Word2Byte(cmsUInt16Number w) +{ + return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5); +} + + +// Write a cooked byte +static +void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) +{ + _cmsIOPrintf(m, "%02x", b); + _cmsPSActualColumn += 2; + + if (_cmsPSActualColumn > MAXPSCOLS) { + + _cmsIOPrintf(m, "\n"); + _cmsPSActualColumn = 0; + } +} + +// ----------------------------------------------------------------- PostScript generation + + +// Removes offending carriage returns + +static +char* RemoveCR(const char* txt) +{ + static char Buffer[2048]; + char* pt; + + strncpy(Buffer, txt, 2047); + Buffer[2047] = 0; + for (pt = Buffer; *pt; pt++) + if (*pt == '\n' || *pt == '\r') *pt = ' '; + + return Buffer; + +} + +static +void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) +{ + time_t timer; + cmsMLU *Description, *Copyright; + char DescASCII[256], CopyrightASCII[256]; + + time(&timer); + + Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); + Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); + + DescASCII[0] = DescASCII[255] = 0; + CopyrightASCII[0] = CopyrightASCII[255] = 0; + + if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255); + if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255); + + _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%% %s\n", Title); + _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII)); + _cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII)); + _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%%%%BeginResource\n"); + +} + + +// Emits White & Black point. White point is always D50, Black point is the device +// Black point adapted to D50. + +static +void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint) +{ + + _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, + BlackPoint -> Y, + BlackPoint -> Z); + + _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, + cmsD50_XYZ()->Y, + cmsD50_XYZ()->Z); +} + + +static +void EmitRangeCheck(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if " + "dup 1.0 gt { pop 1.0 } if "); + +} + +// Does write the intent + +static +void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent) +{ + const char *intent; + + switch (RenderingIntent) { + + case INTENT_PERCEPTUAL: intent = "Perceptual"; break; + case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break; + case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break; + case INTENT_SATURATION: intent = "Saturation"; break; + + default: intent = "Undefined"; break; + } + + _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent ); +} + +// +// Convert L* to Y +// +// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29 +// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29 +// + +// Lab -> XYZ, see the discussion above + +static +void EmitLab2XYZ(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n"); + _cmsIOPrintf(m, "/DecodeABC [\n"); + _cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); + _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); + _cmsIOPrintf(m, "/DecodeLMN [\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); + _cmsIOPrintf(m, "]\n"); +} + +static +void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name) +{ + _cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name); + _cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name); +} + +static +void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth) +{ + _cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name); + if (depth > 1) { + // cycle topmost items on the stack to bring the previous definition to the front + _cmsIOPrintf(m, "%d -1 roll ", depth); + } + _cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name); +} + +// Outputs a table of words. It does use 16 bits + +static +void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name) +{ + cmsUInt32Number i; + cmsFloat64Number gamma; + + if (Table == NULL) return; // Error + + if (Table ->nEntries <= 0) return; // Empty table + + // Suppress whole if identity + if (cmsIsToneCurveLinear(Table)) return; + + // Check if is really an exponential. If so, emit "exp" + gamma = cmsEstimateGamma(Table, 0.001); + if (gamma > 0) { + _cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma); + return; + } + + EmitSafeGuardBegin(m, "lcms2gammatable"); + _cmsIOPrintf(m, "/lcms2gammatable ["); + + for (i=0; i < Table->nEntries; i++) { + if (i % 10 == 0) + _cmsIOPrintf(m, "\n "); + _cmsIOPrintf(m, "%d ", Table->Table16[i]); + } + + _cmsIOPrintf(m, "] def\n"); + + + // Emit interpolation code + + // PostScript code Stack + // =============== ======================== + // v + _cmsIOPrintf(m, "/%s {\n ", name); + + // Bounds check + EmitRangeCheck(m); + + _cmsIOPrintf(m, "\n //lcms2gammatable "); // v tab + _cmsIOPrintf(m, "dup "); // v tab tab + _cmsIOPrintf(m, "length 1 sub "); // v tab dom + _cmsIOPrintf(m, "3 -1 roll "); // tab dom v + _cmsIOPrintf(m, "mul "); // tab val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 val2 + _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0 + _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2 + _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1 + _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab + _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1 + _cmsIOPrintf(m, "get\n "); // tab val2 cell0 y1 + _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab + _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0 + _cmsIOPrintf(m, "get "); // val2 y1 y0 + _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0 + _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0 + _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0) + _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2 + _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2 + _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) + _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest + _cmsIOPrintf(m, "mul "); // y0 t1 + _cmsIOPrintf(m, "add "); // y + _cmsIOPrintf(m, "65535 div\n"); // result + + _cmsIOPrintf(m, "} bind def\n"); + + EmitSafeGuardEnd(m, "lcms2gammatable", 1); +} + + +// Compare gamma table + +static +cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2) +{ + if (nG1 != nG2) return FALSE; + return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0; +} + + +// Does write a set of gamma curves + +static +void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix) +{ + cmsUInt32Number i; + static char buffer[2048]; + + for( i=0; i < n; i++ ) + { + if (g[i] == NULL) return; // Error + + if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) { + + _cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1); + } + else { + snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i); + buffer[sizeof(buffer)-1] = '\0'; + Emit1Gamma(m, g[i], buffer); + } + } + +} + + +// Following code dumps a LUT onto memory stream + + +// This is the sampler. Intended to work in SAMPLER_INSPECT mode, +// that is, the callback will be called for each knot with +// +// In[] The grid location coordinates, normalized to 0..ffff +// Out[] The Pipeline values, normalized to 0..ffff +// +// Returning a value other than 0 does terminate the sampling process +// +// Each row contains Pipeline values for all but first component. So, I +// detect row changing by keeping a copy of last value of first +// component. -1 is used to mark beginning of whole block. + +static +int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo; + cmsUInt32Number i; + + + if (sc -> FixWhite) { + + if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8] + + if ((In[1] >= 0x7800 && In[1] <= 0x8800) && + (In[2] >= 0x7800 && In[2] <= 0x8800)) { + + cmsUInt16Number* Black; + cmsUInt16Number* White; + cmsUInt32Number nOutputs; + + if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) + return 0; + + for (i=0; i < nOutputs; i++) + Out[i] = White[i]; + } + + + } + } + + + // Hadle the parenthesis on rows + + if (In[0] != sc ->FirstComponent) { + + if (sc ->FirstComponent != -1) { + + _cmsIOPrintf(sc ->m, sc ->PostMin); + sc ->SecondComponent = -1; + _cmsIOPrintf(sc ->m, sc ->PostMaj); + } + + // Begin block + _cmsPSActualColumn = 0; + + _cmsIOPrintf(sc ->m, sc ->PreMaj); + sc ->FirstComponent = In[0]; + } + + + if (In[1] != sc ->SecondComponent) { + + if (sc ->SecondComponent != -1) { + + _cmsIOPrintf(sc ->m, sc ->PostMin); + } + + _cmsIOPrintf(sc ->m, sc ->PreMin); + sc ->SecondComponent = In[1]; + } + + // Dump table. + + for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) { + + cmsUInt16Number wWordOut = Out[i]; + cmsUInt8Number wByteOut; // Value as byte + + + // We always deal with Lab4 + + wByteOut = Word2Byte(wWordOut); + WriteByte(sc -> m, wByteOut); + } + + return 1; +} + +// Writes a Pipeline on memstream. Could be 8 or 16 bits based + +static +void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, + const char* PostMaj, + const char* PreMin, + const char* PostMin, + int FixWhite, + cmsColorSpaceSignature ColorSpace) +{ + cmsUInt32Number i; + cmsPsSamplerCargo sc; + + sc.FirstComponent = -1; + sc.SecondComponent = -1; + sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; + sc.m = m; + sc.PreMaj = PreMaj; + sc.PostMaj= PostMaj; + + sc.PreMin = PreMin; + sc.PostMin = PostMin; + sc.FixWhite = FixWhite; + sc.ColorSpace = ColorSpace; + + _cmsIOPrintf(m, "["); + + for (i=0; i < sc.Pipeline->Params->nInputs; i++) + _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); + + _cmsIOPrintf(m, " [\n"); + + cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT); + + _cmsIOPrintf(m, PostMin); + _cmsIOPrintf(m, PostMaj); + _cmsIOPrintf(m, "] "); + +} + + +// Dumps CIEBasedA Color Space Array + +static +int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) +{ + + _cmsIOPrintf(m, "[ /CIEBasedA\n"); + _cmsIOPrintf(m, " <<\n"); + + EmitSafeGuardBegin(m, "lcms2gammaproc"); + Emit1Gamma(m, Curve, "lcms2gammaproc"); + + _cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n"); + EmitSafeGuardEnd(m, "lcms2gammaproc", 3); + + _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); + + return 1; +} + + +// Dumps CIEBasedABC Color Space Array + +static +int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) +{ + int i; + + _cmsIOPrintf(m, "[ /CIEBasedABC\n"); + _cmsIOPrintf(m, "<<\n"); + + EmitSafeGuardBegin(m, "lcms2gammaproc0"); + EmitSafeGuardBegin(m, "lcms2gammaproc1"); + EmitSafeGuardBegin(m, "lcms2gammaproc2"); + EmitNGamma(m, 3, CurveSet, "lcms2gammaproc"); + _cmsIOPrintf(m, "/DecodeABC [\n"); + _cmsIOPrintf(m, " /lcms2gammaproc0 load\n"); + _cmsIOPrintf(m, " /lcms2gammaproc1 load\n"); + _cmsIOPrintf(m, " /lcms2gammaproc2 load\n"); + _cmsIOPrintf(m, "]\n"); + EmitSafeGuardEnd(m, "lcms2gammaproc2", 3); + EmitSafeGuardEnd(m, "lcms2gammaproc1", 3); + EmitSafeGuardEnd(m, "lcms2gammaproc0", 3); + + _cmsIOPrintf(m, "/MatrixABC [ " ); + + for( i=0; i < 3; i++ ) { + + _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0], + Matrix[i + 3*1], + Matrix[i + 3*2]); + } + + + _cmsIOPrintf(m, "]\n"); + + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); + + + return 1; +} + + +static +int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint) +{ + const char* PreMaj; + const char* PostMaj; + const char* PreMin, * PostMin; + cmsStage* mpe; + int i, numchans; + static char buffer[2048]; + + mpe = Pipeline->Elements; + + switch (cmsStageInputChannels(mpe)) { + case 3: + _cmsIOPrintf(m, "[ /CIEBasedDEF\n"); + PreMaj = "<"; + PostMaj = ">\n"; + PreMin = PostMin = ""; + break; + + case 4: + _cmsIOPrintf(m, "[ /CIEBasedDEFG\n"); + PreMaj = "["; + PostMaj = "]\n"; + PreMin = "<"; + PostMin = ">\n"; + break; + + default: + return 0; + + } + + _cmsIOPrintf(m, "<<\n"); + + if (cmsStageType(mpe) == cmsSigCurveSetElemType) { + + numchans = (int) cmsStageOutputChannels(mpe); + for (i = 0; i < numchans; ++i) { + snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i); + buffer[sizeof(buffer) - 1] = '\0'; + EmitSafeGuardBegin(m, buffer); + } + EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc"); + _cmsIOPrintf(m, "/DecodeDEF [\n"); + for (i = 0; i < numchans; ++i) { + snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i); + buffer[sizeof(buffer) - 1] = '\0'; + _cmsIOPrintf(m, buffer); + } + _cmsIOPrintf(m, "]\n"); + for (i = numchans - 1; i >= 0; --i) { + snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i); + buffer[sizeof(buffer) - 1] = '\0'; + EmitSafeGuardEnd(m, buffer, 3); + } + + mpe = mpe->Next; + } + + if (cmsStageType(mpe) == cmsSigCLutElemType) { + + _cmsIOPrintf(m, "/Table "); + WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0); + _cmsIOPrintf(m, "]\n"); + } + + EmitLab2XYZ(m); + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, Intent); + + _cmsIOPrintf(m, " >>\n"); + _cmsIOPrintf(m, "]\n"); + + return 1; +} + +// Generates a curve from a gray profile + +static +cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent) +{ + cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + cmsHPROFILE hXYZ = cmsCreateXYZProfile(); + cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); + int i; + + if (Out != NULL && xform != NULL) { + for (i=0; i < 256; i++) { + + cmsUInt8Number Gray = (cmsUInt8Number) i; + cmsCIEXYZ XYZ; + + cmsDoTransform(xform, &Gray, &XYZ, 1); + + Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0); + } + } + + if (xform) cmsDeleteTransform(xform); + if (hXYZ) cmsCloseProfile(hXYZ); + return Out; +} + + + +// Because PostScript has only 8 bits in /Table, we should use +// a more perceptually uniform space... I do choose Lab. + +static +int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + cmsUInt32Number nChannels; + cmsUInt32Number InputFormat; + int rc; + cmsHPROFILE Profiles[2]; + cmsCIEXYZ BlackPointAdaptedToD50; + + // Does create a device-link based transform. + // The DeviceLink is next dumped as working CSA. + + InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(InputFormat); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); + + // Adjust output to Lab4 + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + + Profiles[0] = hProfile; + Profiles[1] = hLab; + + xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); + return 0; + } + + // Only 1, 3 and 4 channels are allowed + + switch (nChannels) { + + case 1: { + cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); + EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); + cmsFreeToneCurve(Gray2Y); + } + break; + + case 3: + case 4: { + cmsUInt32Number OutFrm = TYPE_Lab_16; + cmsPipeline* DeviceLink; + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; + + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); + + rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); + cmsPipelineFree(DeviceLink); + if (rc == 0) return 0; + } + break; + + default: + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels); + return 0; + } + + + cmsDeleteTransform(xform); + + return 1; +} + +static +cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + + return Data -> Double; +} + + +// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based +static +int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper) +{ + cmsColorSpaceSignature ColorSpace; + int rc; + cmsCIEXYZ BlackPointAdaptedToD50; + + ColorSpace = cmsGetColorSpace(hProfile); + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0); + + if (ColorSpace == cmsSigGrayData) { + + cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); + rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); + + } + else + if (ColorSpace == cmsSigRgbData) { + + cmsMAT3 Mat; + int i, j; + + memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat)); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; + + rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat, + _cmsStageGetPtrToCurveSet(Shaper), + &BlackPointAdaptedToD50); + } + else { + + cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace."); + return 0; + } + + return rc; +} + + + +// Creates a PostScript color list from a named profile data. +// This is a HP extension, and it works in Lab instead of XYZ + +static +int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + cmsUInt32Number i, nColors; + char ColorName[cmsMAX_PATH]; + cmsNAMEDCOLORLIST* NamedColorList; + + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0); + if (xform == NULL) return 0; + + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(NamedColorList); + + + for (i=0; i < nColors; i++) { + + cmsUInt16Number In[1]; + cmsCIELab Lab; + + In[0] = (cmsUInt16Number) i; + + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, &Lab, 1); + _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); + } + + + + _cmsIOPrintf(m, ">>\n"); + + cmsDeleteTransform(xform); + cmsCloseProfile(hLab); + return 1; +} + + +// Does create a Color Space Array on XYZ colorspace for PostScript usage +static +cmsUInt32Number GenerateCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) +{ + cmsUInt32Number dwBytesUsed; + cmsPipeline* lut = NULL; + cmsStage* Matrix, *Shaper; + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; + } + else { + + + // Any profile class are allowed (including devicelink), but + // output (PCS) colorspace must be XYZ or Lab + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + + if (ColorSpace != cmsSigXYZData && + ColorSpace != cmsSigLabData) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); + goto Error; + } + + + // Read the lut with all necessary conversion stages + lut = _cmsReadInputLUT(hProfile, Intent); + if (lut == NULL) goto Error; + + + // Tone curves + matrix can be implemented without any LUT + if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { + + if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; + + } + else { + // We need a LUT for the rest + if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; + } + } + + + // Done, keep memory usage + dwBytesUsed = mem ->UsedSpace; + + // Get rid of LUT + if (lut != NULL) cmsPipelineFree(lut); + + // Finally, return used byte count + return dwBytesUsed; + +Error: + if (lut != NULL) cmsPipelineFree(lut); + return 0; +} + +// ------------------------------------------------------ Color Rendering Dictionary (CRD) + + + +/* + + Black point compensation plus chromatic adaptation: + + Step 1 - Chromatic adaptation + ============================= + + WPout + X = ------- PQR + Wpin + + Step 2 - Black point compensation + ================================= + + (WPout - BPout)*X - WPout*(BPin - BPout) + out = --------------------------------------- + WPout - BPin + + + Algorithm discussion + ==================== + + TransformPQR(WPin, BPin, WPout, BPout, PQR) + + Wpin,etc= { Xws Yws Zws Pws Qws Rws } + + + Algorithm Stack 0...n + =========================================================== + PQR BPout WPout BPin WPin + 4 index 3 get WPin PQR BPout WPout BPin WPin + div (PQR/WPin) BPout WPout BPin WPin + 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin + mult WPout*(PQR/WPin) BPout WPout BPin WPin + + 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin + 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin + sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin + mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + + 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + + sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + + 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + exch + sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + div + + exch pop + exch pop + exch pop + exch pop + +*/ + + +static +void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) +{ + + + if (lIsAbsolute) { + + // For absolute colorimetric intent, encode back to relative + // and generate a relative Pipeline + + // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) + + cmsCIEXYZ White; + + _cmsReadMediaWhitePoint(&White, hProfile); + + _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); + _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" + "/TransformPQR [\n" + "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" + "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" + "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", + White.X, White.Y, White.Z); + return; + } + + + _cmsIOPrintf(m,"%% Bradford Cone Space\n" + "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); + + _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + + // No BPC + + if (!DoBPC) { + + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" + "/TransformPQR [\n" + "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" + "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" + "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); + } else { + + // BPC + + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" + "/TransformPQR [\n"); + + _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " + "2 index 3 get 2 index 3 get sub mul " + "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " + "3 index 3 get 3 index 3 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n"); + + _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " + "2 index 4 get 2 index 4 get sub mul " + "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " + "3 index 4 get 3 index 4 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n"); + + _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " + "2 index 5 get 2 index 5 get sub mul " + "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " + "3 index 5 get 3 index 5 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n]\n"); + + } +} + + +static +void EmitXYZ2Lab(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); + _cmsIOPrintf(m, "/EncodeLMN [\n"); + _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); + _cmsIOPrintf(m, "/EncodeABC [\n"); + + + _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n"); + _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n"); + _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n"); + + + _cmsIOPrintf(m, "]\n"); + + +} + +// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces +// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted +// space on 3D CLUT, but since space seems not to be a problem here, 33 points +// would give a reasonable accuracy. Note also that CRD tables must operate in +// 8 bits. + +static +int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + cmsUInt32Number i, nChannels; + cmsUInt32Number OutputFormat; + _cmsTRANSFORM* v; + cmsPipeline* DeviceLink; + cmsHPROFILE Profiles[3]; + cmsCIEXYZ BlackPointAdaptedToD50; + cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); + cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); + cmsUInt32Number InFrm = TYPE_Lab_16; + cmsUInt32Number RelativeEncodingIntent; + cmsColorSpaceSignature ColorSpace; + + + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + if (hLab == NULL) return 0; + + OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(OutputFormat); + + ColorSpace = cmsGetColorSpace(hProfile); + + // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. + + RelativeEncodingIntent = Intent; + if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) + RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; + + + // Use V4 Lab always + Profiles[0] = hLab; + Profiles[1] = hProfile; + + xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, + Profiles, 2, TYPE_Lab_DBL, + OutputFormat, RelativeEncodingIntent, 0); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); + return 0; + } + + // Get a copy of the internal devicelink + v = (_cmsTRANSFORM*) xform; + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; + + + // We need a CLUT + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "/ColorRenderingType 1\n"); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); + + // Emit headers, etc. + EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); + EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); + EmitXYZ2Lab(m); + + + // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab + // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, + // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to + // zero. This would sacrifice a bit of highlights, but failure to do so would cause + // scum dot. Ouch. + + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + lFixWhite = FALSE; + + _cmsIOPrintf(m, "/RenderTable "); + + + WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); + + _cmsIOPrintf(m, " %d {} bind ", nChannels); + + for (i=1; i < nChannels; i++) + _cmsIOPrintf(m, "dup "); + + _cmsIOPrintf(m, "]\n"); + + + EmitIntent(m, Intent); + + _cmsIOPrintf(m, ">>\n"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); + } + + cmsPipelineFree(DeviceLink); + cmsDeleteTransform(xform); + + return 1; +} + + +// Builds a ASCII string containing colorant list in 0..1.0 range +static +void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[]) +{ + char Buff[32]; + cmsUInt32Number j; + + Colorant[0] = 0; + if (nColorant > cmsMAXCHANNELS) + nColorant = cmsMAXCHANNELS; + + for (j = 0; j < nColorant; j++) { + + snprintf(Buff, 31, "%.3f", Out[j] / 65535.0); + Buff[31] = 0; + strcat(Colorant, Buff); + if (j < nColorant - 1) + strcat(Colorant, " "); + + } +} + + +// Creates a PostScript color list from a named profile data. +// This is a HP extension. + +static +int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsHTRANSFORM xform; + cmsUInt32Number i, nColors, nColorant; + cmsUInt32Number OutputFormat; + char ColorName[cmsMAX_PATH]; + char Colorant[512]; + cmsNAMEDCOLORLIST* NamedColorList; + + + OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); + nColorant = T_CHANNELS(OutputFormat); + + + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); + if (xform == NULL) return 0; + + + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(NamedColorList); + + for (i=0; i < nColors; i++) { + + cmsUInt16Number In[1]; + cmsUInt16Number Out[cmsMAXCHANNELS]; + + In[0] = (cmsUInt16Number) i; + + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, Out, 1); + BuildColorantList(Colorant, nColorant, Out); + _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant); + } + + _cmsIOPrintf(m, " >>"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n"); + } + + cmsDeleteTransform(xform); + return 1; +} + + + +// This one does create a Color Rendering Dictionary. +// CRD are always LUT-Based, no matter if profile is +// implemented as matrix-shaper. + +static +cmsUInt32Number GenerateCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) +{ + cmsUInt32Number dwBytesUsed; + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); + } + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { + return 0; + } + } + else { + + // CRD are always implemented as LUT + + if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { + return 0; + } + } + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(mem, "%%%%EndResource\n"); + _cmsIOPrintf(mem, "\n%% CRD End\n"); + } + + // Done, keep memory usage + dwBytesUsed = mem ->UsedSpace; + + // Finally, return used byte count + return dwBytesUsed; + + cmsUNUSED_PARAMETER(ContextID); +} + + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io) +{ + cmsUInt32Number rc; + + + switch (Type) { + + case cmsPS_RESOURCE_CSA: + rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); + break; + + default: + case cmsPS_RESOURCE_CRD: + rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); + break; + } + + return rc; +} + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + void* Buffer, cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; + + // Set up the serialization engine + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem); + + // Get rid of memory stream + cmsCloseIOhandler(mem); + + return dwBytesUsed; +} + + + +// Does create a Color Space Array on XYZ colorspace for PostScript usage +cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + void* Buffer, + cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; + + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem); + + // Get rid of memory stream + cmsCloseIOhandler(mem); + + return dwBytesUsed; + +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmssamp.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmssamp.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmssamp.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmssamp.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,576 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +#define cmsmin(a, b) (((a) < (b)) ? (a) : (b)) +#define cmsmax(a, b) (((a) > (b)) ? (a) : (b)) + +// This file contains routines for resampling and LUT optimization, black point detection +// and black preservation. + +// Black point detection ------------------------------------------------------------------------- + + +// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs +static +cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + cmsHTRANSFORM xform; + cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE }; + cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 }; + cmsHPROFILE hProfiles[4]; + cmsUInt32Number Intents[4]; + + hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab; + Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC; + + xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents, + States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + + cmsCloseProfile(hLab); + return xform; +} + +// Use darker colorants to obtain black point. This works in the relative colorimetric intent and +// assumes more ink results in darker colors. No ink limit is assumed. +static +cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, + cmsUInt32Number Intent, + cmsCIEXYZ* BlackPoint, + cmsUInt32Number dwFlags) +{ + cmsUInt16Number *Black; + cmsHTRANSFORM xform; + cmsColorSpaceSignature Space; + cmsUInt32Number nChannels; + cmsUInt32Number dwFormat; + cmsHPROFILE hLab; + cmsCIELab Lab; + cmsCIEXYZ BlackXYZ; + cmsContext ContextID = cmsGetProfileContextID(hInput); + + // If the profile does not support input direction, assume Black point 0 + if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create a formatter which has n channels and no floating point + dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE); + + // Try to get black by using black colorant + Space = cmsGetColorSpace(hInput); + + // This function returns darker colorant in 16 bits for several spaces + if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + if (nChannels != T_CHANNELS(dwFormat)) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Lab will be used as the output space, but lab2 will avoid recursion + hLab = cmsCreateLab2ProfileTHR(ContextID, NULL); + if (hLab == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create the transform + xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat, + hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + // Something went wrong. Get rid of open resources and return zero as black + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Convert black to Lab + cmsDoTransform(xform, Black, &Lab, 1); + + // Force it to be neutral, clip to max. L* of 50 + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; + + // Free the resources + cmsDeleteTransform(xform); + + // Convert from Lab (which is now clipped) to XYZ. + cmsLab2XYZ(NULL, &BlackXYZ, &Lab); + + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; + + return TRUE; + + cmsUNUSED_PARAMETER(dwFlags); +} + +// Get a black point of output CMYK profile, discounting any ink-limiting embedded +// in the profile. For doing that, we use perceptual intent in input direction: +// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab +static +cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile) +{ + cmsHTRANSFORM hRoundTrip; + cmsCIELab LabIn, LabOut; + cmsCIEXYZ BlackXYZ; + + // Is the intent supported by the profile? + if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return TRUE; + } + + hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL); + if (hRoundTrip == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + LabIn.L = LabIn.a = LabIn.b = 0; + cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1); + + // Clip Lab to reasonable limits + if (LabOut.L > 50) LabOut.L = 50; + LabOut.a = LabOut.b = 0; + + cmsDeleteTransform(hRoundTrip); + + // Convert it to XYZ + cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); + + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; + + return TRUE; +} + +// This function shouldn't exist at all -- there is such quantity of broken +// profiles on black point tag, that we must somehow fix chromaticity to +// avoid huge tint when doing Black point compensation. This function does +// just that. There is a special flag for using black point tag, but turned +// off by default because it is bogus on most profiles. The detection algorithm +// involves to turn BP to neutral and to use only L component. +cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsProfileClassSignature devClass; + + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { + + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); + + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + + return TRUE; + } + + +#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG + + // v2, v4 rel/abs colorimetric + if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) && + Intent == INTENT_RELATIVE_COLORIMETRIC) { + + cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; + cmsCIELab Lab; + + // If black point is specified, then use it, + + BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag); + if (BlackPtr != NULL) { + + BlackXYZ = *BlackPtr; + _cmsReadMediaWhitePoint(&MediaWhite, hProfile); + + // Black point is absolute XYZ, so adapt to D50 to get PCS value + cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); + + // Force a=b=0 to get rid of any chroma + cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 + cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); + + if (BlackPoint != NULL) + *BlackPoint = TrustedBlackPoint; + + return TRUE; + } + } +#endif + + // That is about v2 profiles. + + // If output profile, discount ink-limiting and that's all + if (Intent == INTENT_RELATIVE_COLORIMETRIC && + (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) && + (cmsGetColorSpace(hProfile) == cmsSigCmykData)) + return BlackPointUsingPerceptualBlack(BlackPoint, hProfile); + + // Nope, compute BP using current intent. + return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); +} + + + +// --------------------------------------------------------------------------------------------------------- + +// Least Squares Fit of a Quadratic Curve to Data +// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html + +static +cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[]) +{ + double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; + double sum_y = 0, sum_yx = 0, sum_yx2 = 0; + double d, a, b, c; + int i; + cmsMAT3 m; + cmsVEC3 v, res; + + if (n < 4) return 0; + + for (i=0; i < n; i++) { + + double xn = x[i]; + double yn = y[i]; + + sum_x += xn; + sum_x2 += xn*xn; + sum_x3 += xn*xn*xn; + sum_x4 += xn*xn*xn*xn; + + sum_y += yn; + sum_yx += yn*xn; + sum_yx2 += yn*xn*xn; + } + + _cmsVEC3init(&m.v[0], n, sum_x, sum_x2); + _cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3); + _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4); + + _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2); + + if (!_cmsMAT3solve(&res, &m, &v)) return 0; + + + a = res.n[2]; + b = res.n[1]; + c = res.n[0]; + + if (fabs(a) < 1.0E-10) { + + return cmsmin(0, cmsmax(50, -c/b )); + } + else { + + d = b*b - 4.0 * a * c; + if (d <= 0) { + return 0; + } + else { + + double rt = (-b + sqrt(d)) / (2.0 * a); + + return cmsmax(0, cmsmin(50, rt)); + } + } + +} + + + +// Calculates the black point of a destination profile. +// This algorithm comes from the Adobe paper disclosing its black point compensation method. +cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsColorSpaceSignature ColorSpace; + cmsHTRANSFORM hRoundTrip = NULL; + cmsCIELab InitialLab, destLab, Lab; + cmsFloat64Number inRamp[256], outRamp[256]; + cmsFloat64Number MinL, MaxL; + cmsBool NearlyStraightMidrange = TRUE; + cmsFloat64Number yRamp[256]; + cmsFloat64Number x[256], y[256]; + cmsFloat64Number lo, hi; + int n, l; + cmsProfileClassSignature devClass; + + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { + + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); + + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + return TRUE; + } + + + // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document) + ColorSpace = cmsGetColorSpace(hProfile); + if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) || + (ColorSpace != cmsSigGrayData && + ColorSpace != cmsSigRgbData && + ColorSpace != cmsSigCmykData)) { + + // In this case, handle as input case + return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags); + } + + // It is one of the valid cases!, use Adobe algorithm + + + // Set a first guess, that should work on good profiles. + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + cmsCIEXYZ IniXYZ; + + // calculate initial Lab as source black point + if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) { + return FALSE; + } + + // convert the XYZ to lab + cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ); + + } else { + + // set the initial Lab to zero, that should be the black point for perceptual and saturation + InitialLab.L = 0; + InitialLab.a = 0; + InitialLab.b = 0; + } + + + // Step 2 + // ====== + + // Create a roundtrip. Define a Transform BT for all x in L*a*b* + hRoundTrip = CreateRoundtripXForm(hProfile, Intent); + if (hRoundTrip == NULL) return FALSE; + + // Compute ramps + + for (l=0; l < 256; l++) { + + Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0; + Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a)); + Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b)); + + cmsDoTransform(hRoundTrip, &Lab, &destLab, 1); + + inRamp[l] = Lab.L; + outRamp[l] = destLab.L; + } + + // Make monotonic + for (l = 254; l > 0; --l) { + outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]); + } + + // Check + if (! (outRamp[0] < outRamp[255])) { + + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // Test for mid range straight (only on relative colorimetric) + NearlyStraightMidrange = TRUE; + MinL = outRamp[0]; MaxL = outRamp[255]; + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + for (l=0; l < 256; l++) { + + if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) || + (fabs(inRamp[l] - outRamp[l]) < 4.0 ))) + NearlyStraightMidrange = FALSE; + } + + // If the mid range is straight (as determined above) then the + // DestinationBlackPoint shall be the same as initialLab. + // Otherwise, the DestinationBlackPoint shall be determined + // using curve fitting. + if (NearlyStraightMidrange) { + + cmsLab2XYZ(NULL, BlackPoint, &InitialLab); + cmsDeleteTransform(hRoundTrip); + return TRUE; + } + } + + + // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point, + // with a corner and a nearly straight line to the white point. + for (l=0; l < 256; l++) { + + yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL); + } + + // find the black point using the least squares error quadratic curve fitting + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + lo = 0.1; + hi = 0.5; + } + else { + + // Perceptual and saturation + lo = 0.03; + hi = 0.25; + } + + // Capture shadow points for the fitting. + n = 0; + for (l=0; l < 256; l++) { + + cmsFloat64Number ff = yRamp[l]; + + if (ff >= lo && ff < hi) { + x[n] = inRamp[l]; + y[n] = yRamp[l]; + n++; + } + } + + + // No suitable points + if (n < 3 ) { + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // fit and get the vertex of quadratic curve + Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y); + + if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative + Lab.L = 0; + } + + Lab.a = InitialLab.a; + Lab.b = InitialLab.b; + + cmsLab2XYZ(NULL, BlackPoint, &Lab); + + cmsDeleteTransform(hRoundTrip); + return TRUE; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmssm.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmssm.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmssm.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmssm.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,765 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// ------------------------------------------------------------------------ + +// Gamut boundary description by using Jan Morovic's Segment maxima method +// Many thanks to Jan for allowing me to use his algorithm. + +// r = C* +// alpha = Hab +// theta = L* + +#define SECTORS 16 // number of divisions in alpha and theta + +// Spherical coordinates +typedef struct { + + cmsFloat64Number r; + cmsFloat64Number alpha; + cmsFloat64Number theta; + +} cmsSpherical; + +typedef enum { + GP_EMPTY, + GP_SPECIFIED, + GP_MODELED + + } GDBPointType; + + +typedef struct { + + GDBPointType Type; + cmsSpherical p; // Keep also alpha & theta of maximum + +} cmsGDBPoint; + + +typedef struct { + + cmsContext ContextID; + cmsGDBPoint Gamut[SECTORS][SECTORS]; + +} cmsGDB; + + +// A line using the parametric form +// P = a + t*u +typedef struct { + + cmsVEC3 a; + cmsVEC3 u; + +} cmsLine; + + +// A plane using the parametric form +// Q = b + r*v + s*w +typedef struct { + + cmsVEC3 b; + cmsVEC3 v; + cmsVEC3 w; + +} cmsPlane; + + + +// -------------------------------------------------------------------------------------------- + +// ATAN2() which always returns degree positive numbers + +static +cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x) +{ + cmsFloat64Number a; + + // Deal with undefined case + if (x == 0.0 && y == 0.0) return 0; + + a = (atan2(y, x) * 180.0) / M_PI; + + while (a < 0) { + a += 360; + } + + return a; +} + +// Convert to spherical coordinates +static +void ToSpherical(cmsSpherical* sp, const cmsVEC3* v) +{ + + cmsFloat64Number L, a, b; + + L = v ->n[VX]; + a = v ->n[VY]; + b = v ->n[VZ]; + + sp ->r = sqrt( L*L + a*a + b*b ); + + if (sp ->r == 0) { + sp ->alpha = sp ->theta = 0; + return; + } + + sp ->alpha = _cmsAtan2(a, b); + sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L); +} + + +// Convert to cartesian from spherical +static +void ToCartesian(cmsVEC3* v, const cmsSpherical* sp) +{ + cmsFloat64Number sin_alpha; + cmsFloat64Number cos_alpha; + cmsFloat64Number sin_theta; + cmsFloat64Number cos_theta; + cmsFloat64Number L, a, b; + + sin_alpha = sin((M_PI * sp ->alpha) / 180.0); + cos_alpha = cos((M_PI * sp ->alpha) / 180.0); + sin_theta = sin((M_PI * sp ->theta) / 180.0); + cos_theta = cos((M_PI * sp ->theta) / 180.0); + + a = sp ->r * sin_theta * sin_alpha; + b = sp ->r * sin_theta * cos_alpha; + L = sp ->r * cos_theta; + + v ->n[VX] = L; + v ->n[VY] = a; + v ->n[VZ] = b; +} + + +// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector +// The limits are the centers of each sector, so +static +void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta) +{ + *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) ); + *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); + + if (*alpha >= SECTORS) + *alpha = SECTORS-1; + if (*theta >= SECTORS) + *theta = SECTORS-1; +} + + +// Line determined by 2 points +static +void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b) +{ + + _cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]); + _cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX], + b ->n[VY] - a ->n[VY], + b ->n[VZ] - a ->n[VZ]); +} + + +// Evaluate parametric line +static +void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t) +{ + p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX]; + p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY]; + p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ]; +} + + + +/* + Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1) + http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm + + Copyright 2001, softSurfer (www.softsurfer.com) + This code may be freely used and modified for any purpose + providing that this copyright notice is included with it. + SoftSurfer makes no warranty for this code, and cannot be held + liable for any real or imagined damage resulting from its use. + Users of this code must verify correctness for their application. + +*/ + +static +cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2) +{ + cmsFloat64Number a, b, c, d, e, D; + cmsFloat64Number sc, sN, sD; + //cmsFloat64Number tc; // left for future use + cmsFloat64Number tN, tD; + cmsVEC3 w0; + + _cmsVEC3minus(&w0, &line1 ->a, &line2 ->a); + + a = _cmsVEC3dot(&line1 ->u, &line1 ->u); + b = _cmsVEC3dot(&line1 ->u, &line2 ->u); + c = _cmsVEC3dot(&line2 ->u, &line2 ->u); + d = _cmsVEC3dot(&line1 ->u, &w0); + e = _cmsVEC3dot(&line2 ->u, &w0); + + D = a*c - b * b; // Denominator + sD = tD = D; // default sD = D >= 0 + + if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel + + sN = 0.0; // force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } + else { // get the closest points on the infinite lines + + sN = (b*e - c*d); + tN = (a*e - b*d); + + if (sN < 0.0) { // sc < 0 => the s=0 edge is visible + + sN = 0.0; + tN = e; + tD = c; + } + else if (sN > sD) { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) { // tc < 0 => the t=0 edge is visible + + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + sN = 0.0; + else if (-d > a) + sN = sD; + else { + sN = -d; + sD = a; + } + } + else if (tN > tD) { // tc > 1 => the t=1 edge is visible + + tN = tD; + + // recompute sc for this edge + if ((-d + b) < 0.0) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); + //tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); // left for future use. + + GetPointOfLine(r, line1, sc); + return TRUE; +} + + + +// ------------------------------------------------------------------ Wrapper + + +// Allocate & free structure +cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID) +{ + cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB)); + if (gbd == NULL) return NULL; + + gbd -> ContextID = ContextID; + + return (cmsHANDLE) gbd; +} + + +void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + if (hGBD != NULL) + _cmsFree(gbd->ContextID, (void*) gbd); +} + + +// Auxiliary to retrieve a pointer to the segmentr containing the Lab value +static +cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp) +{ + cmsVEC3 v; + int alpha, theta; + + // Housekeeping + _cmsAssert(gbd != NULL); + _cmsAssert(Lab != NULL); + _cmsAssert(sp != NULL); + + // Center L* by subtracting half of its domain, that's 50 + _cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); + + // Convert to spherical coordinates + ToSpherical(sp, &v); + + if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); + return NULL; + } + + // On which sector it falls? + QuantizeToSector(sp, &alpha, &theta); + + if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); + return NULL; + } + + // Get pointer to the sector + return &gbd ->Gamut[theta][alpha]; +} + +// Add a point to gamut descriptor. Point to add is in Lab color space. +// GBD is centered on a=b=0 and L*=50 +cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, add it + if (ptr ->Type == GP_EMPTY) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + else { + + + // Substitute only if radius is greater + if (sp.r > ptr -> p.r) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + } + + return TRUE; +} + +// Check if a given point falls inside gamut +cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, return no data + if (ptr ->Type == GP_EMPTY) return FALSE; + + // In gamut only if radius is greater + + return (sp.r <= ptr -> p.r); +} + +// ----------------------------------------------------------------------------------------------------------------------- + +// Find near sectors. The list of sectors found is returned on Close[]. +// The function returns the number of sectors as well. + +// 24 9 10 11 12 +// 23 8 1 2 13 +// 22 7 * 3 14 +// 21 6 5 4 15 +// 20 19 18 17 16 +// +// Those are the relative movements +// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, +// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1}, +// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0}, +// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1}, +// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}}; + + +static +const struct _spiral { + + int AdvX, AdvY; + + } Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, + {-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, + {+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2}, + {-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} }; + +#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral)) + +static +int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[]) +{ + int nSectors = 0; + int a, t; + cmsUInt32Number i; + cmsGDBPoint* pt; + + for (i=0; i < NSTEPS; i++) { + + a = alpha + Spiral[i].AdvX; + t = theta + Spiral[i].AdvY; + + // Cycle at the end + a %= SECTORS; + t %= SECTORS; + + // Cycle at the begin + if (a < 0) a = SECTORS + a; + if (t < 0) t = SECTORS + t; + + pt = &gbd ->Gamut[t][a]; + + if (pt -> Type != GP_EMPTY) { + + Close[nSectors++] = pt; + } + } + + return nSectors; +} + + +// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid +static +cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta) +{ + cmsSpherical sp; + cmsVEC3 Lab; + cmsVEC3 Centre; + cmsLine ray; + int nCloseSectors; + cmsGDBPoint* Close[NSTEPS + 1]; + cmsSpherical closel, templ; + cmsLine edge; + int k, m; + + // Is that point already specified? + if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE; + + // Fill close points + nCloseSectors = FindNearSectors(gbd, alpha, theta, Close); + + + // Find a central point on the sector + sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS); + sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS); + sp.r = 50.0; + + // Convert to Cartesian + ToCartesian(&Lab, &sp); + + // Create a ray line from centre to this point + _cmsVEC3init(&Centre, 50.0, 0, 0); + LineOf2Points(&ray, &Lab, &Centre); + + // For all close sectors + closel.r = 0.0; + closel.alpha = 0; + closel.theta = 0; + + for (k=0; k < nCloseSectors; k++) { + + for(m = k+1; m < nCloseSectors; m++) { + + cmsVEC3 temp, a1, a2; + + // A line from sector to sector + ToCartesian(&a1, &Close[k]->p); + ToCartesian(&a2, &Close[m]->p); + + LineOf2Points(&edge, &a1, &a2); + + // Find a line + ClosestLineToLine(&temp, &ray, &edge); + + // Convert to spherical + ToSpherical(&templ, &temp); + + + if ( templ.r > closel.r && + templ.theta >= (theta*180.0/SECTORS) && + templ.theta <= ((theta+1)*180.0/SECTORS) && + templ.alpha >= (alpha*360.0/SECTORS) && + templ.alpha <= ((alpha+1)*360.0/SECTORS)) { + + closel = templ; + } + } + } + + gbd ->Gamut[theta][alpha].p = closel; + gbd ->Gamut[theta][alpha].Type = GP_MODELED; + + return TRUE; + +} + + +// Interpolate missing parts. The algorithm fist computes slices at +// theta=0 and theta=Max. +cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) +{ + int alpha, theta; + cmsGDB* gbd = (cmsGDB*) hGBD; + + _cmsAssert(hGBD != NULL); + + // Interpolate black + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; + } + + // Interpolate white + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; + } + + + // Interpolate Mid + for (theta = 1; theta < SECTORS; theta++) { + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; + } + } + + // Done + return TRUE; + + cmsUNUSED_PARAMETER(dwFlags); +} + + + + +// -------------------------------------------------------------------------------------------------------- + +// Great for debug, but not suitable for real use + +#if 0 +cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname) +{ + FILE* fp; + int i, j; + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* pt; + + fp = fopen (fname, "wt"); + if (fp == NULL) + return FALSE; + + fprintf (fp, "#VRML V2.0 utf8\n"); + + // set the viewing orientation and distance + fprintf (fp, "DEF CamTest Group {\n"); + fprintf (fp, "\tchildren [\n"); + fprintf (fp, "\t\tDEF Cameras Group {\n"); + fprintf (fp, "\t\t\tchildren [\n"); + fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); + fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); + fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); + fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t]\n"); + fprintf (fp, "\t\t},\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the background stuff + fprintf (fp, "Background {\n"); + fprintf (fp, "\tskyColor [\n"); + fprintf (fp, "\t\t.5 .5 .5\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the shape stuff + fprintf (fp, "Transform {\n"); + fprintf (fp, "\tscale .3 .3 .3\n"); + fprintf (fp, "\tchildren [\n"); + + // Draw the axes as a shape: + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); + fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t\tcoordIndex [\n"); + fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + + + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry PointSet {\n"); + + // fill in the points here + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + + // We need to transverse all gamut hull. + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + ToCartesian(&v, &pt ->p); + + fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]); + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + + } + + fprintf (fp, "\t\t\t\t}\n"); + + + + // fill in the face colors + fprintf (fp, "\t\t\t\tcolor Color {\n"); + fprintf (fp, "\t\t\t\t\tcolor [\n"); + + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + + + ToCartesian(&v, &pt ->p); + + + if (pt ->Type == GP_EMPTY) + fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0); + else + if (pt ->Type == GP_MODELED) + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); + else { + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0); + + } + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + } + fprintf (fp, "\t\t\t}\n"); + + + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + fclose (fp); + + return TRUE; +} +#endif + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmstypes.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmstypes.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmstypes.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmstypes.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,5656 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Tag Serialization ----------------------------------------------------------------------------- +// This file implements every single tag and tag type as described in the ICC spec. Some types +// have been deprecated, like ncl and Data. There is no implementation for those types as there +// are no profiles holding them. The programmer can also extend this list by defining his own types +// by using the appropriate plug-in. There are three types of plug ins regarding that. First type +// allows to define new tags using any existing type. Next plug-in type allows to define new types +// and the third one is very specific: allows to extend the number of elements in the multiprocessing +// elements special type. +//-------------------------------------------------------------------------------------------------- + +// Some broken types +#define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8) +#define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00) + +// This is the linked list that keeps track of the defined types +typedef struct _cmsTagTypeLinkedList_st { + + cmsTagTypeHandler Handler; + struct _cmsTagTypeLinkedList_st* Next; + +} _cmsTagTypeLinkedList; + +// Some macros to define callbacks. +#define READ_FN(x) Type_##x##_Read +#define WRITE_FN(x) Type_##x##_Write +#define FREE_FN(x) Type_##x##_Free +#define DUP_FN(x) Type_##x##_Dup + +// Helper macro to define a handler. Callbacks do have a fixed naming convention. +#define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 } + +// Helper macro to define a MPE handler. Callbacks do have a fixed naming convention +#define TYPE_MPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 } + +// Infinites +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) + + +// Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head +static +cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) +{ + cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); + _cmsTagTypeLinkedList *pt; + + // Calling the function with NULL as plug-in would unregister the plug in. + if (Data == NULL) { + + // There is no need to set free the memory, as pool is destroyed as a whole. + ctx ->TagTypes = NULL; + return TRUE; + } + + // Registering happens in plug-in memory pool. + pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Handler = Plugin ->Handler; + pt ->Next = ctx ->TagTypes; + + ctx ->TagTypes = pt; + + return TRUE; +} + +// Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons +// made by plug-ins and then the built-in defaults. +static +cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) +{ + _cmsTagTypeLinkedList* pt; + + for (pt = PluginLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + for (pt = DefaultLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + return NULL; +} + + +// Auxiliary to convert UTF-32 to UTF-16 in some cases +static +cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(!(Array == NULL && n > 0)); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE; + } + + return TRUE; +} + +// Auxiliary to read an array of wchar_t +static +cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) +{ + cmsUInt32Number i; + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + + if (!_cmsReadUInt16Number(io, &tmp)) return FALSE; + Array[i] = (wchar_t) tmp; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +// To deal with position tables +typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag); + +// Helper function to deal with position tables as described in ICC spec 4.3 +// A table of n elements is read, where first comes n records containing offsets and sizes and +// then a block containing the data itself. This allows to reuse same data in more than one entry +static +cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + cmsUInt32Number currentPosition; + + currentPosition = io->Tell(io); + + // Verify there is enough space left to read at least two cmsUInt32Number items for Count items. + if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) + return FALSE; + + // Let's take the offsets to each element + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error; + if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error; + + ElementOffsets[i] += BaseOffset; + } + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!io -> Seek(io, ElementOffsets[i])) goto Error; + + // This is the reader callback + if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error; + } + + // Success + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + +// Same as anterior, but for write position tables +static +cmsBool WritePositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number SizeOfTag, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number DirectoryPos, CurrentPos, Before; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + + // Create table + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Keep starting position of curve offsets + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < Count; i++) { + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each element. Keep track of the size as well. + for (i=0; i < Count; i++) { + + Before = io ->Tell(io); + ElementOffsets[i] = Before - BaseOffset; + + // Callback to write... + if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error; + + // Now the size + ElementSizes[i] = io ->Tell(io) - Before; + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < Count; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + + +// ******************************************************************************** +// Type XYZ. Only one value is allowed +// ******************************************************************************** + +//The XYZType contains an array of three encoded values for the XYZ tristimulus +//values. Tristimulus values must be non-negative. The signed encoding allows for +//implementation optimizations by minimizing the number of fixed formats. + + +static +void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIEXYZ* xyz; + + *nItems = 0; + xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ)); + if (xyz == NULL) return NULL; + + if (!_cmsReadXYZNumber(io, xyz)) { + _cmsFree(self ->ContextID, xyz); + return NULL; + } + + *nItems = 1; + return (void*) xyz; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +static +cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data) +{ + return cmsSigXYZType; + + cmsUNUSED_PARAMETER(ICCVersion); + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type chromaticity. Only one value is allowed +// ******************************************************************************** +// The chromaticity tag type provides basic chromaticity data and type of +// phosphors or colorants of a monitor to applications and utilities. + +static +void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIExyYTRIPLE* chrm; + cmsUInt16Number nChans, Table; + + *nItems = 0; + chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE)); + if (chrm == NULL) return NULL; + + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + + // Let's recover from a bug introduced in early versions of lcms1 + if (nChans == 0 && SizeOfTag == 32) { + + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + } + + if (nChans != 3) goto Error; + + if (!_cmsReadUInt16Number(io, &Table)) goto Error; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error; + + chrm ->Red.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error; + + chrm ->Green.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error; + + chrm ->Blue.Y = 1.0; + + *nItems = 1; + return (void*) chrm; + +Error: + _cmsFree(self ->ContextID, (void*) chrm); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io) +{ + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE; + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE; + + return TRUE; +} + +static +cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr; + + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table + + if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigColorantOrderType +// ******************************************************************************** + +// This is an optional tag which specifies the laydown order in which colorants will +// be printed on an n-colorant device. The laydown order may be the same as the +// channel generation order listed in the colorantTableTag or the channel order of a +// colour space such as CMYK, in which case this tag is not needed. When this is not +// the case (for example, ink-towers sometimes use the order KCMY), this tag may be +// used to specify the laydown order of the colorants. + + +static +void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number* ColorantOrder; + cmsUInt32Number Count; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (Count > cmsMAXCHANNELS) return NULL; + + ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number)); + if (ColorantOrder == NULL) return NULL; + + // We use FF as end marker + memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) { + + _cmsFree(self ->ContextID, (void*) ColorantOrder); + return NULL; + } + + *nItems = 1; + return (void*) ColorantOrder; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr; + cmsUInt32Number i, sz, Count; + + // Get the length + for (Count=i=0; i < cmsMAXCHANNELS; i++) { + if (ColorantOrder[i] != 0xFF) Count++; + } + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + + sz = Count * sizeof(cmsUInt8Number); + if (!io -> Write(io, sz, ColorantOrder)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigS15Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit fixed point quantity. +// The number of values is determined from the size of the tag. + +static +void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &array_double[i])) { + + _cmsFree(self ->ContextID, array_double); + return NULL; + } + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + + +static +void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigU16Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit quantity. +// The number of values is determined from the size of the tag. + + +static +void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number v; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsReadUInt32Number(io, &v)) { + _cmsFree(self ->ContextID, (void*) array_double); + return NULL; + } + + // Convert to cmsFloat64Number + array_double[i] = (cmsFloat64Number) (v / 65536.0); + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5); + + if (!_cmsWriteUInt32Number(io, v)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + +static +void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigSignatureType +// ******************************************************************************** +// +// The signatureType contains a four-byte sequence, Sequences of less than four +// characters are padded at the end with spaces, 20h. +// Typically this type is used for registered tags that can be displayed on many +// development systems as a sequence of four characters. + +static +void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature)); + if (SigPtr == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, SigPtr)) return NULL; + *nItems = 1; + + return SigPtr; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSignature* SigPtr = (cmsSignature*) Ptr; + + return _cmsWriteUInt32Number(io, *SigPtr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature)); +} + +static +void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigTextType +// ******************************************************************************** +// +// The textType is a simple text structure that contains a 7-bit ASCII text string. +// The length of the string is obtained by subtracting 8 from the element size portion +// of the tag itself. This string must be terminated with a 00h byte. + +static +void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + + // Create a container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + *nItems = 0; + + // We need to store the "\0" at the end, so +1 + if (SizeOfTag == UINT_MAX) goto Error; + + Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (Text == NULL) goto Error; + + if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error; + + // Make sure text is properly ended + Text[SizeOfTag] = 0; + *nItems = 1; + + // Keep the result + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + + _cmsFree(self ->ContextID, Text); + return (void*) mlu; + +Error: + if (mlu != NULL) + cmsMLUfree(mlu); + if (Text != NULL) + _cmsFree(self ->ContextID, Text); + + return NULL; +} + +// The conversion implies to choose a language. So, we choose the actual language. +static +cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsUInt32Number size; + cmsBool rc; + char* Text; + + // Get the size of the string. Note there is an extra "\0" at the end + size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + if (size == 0) return FALSE; // Cannot be zero! + + // Create memory + Text = (char*) _cmsMalloc(self ->ContextID, size); + if (Text == NULL) return FALSE; + + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); + + // Write it, including separator + rc = io ->Write(io, size, Text); + + _cmsFree(self ->ContextID, Text); + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + +static +cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigDataType +// ******************************************************************************** + +// General purpose data type +static +void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCData* BinData; + cmsUInt32Number LenOfData; + + *nItems = 0; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + LenOfData = SizeOfTag - sizeof(cmsUInt32Number); + if (LenOfData > INT_MAX) return NULL; + + BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1); + if (BinData == NULL) return NULL; + + BinData ->len = LenOfData; + if (!_cmsReadUInt32Number(io, &BinData->flag)) { + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) { + + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + *nItems = 1; + + return (void*) BinData; +} + + +static +cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE; + + return io ->Write(io, BinData ->len, BinData ->data); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigTextDescriptionType +// ******************************************************************************** + +static +void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + cmsUInt32Number AsciiCount; + cmsUInt32Number i, UnicodeCode, UnicodeCount; + cmsUInt16Number ScriptCodeCode, Dummy; + cmsUInt8Number ScriptCodeCount; + + *nItems = 0; + + // One dword should be there + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + // Read len of ASCII + if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for size + if (SizeOfTag < AsciiCount) return NULL; + + // All seems Ok, allocate the container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + // As many memory as size of tag + Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1); + if (Text == NULL) goto Error; + + // Read it + if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error; + SizeOfTag -= AsciiCount; + + // Make sure there is a terminator + Text[AsciiCount] = 0; + + // Set the MLU entry. From here we can be tolerant to wrong types + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + _cmsFree(self ->ContextID, (void*) Text); + Text = NULL; + + // Skip Unicode code + if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; + SizeOfTag -= 2* sizeof(cmsUInt32Number); + + if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; + + for (i=0; i < UnicodeCount; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done; + } + SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number); + + // Skip ScriptCode code if present. Some buggy profiles does have less + // data that stricttly required. We need to skip it as this type may come + // embedded in other types. + + if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) { + + if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done; + if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done; + + // Skip rest of tag + for (i=0; i < 67; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error; + } + } + +Done: + + *nItems = 1; + return mlu; + +Error: + if (Text) _cmsFree(self ->ContextID, (void*) Text); + if (mlu) cmsMLUfree(mlu); + return NULL; +} + + +// This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it +static +cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + char *Text = NULL; + wchar_t *Wide = NULL; + cmsUInt32Number len, len_text, len_tag_requirement, len_aligned; + cmsBool rc = FALSE; + char Filler[68]; + + // Used below for writing zeroes + memset(Filler, 0, sizeof(Filler)); + + // Get the len of string + len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + + // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data + //(see clause 4.1 for the definition of 'aligned'). Because the Unicode language + // code and Unicode count immediately follow the ASCII description, their + // alignment is not correct if the ASCII count is not a multiple of four. The + // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and + // writing software must be written carefully in order to handle these alignment + // problems. + // + // The above last sentence suggest to handle alignment issues in the + // parser. The provided example (Table 69 on Page 60) makes this clear. + // The padding only in the ASCII count is not sufficient for a aligned tag + // size, with the same text size in ASCII and Unicode. + + // Null strings + if (len <= 0) { + + Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char)); + Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t)); + } + else { + // Create independent buffers + Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char)); + if (Text == NULL) goto Error; + + Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t)); + if (Wide == NULL) goto Error; + + // Get both representations. + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char)); + cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t)); + } + + // Tell the real text len including the null terminator and padding + len_text = (cmsUInt32Number) strlen(Text) + 1; + // Compute an total tag size requirement + len_tag_requirement = (8+4+len_text+4+4+2*len_text+2+1+67); + len_aligned = _cmsALIGNLONG(len_tag_requirement); + + // * cmsUInt32Number count; * Description length + // * cmsInt8Number desc[count] * NULL terminated ascii string + // * cmsUInt32Number ucLangCode; * UniCode language code + // * cmsUInt32Number ucCount; * UniCode description length + // * cmsInt16Number ucDesc[ucCount];* The UniCode description + // * cmsUInt16Number scCode; * ScriptCode code + // * cmsUInt8Number scCount; * ScriptCode count + // * cmsInt8Number scDesc[67]; * ScriptCode Description + + if (!_cmsWriteUInt32Number(io, len_text)) goto Error; + if (!io ->Write(io, len_text, Text)) goto Error; + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode + + if (!_cmsWriteUInt32Number(io, len_text)) goto Error; + // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t) + if (!_cmsWriteWCharArray(io, len_text, Wide)) goto Error; + + // ScriptCode Code & count (unused) + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + if (!_cmsWriteUInt8Number(io, 0)) goto Error; + + if (!io ->Write(io, 67, Filler)) goto Error; + + // possibly add pad at the end of tag + if(len_aligned - len_tag_requirement > 0) + if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error; + + rc = TRUE; + +Error: + if (Text) _cmsFree(self ->ContextID, Text); + if (Wide) _cmsFree(self ->ContextID, Wide); + + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + + +static +cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextDescriptionType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigCurveType +// ******************************************************************************** + +static +void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number Count; + cmsToneCurve* NewGamma; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + switch (Count) { + + case 0: // Linear. + { + cmsFloat64Number SingleGamma = 1.0; + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + if (!NewGamma) return NULL; + *nItems = 1; + return NewGamma; + } + + case 1: // Specified as the exponent of gamma function + { + cmsUInt16Number SingleGammaFixed; + cmsFloat64Number SingleGamma; + + if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL; + SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed); + + *nItems = 1; + return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + } + + default: // Curve + + if (Count > 0x7FFF) + return NULL; // This is to prevent bad guys for doing bad things + + NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); + if (!NewGamma) return NULL; + + if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { + cmsFreeToneCurve(NewGamma); + return NULL; + } + + *nItems = 1; + return NewGamma; + } + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + + if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) { + + // Single gamma, preserve number + cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]); + + if (!_cmsWriteUInt32Number(io, 1)) return FALSE; + if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE; + return TRUE; + + } + + if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE; + return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigParametricCurveType +// ******************************************************************************** + + +// Decide which curve type to use on writing +static +cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Data; + + if (ICCVersion < 4.0) return cmsSigCurveType; + if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric + if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves + if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves + + return cmsSigParametricCurveType; +} + +static +void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + static const int ParamsByType[] = { 1, 3, 4, 5, 7 }; + cmsFloat64Number Params[10]; + cmsUInt16Number Type; + int i, n; + cmsToneCurve* NewGamma; + + if (!_cmsReadUInt16Number(io, &Type)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved + + if (Type > 4) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type); + return NULL; + } + + memset(Params, 0, sizeof(Params)); + n = ParamsByType[Type]; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL; + } + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params); + + *nItems = 1; + return NewGamma; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + int i, nParams, typen; + static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; + + typen = Curve -> Segments[0].Type; + + if (Curve ->nSegments > 1 || typen < 1) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written"); + return FALSE; + } + + if (typen > 5) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve"); + return FALSE; + } + + nParams = ParamsByType[typen]; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved + + for (i=0; i < nParams; i++) { + + if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigDateTimeType +// ******************************************************************************** + +// A 12-byte value representation of the time and date, where the byte usage is assigned +// as specified in table 1. The actual values are encoded as 16-bit unsigned integers +// (uInt16Number - see 5.1.6). +// +// All the dateTimeNumber values in a profile shall be in Coordinated Universal Time +// (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local +// time to UTC when setting these values. Programmes that display these values may show +// the dateTimeNumber as UTC, show the equivalent local time (at current locale), or +// display both UTC and local versions of the dateTimeNumber. + +static +void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsDateTimeNumber timestamp; + struct tm * NewDateTime; + + *nItems = 0; + NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm)); + if (NewDateTime == NULL) return NULL; + + if (io->Read(io, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; + + _cmsDecodeDateTimeNumber(×tamp, NewDateTime); + + *nItems = 1; + return NewDateTime; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + struct tm * DateTime = (struct tm*) Ptr; + cmsDateTimeNumber timestamp; + + _cmsEncodeDateTimeNumber(×tamp, DateTime); + if (!io ->Write(io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + + +// ******************************************************************************** +// Type icMeasurementType +// ******************************************************************************** + +/* +The measurementType information refers only to the internal profile data and is +meant to provide profile makers an alternative to the default measurement +specifications. +*/ + +static +void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCMeasurementConditions mc; + + + memset(&mc, 0, sizeof(mc)); + + if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL; + if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL; + if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL; + + *nItems = 1; + return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr; + + if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE; + if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiLocalizedUnicodeType +// ******************************************************************************** +// +// Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from +// Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be +// taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance) +// + +static +void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu; + cmsUInt32Number Count, RecLen, NumOfWchar; + cmsUInt32Number SizeOfHeader; + cmsUInt32Number Len, Offset; + cmsUInt32Number i; + wchar_t* Block; + cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (!_cmsReadUInt32Number(io, &RecLen)) return NULL; + + if (RecLen != 12) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported."); + return NULL; + } + + mlu = cmsMLUalloc(self ->ContextID, Count); + if (mlu == NULL) return NULL; + + mlu ->UsedEntries = Count; + + SizeOfHeader = 12 * Count + sizeof(_cmsTagBase); + LargestPosition = 0; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error; + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error; + + // Now deal with Len and offset. + if (!_cmsReadUInt32Number(io, &Len)) goto Error; + if (!_cmsReadUInt32Number(io, &Offset)) goto Error; + + // Check for overflow + if (Offset < (SizeOfHeader + 8)) goto Error; + if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error; + + // True begin of the string + BeginOfThisString = Offset - SizeOfHeader - 8; + + // Adjust to wchar_t elements + mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + + // To guess maximum size, add offset + len + EndOfThisString = BeginOfThisString + Len; + if (EndOfThisString > LargestPosition) + LargestPosition = EndOfThisString; + } + + // Now read the remaining of tag and fill all strings. Subtract the directory + SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + if (SizeOfTag == 0) + { + Block = NULL; + NumOfWchar = 0; + + } + else + { + Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); + if (Block == NULL) goto Error; + NumOfWchar = SizeOfTag / sizeof(wchar_t); + if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; + } + + mlu ->MemPool = Block; + mlu ->PoolSize = SizeOfTag; + mlu ->PoolUsed = SizeOfTag; + + *nItems = 1; + return (void*) mlu; + +Error: + if (mlu) cmsMLUfree(mlu); + return NULL; +} + +static +cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu =(cmsMLU*) Ptr; + cmsUInt32Number HeaderSize; + cmsUInt32Number Len, Offset; + cmsUInt32Number i; + + if (Ptr == NULL) { + + // Empty placeholder + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + return TRUE; + } + + if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + + HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase); + + for (i=0; i < mlu ->UsedEntries; i++) { + + Len = mlu ->Entries[i].Len; + Offset = mlu ->Entries[i].StrW; + + Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t); + Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8; + + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE; + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE; + if (!_cmsWriteUInt32Number(io, Len)) return FALSE; + if (!_cmsWriteUInt32Number(io, Offset)) return FALSE; + } + + if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLut8Type +// ******************************************************************************** + +// Decide which LUT type to use on writing +static +cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutAtoBType; + } +} + +static +cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutBtoAType; + } +} + +/* +This structure represents a colour transform using tables of 8-bit precision. +This type contains four processing elements: a 3 by 3 matrix (which shall be +the identity matrix unless the input colour space is XYZ), a set of one dimensional +input tables, a multidimensional lookup table, and a set of one dimensional output +tables. Data is processed using these elements via the following sequence: +(matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) + +Byte Position Field Length (bytes) Content Encoded as... +8 1 Number of Input Channels (i) uInt8Number +9 1 Number of Output Channels (o) uInt8Number +10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number +11 1 Reserved for padding (fill with 00h) + +12..15 4 Encoded e00 parameter s15Fixed16Number +*/ + + +// Read 8 bit tables as gamma functions +static +cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels) +{ + cmsUInt8Number* Temp = NULL; + cmsUInt32Number i, j; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + if (nChannels > cmsMAXCHANNELS) return FALSE; + if (nChannels <= 0) return FALSE; + + memset(Tables, 0, sizeof(Tables)); + + Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256); + if (Temp == NULL) return FALSE; + + for (i=0; i < nChannels; i++) { + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + if (Tables[i] == NULL) goto Error; + } + + for (i=0; i < nChannels; i++) { + + if (io ->Read(io, Temp, 256, 1) != 1) goto Error; + + for (j=0; j < 256; j++) + Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]); + } + + _cmsFree(ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + if (Temp) _cmsFree(ContextID, Temp); + return FALSE; +} + + +static +cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables) +{ + int j; + cmsUInt32Number i; + cmsUInt8Number val; + + for (i=0; i < n; i++) { + + if (Tables) { + + // Usual case of identity curves + if ((Tables ->TheCurves[i]->nEntries == 2) && + (Tables->TheCurves[i]->Table16[0] == 0) && + (Tables->TheCurves[i]->Table16[1] == 65535)) { + + for (j=0; j < 256; j++) { + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE; + } + } + else + if (Tables ->TheCurves[i]->nEntries != 256) { + cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization"); + return FALSE; + } + else + for (j=0; j < 256; j++) { + + val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]); + + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + return TRUE; +} + + +// Check overflow +static +cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) +{ + cmsUInt32Number rv = 1, rc; + + if (a == 0) return 0; + if (n == 0) return 0; + + for (; b > 0; b--) { + + rv *= a; + + // Check for overflow + if (rv > UINT_MAX / a) return (cmsUInt32Number) -1; + + } + + rc = rv * n; + + if (rv != rc / n) return (cmsUInt32Number) -1; + return rc; +} + + +// That will create a MPE LUT with Matrix, pre tables, CLUT and post tables. +// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust +// PCS on BToAxx tags and AtoB if abstract. We need to fix input direction. + +static +void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsUInt8Number* Temp = NULL; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize, i; + cmsFloat64Number Matrix[3*3]; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error; + + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) goto Error; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty Pipeline + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates if not identity... + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + // Get input tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error; + + // Get 3D CLUT. Check the overflow.... + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *PtrW, *T; + + PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize); + if (Temp == NULL) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (io ->Read(io, Temp, nTabSize, 1) != 1) { + _cmsFree(self ->ContextID, T); + _cmsFree(self ->ContextID, Temp); + goto Error; + } + + for (i = 0; i < nTabSize; i++) { + + *PtrW++ = FROM_8_TO_16(Temp[i]); + } + _cmsFree(self ->ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { + _cmsFree(self ->ContextID, T); + goto Error; + } + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin. +static +cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number j, nTabSize, i, n; + cmsUInt8Number val; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + cmsUInt32Number clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe ->Type == cmsSigMatrixElemType) { + + if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE; + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); + return FALSE; + } + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + n = NewLUT->InputChannels * NewLUT->OutputChannels; + + if (MatMPE != NULL) { + + for (i = 0; i < 9; i++) + { + if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; + } + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + // The prelinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE; + + nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + + // The 3D CLUT. + if (clut != NULL) { + + for (j=0; j < nTabSize; j++) { + + val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]); + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + + // The postlinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigLut16Type +// ******************************************************************************** + +// Read 16 bit tables as gamma functions +static +cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, + cmsUInt32Number nChannels, cmsUInt32Number nEntries) +{ + cmsUInt32Number i; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + // Maybe an empty table? (this is a lcms extension) + if (nEntries <= 0) return TRUE; + + // Check for malicious profiles + if (nEntries < 2) return FALSE; + if (nChannels > cmsMAXCHANNELS) return FALSE; + + // Init table to zero + memset(Tables, 0, sizeof(Tables)); + + for (i=0; i < nChannels; i++) { + + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL); + if (Tables[i] == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error; + } + + + // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code) + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + return FALSE; +} + +static +cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables) +{ + cmsUInt32Number j; + cmsUInt32Number i; + cmsUInt16Number val; + cmsUInt32Number nEntries; + + _cmsAssert(Tables != NULL); + + nEntries = Tables->TheCurves[0]->nEntries; + + for (i=0; i < Tables ->nCurves; i++) { + + for (j=0; j < nEntries; j++) { + + val = Tables->TheCurves[i]->Table16[j]; + if (!_cmsWriteUInt16Number(io, val)) return FALSE; + } + } + return TRUE; + + cmsUNUSED_PARAMETER(ContextID); +} + +static +void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize; + cmsFloat64Number Matrix[3*3]; + cmsUInt16Number InputEntries, OutputEntries; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates on 3 channels + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error; + if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error; + + if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error; + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Get input tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error; + + // Get 3D CLUT + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *T; + + T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nTabSize, T)) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { + _cmsFree(self ->ContextID, T); + goto Error; + } + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin. +// Some empty defaults are created for missing parts + +static +cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number nTabSize; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + cmsUInt32Number i, InputChannels, OutputChannels, clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) { + + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE; + mpe = mpe -> Next; + } + + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); + return FALSE; + } + + InputChannels = cmsPipelineInputChannels(NewLUT); + OutputChannels = cmsPipelineOutputChannels(NewLUT); + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + if (MatMPE != NULL) { + + for (i = 0; i < 9; i++) + { + if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; + } + + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + + if (PreMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + } + + if (PostMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + } + + // The prelinearization table + + if (PreMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE; + } + else { + for (i=0; i < InputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + nTabSize = uipow(OutputChannels, clutPoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + // The 3D CLUT. + if (clut != NULL) { + if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE; + } + } + + // The postlinearization table + if (PostMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE; + } + else { + for (i=0; i < OutputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLutAToBType +// ******************************************************************************** + + +// V4 stuff. Read matrix for LutAtoB and LutBtoA + +static +cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset) +{ + cmsFloat64Number dMat[3*3]; + cmsFloat64Number dOff[3]; + cmsStage* Mat; + + // Go to address + if (!io -> Seek(io, Offset)) return NULL; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL; + + if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL; + + Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff); + + return Mat; +} + + + + +// V4 stuff. Read CLUT part for LutAtoB and LutBtoA + +static +cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, + cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number GridPoints[cmsMAXCHANNELS], i; + cmsUInt8Number Precision; + cmsStage* CLUT; + _cmsStageCLutData* Data; + + if (!io -> Seek(io, Offset)) return NULL; + if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL; + + + for (i=0; i < cmsMAXCHANNELS; i++) { + + if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = gridPoints8[i]; + } + + if (!_cmsReadUInt8Number(io, &Precision)) return NULL; + + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL); + if (CLUT == NULL) return NULL; + + Data = (_cmsStageCLutData*) CLUT ->Data; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + cmsUInt8Number v; + + for (i=0; i < Data ->nEntries; i++) { + + if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { + cmsStageFree(CLUT); + return NULL; + } + Data ->Tab.T[i] = FROM_8_TO_16(v); + } + + } + else + if (Precision == 2) { + + if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) { + cmsStageFree(CLUT); + return NULL; + } + } + else { + cmsStageFree(CLUT); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return NULL; + } + + return CLUT; +} + +static +cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + switch (BaseType) { + + case cmsSigCurveType: + return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0); + + case cmsSigParametricCurveType: + return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0); + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) BaseType); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return NULL; + } +} + + +// Read a set of curves from specific offset +static +cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves) +{ + cmsToneCurve* Curves[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsStage* Lin = NULL; + + if (nCurves > cmsMAXCHANNELS) return FALSE; + + if (!io -> Seek(io, Offset)) return FALSE; + + for (i=0; i < nCurves; i++) + Curves[i] = NULL; + + for (i=0; i < nCurves; i++) { + + Curves[i] = ReadEmbeddedCurve(self, io); + if (Curves[i] == NULL) goto Error; + if (!_cmsReadAlignment(io)) goto Error; + + } + + Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves); + +Error: + for (i=0; i < nCurves; i++) + cmsFreeToneCurve(Curves[i]); + + return Lin; +} + + +// LutAtoB type + +// This structure represents a colour transform. The type contains up to five processing +// elements which are stored in the AtoBTag tag in the following order: a set of one +// dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves, +// a multidimensional lookup table, and a set of one dimensional output curves. +// Data are processed using these elements via the following sequence: +// +//("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves). +// +/* +It is possible to use any or all of these processing elements. At least one processing element +must be included.Only the following combinations are allowed: + +B +M - Matrix - B +A - CLUT - B +A - CLUT - M - Matrix - B + +*/ + +static +void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number BaseOffset; + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a set of curves +static +cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) +{ + cmsUInt32Number i, n; + + _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; + + n = mpe->InputChannels * mpe->OutputChannels; + + // Write the Matrix + for (i = 0; i < n; i++) + { + if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE; + } + + if (m->Offset != NULL) { + + for (i = 0; i < mpe->OutputChannels; i++) + { + if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE; + } + } + else { + for (i = 0; i < mpe->OutputChannels; i++) + { + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + } + } + + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +// Write a set of curves +static +cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe) +{ + cmsUInt32Number i, n; + cmsTagTypeSignature CurrentType; + cmsToneCurve** Curves; + + + n = cmsStageOutputChannels(mpe); + Curves = _cmsStageGetPtrToCurveSet(mpe); + + for (i=0; i < n; i++) { + + // If this is a table-based curve, use curve type even on V4 + CurrentType = Type; + + if ((Curves[i] ->nSegments == 0)|| + ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) ) + CurrentType = cmsSigCurveType; + else + if (Curves[i] ->Segments[0].Type < 0) + CurrentType = cmsSigCurveType; + + if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE; + + switch (CurrentType) { + + case cmsSigCurveType: + if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + case cmsSigParametricCurveType: + if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) Type); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + } + + + return TRUE; +} + + +static +cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe) +{ + cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number i; + _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data; + + if (CLUT ->HasFloatValues) { + cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only"); + return FALSE; + } + + memset(gridPoints, 0, sizeof(gridPoints)); + for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++) + gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i]; + + if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + for (i=0; i < CLUT->nEntries; i++) { + + if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE; + } + } + else + if (Precision == 2) { + + if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE; + } + else { + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + + return TRUE; +} + + + + +static +cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsUInt32Number inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage * Matrix = NULL; + cmsStage * CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + // Get the base for all offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (Lut ->Elements != NULL) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, + cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) { + + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB"); + return FALSE; + } + + // Get input, output channels + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + // Write channel count + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + // Keep directory to be filled latter + DirectoryPos = io ->Tell(io); + + // Write the directory + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// LutBToA type + +static +void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number BaseOffset; // Actual position in file + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Padding + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +/* +B +B - Matrix - M +B - CLUT - A +B - Matrix - M - CLUT - A +*/ + +static +cmsBool Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsUInt32Number inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage *Matrix = NULL; + cmsStage *CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) { + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA"); + return FALSE; + } + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + DirectoryPos = io ->Tell(io); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +static +void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + + +// ******************************************************************************** +// Type cmsSigColorantTableType +// ******************************************************************************** +/* +The purpose of this tag is to identify the colorants used in the profile by a +unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous +value. The first colorant listed is the colorant of the first device channel of +a lut tag. The second colorant listed is the colorant of the second device channel +of a lut tag, and so on. +*/ + +static +void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number i, Count; + cmsNAMEDCOLORLIST* List; + char Name[34]; + cmsUInt16Number PCS[3]; + + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (Count > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count); + return NULL; + } + + List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", ""); + if (List == NULL) + return NULL; + + for (i=0; i < Count; i++) { + + if (io ->Read(io, Name, 32, 1) != 1) goto Error; + Name[32] = 0; + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + + if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error; + + } + + *nItems = 1; + return List; + +Error: + *nItems = 0; + cmsFreeNamedColorList(List); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// Saves a colorant table. It is using the named color structure for simplicity sake +static +cmsBool Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + cmsUInt32Number i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + + for (i=0; i < nColors; i++) { + + char root[cmsMAX_PATH]; + cmsUInt16Number PCS[3]; + + memset(root, 0, sizeof(root)); + + if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0; + root[32] = 0; + + if (!io ->Write(io, 32, root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigNamedColor2Type +// ******************************************************************************** +// +//The namedColor2Type is a count value and array of structures that provide color +//coordinates for 7-bit ASCII color names. For each named color, a PCS and optional +//device representation of the color are given. Both representations are 16-bit values. +//The device representation corresponds to the header's 'color space of data' field. +//This representation should be consistent with the 'number of device components' +//field in the namedColor2Type. If this field is 0, device coordinates are not provided. +//The PCS representation corresponds to the header's PCS field. The PCS representation +//is always provided. Color names are fixed-length, 32-byte fields including null +//termination. In order to maintain maximum portability, it is strongly recommended +//that special characters of the 7-bit ASCII set not be used. + +static +void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + + cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use + cmsUInt32Number count; // Count of named colors + cmsUInt32Number nDeviceCoords; // Num of device coordinates + char prefix[32]; // Prefix for each color name + char suffix[32]; // Suffix for each color name + cmsNAMEDCOLORLIST* v; + cmsUInt32Number i; + + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL; + if (!_cmsReadUInt32Number(io, &count)) return NULL; + if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL; + + if (io -> Read(io, prefix, 32, 1) != 1) return NULL; + if (io -> Read(io, suffix, 32, 1) != 1) return NULL; + + prefix[31] = suffix[31] = 0; + + v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix); + if (v == NULL) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count); + return NULL; + } + + if (nDeviceCoords > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); + goto Error; + } + for (i=0; i < count; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[33]; + + memset(Colorant, 0, sizeof(Colorant)); + if (io -> Read(io, Root, 32, 1) != 1) goto Error; + Root[32] = 0; // To prevent exploits + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; + + if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error; + } + + *nItems = 1; + return (void*) v ; + +Error: + cmsFreeNamedColorList(v); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Saves a named color list into a named color profile +static +cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + char prefix[33]; // Prefix for each color name + char suffix[33]; // Suffix for each color name + cmsUInt32Number i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE; + + strncpy(prefix, (const char*) NamedColorList->Prefix, 32); + strncpy(suffix, (const char*) NamedColorList->Suffix, 32); + + suffix[32] = prefix[32] = 0; + + if (!io ->Write(io, 32, prefix)) return FALSE; + if (!io ->Write(io, 32, suffix)) return FALSE; + + for (i=0; i < nColors; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[cmsMAX_PATH]; + + if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0; + Root[32] = 0; + if (!io ->Write(io, 32 , Root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceDescType +// ******************************************************************************** + +// This type is an array of structures, each of which contains information from the +// header fields and tags from the original profiles which were combined to create +// the final profile. The order of the structures is the order in which the profiles +// were combined and includes a structure for the final profile. This provides a +// description of the profile sequence from source to destination, +// typically used with the DeviceLink profile. + +static +cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + + switch (BaseType) { + + case cmsSigTextType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + case cmsSigTextDescriptionType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + /* + TBD: Size is needed for MLU, and we have no idea on which is the available size + */ + + case cmsSigMultiLocalizedUnicodeType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + default: return FALSE; + } +} + + +static +void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number i, Count; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + OutSeq ->n = Count; + + // Get structures as well + + for (i=0; i < Count; i++) { + + cmsPSEQDESC* sec = &OutSeq -> seq[i]; + + if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error; + if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt64Number); + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error; + if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error; + } + + *nItems = 1; + return OutSeq; + +Error: + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; +} + + +// Aux--Embed a text description type. It can be of type text description or multilocalized unicode +// and it depends of the version number passed on cmsTagDescriptor structure instead of stack +static +cmsBool SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text) +{ + if (self ->ICCVersion < 0x4000000) { + + if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE; + return Type_Text_Description_Write(self, io, Text, 1); + } + else { + if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE; + return Type_MLU_Write(self, io, Text, 1); + } +} + + +static +cmsBool Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE; + + for (i=0; i < Seq ->n; i++) { + + cmsPSEQDESC* sec = &Seq -> seq[i]; + + if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE; + if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE; + + if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE; + if (!SaveDescription(self, io, sec ->Model)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceIdType +// ******************************************************************************** +/* +In certain workflows using ICC Device Link Profiles, it is necessary to identify the +original profiles that were combined to create the Device Link Profile. +This type is an array of structures, each of which contains information for +identification of a profile used in a sequence +*/ + + +static +cmsBool ReadSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq = (cmsSEQ*) Cargo; + cmsPSEQDESC* seq = &OutSeq ->seq[n]; + + if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE; + if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE; + + return TRUE; +} + + + +static +void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number Count; + cmsUInt32Number BaseOffset; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get table count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Allocate an empty structure + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + + // Read the position table + if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) { + + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; + } + + // Success + *nItems = 1; + return OutSeq; + +} + + +static +cmsBool WriteSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* Seq = (cmsSEQ*) Cargo; + + if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE; + + // Store here the MLU + if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number BaseOffset; + + // Keep the base offset + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // This is the table count + if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE; + + // This is the position table and content + if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigUcrBgType +// ******************************************************************************** +/* +This type contains curves representing the under color removal and black +generation and a text string which is a general description of the method used +for the ucr/bg. +*/ + +static +void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + cmsUInt32Number CountUcr, CountBg; + char* ASCIIString; + + *nItems = 0; + if (n == NULL) return NULL; + + // First curve is Under color removal + if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL); + if (n ->Ucr == NULL) return NULL; + + if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= CountUcr * sizeof(cmsUInt16Number); + + // Second curve is Black generation + if (!_cmsReadUInt32Number(io, &CountBg)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL); + if (n ->Bg == NULL) return NULL; + if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL; + if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL; + SizeOfTag -= CountBg * sizeof(cmsUInt16Number); + if (SizeOfTag == UINT_MAX) return NULL; + + // Now comes the text. The length is specified by the tag size + n ->Desc = cmsMLUalloc(self ->ContextID, 1); + if (n ->Desc == NULL) return NULL; + + ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL; + ASCIIString[SizeOfTag] = 0; + cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString); + _cmsFree(self ->ContextID, ASCIIString); + + *nItems = 1; + return (void*) n; +} + +static +cmsBool Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUcrBg* Value = (cmsUcrBg*) Ptr; + cmsUInt32Number TextSize; + char* Text; + + // First curve is Under color removal + if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE; + + // Then black generation + if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE; + + // Now comes the text. The length is specified by the tag size + TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + + if (NewUcrBg == NULL) return NULL; + + NewUcrBg ->Bg = cmsDupToneCurve(Src ->Bg); + NewUcrBg ->Ucr = cmsDupToneCurve(Src ->Ucr); + NewUcrBg ->Desc = cmsMLUdup(Src ->Desc); + + return (void*) NewUcrBg; + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + + if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr); + if (Src ->Bg) cmsFreeToneCurve(Src ->Bg); + if (Src ->Desc) cmsMLUfree(Src ->Desc); + + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigCrdInfoType +// ******************************************************************************** + +/* +This type contains the PostScript product name to which this profile corresponds +and the names of the companion CRDs. Recall that a single profile can generate +multiple CRDs. It is implemented as a MLU being the language code "PS" and then +country varies for each element: + + nm: PostScript product name + #0: Rendering intent 0 CRD name + #1: Rendering intent 1 CRD name + #2: Rendering intent 2 CRD name + #3: Rendering intent 3 CRD name +*/ + + + +// Auxiliary, read an string specified as count + string +static +cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) +{ + cmsUInt32Number Count; + char* Text; + + if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE; + + if (!_cmsReadUInt32Number(io, &Count)) return FALSE; + + if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE; + if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE; + + Text = (char*) _cmsMalloc(self ->ContextID, Count+1); + if (Text == NULL) return FALSE; + + if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) { + _cmsFree(self ->ContextID, Text); + return FALSE; + } + + Text[Count] = 0; + + cmsMLUsetASCII(mlu, "PS", Section, Text); + _cmsFree(self ->ContextID, Text); + + *SizeOfTag -= (Count + sizeof(cmsUInt32Number)); + return TRUE; +} + +static +cmsBool WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) +{ + cmsUInt32Number TextSize; + char* Text; + + TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + + if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE; + + if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; +} + +static +void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5); + + *nItems = 0; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error; + + *nItems = 1; + return (void*) mlu; + +Error: + cmsMLUfree(mlu); + return NULL; + +} + +static +cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + + cmsMLU* mlu = (cmsMLU*) Ptr; + + if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error; + + return TRUE; + +Error: + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigScreeningType +// ******************************************************************************** +// +//The screeningType describes various screening parameters including screen +//frequency, screening angle, and spot shape. + +static +void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsScreening* sc = NULL; + cmsUInt32Number i; + + sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening)); + if (sc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error; + + if (sc ->nChannels > cmsMAXCHANNELS - 1) + sc ->nChannels = cmsMAXCHANNELS - 1; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error; + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error; + } + + + *nItems = 1; + + return (void*) sc; + +Error: + if (sc != NULL) + _cmsFree(self ->ContextID, sc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsScreening* sc = (cmsScreening* ) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigViewingConditionsType +// ******************************************************************************** +// +//This type represents a set of viewing condition parameters including: +//CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute' +//surround tristimulus values. + +static +void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCViewingConditions* vc = NULL; + + vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions)); + if (vc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error; + if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error; + if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error; + + *nItems = 1; + + return (void*) vc; + +Error: + if (vc != NULL) + _cmsFree(self ->ContextID, vc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr; + + if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE; + if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsICCViewingConditions)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiProcessElementType +// ******************************************************************************** + + +static +void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsStageDup((cmsStage*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsStageFree((cmsStage*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// Each curve is stored in one or more curve segments, with break-points specified between curve segments. +// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The +// first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be +// specified either in terms of a formula, or by a sampled curve. + + +// Read an embedded segmented curve +static +cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsCurveSegSignature ElementSig; + cmsUInt32Number i, j; + cmsUInt16Number nSegments; + cmsCurveSegment* Segments; + cmsToneCurve* Curve; + cmsFloat32Number PrevBreak = MINUS_INF; // - infinite + + // Take signature and channels for each element. + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL; + + // That should be a segmented curve + if (ElementSig != cmsSigSegmentedCurve) return NULL; + + if (!_cmsReadUInt32Number(io, NULL)) return NULL; + if (!_cmsReadUInt16Number(io, &nSegments)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (nSegments < 1) return NULL; + Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment)); + if (Segments == NULL) return NULL; + + // Read breakpoints + for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) { + + Segments[i].x0 = PrevBreak; + if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error; + PrevBreak = Segments[i].x1; + } + + Segments[nSegments-1].x0 = PrevBreak; + Segments[nSegments-1].x1 = PLUS_INF; // A big cmsFloat32Number number + + // Read segments + for (i=0; i < nSegments; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error; + if (!_cmsReadUInt32Number(io, NULL)) goto Error; + + switch (ElementSig) { + + case cmsSigFormulaCurveSeg: { + + cmsUInt16Number Type; + cmsUInt32Number ParamsByType[] = {4, 5, 5 }; + + if (!_cmsReadUInt16Number(io, &Type)) goto Error; + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + + Segments[i].Type = Type + 6; + if (Type > 2) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + + cmsFloat32Number f; + if (!_cmsReadFloat32Number(io, &f)) goto Error; + Segments[i].Params[j] = f; + } + } + break; + + + case cmsSigSampledCurveSeg: { + cmsUInt32Number Count; + + if (!_cmsReadUInt32Number(io, &Count)) goto Error; + + Segments[i].nGridPoints = Count; + Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); + if (Segments[i].SampledPoints == NULL) goto Error; + + for (j=0; j < Count; j++) { + if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; + } + } + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); + } + goto Error; + + } + } + + Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments); + + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + return Curve; + +Error: + if (Segments) { + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + } + return NULL; +} + + +static +cmsBool ReadMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo; + + GammaTables[n] = ReadSegmentedCurve(self, io); + return (GammaTables[n] != NULL); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number i, BaseOffset; + cmsToneCurve** GammaTables; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans != OutputChans) return NULL; + + GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) return NULL; + + if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) { + + mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables); + } + else { + mpe = NULL; + } + + for (i=0; i < InputChans; i++) { + if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(self ->ContextID, GammaTables); + *nItems = (mpe != NULL) ? 1U : 0; + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY +static +cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g) +{ + cmsUInt32Number i, j; + cmsCurveSegment* Segments = g ->Segments; + cmsUInt32Number nSegments = g ->nSegments; + + if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + // Write the break-points + for (i=0; i < nSegments - 1; i++) { + if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error; + } + + // Write the segments + for (i=0; i < g ->nSegments; i++) { + + cmsCurveSegment* ActualSeg = Segments + i; + + if (ActualSeg -> Type == 0) { + + // This is a sampled curve + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error; + + for (j=0; j < g ->Segments[i].nGridPoints; j++) { + if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error; + } + + } + else { + int Type; + cmsUInt32Number ParamsByType[] = { 4, 5, 5 }; + + // This is a formula-based + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + + // We only allow 1, 2 and 3 as types + Type = ActualSeg ->Type - 6; + if (Type > 2 || Type < 0) goto Error; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error; + } + } + + // It seems there is no need to align. Code is here, and for safety commented out + // if (!_cmsWriteAlignment(io)) goto Error; + } + + return TRUE; + +Error: + return FALSE; +} + + +static +cmsBool WriteMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) Cargo; + + return WriteSegmentedCurve(io, Curves ->TheCurves[n]); + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(self); +} + +// Write a curve, checking first for validity +static +cmsBool Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number BaseOffset; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Write the header. Since those are curves, input and output channels are same + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + + if (!WritePositionTable(self, io, 0, + mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE; + + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +// The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the +// matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array +// is organized as follows: +// array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ] + +static +void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number nElems, i; + cmsFloat64Number* Matrix; + cmsFloat64Number* Offsets; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + + // Input and output chans may be ANY (up to 0xffff), + // but we choose to limit to 16 channels for now + if (InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans >= cmsMAXCHANNELS) return NULL; + + nElems = (cmsUInt32Number) InputChans * OutputChans; + + Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); + if (Matrix == NULL) return NULL; + + Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number)); + if (Offsets == NULL) { + + _cmsFree(self ->ContextID, Matrix); + return NULL; + } + + for (i=0; i < nElems; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Matrix[i] = v; + } + + + for (i=0; i < OutputChans; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Offsets[i] = v; + } + + + mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets); + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + + *nItems = 1; + + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, nElems; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + nElems = mpe ->InputChannels * mpe ->OutputChannels; + + for (i=0; i < nElems; i++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE; + } + + + for (i=0; i < mpe ->OutputChannels; i++) { + + if (Matrix ->Offset == NULL) { + + if (!_cmsWriteFloat32Number(io, 0)) return FALSE; + } + else { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +static +void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt8Number Dimensions8[16]; + cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS]; + _cmsStageCLutData* clut; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0) goto Error; + if (OutputChans == 0) goto Error; + + if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16) + goto Error; + + // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number + nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? (cmsUInt32Number) MAX_INPUT_DIMENSIONS : InputChans; + + for (i = 0; i < nMaxGrids; i++) { + if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; + } + + // Allocate the true CLUT + mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); + if (mpe == NULL) goto Error; + + // Read and sanitize the data + clut = (_cmsStageCLutData*) mpe ->Data; + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsReadFloat32Number(io, &clut->Tab.TFloat[i])) goto Error; + } + + *nItems = 1; + return mpe; + +Error: + *nItems = 0; + if (mpe != NULL) cmsStageFree(mpe); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a CLUT in floating point +static +cmsBool Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number Dimensions8[16]; // 16 because the spec says 16 and not max number of channels + cmsUInt32Number i; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data; + + // Check for maximum number of channels supported by lcms + if (mpe -> InputChannels > MAX_INPUT_DIMENSIONS) return FALSE; + + // Only floats are supported in MPE + if (clut ->HasFloatValues == FALSE) return FALSE; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + memset(Dimensions8, 0, sizeof(Dimensions8)); + + for (i=0; i < mpe ->InputChannels; i++) + Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i]; + + if (!io ->Write(io, 16, Dimensions8)) return FALSE; + + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +// This is the list of built-in MPE types +static _cmsTagTypeLinkedList SupportedMPEtypes[] = { + +{{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] }, // Ignore those elements for now +{{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] }, // (That's what the spec says) + +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType, MPEcurve), &SupportedMPEtypes[3] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType, MPEmatrix), &SupportedMPEtypes[4] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL }, +}; + +_cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL }; + +static +cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsStageSignature ElementSig; + cmsTagTypeHandler* TypeHandler; + cmsUInt32Number nItems; + cmsPipeline *NewLUT = (cmsPipeline *) Cargo; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + + + // Take signature and channels for each element. + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE; + + // The reserved placeholder + if (!_cmsReadUInt32Number(io, NULL)) return FALSE; + + // Read diverse MPE types + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknown element was found. + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String); + return FALSE; + } + + // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType) + // Read the MPE. No size is given + if (TypeHandler ->ReadPtr != NULL) { + + // This is a real element which should be read and processed + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag))) + return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(n); +} + + +// This is the main dispatcher for MPE +static +void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number ElementCount; + cmsPipeline *NewLUT = NULL; + cmsUInt32Number BaseOffset; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Read channels and element count + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); + if (NewLUT == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; + if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; + + // Check channel count + if (InputChans != NewLUT->InputChannels || + OutputChans != NewLUT->OutputChannels) goto Error; + + // Success + *nItems = 1; + return NewLUT; + + // Error +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + *nItems = 0; + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// This one is a liitle bit more complex, so we don't use position tables this time. +static +cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos; + cmsUInt32Number inputChan, outputChan; + cmsUInt32Number ElemCount; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before; + cmsStageSignature ElementSig; + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsStage* Elem = Lut ->Elements; + cmsTagTypeHandler* TypeHandler; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + ElemCount = cmsPipelineStageCount(Lut); + + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Write the head + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error; + if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error; + + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each single tag. Keep track of the size as well. + for (i=0; i < ElemCount; i++) { + + ElementOffsets[i] = io ->Tell(io) - BaseOffset; + + ElementSig = Elem ->Type; + + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknown element was found. + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String); + goto Error; + } + + if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + Before = io ->Tell(io); + if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error; + if (!_cmsWriteAlignment(io)) goto Error; + + ElementSizes[i] = io ->Tell(io) - Before; + + Elem = Elem ->Next; + } + + // Write the directory + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigVcgtType +// ******************************************************************************** + + +#define cmsVideoCardGammaTableType 0 +#define cmsVideoCardGammaFormulaType 1 + +// Used internally +typedef struct { + double Gamma; + double Min; + double Max; +} _cmsVCGTGAMMA; + + +static +void *Type_vcgt_Read(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number TagType, n, i; + cmsToneCurve** Curves; + + *nItems = 0; + + // Read tag type + if (!_cmsReadUInt32Number(io, &TagType)) return NULL; + + // Allocate space for the array + Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (Curves == NULL) return NULL; + + // There are two possible flavors + switch (TagType) { + + // Gamma is stored as a table + case cmsVideoCardGammaTableType: + { + cmsUInt16Number nChannels, nElems, nBytes; + + // Check channel count, which should be 3 (we don't support monochrome this time) + if (!_cmsReadUInt16Number(io, &nChannels)) goto Error; + + if (nChannels != 3) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels); + goto Error; + } + + // Get Table element count and bytes per element + if (!_cmsReadUInt16Number(io, &nElems)) goto Error; + if (!_cmsReadUInt16Number(io, &nBytes)) goto Error; + + // Adobe's quirk fixup. Fixing broken profiles... + if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576) + nBytes = 2; + + + // Populate tone curves + for (n=0; n < 3; n++) { + + Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL); + if (Curves[n] == NULL) goto Error; + + // On depending on byte depth + switch (nBytes) { + + // One byte, 0..255 + case 1: + for (i=0; i < nElems; i++) { + + cmsUInt8Number v; + + if (!_cmsReadUInt8Number(io, &v)) goto Error; + Curves[n] ->Table16[i] = FROM_8_TO_16(v); + } + break; + + // One word 0..65535 + case 2: + if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error; + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8); + goto Error; + } + } // For all 3 channels + } + break; + + // In this case, gamma is stored as a formula + case cmsVideoCardGammaFormulaType: + { + _cmsVCGTGAMMA Colorant[3]; + + // Populate tone curves + for (n=0; n < 3; n++) { + + double Params[10]; + + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error; + + // Parametric curve type 5 is: + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + + // vcgt formula is: + // Y = (Max - Min) * (X ^ Gamma) + Min + + // So, the translation is + // a = (Max - Min) ^ ( 1 / Gamma) + // e = Min + // b=c=d=f=0 + + Params[0] = Colorant[n].Gamma; + Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma)); + Params[2] = 0; + Params[3] = 0; + Params[4] = 0; + Params[5] = Colorant[n].Min; + Params[6] = 0; + + Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params); + if (Curves[n] == NULL) goto Error; + } + } + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType); + goto Error; + } + + *nItems = 1; + return (void*) Curves; + +// Regret, free all resources +Error: + + cmsFreeToneCurveTriple(Curves); + _cmsFree(self ->ContextID, Curves); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// We don't support all flavors, only 16bits tables and formula +static +cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve** Curves = (cmsToneCurve**) Ptr; + cmsUInt32Number i, j; + + if (cmsGetToneCurveParametricType(Curves[0]) == 5 && + cmsGetToneCurveParametricType(Curves[1]) == 5 && + cmsGetToneCurveParametricType(Curves[2]) == 5) { + + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE; + + // Save parameters + for (i=0; i < 3; i++) { + + _cmsVCGTGAMMA v; + + v.Gamma = Curves[i] ->Segments[0].Params[0]; + v.Min = Curves[i] ->Segments[0].Params[5]; + v.Max = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min; + + if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE; + } + } + + else { + + // Always store as a table of 256 words + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE; + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; + if (!_cmsWriteUInt16Number(io, 256)) return FALSE; + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + for (i=0; i < 3; i++) { + for (j=0; j < 256; j++) { + + cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0)); + cmsUInt16Number n = _cmsQuickSaturateWord(v * 65535.0); + + if (!_cmsWriteUInt16Number(io, n)) return FALSE; + } + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsToneCurve** OldCurves = (cmsToneCurve**) Ptr; + cmsToneCurve** NewCurves; + + NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (NewCurves == NULL) return NULL; + + NewCurves[0] = cmsDupToneCurve(OldCurves[0]); + NewCurves[1] = cmsDupToneCurve(OldCurves[1]); + NewCurves[2] = cmsDupToneCurve(OldCurves[2]); + + return (void*) NewCurves; + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeToneCurveTriple((cmsToneCurve**) Ptr); + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigDictType +// ******************************************************************************** + +// Single column of the table can point to wchar or MLUC elements. Holds arrays of data +typedef struct { + cmsContext ContextID; + cmsUInt32Number *Offsets; + cmsUInt32Number *Sizes; +} _cmsDICelem; + +typedef struct { + _cmsDICelem Name, Value, DisplayName, DisplayValue; + +} _cmsDICarray; + +// Allocate an empty array element +static +cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e, cmsUInt32Number Count) +{ + e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Offsets == NULL) return FALSE; + + e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Sizes == NULL) { + + _cmsFree(ContextID, e -> Offsets); + return FALSE; + } + + e ->ContextID = ContextID; + return TRUE; +} + +// Free an array element +static +void FreeElem(_cmsDICelem* e) +{ + if (e ->Offsets != NULL) _cmsFree(e -> ContextID, e -> Offsets); + if (e ->Sizes != NULL) _cmsFree(e -> ContextID, e -> Sizes); + e->Offsets = e ->Sizes = NULL; +} + +// Get rid of whole array +static +void FreeArray( _cmsDICarray* a) +{ + if (a ->Name.Offsets != NULL) FreeElem(&a->Name); + if (a ->Value.Offsets != NULL) FreeElem(&a ->Value); + if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName); + if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue); +} + + +// Allocate whole array +static +cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + // Empty values + memset(a, 0, sizeof(_cmsDICarray)); + + // On depending on record size, create column arrays + if (!AllocElem(ContextID, &a ->Name, Count)) goto Error; + if (!AllocElem(ContextID, &a ->Value, Count)) goto Error; + + if (Length > 16) { + if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error; + + } + if (Length > 24) { + if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error; + } + return TRUE; + +Error: + FreeArray(a); + return FALSE; +} + +// Read one element +static +cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset) +{ + if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE; + if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE; + + // An offset of zero has special meaning and shal be preserved + if (e ->Offsets[i] > 0) + e ->Offsets[i] += BaseOffset; + return TRUE; +} + + +static +cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number i; + + // Read column arrays + for (i=0; i < Count; i++) { + + if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE; + if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE; + + if (Length > 16) { + + if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE; + + } + + if (Length > 24) { + + if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE; + } + } + return TRUE; +} + + +// Write one element +static +cmsBool WriteOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i) +{ + if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE; + if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE; + + return TRUE; +} + +static +cmsBool WriteOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + cmsUInt32Number i; + + for (i=0; i < Count; i++) { + + if (!WriteOneElem(io, &a -> Name, i)) return FALSE; + if (!WriteOneElem(io, &a -> Value, i)) return FALSE; + + if (Length > 16) { + + if (!WriteOneElem(io, &a -> DisplayName, i)) return FALSE; + } + + if (Length > 24) { + + if (!WriteOneElem(io, &a -> DisplayValue, i)) return FALSE; + } + } + + return TRUE; +} + +static +cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr) +{ + + cmsUInt32Number nChars; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (e -> Offsets[i] == 0) { + + *wcstr = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + nChars = e ->Sizes[i] / sizeof(cmsUInt16Number); + + + *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t)); + if (*wcstr == NULL) return FALSE; + + if (!_cmsReadWCharArray(io, nChars, *wcstr)) { + _cmsFree(e ->ContextID, *wcstr); + return FALSE; + } + + // End of string marker + (*wcstr)[nChars] = 0; + return TRUE; +} + +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +static +cmsBool WriteOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before = io ->Tell(io); + cmsUInt32Number n; + + e ->Offsets[i] = Before - BaseOffset; + + if (wcstr == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + n = mywcslen(wcstr); + if (!_cmsWriteWCharArray(io, n, wcstr)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + +static +cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu) +{ + cmsUInt32Number nItems = 0; + + // A way to get null MLUCs + if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) { + + *mlu = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]); + return *mlu != NULL; +} + +static +cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (mlu == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + Before = io ->Tell(io); + e ->Offsets[i] = Before - BaseOffset; + + if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + + +static +void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsHANDLE hDict; + cmsUInt32Number i, Count, Length; + cmsUInt32Number BaseOffset; + _cmsDICarray a; + wchar_t *NameWCS = NULL, *ValueWCS = NULL; + cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL; + cmsBool rc; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get name-value record count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Get rec length + if (!_cmsReadUInt32Number(io, &Length)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for valid lengths + if (Length != 16 && Length != 24 && Length != 32) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length); + return NULL; + } + + // Creates an empty dictionary + hDict = cmsDictAlloc(self -> ContextID); + if (hDict == NULL) return NULL; + + // On depending on record size, create column arrays + if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error; + + // Read column arrays + if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error; + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error; + if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error; + + if (Length > 16) { + if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error; + } + + if (Length > 24) { + if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error; + } + + if (NameWCS == NULL || ValueWCS == NULL) { + + cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value"); + rc = FALSE; + } + else { + + rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU); + } + + if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS); + if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS); + if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU); + if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU); + + if (!rc) goto Error; + } + + FreeArray(&a); + *nItems = 1; + return (void*) hDict; + +Error: + FreeArray(&a); + cmsDictFree(hDict); + return NULL; +} + + +static +cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsHANDLE hDict = (cmsHANDLE) Ptr; + const cmsDICTentry* p; + cmsBool AnyName, AnyValue; + cmsUInt32Number i, Count, Length; + cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset; + _cmsDICarray a; + + if (hDict == NULL) return FALSE; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Let's inspect the dictionary + Count = 0; AnyName = FALSE; AnyValue = FALSE; + for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) { + + if (p ->DisplayName != NULL) AnyName = TRUE; + if (p ->DisplayValue != NULL) AnyValue = TRUE; + Count++; + } + + Length = 16; + if (AnyName) Length += 8; + if (AnyValue) Length += 8; + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + if (!_cmsWriteUInt32Number(io, Length)) return FALSE; + + // Keep starting position of offsets table + DirectoryPos = io ->Tell(io); + + // Allocate offsets array + if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error; + + // Write a fake directory to be filled latter on + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + // Write each element. Keep track of the size as well. + p = cmsDictGetEntryList(hDict); + for (i=0; i < Count; i++) { + + if (!WriteOneWChar(io, &a.Name, i, p ->Name, BaseOffset)) goto Error; + if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error; + + if (p ->DisplayName != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error; + } + + if (p ->DisplayValue != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error; + } + + p = cmsDictNextEntry(p); + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + if (!io ->Seek(io, CurrentPos)) goto Error; + + FreeArray(&a); + return TRUE; + +Error: + FreeArray(&a); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDictDup((cmsHANDLE) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsDictFree((cmsHANDLE) Ptr); + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type support main routines +// ******************************************************************************** + + +// This is the list of built-in types +static const _cmsTagTypeLinkedList SupportedTagTypes[] = { + +{TYPE_HANDLER(cmsSigChromaticityType, Chromaticity), (_cmsTagTypeLinkedList*) &SupportedTagTypes[1] }, +{TYPE_HANDLER(cmsSigColorantOrderType, ColorantOrderType), (_cmsTagTypeLinkedList*) &SupportedTagTypes[2] }, +{TYPE_HANDLER(cmsSigS15Fixed16ArrayType, S15Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[3] }, +{TYPE_HANDLER(cmsSigU16Fixed16ArrayType, U16Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[4] }, +{TYPE_HANDLER(cmsSigTextType, Text), (_cmsTagTypeLinkedList*) &SupportedTagTypes[5] }, +{TYPE_HANDLER(cmsSigTextDescriptionType, Text_Description), (_cmsTagTypeLinkedList*) &SupportedTagTypes[6] }, +{TYPE_HANDLER(cmsSigCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[7] }, +{TYPE_HANDLER(cmsSigParametricCurveType, ParametricCurve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[8] }, +{TYPE_HANDLER(cmsSigDateTimeType, DateTime), (_cmsTagTypeLinkedList*) &SupportedTagTypes[9] }, +{TYPE_HANDLER(cmsSigLut8Type, LUT8), (_cmsTagTypeLinkedList*) &SupportedTagTypes[10] }, +{TYPE_HANDLER(cmsSigLut16Type, LUT16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[11] }, +{TYPE_HANDLER(cmsSigColorantTableType, ColorantTable), (_cmsTagTypeLinkedList*) &SupportedTagTypes[12] }, +{TYPE_HANDLER(cmsSigNamedColor2Type, NamedColor), (_cmsTagTypeLinkedList*) &SupportedTagTypes[13] }, +{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU), (_cmsTagTypeLinkedList*) &SupportedTagTypes[14] }, +{TYPE_HANDLER(cmsSigProfileSequenceDescType, ProfileSequenceDesc),(_cmsTagTypeLinkedList*) &SupportedTagTypes[15] }, +{TYPE_HANDLER(cmsSigSignatureType, Signature), (_cmsTagTypeLinkedList*) &SupportedTagTypes[16] }, +{TYPE_HANDLER(cmsSigMeasurementType, Measurement), (_cmsTagTypeLinkedList*) &SupportedTagTypes[17] }, +{TYPE_HANDLER(cmsSigDataType, Data), (_cmsTagTypeLinkedList*) &SupportedTagTypes[18] }, +{TYPE_HANDLER(cmsSigLutAtoBType, LUTA2B), (_cmsTagTypeLinkedList*) &SupportedTagTypes[19] }, +{TYPE_HANDLER(cmsSigLutBtoAType, LUTB2A), (_cmsTagTypeLinkedList*) &SupportedTagTypes[20] }, +{TYPE_HANDLER(cmsSigUcrBgType, UcrBg), (_cmsTagTypeLinkedList*) &SupportedTagTypes[21] }, +{TYPE_HANDLER(cmsSigCrdInfoType, CrdInfo), (_cmsTagTypeLinkedList*) &SupportedTagTypes[22] }, +{TYPE_HANDLER(cmsSigMultiProcessElementType, MPE), (_cmsTagTypeLinkedList*) &SupportedTagTypes[23] }, +{TYPE_HANDLER(cmsSigScreeningType, Screening), (_cmsTagTypeLinkedList*) &SupportedTagTypes[24] }, +{TYPE_HANDLER(cmsSigViewingConditionsType, ViewingConditions), (_cmsTagTypeLinkedList*) &SupportedTagTypes[25] }, +{TYPE_HANDLER(cmsSigXYZType, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[26] }, +{TYPE_HANDLER(cmsCorbisBrokenXYZtype, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[27] }, +{TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] }, +{TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] }, +{TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] }, +{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } +}; + + +_cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL }; + + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagTypeList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src, + int loc) +{ + _cmsTagTypePluginChunkType newHead = { NULL }; + _cmsTagTypeLinkedList* entry; + _cmsTagTypeLinkedList* Anterior = NULL; + _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc]; + + // Walk the list copying all nodes + for (entry = head->TagTypes; + entry != NULL; + entry = entry ->Next) { + + _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TagTypes == NULL) + newHead.TagTypes = newEntry; + } + + ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType)); +} + + +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, TagTypePlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } +} + +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, MPEPlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } + +} + + +// Both kind of plug-ins share same structure +cmsBool _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data) +{ + return RegisterTypesPlugin(id, Data, TagTypePlugin); +} + +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data) +{ + return RegisterTypesPlugin(id, Data,MPEPlugin); +} + + +// Wrapper for tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig) +{ + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin); + + return GetHandler(sig, ctx->TagTypes, (_cmsTagTypeLinkedList*) SupportedTagTypes); +} + +// ******************************************************************************** +// Tag support main routines +// ******************************************************************************** + +typedef struct _cmsTagLinkedList_st { + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + struct _cmsTagLinkedList_st* Next; + +} _cmsTagLinkedList; + +// This is the list of built-in tags. The data of this list can be modified by plug-ins +static _cmsTagLinkedList SupportedTags[] = { + + { cmsSigAToB0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]}, + { cmsSigAToB1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]}, + { cmsSigAToB2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]}, + { cmsSigBToA0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]}, + { cmsSigBToA1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]}, + { cmsSigBToA2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]}, + + // Allow corbis and its broken XYZ type + { cmsSigRedColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]}, + { cmsSigGreenColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]}, + { cmsSigBlueColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]}, + + { cmsSigRedTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]}, + { cmsSigGreenTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]}, + { cmsSigBlueTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]}, + + { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]}, + { cmsSigCharTargetTag, { 1, 1, { cmsSigTextType }, NULL}, &SupportedTags[14]}, + + { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]}, + { cmsSigChromaticityTag, { 1, 1, { cmsSigChromaticityType }, NULL}, &SupportedTags[16]}, + { cmsSigColorantOrderTag, { 1, 1, { cmsSigColorantOrderType }, NULL}, &SupportedTags[17]}, + { cmsSigColorantTableTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[18]}, + { cmsSigColorantTableOutTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[19]}, + + { cmsSigCopyrightTag, { 1, 3, { cmsSigTextType, cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]}, + { cmsSigDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]}, + + { cmsSigDeviceMfgDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]}, + { cmsSigDeviceModelDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]}, + + { cmsSigGamutTag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]}, + + { cmsSigGrayTRCTag, { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]}, + { cmsSigLuminanceTag, { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]}, + + { cmsSigMediaBlackPointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]}, + { cmsSigMediaWhitePointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]}, + + { cmsSigNamedColor2Tag, { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]}, + + { cmsSigPreview0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]}, + { cmsSigPreview1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]}, + { cmsSigPreview2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]}, + + { cmsSigProfileDescriptionTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]}, + { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]}, + { cmsSigTechnologyTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[35]}, + + { cmsSigColorimetricIntentImageStateTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]}, + { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]}, + { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]}, + + { cmsSigMeasurementTag, { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]}, + + { cmsSigPs2CRD0Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]}, + { cmsSigPs2CRD1Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]}, + { cmsSigPs2CRD2Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]}, + { cmsSigPs2CRD3Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]}, + { cmsSigPs2CSATag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]}, + { cmsSigPs2RenderingIntentTag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]}, + + { cmsSigViewingCondDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]}, + + { cmsSigUcrBgTag, { 1, 1, { cmsSigUcrBgType}, NULL}, &SupportedTags[47]}, + { cmsSigCrdInfoTag, { 1, 1, { cmsSigCrdInfoType}, NULL}, &SupportedTags[48]}, + + { cmsSigDToB0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]}, + { cmsSigDToB1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]}, + { cmsSigDToB2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]}, + { cmsSigDToB3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]}, + { cmsSigBToD0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]}, + { cmsSigBToD1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]}, + { cmsSigBToD2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]}, + { cmsSigBToD3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]}, + + { cmsSigScreeningDescTag, { 1, 1, { cmsSigTextDescriptionType }, NULL}, &SupportedTags[57]}, + { cmsSigViewingConditionsTag, { 1, 1, { cmsSigViewingConditionsType }, NULL}, &SupportedTags[58]}, + + { cmsSigScreeningTag, { 1, 1, { cmsSigScreeningType}, NULL }, &SupportedTags[59]}, + { cmsSigVcgtTag, { 1, 1, { cmsSigVcgtType}, NULL }, &SupportedTags[60]}, + { cmsSigMetaTag, { 1, 1, { cmsSigDictType}, NULL }, &SupportedTags[61]}, + { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]}, + + { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]}, + { cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, NULL} + +}; + +/* + Not supported Why + ======================= ========================================= + cmsSigOutputResponseTag ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT! + cmsSigNamedColorTag ==> Deprecated + cmsSigDataTag ==> Ancient, unused + cmsSigDeviceSettingsTag ==> Deprecated, useless +*/ + + +_cmsTagPluginChunkType _cmsTagPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsTagPluginChunkType newHead = { NULL }; + _cmsTagLinkedList* entry; + _cmsTagLinkedList* Anterior = NULL; + _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin]; + + // Walk the list copying all nodes + for (entry = head->Tag; + entry != NULL; + entry = entry ->Next) { + + _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Tag == NULL) + newHead.Tag = newEntry; + } + + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType)); +} + +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + DupTagList(ctx, src); + } + else { + static _cmsTagPluginChunkType TagPluginChunk = { NULL }; + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType)); + } + +} + +cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) +{ + cmsPluginTag* Plugin = (cmsPluginTag*) Data; + _cmsTagLinkedList *pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin); + + if (Data == NULL) { + + TagPluginChunk->Tag = NULL; + return TRUE; + } + + pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Signature = Plugin ->Signature; + pt ->Descriptor = Plugin ->Descriptor; + pt ->Next = TagPluginChunk ->Tag; + + TagPluginChunk ->Tag = pt; + + return TRUE; +} + +// Return a descriptor for a given tag or NULL +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig) +{ + _cmsTagLinkedList* pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin); + + for (pt = TagPluginChunk->Tag; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } + + for (pt = SupportedTags; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } + + return NULL; +} + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsvirt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsvirt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsvirt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsvirt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1246 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Virtual (built-in) profiles +// ----------------------------------------------------------------------------------- + +static +cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description) +{ + cmsMLU *DescriptionMLU, *CopyrightMLU; + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + DescriptionMLU = cmsMLUalloc(ContextID, 1); + CopyrightMLU = cmsMLUalloc(ContextID, 1); + + if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error; + + if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error; + if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error; + + if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error; + + rc = TRUE; + +Error: + + if (DescriptionMLU) + cmsMLUfree(DescriptionMLU); + if (CopyrightMLU) + cmsMLUfree(CopyrightMLU); + return rc; +} + + +static +cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model) +{ + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1); + + if (Seq == NULL) return FALSE; + + Seq->seq[0].deviceMfg = (cmsSignature) 0; + Seq->seq[0].deviceModel = (cmsSignature) 0; + +#ifdef CMS_DONT_USE_INT64 + Seq->seq[0].attributes[0] = 0; + Seq->seq[0].attributes[1] = 0; +#else + Seq->seq[0].attributes = 0; +#endif + + Seq->seq[0].technology = (cmsTechnologySignature) 0; + + cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS"); + cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model); + + if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error; + + rc = TRUE; + +Error: + if (Seq) + cmsFreeProfileSequenceDescription(Seq); + + return rc; +} + + + +// This function creates a profile based on White point, primaries and +// transfer functions. +cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) +{ + cmsHPROFILE hICC; + cmsMAT3 MColorants; + cmsCIEXYZTRIPLE Colorants; + cmsCIExyY MaxWhite; + cmsMAT3 CHAD; + cmsCIEXYZ WhitePointXYZ; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigRgbData); + cmsSetPCS(hICC, cmsSigXYZData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigRedColorantTag + // 4 cmsSigGreenColorantTag + // 5 cmsSigBlueColorantTag + // 6 cmsSigRedTRCTag + // 7 cmsSigGreenTRCTag + // 8 cmsSigBlueTRCTag + // 9 Chromatic adaptation Tag + // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) + // 10 cmsSigChromaticityTag + + + if (!SetTextTags(hICC, L"RGB built-in")) goto Error; + + if (WhitePoint) { + + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; + + cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); + _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); + + // This is a V4 tag, but many CMM does read and understand it no matter which version + if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; + } + + if (WhitePoint && Primaries) { + + MaxWhite.x = WhitePoint -> x; + MaxWhite.y = WhitePoint -> y; + MaxWhite.Y = 1.0; + + if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; + + Colorants.Red.X = MColorants.v[0].n[0]; + Colorants.Red.Y = MColorants.v[1].n[0]; + Colorants.Red.Z = MColorants.v[2].n[0]; + + Colorants.Green.X = MColorants.v[0].n[1]; + Colorants.Green.Y = MColorants.v[1].n[1]; + Colorants.Green.Z = MColorants.v[2].n[1]; + + Colorants.Blue.X = MColorants.v[0].n[2]; + Colorants.Blue.Y = MColorants.v[1].n[2]; + Colorants.Blue.Z = MColorants.v[2].n[2]; + + if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; + if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; + if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; + } + + + if (TransferFunction) { + + // Tries to minimize space. Thanks to Richard Hughes for this nice idea + if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error; + + if (TransferFunction[1] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error; + + } else { + + if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error; + } + + if (TransferFunction[2] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error; + + } else { + + if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error; + } + } + + if (Primaries) { + if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; + } + + + return hICC; + +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) +{ + return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction); +} + + + +// This function creates a profile based on White point and transfer function. +cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) +{ + cmsHPROFILE hICC; + cmsCIEXYZ tmp; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigGrayData); + cmsSetPCS(hICC, cmsSigXYZData); + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigGrayTRCTag + + // This conforms a standard Gray DisplayProfile + + // Fill-in the tags + + if (!SetTextTags(hICC, L"gray built-in")) goto Error; + + + if (WhitePoint) { + + cmsxyY2XYZ(&tmp, WhitePoint); + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error; + } + + if (TransferFunction) { + + if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error; + } + + return hICC; + +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; +} + + + +cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) +{ + return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction); +} + +// This is a devicelink operating in the target colorspace with as many transfer functions as components + +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) +{ + cmsHPROFILE hICC; + cmsPipeline* Pipeline; + cmsUInt32Number nChannels; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + // Set up channels + nChannels = cmsChannelsOf(ColorSpace); + + // Creates a Pipeline with prelinearization step only + Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); + if (Pipeline == NULL) goto Error; + + + // Copy tables to Pipeline + if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions))) + goto Error; + + // Create tags + if (!SetTextTags(hICC, L"Linearization built-in")) goto Error; + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error; + if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error; + + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); + + // Ok, done + return hICC; + +Error: + cmsPipelineFree(Pipeline); + if (hICC) + cmsCloseProfile(hICC); + + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) +{ + return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions); +} + +// Ink-limiting algorithm +// +// Sum = C + M + Y + K +// If Sum > InkLimit +// Ratio= 1 - (Sum - InkLimit) / (C + M + Y) +// if Ratio <0 +// Ratio=0 +// endif +// Else +// Ratio=1 +// endif +// +// C = Ratio * C +// M = Ratio * M +// Y = Ratio * Y +// K: Does not change + +static +int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo; + cmsFloat64Number SumCMY, SumCMYK, Ratio; + + InkLimit = (InkLimit * 655.35); + + SumCMY = In[0] + In[1] + In[2]; + SumCMYK = SumCMY + In[3]; + + if (SumCMYK > InkLimit) { + + Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else Ratio = 1; + + Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C + Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M + Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y + + Out[3] = In[3]; // K (untouched) + + return TRUE; +} + +// This is a devicelink operating in CMYK for ink-limiting + +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsFloat64Number Limit) +{ + cmsHPROFILE hICC; + cmsPipeline* LUT; + cmsStage* CLUT; + cmsUInt32Number nChannels; + + if (ColorSpace != cmsSigCmykData) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); + return NULL; + } + + if (Limit < 0.0 || Limit > 400) { + + cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); + if (Limit < 0) Limit = 0; + if (Limit > 400) Limit = 400; + + } + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Creates a Pipeline with 3D grid only + LUT = cmsPipelineAlloc(ContextID, 4, 4); + if (LUT == NULL) goto Error; + + + nChannels = cmsChannelsOf(ColorSpace); + + CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); + if (CLUT == NULL) goto Error; + + if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || + !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || + !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) + goto Error; + + // Create tags + if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; + + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; + if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; + + // cmsPipeline is already on virtual profile + cmsPipelineFree(LUT); + + // Ok, done + return hICC; + +Error: + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hICC != NULL) + cmsCloseProfile(hICC); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) +{ + return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit); +} + + +// Creates a fake Lab identity. +cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 2.1); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL; + + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab2ProfileTHR(NULL, WhitePoint); +} + + +// Creates a fake Lab V4 identity. +cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error; + + // An empty LUTs is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab4ProfileTHR(NULL, WhitePoint); +} + + +// Creates a fake XYZ identity +cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigXYZData); + cmsSetPCS(hProfile, cmsSigXYZData); + + if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error; + + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) +{ + return cmsCreateXYZProfileTHR(NULL); +} + + +//sRGB Curves are defined by: +// +//If R'sRGB,G'sRGB, B'sRGB < 0.04045 +// +// R = R'sRGB / 12.92 +// G = G'sRGB / 12.92 +// B = B'sRGB / 12.92 +// +// +//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045 +// +// R = ((R'sRGB + 0.055) / 1.055)^2.4 +// G = ((G'sRGB + 0.055) / 1.055)^2.4 +// B = ((B'sRGB + 0.055) / 1.055)^2.4 + +static +cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) +{ + cmsFloat64Number Parameters[5]; + + Parameters[0] = 2.4; + Parameters[1] = 1. / 1.055; + Parameters[2] = 0.055 / 1.055; + Parameters[3] = 1. / 12.92; + Parameters[4] = 0.04045; + + return cmsBuildParametricToneCurve(ContextID, 4, Parameters); +} + +// Create the ICC virtual profile for sRGB space +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID) +{ + cmsCIExyY D65 = { 0.3127, 0.3290, 1.0 }; + cmsCIExyYTRIPLE Rec709Primaries = { + {0.6400, 0.3300, 1.0}, + {0.3000, 0.6000, 1.0}, + {0.1500, 0.0600, 1.0} + }; + cmsToneCurve* Gamma22[3]; + cmsHPROFILE hsRGB; + + // cmsWhitePointFromTemp(&D65, 6504); + Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID); + if (Gamma22[0] == NULL) return NULL; + + hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22); + cmsFreeToneCurve(Gamma22[0]); + if (hsRGB == NULL) return NULL; + + if (!SetTextTags(hsRGB, L"sRGB built-in")) { + cmsCloseProfile(hsRGB); + return NULL; + } + + return hsRGB; +} + +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) +{ + return cmsCreate_sRGBProfileTHR(NULL); +} + + + +typedef struct { + cmsFloat64Number Brightness; + cmsFloat64Number Contrast; + cmsFloat64Number Hue; + cmsFloat64Number Saturation; + cmsBool lAdjustWP; + cmsCIEXYZ WPsrc, WPdest; + +} BCHSWADJUSTS, *LPBCHSWADJUSTS; + + +static +int bchswSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) +{ + cmsCIELab LabIn, LabOut; + cmsCIELCh LChIn, LChOut; + cmsCIEXYZ XYZ; + LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; + + + cmsLabEncoded2Float(&LabIn, In); + + + cmsLab2LCh(&LChIn, &LabIn); + + // Do some adjusts on LCh + + LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness; + LChOut.C = LChIn.C + bchsw -> Saturation; + LChOut.h = LChIn.h + bchsw -> Hue; + + + cmsLCh2Lab(&LabOut, &LChOut); + + // Move white point in Lab + if (bchsw->lAdjustWP) { + cmsLab2XYZ(&bchsw->WPsrc, &XYZ, &LabOut); + cmsXYZ2Lab(&bchsw->WPdest, &LabOut, &XYZ); + } + + // Back to encoded + + cmsFloat2LabEncoded(Out, &LabOut); + + return TRUE; +} + + +// Creates an abstract profile operating in Lab space for Brightness, +// contrast, Saturation and white point displacement + +cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest) +{ + cmsHPROFILE hICC; + cmsPipeline* Pipeline; + BCHSWADJUSTS bchsw; + cmsCIExyY WhitePnt; + cmsStage* CLUT; + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsUInt32Number i; + + bchsw.Brightness = Bright; + bchsw.Contrast = Contrast; + bchsw.Hue = Hue; + bchsw.Saturation = Saturation; + if (TempSrc == TempDest) { + + bchsw.lAdjustWP = FALSE; + } + else { + bchsw.lAdjustWP = TRUE; + cmsWhitePointFromTemp(&WhitePnt, TempSrc); + cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); + cmsWhitePointFromTemp(&WhitePnt, TempDest); + cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); + + } + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetDeviceClass(hICC, cmsSigAbstractClass); + cmsSetColorSpace(hICC, cmsSigLabData); + cmsSetPCS(hICC, cmsSigLabData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + // Creates a Pipeline with 3D grid only + Pipeline = cmsPipelineAlloc(ContextID, 3, 3); + if (Pipeline == NULL) { + cmsCloseProfile(hICC); + return NULL; + } + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; + CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); + if (CLUT == NULL) goto Error; + + + if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { + + // Shouldn't reach here + goto Error; + } + + if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { + goto Error; + } + + // Create tags + if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; + + cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); + + cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); + + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); + + // Ok, done + return hICC; + +Error: + cmsPipelineFree(Pipeline); + cmsCloseProfile(hICC); + return NULL; +} + + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest) +{ + return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest); +} + + +// Creates a fake NULL profile. This profile return 1 channel as always 0. +// Is useful only for gamut checking tricks +cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + cmsStage* PostLin; + cmsStage* OutLin; + cmsToneCurve* EmptyTab[3]; + cmsUInt16Number Zero[2] = { 0, 0 }; + const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) // can't allocate + return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; + + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetColorSpace(hProfile, cmsSigGrayData); + cmsSetPCS(hProfile, cmsSigLabData); + + // Create a valid ICC 4 structure + LUT = cmsPipelineAlloc(ContextID, 3, 1); + if (LUT == NULL) goto Error; + + EmptyTab[0] = EmptyTab[1] = EmptyTab[2] = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + PostLin = cmsStageAllocToneCurves(ContextID, 3, EmptyTab); + OutLin = cmsStageAllocToneCurves(ContextID, 1, EmptyTab); + cmsFreeToneCurve(EmptyTab[0]); + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) + goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, OutLin)) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; + + cmsPipelineFree(LUT); + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void) +{ + return cmsCreateNULLProfileTHR(NULL); +} + + +static +int IsPCS(cmsColorSpaceSignature ColorSpace) +{ + return (ColorSpace == cmsSigXYZData || + ColorSpace == cmsSigLabData); +} + + +static +void FixColorSpaces(cmsHPROFILE hProfile, + cmsColorSpaceSignature ColorSpace, + cmsColorSpaceSignature PCS, + cmsUInt32Number dwFlags) +{ + if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { + + if (IsPCS(ColorSpace) && IsPCS(PCS)) { + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + + if (IsPCS(ColorSpace) && !IsPCS(PCS)) { + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetPCS(hProfile, ColorSpace); + cmsSetColorSpace(hProfile, PCS); + return; + } + + if (IsPCS(PCS) && !IsPCS(ColorSpace)) { + + cmsSetDeviceClass(hProfile, cmsSigInputClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + } + + cmsSetDeviceClass(hProfile, cmsSigLinkClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); +} + + + +// This function creates a named color profile dumping all the contents of transform to a single profile +// In this way, LittleCMS may be used to "group" several named color databases into a single profile. +// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this +// is the normal PCS for named color profiles. +static +cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsHPROFILE hICC = NULL; + cmsUInt32Number i, nColors; + cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; + + // Create an empty placeholder + hICC = cmsCreateProfilePlaceholder(v->ContextID); + if (hICC == NULL) return NULL; + + // Critical information + cmsSetDeviceClass(hICC, cmsSigNamedColorClass); + cmsSetColorSpace(hICC, v ->ExitColorSpace); + cmsSetPCS(hICC, cmsSigLabData); + + // Tag profile with information + if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; + + Original = cmsGetNamedColorList(xform); + if (Original == NULL) goto Error; + + nColors = cmsNamedColorCount(Original); + nc2 = cmsDupNamedColorList(Original); + if (nc2 == NULL) goto Error; + + // Colorant count now depends on the output space + nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); + + // Make sure we have proper formatters + cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, + FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) + | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); + + // Apply the transfor to colorants. + for (i=0; i < nColors; i++) { + cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); + } + + if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; + cmsFreeNamedColorList(nc2); + + return hICC; + +Error: + if (hICC != NULL) cmsCloseProfile(hICC); + return NULL; +} + + +// This structure holds information about which MPU can be stored on a profile based on the version + +typedef struct { + cmsBool IsV4; // Is a V4 tag? + cmsTagSignature RequiredTag; // Set to 0 for both types + cmsTagTypeSignature LutType; // The LUT type + int nTypes; // Number of types (up to 5) + cmsStageSignature MpeTypes[5]; // 5 is the maximum number + +} cmsAllowedLUT; + +#define cmsSig0 ((cmsTagSignature) 0) + +static const cmsAllowedLUT AllowedLUTTypes[] = { + + { FALSE, cmsSig0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { FALSE, cmsSig0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { FALSE, cmsSig0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType } }, + { TRUE, cmsSig0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }} +}; + +#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT)) + +// Check a single entry +static +cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut) +{ + cmsStage* mpe; + int n; + + for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) { + + if (n > Tab ->nTypes) return FALSE; + if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE; + } + + return (n == Tab ->nTypes); +} + + +static +const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag) +{ + cmsUInt32Number n; + + for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) { + + const cmsAllowedLUT* Tab = AllowedLUTTypes + n; + + if (IsV4 ^ Tab -> IsV4) continue; + if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue; + + if (CheckOne(Tab, Lut)) return Tab; + } + + return NULL; +} + + +// Does convert a transform into a device link profile +cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hProfile = NULL; + cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; + int ColorSpaceBitsIn, ColorSpaceBitsOut; + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsPipeline* LUT = NULL; + cmsStage* mpe; + cmsContext ContextID = cmsGetTransformContextID(hTransform); + const cmsAllowedLUT* AllowedLUT; + cmsTagSignature DestinationTag; + cmsProfileClassSignature deviceClass; + + _cmsAssert(hTransform != NULL); + + // Get the first mpe to check for named color + mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); + + // Check if is a named color transform + if (mpe != NULL) { + + if (cmsStageType(mpe) == cmsSigNamedColorElemType) { + return CreateNamedColorDevicelink(hTransform); + } + } + + // First thing to do is to get a copy of the transformation + LUT = cmsPipelineDup(xform ->Lut); + if (LUT == NULL) return NULL; + + // Time to fix the Lab2/Lab4 issue. + if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) + goto Error; + } + + // On the output side too. Note that due to V2/V4 PCS encoding on lab we cannot fix white misalignments + if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { + + dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + } + + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) goto Error; // can't allocate + + cmsSetProfileVersion(hProfile, Version); + + FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); + + // Optimize the LUT and precalculate a devicelink + + ChansIn = cmsChannelsOf(xform -> EntryColorSpace); + ChansOut = cmsChannelsOf(xform -> ExitColorSpace); + + ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); + ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); + + FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); + FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); + + deviceClass = cmsGetDeviceClass(hProfile); + + if (deviceClass == cmsSigOutputClass) + DestinationTag = cmsSigBToA0Tag; + else + DestinationTag = cmsSigAToB0Tag; + + // Check if the profile/version can store the result + if (dwFlags & cmsFLAGS_FORCE_CLUT) + AllowedLUT = NULL; + else + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + if (AllowedLUT == NULL) { + + // Try to optimize + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + } + + // If no way, then force CLUT that for sure can be written + if (AllowedLUT == NULL) { + + cmsStage* FirstStage; + cmsStage* LastStage; + + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + + // Put identity curves if needed + FirstStage = cmsPipelineGetPtrToFirstStage(LUT); + if (FirstStage != NULL && FirstStage ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) + goto Error; + + LastStage = cmsPipelineGetPtrToLastStage(LUT); + if (LastStage != NULL && LastStage ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) + goto Error; + + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + } + + // Somethings is wrong... + if (AllowedLUT == NULL) { + goto Error; + } + + + if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) + cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); + + // Tag profile with information + if (!SetTextTags(hProfile, L"devicelink")) goto Error; + + // Store result + if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; + + + if (xform -> InputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; + } + + if (xform -> OutputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; + } + + if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { + if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; + } + + // Set the white point + if (deviceClass == cmsSigInputClass) { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; + } + else { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; + } + + + // Per 7.2.15 in spec 4.3 + cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); + + cmsPipelineFree(LUT); + return hProfile; + +Error: + if (LUT != NULL) cmsPipelineFree(LUT); + cmsCloseProfile(hProfile); + return NULL; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmswtpnt.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmswtpnt.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmswtpnt.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmswtpnt.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,379 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// D50 - Widely used +const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void) +{ + static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z}; + + return &D50XYZ; +} + +const cmsCIExyY* CMSEXPORT cmsD50_xyY(void) +{ + static cmsCIExyY D50xyY; + + cmsXYZ2xyY(&D50xyY, cmsD50_XYZ()); + + return &D50xyY; +} + +// Obtains WhitePoint from Temperature +cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK) +{ + cmsFloat64Number x, y; + cmsFloat64Number T, T2, T3; + // cmsFloat64Number M1, M2; + + _cmsAssert(WhitePoint != NULL); + + T = TempK; + T2 = T*T; // Square + T3 = T2*T; // Cube + + // For correlated color temperature (T) between 4000K and 7000K: + + if (T >= 4000. && T <= 7000.) + { + x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; + } + else + // or for correlated color temperature (T) between 7000K and 25000K: + + if (T > 7000.0 && T <= 25000.0) + { + x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); + return FALSE; + } + + // Obtain y(x) + y = -3.000*(x*x) + 2.870*x - 0.275; + + // wave factors (not used, but here for futures extensions) + + // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); + // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); + + WhitePoint -> x = x; + WhitePoint -> y = y; + WhitePoint -> Y = 1.0; + + return TRUE; +} + + + +typedef struct { + + cmsFloat64Number mirek; // temp (in microreciprocal kelvin) + cmsFloat64Number ut; // u coord of intersection w/ blackbody locus + cmsFloat64Number vt; // v coord of intersection w/ blackbody locus + cmsFloat64Number tt; // slope of ISOTEMPERATURE. line + + } ISOTEMPERATURE; + +static const ISOTEMPERATURE isotempdata[] = { +// {Mirek, Ut, Vt, Tt } + {0, 0.18006, 0.26352, -0.24341}, + {10, 0.18066, 0.26589, -0.25479}, + {20, 0.18133, 0.26846, -0.26876}, + {30, 0.18208, 0.27119, -0.28539}, + {40, 0.18293, 0.27407, -0.30470}, + {50, 0.18388, 0.27709, -0.32675}, + {60, 0.18494, 0.28021, -0.35156}, + {70, 0.18611, 0.28342, -0.37915}, + {80, 0.18740, 0.28668, -0.40955}, + {90, 0.18880, 0.28997, -0.44278}, + {100, 0.19032, 0.29326, -0.47888}, + {125, 0.19462, 0.30141, -0.58204}, + {150, 0.19962, 0.30921, -0.70471}, + {175, 0.20525, 0.31647, -0.84901}, + {200, 0.21142, 0.32312, -1.0182 }, + {225, 0.21807, 0.32909, -1.2168 }, + {250, 0.22511, 0.33439, -1.4512 }, + {275, 0.23247, 0.33904, -1.7298 }, + {300, 0.24010, 0.34308, -2.0637 }, + {325, 0.24702, 0.34655, -2.4681 }, + {350, 0.25591, 0.34951, -2.9641 }, + {375, 0.26400, 0.35200, -3.5814 }, + {400, 0.27218, 0.35407, -4.3633 }, + {425, 0.28039, 0.35577, -5.3762 }, + {450, 0.28863, 0.35714, -6.7262 }, + {475, 0.29685, 0.35823, -8.5955 }, + {500, 0.30505, 0.35907, -11.324 }, + {525, 0.31320, 0.35968, -15.628 }, + {550, 0.32129, 0.36011, -23.325 }, + {575, 0.32931, 0.36038, -40.770 }, + {600, 0.33724, 0.36051, -116.45 } +}; + +#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE) + + +// Robertson's method +cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) +{ + cmsUInt32Number j; + cmsFloat64Number us,vs; + cmsFloat64Number uj,vj,tj,di,dj,mi,mj; + cmsFloat64Number xs, ys; + + _cmsAssert(WhitePoint != NULL); + _cmsAssert(TempK != NULL); + + di = mi = 0; + xs = WhitePoint -> x; + ys = WhitePoint -> y; + + // convert (x,y) to CIE 1960 (u,WhitePoint) + + us = (2*xs) / (-xs + 6*ys + 1.5); + vs = (3*ys) / (-xs + 6*ys + 1.5); + + + for (j=0; j < NISO; j++) { + + uj = isotempdata[j].ut; + vj = isotempdata[j].vt; + tj = isotempdata[j].tt; + mj = isotempdata[j].mirek; + + dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj); + + if ((j != 0) && (di/dj < 0.0)) { + + // Found a match + *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); + return TRUE; + } + + di = dj; + mi = mj; + } + + // Not found + return FALSE; +} + + +// Compute chromatic adaptation matrix using Chad as cone matrix + +static +cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion, + const cmsCIEXYZ* SourceWhitePoint, + const cmsCIEXYZ* DestWhitePoint, + const cmsMAT3* Chad) + +{ + + cmsMAT3 Chad_Inv; + cmsVEC3 ConeSourceXYZ, ConeSourceRGB; + cmsVEC3 ConeDestXYZ, ConeDestRGB; + cmsMAT3 Cone, Tmp; + + + Tmp = *Chad; + if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE; + + _cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, + SourceWhitePoint -> Y, + SourceWhitePoint -> Z); + + _cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X, + DestWhitePoint -> Y, + DestWhitePoint -> Z); + + _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); + _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); + + // Build matrix + _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); + _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); + _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); + + + // Normalize + _cmsMAT3per(&Tmp, &Cone, Chad); + _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); + + return TRUE; +} + +// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll +// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll) +{ + cmsMAT3 LamRigg = {{ // Bradford matrix + {{ 0.8951, 0.2664, -0.1614 }}, + {{ -0.7502, 1.7135, 0.0367 }}, + {{ 0.0389, -0.0685, 1.0296 }} + }}; + + if (ConeMatrix == NULL) + ConeMatrix = &LamRigg; + + return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); +} + +// Same as anterior, but assuming D50 destination. White point is given in xyY +static +cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) +{ + cmsCIEXYZ Dn; + cmsMAT3 Bradford; + cmsMAT3 Tmp; + + cmsxyY2XYZ(&Dn, SourceWhitePt); + + if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE; + + Tmp = *r; + _cmsMAT3per(r, &Bradford, &Tmp); + + return TRUE; +} + +// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ +// This is just an approximation, I am not handling all the non-linear +// aspects of the RGB to XYZ process, and assumming that the gamma correction +// has transitive property in the transformation chain. +// +// the alghoritm: +// +// - First I build the absolute conversion matrix using +// primaries in XYZ. This matrix is next inverted +// - Then I eval the source white point across this matrix +// obtaining the coeficients of the transformation +// - Then, I apply these coeficients to the original matrix +// +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) +{ + cmsVEC3 WhitePoint, Coef; + cmsMAT3 Result, Primaries; + cmsFloat64Number xn, yn; + cmsFloat64Number xr, yr; + cmsFloat64Number xg, yg; + cmsFloat64Number xb, yb; + + xn = WhitePt -> x; + yn = WhitePt -> y; + xr = Primrs -> Red.x; + yr = Primrs -> Red.y; + xg = Primrs -> Green.x; + yg = Primrs -> Green.y; + xb = Primrs -> Blue.x; + yb = Primrs -> Blue.y; + + // Build Primaries matrix + _cmsVEC3init(&Primaries.v[0], xr, xg, xb); + _cmsVEC3init(&Primaries.v[1], yr, yg, yb); + _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); + + + // Result = Primaries ^ (-1) inverse matrix + if (!_cmsMAT3inverse(&Primaries, &Result)) + return FALSE; + + + _cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); + + // Across inverse primaries ... + _cmsMAT3eval(&Coef, &Result, &WhitePoint); + + // Give us the Coefs, then I build transformation matrix + _cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); + _cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); + _cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); + + + return _cmsAdaptMatrixToD50(r, WhitePt); + +} + + +// Adapts a color to a given illuminant. Original color is expected to have +// a SourceWhitePt white point. +cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, + const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value) +{ + cmsMAT3 Bradford; + cmsVEC3 In, Out; + + _cmsAssert(Result != NULL); + _cmsAssert(SourceWhitePt != NULL); + _cmsAssert(Illuminant != NULL); + _cmsAssert(Value != NULL); + + if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE; + + _cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z); + _cmsMAT3eval(&Out, &Bradford, &In); + + Result -> X = Out.n[0]; + Result -> Y = Out.n[1]; + Result -> Z = Out.n[2]; + + return TRUE; +} + + diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsxform.c openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsxform.c --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/cmsxform.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/cmsxform.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1394 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// Transformations stuff +// ----------------------------------------------------------------------- + +#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0 + +// The Context0 observer adaptation state. +_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + +// Init and duplicate observer adaptation state +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + void* from; + + if (src != NULL) { + from = src ->chunks[AdaptationStateContext]; + } + else { + from = &AdaptationStateChunk; + } + + ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType)); +} + + +// Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all +// but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states. +cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d) +{ + cmsFloat64Number prev; + _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext); + + // Get previous value for return + prev = ptr ->AdaptationState; + + // Set the value if d is positive or zero + if (d >= 0.0) { + + ptr ->AdaptationState = d; + } + + // Always return previous value + return prev; +} + + +// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine +cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d) +{ + return cmsSetAdaptationStateTHR(NULL, d); +} + +// ----------------------------------------------------------------------- + +// Alarm codes for 16-bit transformations, because the fixed range of containers there are +// no values left to mark out of gamut. + +#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; + +// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be +// encoded in 16 bits. +void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); + + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen + + memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes)); +} + +// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context. +// Values are meant to be encoded in 16 bits. +void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); + + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen + + memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); +} + +void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(NewAlarm != NULL); + + cmsSetAlarmCodesTHR(NULL, NewAlarm); +} + +void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(OldAlarm != NULL); + cmsGetAlarmCodesTHR(NULL, OldAlarm); +} + + +// Init and duplicate alarm codes +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; + void* from; + + if (src != NULL) { + from = src ->chunks[AlarmCodesContext]; + } + else { + from = &AlarmCodesChunk; + } + + ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType)); +} + +// ----------------------------------------------------------------------- + +// Get rid of transform resources +void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform; + + _cmsAssert(p != NULL); + + if (p -> GamutCheck) + cmsPipelineFree(p -> GamutCheck); + + if (p -> Lut) + cmsPipelineFree(p -> Lut); + + if (p ->InputColorant) + cmsFreeNamedColorList(p ->InputColorant); + + if (p -> OutputColorant) + cmsFreeNamedColorList(p ->OutputColorant); + + if (p ->Sequence) + cmsFreeProfileSequenceDescription(p ->Sequence); + + if (p ->UserData) + p ->FreeUserData(p ->ContextID, p ->UserData); + + _cmsFree(p ->ContextID, (void *) p); +} + + +static +cmsUInt32Number PixelSize(cmsUInt32Number Format) +{ + cmsUInt32Number fmt_bytes = T_BYTES(Format); + + // For double, the T_BYTES field is zero + if (fmt_bytes == 0) + return sizeof(cmsUInt64Number); + + // Otherwise, it is already correct for all formats + return fmt_bytes; +} + + + + +// Apply transform. +void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size) + +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; + + stride.BytesPerLineIn = 0; // Not used + stride.BytesPerLineOut = 0; + stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat); + stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat); + + p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); +} + + +// This is a legacy stride for planar +void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, cmsUInt32Number Stride) + +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; + + stride.BytesPerLineIn = 0; + stride.BytesPerLineOut = 0; + stride.BytesPerPlaneIn = Stride; + stride.BytesPerPlaneOut = Stride; + + p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride); +} + +// This is the "fast" function for plugins +void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + cmsUInt32Number BytesPerLineIn, + cmsUInt32Number BytesPerLineOut, + cmsUInt32Number BytesPerPlaneIn, + cmsUInt32Number BytesPerPlaneOut) + +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + cmsStride stride; + + stride.BytesPerLineIn = BytesPerLineIn; + stride.BytesPerLineOut = BytesPerLineOut; + stride.BytesPerPlaneIn = BytesPerPlaneIn; + stride.BytesPerPlaneOut = BytesPerPlaneOut; + + p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride); +} + + + +// Transform routines ---------------------------------------------------------------------------------------------------------- + +// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. +// Note that because extended range, we can use a -1.0 value for out of gamut in this case. +static +void FloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; + cmsFloat32Number OutOfGamut; + cmsUInt32Number i, j, c, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(fIn, 0, sizeof(fIn)); + memset(fOut, 0, sizeof(fOut)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn); + + // Any gamut chack to do? + if (p->GamutCheck != NULL) { + + // Evaluate gamut marker. + cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck); + + // Is current color out of gamut? + if (OutOfGamut > 0.0) { + + // Certainly, out of gamut + for (c = 0; c < cmsMAXCHANNELS; c++) + fOut[c] = -1.0; + + } + else { + // No, proceed normally + cmsPipelineEvalFloat(fIn, fOut, p->Lut); + } + } + else { + + // No gamut check at all + cmsPipelineEvalFloat(fIn, fOut, p->Lut); + } + + + output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } + +} + + +static +void NullFloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) + +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(fIn, 0, sizeof(fIn)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*) in + strideIn; + output = (cmsUInt8Number*) out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn); + output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// 16 bit precision ----------------------------------------------------------------------------------------------------------- + +// Null transformation, only applies formatters. No cache +static +void NullXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } + +} + + +// No gamut check, no cache, 16 bits +static +void PrecalculatedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + CMSREGISTER cmsUInt8Number* accum; + CMSREGISTER cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } + +} + + +// Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical. +static +void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, + const cmsUInt16Number wIn[], + cmsUInt16Number wOut[]) +{ + cmsUInt16Number wOutOfGamut; + + p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); + if (wOutOfGamut >= 1) { + + cmsUInt32Number i; + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); + + for (i=0; i < p ->Lut->OutputChannels; i++) { + + wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; + } + } + else + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); +} + +// Gamut check, No cache, 16 bits. +static +void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + TransformOnePixelWithGamutCheck(p, wIn, wOut); + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + + +// No gamut check, Cache, 16 bits, +static +void CachedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + _cmsCACHE Cache; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + // Get copy of zero cache + memcpy(&Cache, &p->Cache, sizeof(Cache)); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data); + + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } + + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// All those nice features together +static +void CachedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + _cmsCACHE Cache; + cmsUInt32Number i, j, strideIn, strideOut; + + _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride); + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + // Get copy of zero cache + memcpy(&Cache, &p->Cache, sizeof(Cache)); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + accum = (cmsUInt8Number*)in + strideIn; + output = (cmsUInt8Number*)out + strideOut; + + for (j = 0; j < PixelsPerLine; j++) { + + accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + TransformOnePixelWithGamutCheck(p, wIn, wOut); + + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } + + output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut); + } + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + +// Transform plug-ins ---------------------------------------------------------------------------------------------------- + +// List of used-defined transform factories +typedef struct _cmsTransformCollection_st { + + _cmsTransform2Factory Factory; + cmsBool OldXform; // Factory returns xform function in the old style + + struct _cmsTransformCollection_st *Next; + +} _cmsTransformCollection; + +// The linked list head +_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginTransformList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsTransformPluginChunkType newHead = { NULL }; + _cmsTransformCollection* entry; + _cmsTransformCollection* Anterior = NULL; + _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin]; + + // Walk the list copying all nodes + for (entry = head->TransformCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TransformCollection == NULL) + newHead.TransformCollection = newEntry; + } + + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType)); +} + +// Allocates memory for transform plugin factory +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginTransformList(ctx, src); + } + else { + static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL }; + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType)); + } +} + +// Adaptor for old versions of plug-in +static +void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride) +{ + + cmsUInt32Number i, strideIn, strideOut; + + _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride); + + strideIn = 0; + strideOut = 0; + + for (i = 0; i < LineCount; i++) { + + void *accum = (cmsUInt8Number*)InputBuffer + strideIn; + void *output = (cmsUInt8Number*)OutputBuffer + strideOut; + + CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn); + + strideIn += Stride->BytesPerLineIn; + strideOut += Stride->BytesPerLineOut; + } +} + + + +// Register new ways to transform +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; + _cmsTransformCollection* fl; + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); + + if (Data == NULL) { + + // Free the chain. Memory is safely freed at exit + ctx->TransformCollection = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin->factories.xform == NULL) return FALSE; + + + fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); + if (fl == NULL) return FALSE; + + // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case + if (Plugin->base.ExpectedVersion < 2080) { + + fl->OldXform = TRUE; + } + else + fl->OldXform = FALSE; + + // Copy the parameters + fl->Factory = Plugin->factories.xform; + + // Keep linked list + fl ->Next = ctx->TransformCollection; + ctx->TransformCollection = fl; + + // All is ok + return TRUE; +} + + +void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) +{ + _cmsAssert(CMMcargo != NULL); + CMMcargo ->UserData = ptr; + CMMcargo ->FreeUserData = FreePrivateDataFn; +} + +// returns the pointer defined by the plug-in to store private data +void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo ->UserData; +} + +// returns the current formatters +void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) +{ + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInput; + if (ToOutput) *ToOutput = CMMcargo ->ToOutput; +} + +void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) +{ + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInputFloat; + if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; +} + +// returns original flags +cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo->dwOriginalFlags; +} + +// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper +// for separated transforms. If this is the case, +static +_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, + cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin); + _cmsTransformCollection* Plugin; + + // Allocate needed memory + _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); + if (!p) { + cmsPipelineFree(lut); + return NULL; + } + + // Store the proposed pipeline + p->Lut = lut; + + // Let's see if any plug-in want to do the transform by itself + if (p->Lut != NULL) { + + if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE)) + { + for (Plugin = ctx->TransformCollection; + Plugin != NULL; + Plugin = Plugin->Next) { + + if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) { + + // Last plugin in the declaration order takes control. We just keep + // the original parameters as a logging. + // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default + // an optimized transform is not reusable. The plug-in can, however, change + // the flags and make it suitable. + + p->ContextID = ContextID; + p->InputFormat = *InputFormat; + p->OutputFormat = *OutputFormat; + p->dwOriginalFlags = *dwFlags; + + // Fill the formatters just in case the optimized routine is interested. + // No error is thrown if the formatter doesn't exist. It is up to the optimization + // factory to decide what to do in those cases. + p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + + // Save the day? (Ignore the warning) + if (Plugin->OldXform) { + p->OldXform = (_cmsTransformFn)(void*)p->xform; + p->xform = _cmsTransform2toTransformAdaptor; + } + + return p; + } + } + } + + // Not suitable for the transform plug-in, let's check the pipeline plug-in + _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); + } + + // Check whatever this is a true floating point transform + if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { + + // Get formatter function always return a valid union, but the contents of this union may be NULL. + p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + + if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } + + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullFloatXFORM; + } + else { + // Float transforms don't use cache, always are non-NULL + p ->xform = FloatXFORM; + } + + } + else { + + if (*InputFormat == 0 && *OutputFormat == 0) { + p ->FromInput = p ->ToOutput = NULL; + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + } + else { + + cmsUInt32Number BytesPerPixelInput; + + p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + + if (p ->FromInput == NULL || p ->ToOutput == NULL) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } + + BytesPerPixelInput = T_BYTES(p ->InputFormat); + if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + + } + + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullXFORM; + } + else { + if (*dwFlags & cmsFLAGS_NOCACHE) { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cache + else + p ->xform = PrecalculatedXFORM; // No cache, no gamut check + } + else { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = CachedXFORMGamutCheck; // Gamut check, cache + else + p ->xform = CachedXFORM; // No gamut check, cache + + } + } + } + + p ->InputFormat = *InputFormat; + p ->OutputFormat = *OutputFormat; + p ->dwOriginalFlags = *dwFlags; + p ->ContextID = ContextID; + p ->UserData = NULL; + return p; +} + +static +cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) +{ + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; + cmsColorSpaceSignature PostColorSpace; + cmsUInt32Number i; + + if (nProfiles == 0) return FALSE; + if (hProfiles[0] == NULL) return FALSE; + + *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsProfileClassSignature cls; + cmsHPROFILE hProfile = hProfiles[i]; + + int lIsInput = (PostColorSpace != cmsSigXYZData) && + (PostColorSpace != cmsSigLabData); + + if (hProfile == NULL) return FALSE; + + cls = cmsGetDeviceClass(hProfile); + + if (cls == cmsSigNamedColorClass) { + + ColorSpaceIn = cmsSig1colorData; + ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); + } + else + if (lIsInput || (cls == cmsSigLinkClass)) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else + { + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (i==0) + *Input = ColorSpaceIn; + + PostColorSpace = ColorSpaceOut; + } + + *Output = PostColorSpace; + + return TRUE; +} + +// Check colorspace +static +cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) +{ + int Space1 = (int) T_COLORSPACE(dwFormat); + int Space2 = _cmsLCMScolorSpace(Check); + + if (Space1 == PT_ANY) return TRUE; + if (Space1 == Space2) return TRUE; + + if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; + if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; + + return FALSE; +} + +// ---------------------------------------------------------------------------------------------------------------- + +// Jun-21-2000: Some profiles (those that comes with W2K) comes +// with the media white (media black?) x 100. Add a sanity check + +static +void NormalizeXYZ(cmsCIEXYZ* Dest) +{ + while (Dest -> X > 2. && + Dest -> Y > 2. && + Dest -> Z > 2.) { + + Dest -> X /= 10.; + Dest -> Y /= 10.; + Dest -> Z /= 10.; + } +} + +static +void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) +{ + if (src == NULL) { + wtPt ->X = cmsD50X; + wtPt ->Y = cmsD50Y; + wtPt ->Z = cmsD50Z; + } + else { + wtPt ->X = src->X; + wtPt ->Y = src->Y; + wtPt ->Z = src->Z; + + NormalizeXYZ(wtPt); + } + +} + +// New to lcms 2.0 -- have all parameters available. +cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags) +{ + _cmsTRANSFORM* xform; + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + cmsPipeline* Lut; + cmsUInt32Number LastIntent = Intents[nProfiles-1]; + + // If it is a fake transform + if (dwFlags & cmsFLAGS_NULLTRANSFORM) + { + return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); + } + + // If gamut check is requested, make sure we have a gamut profile + if (dwFlags & cmsFLAGS_GAMUTCHECK) { + if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; + } + + // On floating point transforms, inhibit cache + if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) + dwFlags |= cmsFLAGS_NOCACHE; + + // Mark entry/exit spaces + if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { + cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); + return NULL; + } + + // Check if proper colorspaces + if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); + return NULL; + } + + if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); + return NULL; + } + + // Create a pipeline with all transformations + Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (Lut == NULL) { + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); + return NULL; + } + + // Check channel count + if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || + (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { + cmsPipelineFree(Lut); + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); + return NULL; + } + + + // All seems ok + xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); + if (xform == NULL) { + return NULL; + } + + // Keep values + xform ->EntryColorSpace = EntryColorSpace; + xform ->ExitColorSpace = ExitColorSpace; + xform ->RenderingIntent = Intents[nProfiles-1]; + + // Take white points + SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); + SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); + + + // Create a gamut check LUT if requested + if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) + xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, + BPC, Intents, + AdaptationStates, + nGamutPCSposition, + hGamutProfile); + + + // Try to read input and output colorant table + if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { + + // Input table can only come in this way. + xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); + } + + // Output is a little bit more complex. + if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { + + // This tag may exist only on devicelink profiles. + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { + + // It may be NULL if error + xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); + } + + } else { + + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { + + xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); + } + } + + // Store the sequence of profiles + if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { + xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); + } + else + xform ->Sequence = NULL; + + // If this is a cached transform, init first value, which is zero (16 bits only) + if (!(dwFlags & cmsFLAGS_NOCACHE)) { + + memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); + + if (xform ->GamutCheck != NULL) { + TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); + } + else { + + xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); + } + + } + + return (cmsHTRANSFORM) xform; +} + +// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsBool BPC[256]; + cmsUInt32Number Intents[256]; + cmsFloat64Number AdaptationStates[256]; + + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } + + for (i=0; i < nProfiles; i++) { + BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; + Intents[i] = Intent; + AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); + } + + + return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); +} + + + +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } + + return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), + hProfiles, + nProfiles, + InputFormat, + OutputFormat, + Intent, + dwFlags); +} + +cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + + cmsHPROFILE hArray[2]; + + hArray[0] = Input; + hArray[1] = Output; + + return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags); +} + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); +} + + +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) +{ + cmsHPROFILE hArray[4]; + cmsUInt32Number Intents[4]; + cmsBool BPC[4]; + cmsFloat64Number Adaptation[4]; + cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; + + + hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; + Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; + BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; + + Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); + + if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) + return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); + + return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, + ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); + +} + + +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) +{ + return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), + InputProfile, + InputFormat, + OutputProfile, + OutputFormat, + ProofingProfile, + nIntent, + ProofingIntent, + dwFlags); +} + + +// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed +cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return NULL; + return xform -> ContextID; +} + +// Grab the input/output formats +cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return 0; + return xform->InputFormat; +} + +cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return 0; + return xform->OutputFormat; +} + +// For backwards compatibility +cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsFormatter16 FromInput, ToOutput; + + + // We only can afford to change formatters if previous transform is at least 16 bits + if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { + + cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); + return FALSE; + } + + FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + + if (FromInput == NULL || ToOutput == NULL) { + + cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + return FALSE; + } + + xform ->InputFormat = InputFormat; + xform ->OutputFormat = OutputFormat; + xform ->FromInput = FromInput; + xform ->ToOutput = ToOutput; + return TRUE; +} diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2.h openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2.h --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2.h 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2.h 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1951 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2021 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// Version 2.12 +// + +#ifndef _lcms2_H + +// ********** Configuration toggles **************************************** + +// Uncomment this one if you are using big endian machines +// #define CMS_USE_BIG_ENDIAN 1 + +// Uncomment this one if your compiler/machine does NOT support the +// "long long" type. +// #define CMS_DONT_USE_INT64 1 + +// Uncomment this if your compiler doesn't work with fast floor function +// #define CMS_DONT_USE_FAST_FLOOR 1 + +// Uncomment this line if you want lcms to use the black point tag in profile, +// if commented, lcms will compute the black point by its own. +// It is safer to leave it commented out +// #define CMS_USE_PROFILE_BLACK_POINT_TAG 1 + +// Uncomment this line if you are compiling as C++ and want a C++ API +// #define CMS_USE_CPP_API + +// Uncomment this line if you need strict CGATS syntax. Makes CGATS files to +// require "KEYWORD" on undefined identifiers, keep it commented out unless needed +// #define CMS_STRICT_CGATS 1 + +// Uncomment to get rid of the tables for "half" float support +// #define CMS_NO_HALF_SUPPORT 1 + +// Uncomment to get rid of pthreads/windows dependency +// #define CMS_NO_PTHREADS 1 + +// Uncomment this for special windows mutex initialization (see lcms2_internal.h) +// #define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT + +// Uncomment this to remove the "register" storage class +// #define CMS_NO_REGISTER_KEYWORD 1 + +// ********** End of configuration toggles ****************************** + +// Needed for streams +#include + +// Needed for portability (C99 per 7.1.2) +#include +#include +#include + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Version/release +#define LCMS_VERSION 2120 + +// I will give the chance of redefining basic types for compilers that are not fully C99 compliant +#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED + +// Base types +typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec +typedef signed char cmsInt8Number; // That is guaranteed by the C99 spec + +#if CHAR_BIT != 8 +# error "Unable to find 8 bit type, unsupported compiler" +#endif + +// IEEE float storage numbers +typedef float cmsFloat32Number; +typedef double cmsFloat64Number; + +// 16-bit base types +#if (USHRT_MAX == 65535U) + typedef unsigned short cmsUInt16Number; +#elif (UINT_MAX == 65535U) + typedef unsigned int cmsUInt16Number; +#else +# error "Unable to find 16 bits unsigned type, unsupported compiler" +#endif + +#if (SHRT_MAX == 32767) + typedef short cmsInt16Number; +#elif (INT_MAX == 32767) + typedef int cmsInt16Number; +#else +# error "Unable to find 16 bits signed type, unsupported compiler" +#endif + +// 32-bit base type +#if (UINT_MAX == 4294967295U) + typedef unsigned int cmsUInt32Number; +#elif (ULONG_MAX == 4294967295U) + typedef unsigned long cmsUInt32Number; +#else +# error "Unable to find 32 bit unsigned type, unsupported compiler" +#endif + +#if (INT_MAX == +2147483647) + typedef int cmsInt32Number; +#elif (LONG_MAX == +2147483647) + typedef long cmsInt32Number; +#else +# error "Unable to find 32 bit signed type, unsupported compiler" +#endif + +// 64-bit base types +#ifndef CMS_DONT_USE_INT64 +# if (ULONG_MAX == 18446744073709551615U) + typedef unsigned long cmsUInt64Number; +# elif (ULLONG_MAX == 18446744073709551615U) + typedef unsigned long long cmsUInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +# if (LONG_MAX == +9223372036854775807) + typedef long cmsInt64Number; +# elif (LLONG_MAX == +9223372036854775807) + typedef long long cmsInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +#endif +#endif + +// Handle "register" keyword +#if defined(CMS_NO_REGISTER_KEYWORD) && !defined(CMS_DLL) && !defined(CMS_DLL_BUILD) +# define CMSREGISTER +#else +# define CMSREGISTER register +#endif + +// In the case 64 bit numbers are not supported by the compiler +#ifdef CMS_DONT_USE_INT64 + typedef cmsUInt32Number cmsUInt64Number[2]; + typedef cmsInt32Number cmsInt64Number[2]; +#endif + +// Derivative types +typedef cmsUInt32Number cmsSignature; +typedef cmsUInt16Number cmsU8Fixed8Number; +typedef cmsInt32Number cmsS15Fixed16Number; +typedef cmsUInt32Number cmsU16Fixed16Number; + +// Boolean type, which will be using the native integer +typedef int cmsBool; + +// Try to detect windows +#if defined (_WIN32) || defined(_WIN64) || defined(WIN32) || defined(_WIN32_) +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef _MSC_VER +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef __BORLANDC__ +# define CMS_IS_WINDOWS_ 1 +#endif + +// Try to detect big endian platforms. This list can be endless, so primarily rely on the configure script +// on Unix-like systems, and allow it to be set on the compiler command line using +// -DCMS_USE_BIG_ENDIAN or something similar +#ifdef CMS_USE_BIG_ENDIAN // set at compiler command line takes overall precedence + +# if CMS_USE_BIG_ENDIAN == 0 +# undef CMS_USE_BIG_ENDIAN +# endif + +#else // CMS_USE_BIG_ENDIAN + +# ifdef WORDS_BIGENDIAN // set by configure (or explicitly on compiler command line) +# define CMS_USE_BIG_ENDIAN 1 +# else // WORDS_BIGENDIAN +// Fall back to platform/compiler specific tests +# if defined(__sgi__) || defined(__sgi) || defined(sparc) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +# if defined(__s390__) || defined(__s390x__) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +# ifdef macintosh +# ifdef __BIG_ENDIAN__ +# define CMS_USE_BIG_ENDIAN 1 +# endif +# ifdef __LITTLE_ENDIAN__ +# undef CMS_USE_BIG_ENDIAN +# endif +# endif +# endif // WORDS_BIGENDIAN + +# if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) +# define CMS_USE_BIG_ENDIAN 1 +# endif + +#endif // CMS_USE_BIG_ENDIAN + + +// Calling convention -- this is hardly platform and compiler dependent +#ifdef CMS_IS_WINDOWS_ +# if defined(CMS_DLL) || defined(CMS_DLL_BUILD) +# ifdef __BORLANDC__ +# define CMSEXPORT __stdcall _export +# define CMSAPI +# else +# define CMSEXPORT __stdcall +# ifdef CMS_DLL_BUILD +# define CMSAPI __declspec(dllexport) +# else +# define CMSAPI __declspec(dllimport) +# endif +# endif +# else +# define CMSEXPORT +# define CMSAPI +# endif +#else // not Windows +# ifdef HAVE_FUNC_ATTRIBUTE_VISIBILITY +# define CMSEXPORT +# define CMSAPI __attribute__((visibility("default"))) +# else +# define CMSEXPORT +# define CMSAPI +# endif +#endif // CMS_IS_WINDOWS_ + +#ifdef HasTHREADS +# if HasTHREADS == 1 +# undef CMS_NO_PTHREADS +# else +# define CMS_NO_PTHREADS 1 +# endif +#endif + +// Some common definitions +#define cmsMAX_PATH 256 + +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +// D50 XYZ normalized to Y=1.0 +#define cmsD50X 0.9642 +#define cmsD50Y 1.0 +#define cmsD50Z 0.8249 + +// V4 perceptual black +#define cmsPERCEPTUAL_BLACK_X 0.00336 +#define cmsPERCEPTUAL_BLACK_Y 0.0034731 +#define cmsPERCEPTUAL_BLACK_Z 0.00287 + +// Definitions in ICC spec +#define cmsMagicNumber 0x61637370 // 'acsp' +#define lcmsSignature 0x6c636d73 // 'lcms' + + +// Base ICC type definitions +typedef enum { + cmsSigChromaticityType = 0x6368726D, // 'chrm' + cmsSigColorantOrderType = 0x636C726F, // 'clro' + cmsSigColorantTableType = 0x636C7274, // 'clrt' + cmsSigCrdInfoType = 0x63726469, // 'crdi' + cmsSigCurveType = 0x63757276, // 'curv' + cmsSigDataType = 0x64617461, // 'data' + cmsSigDictType = 0x64696374, // 'dict' + cmsSigDateTimeType = 0x6474696D, // 'dtim' + cmsSigDeviceSettingsType = 0x64657673, // 'devs' + cmsSigLut16Type = 0x6d667432, // 'mft2' + cmsSigLut8Type = 0x6d667431, // 'mft1' + cmsSigLutAtoBType = 0x6d414220, // 'mAB ' + cmsSigLutBtoAType = 0x6d424120, // 'mBA ' + cmsSigMeasurementType = 0x6D656173, // 'meas' + cmsSigMultiLocalizedUnicodeType = 0x6D6C7563, // 'mluc' + cmsSigMultiProcessElementType = 0x6D706574, // 'mpet' + cmsSigNamedColorType = 0x6E636f6C, // 'ncol' -- DEPRECATED! + cmsSigNamedColor2Type = 0x6E636C32, // 'ncl2' + cmsSigParametricCurveType = 0x70617261, // 'para' + cmsSigProfileSequenceDescType = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdType = 0x70736964, // 'psid' + cmsSigResponseCurveSet16Type = 0x72637332, // 'rcs2' + cmsSigS15Fixed16ArrayType = 0x73663332, // 'sf32' + cmsSigScreeningType = 0x7363726E, // 'scrn' + cmsSigSignatureType = 0x73696720, // 'sig ' + cmsSigTextType = 0x74657874, // 'text' + cmsSigTextDescriptionType = 0x64657363, // 'desc' + cmsSigU16Fixed16ArrayType = 0x75663332, // 'uf32' + cmsSigUcrBgType = 0x62666420, // 'bfd ' + cmsSigUInt16ArrayType = 0x75693136, // 'ui16' + cmsSigUInt32ArrayType = 0x75693332, // 'ui32' + cmsSigUInt64ArrayType = 0x75693634, // 'ui64' + cmsSigUInt8ArrayType = 0x75693038, // 'ui08' + cmsSigVcgtType = 0x76636774, // 'vcgt' + cmsSigViewingConditionsType = 0x76696577, // 'view' + cmsSigXYZType = 0x58595A20 // 'XYZ ' + + +} cmsTagTypeSignature; + +// Base ICC tag definitions +typedef enum { + cmsSigAToB0Tag = 0x41324230, // 'A2B0' + cmsSigAToB1Tag = 0x41324231, // 'A2B1' + cmsSigAToB2Tag = 0x41324232, // 'A2B2' + cmsSigBlueColorantTag = 0x6258595A, // 'bXYZ' + cmsSigBlueMatrixColumnTag = 0x6258595A, // 'bXYZ' + cmsSigBlueTRCTag = 0x62545243, // 'bTRC' + cmsSigBToA0Tag = 0x42324130, // 'B2A0' + cmsSigBToA1Tag = 0x42324131, // 'B2A1' + cmsSigBToA2Tag = 0x42324132, // 'B2A2' + cmsSigCalibrationDateTimeTag = 0x63616C74, // 'calt' + cmsSigCharTargetTag = 0x74617267, // 'targ' + cmsSigChromaticAdaptationTag = 0x63686164, // 'chad' + cmsSigChromaticityTag = 0x6368726D, // 'chrm' + cmsSigColorantOrderTag = 0x636C726F, // 'clro' + cmsSigColorantTableTag = 0x636C7274, // 'clrt' + cmsSigColorantTableOutTag = 0x636C6F74, // 'clot' + cmsSigColorimetricIntentImageStateTag = 0x63696973, // 'ciis' + cmsSigCopyrightTag = 0x63707274, // 'cprt' + cmsSigCrdInfoTag = 0x63726469, // 'crdi' + cmsSigDataTag = 0x64617461, // 'data' + cmsSigDateTimeTag = 0x6474696D, // 'dtim' + cmsSigDeviceMfgDescTag = 0x646D6E64, // 'dmnd' + cmsSigDeviceModelDescTag = 0x646D6464, // 'dmdd' + cmsSigDeviceSettingsTag = 0x64657673, // 'devs' + cmsSigDToB0Tag = 0x44324230, // 'D2B0' + cmsSigDToB1Tag = 0x44324231, // 'D2B1' + cmsSigDToB2Tag = 0x44324232, // 'D2B2' + cmsSigDToB3Tag = 0x44324233, // 'D2B3' + cmsSigBToD0Tag = 0x42324430, // 'B2D0' + cmsSigBToD1Tag = 0x42324431, // 'B2D1' + cmsSigBToD2Tag = 0x42324432, // 'B2D2' + cmsSigBToD3Tag = 0x42324433, // 'B2D3' + cmsSigGamutTag = 0x67616D74, // 'gamt' + cmsSigGrayTRCTag = 0x6b545243, // 'kTRC' + cmsSigGreenColorantTag = 0x6758595A, // 'gXYZ' + cmsSigGreenMatrixColumnTag = 0x6758595A, // 'gXYZ' + cmsSigGreenTRCTag = 0x67545243, // 'gTRC' + cmsSigLuminanceTag = 0x6C756d69, // 'lumi' + cmsSigMeasurementTag = 0x6D656173, // 'meas' + cmsSigMediaBlackPointTag = 0x626B7074, // 'bkpt' + cmsSigMediaWhitePointTag = 0x77747074, // 'wtpt' + cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' // Deprecated by the ICC + cmsSigNamedColor2Tag = 0x6E636C32, // 'ncl2' + cmsSigOutputResponseTag = 0x72657370, // 'resp' + cmsSigPerceptualRenderingIntentGamutTag = 0x72696730, // 'rig0' + cmsSigPreview0Tag = 0x70726530, // 'pre0' + cmsSigPreview1Tag = 0x70726531, // 'pre1' + cmsSigPreview2Tag = 0x70726532, // 'pre2' + cmsSigProfileDescriptionTag = 0x64657363, // 'desc' + cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm' + cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdTag = 0x70736964, // 'psid' + cmsSigPs2CRD0Tag = 0x70736430, // 'psd0' + cmsSigPs2CRD1Tag = 0x70736431, // 'psd1' + cmsSigPs2CRD2Tag = 0x70736432, // 'psd2' + cmsSigPs2CRD3Tag = 0x70736433, // 'psd3' + cmsSigPs2CSATag = 0x70733273, // 'ps2s' + cmsSigPs2RenderingIntentTag = 0x70733269, // 'ps2i' + cmsSigRedColorantTag = 0x7258595A, // 'rXYZ' + cmsSigRedMatrixColumnTag = 0x7258595A, // 'rXYZ' + cmsSigRedTRCTag = 0x72545243, // 'rTRC' + cmsSigSaturationRenderingIntentGamutTag = 0x72696732, // 'rig2' + cmsSigScreeningDescTag = 0x73637264, // 'scrd' + cmsSigScreeningTag = 0x7363726E, // 'scrn' + cmsSigTechnologyTag = 0x74656368, // 'tech' + cmsSigUcrBgTag = 0x62666420, // 'bfd ' + cmsSigViewingCondDescTag = 0x76756564, // 'vued' + cmsSigViewingConditionsTag = 0x76696577, // 'view' + cmsSigVcgtTag = 0x76636774, // 'vcgt' + cmsSigMetaTag = 0x6D657461, // 'meta' + cmsSigArgyllArtsTag = 0x61727473 // 'arts' + +} cmsTagSignature; + + +// ICC Technology tag +typedef enum { + cmsSigDigitalCamera = 0x6463616D, // 'dcam' + cmsSigFilmScanner = 0x6673636E, // 'fscn' + cmsSigReflectiveScanner = 0x7273636E, // 'rscn' + cmsSigInkJetPrinter = 0x696A6574, // 'ijet' + cmsSigThermalWaxPrinter = 0x74776178, // 'twax' + cmsSigElectrophotographicPrinter = 0x6570686F, // 'epho' + cmsSigElectrostaticPrinter = 0x65737461, // 'esta' + cmsSigDyeSublimationPrinter = 0x64737562, // 'dsub' + cmsSigPhotographicPaperPrinter = 0x7270686F, // 'rpho' + cmsSigFilmWriter = 0x6670726E, // 'fprn' + cmsSigVideoMonitor = 0x7669646D, // 'vidm' + cmsSigVideoCamera = 0x76696463, // 'vidc' + cmsSigProjectionTelevision = 0x706A7476, // 'pjtv' + cmsSigCRTDisplay = 0x43525420, // 'CRT ' + cmsSigPMDisplay = 0x504D4420, // 'PMD ' + cmsSigAMDisplay = 0x414D4420, // 'AMD ' + cmsSigPhotoCD = 0x4B504344, // 'KPCD' + cmsSigPhotoImageSetter = 0x696D6773, // 'imgs' + cmsSigGravure = 0x67726176, // 'grav' + cmsSigOffsetLithography = 0x6F666673, // 'offs' + cmsSigSilkscreen = 0x73696C6B, // 'silk' + cmsSigFlexography = 0x666C6578, // 'flex' + cmsSigMotionPictureFilmScanner = 0x6D706673, // 'mpfs' + cmsSigMotionPictureFilmRecorder = 0x6D706672, // 'mpfr' + cmsSigDigitalMotionPictureCamera = 0x646D7063, // 'dmpc' + cmsSigDigitalCinemaProjector = 0x64636A70 // 'dcpj' + +} cmsTechnologySignature; + + +// ICC Color spaces +typedef enum { + cmsSigXYZData = 0x58595A20, // 'XYZ ' + cmsSigLabData = 0x4C616220, // 'Lab ' + cmsSigLuvData = 0x4C757620, // 'Luv ' + cmsSigYCbCrData = 0x59436272, // 'YCbr' + cmsSigYxyData = 0x59787920, // 'Yxy ' + cmsSigRgbData = 0x52474220, // 'RGB ' + cmsSigGrayData = 0x47524159, // 'GRAY' + cmsSigHsvData = 0x48535620, // 'HSV ' + cmsSigHlsData = 0x484C5320, // 'HLS ' + cmsSigCmykData = 0x434D594B, // 'CMYK' + cmsSigCmyData = 0x434D5920, // 'CMY ' + cmsSigMCH1Data = 0x4D434831, // 'MCH1' + cmsSigMCH2Data = 0x4D434832, // 'MCH2' + cmsSigMCH3Data = 0x4D434833, // 'MCH3' + cmsSigMCH4Data = 0x4D434834, // 'MCH4' + cmsSigMCH5Data = 0x4D434835, // 'MCH5' + cmsSigMCH6Data = 0x4D434836, // 'MCH6' + cmsSigMCH7Data = 0x4D434837, // 'MCH7' + cmsSigMCH8Data = 0x4D434838, // 'MCH8' + cmsSigMCH9Data = 0x4D434839, // 'MCH9' + cmsSigMCHAData = 0x4D434841, // 'MCHA' + cmsSigMCHBData = 0x4D434842, // 'MCHB' + cmsSigMCHCData = 0x4D434843, // 'MCHC' + cmsSigMCHDData = 0x4D434844, // 'MCHD' + cmsSigMCHEData = 0x4D434845, // 'MCHE' + cmsSigMCHFData = 0x4D434846, // 'MCHF' + cmsSigNamedData = 0x6e6d636c, // 'nmcl' + cmsSig1colorData = 0x31434C52, // '1CLR' + cmsSig2colorData = 0x32434C52, // '2CLR' + cmsSig3colorData = 0x33434C52, // '3CLR' + cmsSig4colorData = 0x34434C52, // '4CLR' + cmsSig5colorData = 0x35434C52, // '5CLR' + cmsSig6colorData = 0x36434C52, // '6CLR' + cmsSig7colorData = 0x37434C52, // '7CLR' + cmsSig8colorData = 0x38434C52, // '8CLR' + cmsSig9colorData = 0x39434C52, // '9CLR' + cmsSig10colorData = 0x41434C52, // 'ACLR' + cmsSig11colorData = 0x42434C52, // 'BCLR' + cmsSig12colorData = 0x43434C52, // 'CCLR' + cmsSig13colorData = 0x44434C52, // 'DCLR' + cmsSig14colorData = 0x45434C52, // 'ECLR' + cmsSig15colorData = 0x46434C52, // 'FCLR' + cmsSigLuvKData = 0x4C75764B // 'LuvK' + +} cmsColorSpaceSignature; + +// ICC Profile Class +typedef enum { + cmsSigInputClass = 0x73636E72, // 'scnr' + cmsSigDisplayClass = 0x6D6E7472, // 'mntr' + cmsSigOutputClass = 0x70727472, // 'prtr' + cmsSigLinkClass = 0x6C696E6B, // 'link' + cmsSigAbstractClass = 0x61627374, // 'abst' + cmsSigColorSpaceClass = 0x73706163, // 'spac' + cmsSigNamedColorClass = 0x6e6d636c // 'nmcl' + +} cmsProfileClassSignature; + +// ICC Platforms +typedef enum { + cmsSigMacintosh = 0x4150504C, // 'APPL' + cmsSigMicrosoft = 0x4D534654, // 'MSFT' + cmsSigSolaris = 0x53554E57, // 'SUNW' + cmsSigSGI = 0x53474920, // 'SGI ' + cmsSigTaligent = 0x54474E54, // 'TGNT' + cmsSigUnices = 0x2A6E6978 // '*nix' // From argyll -- Not official + +} cmsPlatformSignature; + +// Reference gamut +#define cmsSigPerceptualReferenceMediumGamut 0x70726d67 //'prmg' + +// For cmsSigColorimetricIntentImageStateTag +#define cmsSigSceneColorimetryEstimates 0x73636F65 //'scoe' +#define cmsSigSceneAppearanceEstimates 0x73617065 //'sape' +#define cmsSigFocalPlaneColorimetryEstimates 0x66706365 //'fpce' +#define cmsSigReflectionHardcopyOriginalColorimetry 0x72686F63 //'rhoc' +#define cmsSigReflectionPrintOutputColorimetry 0x72706F63 //'rpoc' + +// Multi process elements types +typedef enum { + cmsSigCurveSetElemType = 0x63767374, //'cvst' + cmsSigMatrixElemType = 0x6D617466, //'matf' + cmsSigCLutElemType = 0x636C7574, //'clut' + + cmsSigBAcsElemType = 0x62414353, // 'bACS' + cmsSigEAcsElemType = 0x65414353, // 'eACS' + + // Custom from here, not in the ICC Spec + cmsSigXYZ2LabElemType = 0x6C327820, // 'l2x ' + cmsSigLab2XYZElemType = 0x78326C20, // 'x2l ' + cmsSigNamedColorElemType = 0x6E636C20, // 'ncl ' + cmsSigLabV2toV4 = 0x32203420, // '2 4 ' + cmsSigLabV4toV2 = 0x34203220, // '4 2 ' + + // Identities + cmsSigIdentityElemType = 0x69646E20, // 'idn ' + + // Float to floatPCS + cmsSigLab2FloatPCS = 0x64326C20, // 'd2l ' + cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d ' + cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x ' + cmsSigFloatPCS2XYZ = 0x78326420, // 'x2d ' + cmsSigClipNegativesElemType = 0x636c7020 // 'clp ' + +} cmsStageSignature; + +// Types of CurveElements +typedef enum { + + cmsSigFormulaCurveSeg = 0x70617266, // 'parf' + cmsSigSampledCurveSeg = 0x73616D66, // 'samf' + cmsSigSegmentedCurve = 0x63757266 // 'curf' + +} cmsCurveSegSignature; + +// Used in ResponseCurveType +#define cmsSigStatusA 0x53746141 //'StaA' +#define cmsSigStatusE 0x53746145 //'StaE' +#define cmsSigStatusI 0x53746149 //'StaI' +#define cmsSigStatusT 0x53746154 //'StaT' +#define cmsSigStatusM 0x5374614D //'StaM' +#define cmsSigDN 0x444E2020 //'DN ' +#define cmsSigDNP 0x444E2050 //'DN P' +#define cmsSigDNN 0x444E4E20 //'DNN ' +#define cmsSigDNNP 0x444E4E50 //'DNNP' + +// Device attributes, currently defined values correspond to the low 4 bytes +// of the 8 byte attribute quantity +#define cmsReflective 0 +#define cmsTransparency 1 +#define cmsGlossy 0 +#define cmsMatte 2 + +// Common structures in ICC tags +typedef struct { + cmsUInt32Number len; + cmsUInt32Number flag; + cmsUInt8Number data[1]; + +} cmsICCData; + +// ICC date time +typedef struct { + cmsUInt16Number year; + cmsUInt16Number month; + cmsUInt16Number day; + cmsUInt16Number hours; + cmsUInt16Number minutes; + cmsUInt16Number seconds; + +} cmsDateTimeNumber; + +// ICC XYZ +typedef struct { + cmsS15Fixed16Number X; + cmsS15Fixed16Number Y; + cmsS15Fixed16Number Z; + +} cmsEncodedXYZNumber; + + +// Profile ID as computed by MD5 algorithm +typedef union { + cmsUInt8Number ID8[16]; + cmsUInt16Number ID16[8]; + cmsUInt32Number ID32[4]; + +} cmsProfileID; + + +// ---------------------------------------------------------------------------------------------- +// ICC profile internal base types. Strictly, shouldn't be declared in this header, but maybe +// somebody want to use this info for accessing profile header directly, so here it is. + +// Profile header -- it is 32-bit aligned, so no issues are expected on alignment +typedef struct { + cmsUInt32Number size; // Profile size in bytes + cmsSignature cmmId; // CMM for this profile + cmsUInt32Number version; // Format version number + cmsProfileClassSignature deviceClass; // Type of profile + cmsColorSpaceSignature colorSpace; // Color space of data + cmsColorSpaceSignature pcs; // PCS, XYZ or Lab only + cmsDateTimeNumber date; // Date profile was created + cmsSignature magic; // Magic Number to identify an ICC profile + cmsPlatformSignature platform; // Primary Platform + cmsUInt32Number flags; // Various bit settings + cmsSignature manufacturer; // Device manufacturer + cmsUInt32Number model; // Device model number + cmsUInt64Number attributes; // Device attributes + cmsUInt32Number renderingIntent;// Rendering intent + cmsEncodedXYZNumber illuminant; // Profile illuminant + cmsSignature creator; // Profile creator + cmsProfileID profileID; // Profile ID using MD5 + cmsInt8Number reserved[28]; // Reserved for future use + +} cmsICCHeader; + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} cmsTagBase; + +// A tag entry in directory +typedef struct { + cmsTagSignature sig; // The tag signature + cmsUInt32Number offset; // Start of tag + cmsUInt32Number size; // Size in bytes + +} cmsTagEntry; + +// ---------------------------------------------------------------------------------------------- + +// Little CMS specific typedefs + +typedef void* cmsHANDLE ; // Generic handle +typedef void* cmsHPROFILE; // Opaque typedefs to hide internals +typedef void* cmsHTRANSFORM; + +#define cmsMAXCHANNELS 16 // Maximum number of channels in ICC profiles + +// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows +// +// 2 1 0 +// 3 2 10987 6 5 4 3 2 1 098 7654 321 +// A O TTTTT U Y F P X S EEE CCCC BBB +// +// A: Floating point -- With this flag we can differentiate 16 bits as float and as int +// O: Optimized -- previous optimization already returns the final 8-bit value +// T: Pixeltype +// F: Flavor 0=MinIsBlack(Chocolate) 1=MinIsWhite(Vanilla) +// P: Planar? 0=Chunky, 1=Planar +// X: swap 16 bps endianness? +// S: Do swap? ie, BGR, KYMC +// E: Extra samples +// C: Channels (Samples per pixel) +// B: bytes per sample +// Y: Swap first - changes ABGR to BGRA and KCMY to CMYK + +#define FLOAT_SH(a) ((a) << 22) +#define OPTIMIZED_SH(s) ((s) << 21) +#define COLORSPACE_SH(s) ((s) << 16) +#define SWAPFIRST_SH(s) ((s) << 14) +#define FLAVOR_SH(s) ((s) << 13) +#define PLANAR_SH(p) ((p) << 12) +#define ENDIAN16_SH(e) ((e) << 11) +#define DOSWAP_SH(e) ((e) << 10) +#define EXTRA_SH(e) ((e) << 7) +#define CHANNELS_SH(c) ((c) << 3) +#define BYTES_SH(b) (b) + +// These macros unpack format specifiers into integers +#define T_FLOAT(a) (((a)>>22)&1) +#define T_OPTIMIZED(o) (((o)>>21)&1) +#define T_COLORSPACE(s) (((s)>>16)&31) +#define T_SWAPFIRST(s) (((s)>>14)&1) +#define T_FLAVOR(s) (((s)>>13)&1) +#define T_PLANAR(p) (((p)>>12)&1) +#define T_ENDIAN16(e) (((e)>>11)&1) +#define T_DOSWAP(e) (((e)>>10)&1) +#define T_EXTRA(e) (((e)>>7)&7) +#define T_CHANNELS(c) (((c)>>3)&15) +#define T_BYTES(b) ((b)&7) + + +// Pixel types +#define PT_ANY 0 // Don't check colorspace + // 1 & 2 are reserved +#define PT_GRAY 3 +#define PT_RGB 4 +#define PT_CMY 5 +#define PT_CMYK 6 +#define PT_YCbCr 7 +#define PT_YUV 8 // Lu'v' +#define PT_XYZ 9 +#define PT_Lab 10 +#define PT_YUVK 11 // Lu'v'K +#define PT_HSV 12 +#define PT_HLS 13 +#define PT_Yxy 14 + +#define PT_MCH1 15 +#define PT_MCH2 16 +#define PT_MCH3 17 +#define PT_MCH4 18 +#define PT_MCH5 19 +#define PT_MCH6 20 +#define PT_MCH7 21 +#define PT_MCH8 22 +#define PT_MCH9 23 +#define PT_MCH10 24 +#define PT_MCH11 25 +#define PT_MCH12 26 +#define PT_MCH13 27 +#define PT_MCH14 28 +#define PT_MCH15 29 + +#define PT_LabV2 30 // Identical to PT_Lab, but using the V2 old encoding + +// Some (not all!) representations + +#ifndef TYPE_RGB_8 // TYPE_RGB_8 is a very common identifier, so don't include ours + // if user has it already defined. + +#define TYPE_GRAY_8 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAY_8_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_GRAY_16 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) + +#define TYPE_RGB_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_BGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_RGB_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGB_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGB_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_BGR_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) + +#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMY_16 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMY_16_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMY_16_SE (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_CMYK_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYKA_8 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYK_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_YUVK_8 TYPE_CMYK_8_REV +#define TYPE_CMYK_8_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) +#define TYPE_CMYK_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_YUVK_16 TYPE_CMYK_16_REV +#define TYPE_CMYK_16_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_KYMC_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_KCMY_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMYK5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)) +#define TYPE_CMYK5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)) +#define TYPE_CMYK5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK6_8 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)) +#define TYPE_CMYK6_8_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK6_16 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)) +#define TYPE_CMYK6_16_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK6_16_SE (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_CMYK7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)) +#define TYPE_CMYK7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)) +#define TYPE_CMYK7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)) +#define TYPE_CMYK8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)) +#define TYPE_CMYK8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)) +#define TYPE_CMYK9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)) +#define TYPE_CMYK9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)) +#define TYPE_CMYK10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)) +#define TYPE_CMYK10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)) +#define TYPE_CMYK11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)) +#define TYPE_CMYK11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)) +#define TYPE_CMYK12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)) +#define TYPE_CMYK12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +// Colorimetric +#define TYPE_XYZ_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_LabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)) + +#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_LabV2_16 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2)) + +// YCbCr +#define TYPE_YCbCr_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YCbCr_8_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YCbCr_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YCbCr_16_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YCbCr_16_SE (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// YUV +#define TYPE_YUV_8 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YUV_8_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YUV_16 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YUV_16_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YUV_16_SE (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HLS +#define TYPE_HLS_8 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HLS_8_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HLS_16 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HLS_16_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HLS_16_SE (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HSV +#define TYPE_HSV_8 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HSV_8_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HSV_16 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// Named color index. Only 16 bits allowed (don't check colorspace) +#define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) + +// Float formatters. +#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)) +#define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)) + +#define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)) +#define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) +#define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) + +#define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4)) + +// Floating point formatters. +// NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield +#define TYPE_XYZ_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_GRAY_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0)) +#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1)) +#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0)) + +// IEEE 754-2008 "half" +#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) + +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_ARGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_BGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGRA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) + +#endif + +// Colorspaces +typedef struct { + cmsFloat64Number X; + cmsFloat64Number Y; + cmsFloat64Number Z; + + } cmsCIEXYZ; + +typedef struct { + cmsFloat64Number x; + cmsFloat64Number y; + cmsFloat64Number Y; + + } cmsCIExyY; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number a; + cmsFloat64Number b; + + } cmsCIELab; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsCIELCh; + +typedef struct { + cmsFloat64Number J; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsJCh; + +typedef struct { + cmsCIEXYZ Red; + cmsCIEXYZ Green; + cmsCIEXYZ Blue; + + } cmsCIEXYZTRIPLE; + +typedef struct { + cmsCIExyY Red; + cmsCIExyY Green; + cmsCIExyY Blue; + + } cmsCIExyYTRIPLE; + +// Illuminant types for structs below +#define cmsILLUMINANT_TYPE_UNKNOWN 0x0000000 +#define cmsILLUMINANT_TYPE_D50 0x0000001 +#define cmsILLUMINANT_TYPE_D65 0x0000002 +#define cmsILLUMINANT_TYPE_D93 0x0000003 +#define cmsILLUMINANT_TYPE_F2 0x0000004 +#define cmsILLUMINANT_TYPE_D55 0x0000005 +#define cmsILLUMINANT_TYPE_A 0x0000006 +#define cmsILLUMINANT_TYPE_E 0x0000007 +#define cmsILLUMINANT_TYPE_F8 0x0000008 + +typedef struct { + cmsUInt32Number Observer; // 0 = unknown, 1=CIE 1931, 2=CIE 1964 + cmsCIEXYZ Backing; // Value of backing + cmsUInt32Number Geometry; // 0=unknown, 1=45/0, 0/45 2=0d, d/0 + cmsFloat64Number Flare; // 0..1.0 + cmsUInt32Number IlluminantType; + + } cmsICCMeasurementConditions; + +typedef struct { + cmsCIEXYZ IlluminantXYZ; // Not the same struct as CAM02, + cmsCIEXYZ SurroundXYZ; // This is for storing the tag + cmsUInt32Number IlluminantType; // viewing condition + + } cmsICCViewingConditions; + +// Get LittleCMS version (for shared objects) ----------------------------------------------------------------------------- + +CMSAPI int CMSEXPORT cmsGetEncodedCMMversion(void); + +// Support of non-standard functions -------------------------------------------------------------------------------------- + +CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); +CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); + + +// Context handling -------------------------------------------------------------------------------------------------------- + +// Each context holds its owns globals and its own plug-ins. There is a global context with the id = 0 for lecacy compatibility +// though using the global context is not recommended. Proper context handling makes lcms more thread-safe. + +typedef struct _cmsContext_struct* cmsContext; + +CMSAPI cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData); +CMSAPI void CMSEXPORT cmsDeleteContext(cmsContext ContextID); +CMSAPI cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData); +CMSAPI void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID); + +// Plug-In registering -------------------------------------------------------------------------------------------------- + +CMSAPI cmsBool CMSEXPORT cmsPlugin(void* Plugin); +CMSAPI cmsBool CMSEXPORT cmsPluginTHR(cmsContext ContextID, void* Plugin); +CMSAPI void CMSEXPORT cmsUnregisterPlugins(void); +CMSAPI void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID); + +// Error logging ---------------------------------------------------------------------------------------------------------- + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other may return FALSE. +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function will get +// an ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user if you wish, or just create some sort of log on disk. +// The logging function should NOT terminate the program, as this obviously can leave +// unfreed resources. It is the programmer's responsibility to check each function +// return code to make sure it didn't fail. + +#define cmsERROR_UNDEFINED 0 +#define cmsERROR_FILE 1 +#define cmsERROR_RANGE 2 +#define cmsERROR_INTERNAL 3 +#define cmsERROR_NULL 4 +#define cmsERROR_READ 5 +#define cmsERROR_SEEK 6 +#define cmsERROR_WRITE 7 +#define cmsERROR_UNKNOWN_EXTENSION 8 +#define cmsERROR_COLORSPACE_CHECK 9 +#define cmsERROR_ALREADY_DEFINED 10 +#define cmsERROR_BAD_SIGNATURE 11 +#define cmsERROR_CORRUPTION_DETECTED 12 +#define cmsERROR_NOT_SUITABLE 13 + +// Error logger is called with the ContextID when a message is raised. This gives the +// chance to know which thread is responsible of the warning and any environment associated +// with it. Non-multithreading applications may safely ignore this parameter. +// Note that under certain special circumstances, ContextID may be NULL. +typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Allows user to set any specific logger +CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); +CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn); + +// Conversions -------------------------------------------------------------------------------------------------------------- + +// Returns pointers to constant structs +CMSAPI const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void); +CMSAPI const cmsCIExyY* CMSEXPORT cmsD50_xyY(void); + +// Colorimetric space conversions +CMSAPI void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source); +CMSAPI void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source); +CMSAPI void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz); +CMSAPI void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLab2LCh(cmsCIELCh*LCh, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh); + +// Encoding /Decoding on PCS +CMSAPI void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fxyz, const cmsUInt16Number XYZ[3]); +CMSAPI void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ); + +// DeltaE metrics +CMSAPI cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh); + +// Temperature <-> Chromaticity (Black body) +CMSAPI cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK); +CMSAPI cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint); + +// Chromatic adaptation +CMSAPI cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value); + +// CIECAM02 --------------------------------------------------------------------------------------------------- + +// Viewing conditions. Please note those are CAM model viewing conditions, and not the ICC tag viewing +// conditions, which I'm naming cmsICCViewingConditions to make differences evident. Unfortunately, the tag +// cannot deal with surround La, Yb and D value so is basically useless to store CAM02 viewing conditions. + + +#define AVG_SURROUND 1 +#define DIM_SURROUND 2 +#define DARK_SURROUND 3 +#define CUTSHEET_SURROUND 4 + +#define D_CALCULATE (-1) + +typedef struct { + cmsCIEXYZ whitePoint; + cmsFloat64Number Yb; + cmsFloat64Number La; + cmsUInt32Number surround; + cmsFloat64Number D_value; + + } cmsViewingConditions; + +CMSAPI cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC); +CMSAPI void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel); +CMSAPI void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut); +CMSAPI void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut); + + +// Tone curves ----------------------------------------------------------------------------------------- + +// This describes a curve segment. For a table of supported types, see the manual. User can increase the number of +// available types by using a proper plug-in. Parametric segments allow 10 parameters at most + +typedef struct { + cmsFloat32Number x0, x1; // Domain; for x0 < x <= x1 + cmsInt32Number Type; // Parametric type, Type == 0 means sampled segment. Negative values are reserved + cmsFloat64Number Params[10]; // Parameters if Type != 0 + cmsUInt32Number nGridPoints; // Number of grid points if Type == 0 + cmsFloat32Number* SampledPoints; // Points to an array of floats if Type == 0 + +} cmsCurveSegment; + +// The internal representation is none of your business. +typedef struct _cms_curve_struct cmsToneCurve; + +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsUInt32Number nSegments, const cmsCurveSegment Segments[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsUInt32Number nEntries, const cmsUInt16Number values[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]); +CMSAPI void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve); +CMSAPI void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]); +CMSAPI cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* Src); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsUInt32Number nResultSamples, const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, const cmsToneCurve* X, const cmsToneCurve* Y, cmsUInt32Number nPoints); +CMSAPI cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda); +CMSAPI cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v); +CMSAPI cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* InGamma); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t); +CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t); +CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision); +CMSAPI cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t); + +// Tone curve tabular estimation +CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t); +CMSAPI const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t); + + +// Implements pipelines of multi-processing elements ------------------------------------------------------------- + +// Nothing to see here, move along +typedef struct _cmsPipeline_struct cmsPipeline; +typedef struct _cmsStage_struct cmsStage; + +// Those are hi-level pipelines +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels); +CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut); +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig); + +CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut); + +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut); + +CMSAPI void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut); +CMSAPI void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], cmsFloat32Number Result[], cmsFloat32Number Hint[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2); +CMSAPI cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On); + +// Where to place/locate the stages in the pipeline chain +typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc; + +CMSAPI cmsBool CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe); +CMSAPI void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe); + +// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements +// that conform the Pipeline. It should be called with the Pipeline, the number of expected elements and +// then a list of expected types followed with a list of double pointers to Stage elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. +CMSAPI cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...); + +// Matrix has double precision and CLUT has only float precision. That is because an ICC profile can encode +// matrices with far more precision that CLUTS +CMSAPI cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe); +CMSAPI void CMSEXPORT cmsStageFree(cmsStage* mpe); +CMSAPI cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe); + +CMSAPI cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe); +CMSAPI cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe); +CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe); +CMSAPI void* CMSEXPORT cmsStageData(const cmsStage* mpe); + +// Sampling +typedef cmsInt32Number (* cmsSAMPLER16) (CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + CMSREGISTER void * Cargo); + +typedef cmsInt32Number (* cmsSAMPLERFLOAT)(CMSREGISTER const cmsFloat32Number In[], + CMSREGISTER cmsFloat32Number Out[], + CMSREGISTER void * Cargo); + +// Use this flag to prevent changes being written to destination +#define SAMPLER_INSPECT 0x01000000 + +// For CLUT only +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags); + +// Slicers +CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo); + +CMSAPI cmsBool CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo); + +// Multilocalized Unicode management --------------------------------------------------------------------------------------- + +typedef struct _cms_MLU_struct cmsMLU; + +#define cmsNoLanguage "\0\0" +#define cmsNoCountry "\0\0" + +CMSAPI cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems); +CMSAPI void CMSEXPORT cmsMLUfree(cmsMLU* mlu); +CMSAPI cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const char* ASCIIString); +CMSAPI cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const wchar_t* WideString); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]); + +// Undercolorremoval & black generation ------------------------------------------------------------------------------------- + +typedef struct { + cmsToneCurve* Ucr; + cmsToneCurve* Bg; + cmsMLU* Desc; + +} cmsUcrBg; + +// Screening ---------------------------------------------------------------------------------------------------------------- + +#define cmsPRINTER_DEFAULT_SCREENS 0x0001 +#define cmsFREQUENCE_UNITS_LINES_CM 0x0000 +#define cmsFREQUENCE_UNITS_LINES_INCH 0x0002 + +#define cmsSPOT_UNKNOWN 0 +#define cmsSPOT_PRINTER_DEFAULT 1 +#define cmsSPOT_ROUND 2 +#define cmsSPOT_DIAMOND 3 +#define cmsSPOT_ELLIPSE 4 +#define cmsSPOT_LINE 5 +#define cmsSPOT_SQUARE 6 +#define cmsSPOT_CROSS 7 + +typedef struct { + cmsFloat64Number Frequency; + cmsFloat64Number ScreenAngle; + cmsUInt32Number SpotShape; + +} cmsScreeningChannel; + +typedef struct { + cmsUInt32Number Flag; + cmsUInt32Number nChannels; + cmsScreeningChannel Channels[cmsMAXCHANNELS]; + +} cmsScreening; + + +// Named color ----------------------------------------------------------------------------------------------------------------- + +typedef struct _cms_NAMEDCOLORLIST_struct cmsNAMEDCOLORLIST; + +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, + cmsUInt32Number n, + cmsUInt32Number ColorantCount, + const char* Prefix, const char* Suffix); + +CMSAPI void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v); +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* v, const char* Name, + cmsUInt16Number PCS[3], + cmsUInt16Number Colorant[cmsMAXCHANNELS]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* v, const char* Name); + +CMSAPI cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant); + +// Retrieve named color list from transform +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform); + +// Profile sequence ----------------------------------------------------------------------------------------------------- + +// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others +// come from Profile Sequence Identifier Tag +typedef struct { + + cmsSignature deviceMfg; + cmsSignature deviceModel; + cmsUInt64Number attributes; + cmsTechnologySignature technology; + cmsProfileID ProfileID; + cmsMLU* Manufacturer; + cmsMLU* Model; + cmsMLU* Description; + +} cmsPSEQDESC; + +typedef struct { + + cmsUInt32Number n; + cmsContext ContextID; + cmsPSEQDESC* seq; + +} cmsSEQ; + +CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n); +CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq); +CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq); + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +typedef struct _cmsDICTentry_struct { + + struct _cmsDICTentry_struct* Next; + + cmsMLU *DisplayName; + cmsMLU *DisplayValue; + wchar_t* Name; + wchar_t* Value; + +} cmsDICTentry; + +CMSAPI cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsDictFree(cmsHANDLE hDict); +CMSAPI cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict); + +CMSAPI cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e); + +// Access to Profile data ---------------------------------------------------------------------------------------------- +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID); + +CMSAPI cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile); +CMSAPI cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile); +CMSAPI cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write pre-formatted data +CMSAPI void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig); +CMSAPI cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data); +CMSAPI cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest); +CMSAPI cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write raw data +CMSAPI cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize); +CMSAPI cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size); + +// Access header data +#define cmsEmbeddedProfileFalse 0x00000000 +#define cmsEmbeddedProfileTrue 0x00000001 +#define cmsUseAnywhere 0x00000000 +#define cmsUseWithEmbeddedDataOnly 0x00000002 + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags); +CMSAPI void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile); + +CMSAPI void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model); +CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags); +CMSAPI void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent); + +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs); +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig); +CMSAPI cmsProfileClassSignature + CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig); +CMSAPI void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version); +CMSAPI cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version); + +// How profiles may be used +#define LCMS_USED_AS_INPUT 0 +#define LCMS_USED_AS_OUTPUT 1 +#define LCMS_USED_AS_PROOF 2 + +CMSAPI cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); +CMSAPI cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); + +// Translate form/to our notation to ICC +CMSAPI cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation); +CMSAPI int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace); + +CMSAPI cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace); + +// Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits. +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); + + +// Localized info +typedef enum { + cmsInfoDescription = 0, + cmsInfoManufacturer = 1, + cmsInfoModel = 2, + cmsInfoCopyright = 3 +} cmsInfoType; + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +// IO handlers ---------------------------------------------------------------------------------------------------------- + +typedef struct _cms_io_handler cmsIOHANDLER; + +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io); + +// MD5 message digest -------------------------------------------------------------------------------------------------- + +CMSAPI cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile); + +// Profile high level functions ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write); +CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); + +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded); +CMSAPI cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io); + +// Predefined virtual profiles ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(cmsUInt32Number nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + cmsUInt32Number TempSrc, + cmsUInt32Number TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void); + +// Converts a transform to a devicelink profile +CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags); + +// Intents ---------------------------------------------------------------------------------------------- + +// ICC Intents +#define INTENT_PERCEPTUAL 0 +#define INTENT_RELATIVE_COLORIMETRIC 1 +#define INTENT_SATURATION 2 +#define INTENT_ABSOLUTE_COLORIMETRIC 3 + +// Non-ICC intents +#define INTENT_PRESERVE_K_ONLY_PERCEPTUAL 10 +#define INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC 11 +#define INTENT_PRESERVE_K_ONLY_SATURATION 12 +#define INTENT_PRESERVE_K_PLANE_PERCEPTUAL 13 +#define INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC 14 +#define INTENT_PRESERVE_K_PLANE_SATURATION 15 + +// Call with NULL as parameters to get the intent count +CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); + +// Flags + +#define cmsFLAGS_NOCACHE 0x0040 // Inhibit 1-pixel cache +#define cmsFLAGS_NOOPTIMIZE 0x0100 // Inhibit optimizations +#define cmsFLAGS_NULLTRANSFORM 0x0200 // Don't transform anyway + +// Proofing flags +#define cmsFLAGS_GAMUTCHECK 0x1000 // Out of Gamut alarm +#define cmsFLAGS_SOFTPROOFING 0x4000 // Do softproofing + +// Misc +#define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 +#define cmsFLAGS_NOWHITEONWHITEFIXUP 0x0004 // Don't fix scum dot +#define cmsFLAGS_HIGHRESPRECALC 0x0400 // Use more memory to give better accuracy +#define cmsFLAGS_LOWRESPRECALC 0x0800 // Use less memory to minimize resources + +// For devicelink creation +#define cmsFLAGS_8BITS_DEVICELINK 0x0008 // Create 8 bits devicelinks +#define cmsFLAGS_GUESSDEVICECLASS 0x0020 // Guess device class (for transform2devicelink) +#define cmsFLAGS_KEEP_SEQUENCE 0x0080 // Keep profile sequence for devicelink creation + +// Specific to a particular optimizations +#define cmsFLAGS_FORCE_CLUT 0x0002 // Force CLUT optimization +#define cmsFLAGS_CLUT_POST_LINEARIZATION 0x0001 // create postlinearization tables if possible +#define cmsFLAGS_CLUT_PRE_LINEARIZATION 0x0010 // create prelinearization tables if possible + +// Specific to unbounded mode +#define cmsFLAGS_NONEGATIVES 0x8000 // Prevent negative numbers in floating point transforms + +// Copy alpha channels when transforming +#define cmsFLAGS_COPY_ALPHA 0x04000000 // Alpha channels are copied on cmsDoTransform() + +// Fine-tune control over number of gridpoints +#define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16) + +// CRD special +#define cmsFLAGS_NODEFAULTRESOURCEDEF 0x01000000 + +// Transforms --------------------------------------------------------------------------------------------------- + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags); + +CMSAPI void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); + +CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size); + +CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, // Deprecated + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); + +CMSAPI void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + cmsUInt32Number BytesPerLineIn, + cmsUInt32Number BytesPerLineOut, + cmsUInt32Number BytesPerPlaneIn, + cmsUInt32Number BytesPerPlaneOut); + + +CMSAPI void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); + + +CMSAPI void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, + const cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); + + + +// Adaptation state for absolute colorimetric intent +CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d); +CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d); + + + +// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed +CMSAPI cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform); + +// Grab the input/output formats +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform); + +// For backwards compatibility +CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat); + + + +// PostScript ColorRenderingDictionary and ColorSpaceArray ---------------------------------------------------- + +typedef enum { cmsPS_RESOURCE_CSA, cmsPS_RESOURCE_CRD } cmsPSResourceType; + +// lcms2 unified method to access postscript color resources +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + +CMSAPI cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8); + +// Tables +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8); +CMSAPI cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE hIT8, cmsUInt32Number nTable); + +// Persistence +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName); +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len); +// CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromIOhandler(cmsContext ContextID, cmsIOHANDLER* io); + +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName); +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded); + +// Properties +CMSAPI const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8); +CMSAPI cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* cComment); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer); + + +CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp); +CMSAPI const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames); + +// Datasets +CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, + const char* Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, + cmsFloat64Number Val); + +CMSAPI const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + + +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + const char *Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val); + +CMSAPI int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample); +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE hIT8, int n, const char *Sample); +CMSAPI int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames); + +CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer); +CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch); + +// The LABEL extension +CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample); + +// Formatter for double +CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter); + +// Gamut boundary description routines ------------------------------------------------------------------------------ + +CMSAPI cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD); +CMSAPI cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); +CMSAPI cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGDB, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); + +// Feature detection ---------------------------------------------------------------------------------------------- + +// Estimate the black point +CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); + +// Estimate total area coverage +CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); + + +// Poor man's gamut mapping +CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin); + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus + } +# endif +#endif + +#define _lcms2_H +#endif diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2_internal.h openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2_internal.h --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2_internal.h 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2_internal.h 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,1151 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// + +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#ifndef _lcms_internal_H + +// Include plug-in foundation +#ifndef _lcms_plugin_H +# include "lcms2_plugin.h" +#endif + +// ctype is part of C99 as per 7.1.2 +#include + +// assert macro is part of C99 as per 7.2 +#include + +// Some needed constants +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LOG10E +# define M_LOG10E 0.434294481903251827651 +#endif + +// BorlandC 5.5, VC2003 are broken on that +#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER < 1400)) // 1400 == VC++ 8.0 +#define sinf(x) (float)sin((float)x) +#define sqrtf(x) (float)sqrt((float)x) +#endif + + +// Alignment of ICC file format uses 4 bytes (cmsUInt32Number) +#define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1)) + +// Alignment to memory pointer + +// (Ultra)SPARC with gcc requires ptr alignment of 8 bytes +// even though sizeof(void *) is only four: for greatest flexibility +// allow the build to specify ptr alignment. +#ifndef CMS_PTR_ALIGNMENT +# define CMS_PTR_ALIGNMENT sizeof(void *) +#endif + +#define _cmsALIGNMEM(x) (((x)+(CMS_PTR_ALIGNMENT - 1)) & ~(CMS_PTR_ALIGNMENT - 1)) + +// Maximum encodeable values in floating point +#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0) +#define MIN_ENCODEABLE_ab2 (-128.0) +#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0) +#define MIN_ENCODEABLE_ab4 (-128.0) +#define MAX_ENCODEABLE_ab4 (127.0) + +// Maximum of channels for internal pipeline evaluation +#define MAX_STAGE_CHANNELS 128 + +// Unused parameter warning suppression +#define cmsUNUSED_PARAMETER(x) ((void)x) + +// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999). +// unfortunately VisualC++ does not conform that +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define cmsINLINE __inline +#else +# define cmsINLINE static inline +#endif + +// Allow signed overflow, we know this is harmless in this particular context +#if defined(__clang__) +# define CMS_NO_SANITIZE __attribute__((no_sanitize("signed-integer-overflow"))) +#else +# define CMS_NO_SANITIZE +#endif + +// Other replacement functions +#ifdef _MSC_VER +# ifndef snprintf +# define snprintf _snprintf +# endif +# ifndef vsnprintf +# define vsnprintf _vsnprintf +# endif + +/// Properly define some macros to accommodate +/// older MSVC versions. +# if defined(_MSC_VER) && _MSC_VER <= 1700 + #include + #define isnan _isnan + #define isinf(x) (!_finite((x))) +# endif + +#if !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L) + #if !defined(isinf) + #define isinf(x) (!finite((x))) + #endif +#endif + + +#endif + +// A fast way to convert from/to 16 <-> 8 bits +#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) +#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) + +// Code analysis is broken on asserts +#ifdef _MSC_VER +# if (_MSC_VER >= 1500) +# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); } +# else +# define _cmsAssert(a) assert((a)) +# endif +#else +# define _cmsAssert(a) assert((a)) +#endif + +//--------------------------------------------------------------------------------- + +// Determinant lower than that are assumed zero (used on matrix invert) +#define MATRIX_DET_TOLERANCE 0.0001 + +//--------------------------------------------------------------------------------- + +// Fixed point +#define FIXED_TO_INT(x) ((x)>>16) +#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU) +#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16) + +cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); } +cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); } + +// ----------------------------------------------------------------------------------------------------------- + +// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon +// note than this only works in the range ..-32767...+32767 because +// mantissa is interpreted as 15.16 fixed point. +// The union is to avoid pointer aliasing overoptimization. +cmsINLINE int _cmsQuickFloor(cmsFloat64Number val) +{ +#ifdef CMS_DONT_USE_FAST_FLOOR + return (int) floor(val); +#else + const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor + union { + cmsFloat64Number val; + int halves[2]; + } temp; + + temp.val = val + _lcms_double2fixmagic; + +#ifdef CMS_USE_BIG_ENDIAN + return temp.halves[1] >> 16; +#else + return temp.halves[0] >> 16; +#endif +#endif +} + +// Fast floor restricted to 0..65535.0 +cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d) +{ + return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U; +} + +// Floor to word, taking care of saturation +cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 65535.0) return 0xffff; + + return _cmsQuickFloorWord(d); +} + +// Test bed entry points--------------------------------------------------------------- +#define CMSCHECKPOINT CMSAPI + +// Pthread support -------------------------------------------------------------------- +#ifndef CMS_NO_PTHREADS + +// This is the threading support. Unfortunately, it has to be platform-dependent because +// windows does not support pthreads. +#ifdef CMS_IS_WINDOWS_ + +#define WIN32_LEAN_AND_MEAN 1 +#include + + +// The locking scheme in LCMS requires a single 'top level' mutex +// to work. This is actually implemented on Windows as a +// CriticalSection, because they are lighter weight. With +// pthreads, this is statically inited. Unfortunately, windows +// can't officially statically init critical sections. +// +// We can work around this in 2 ways. +// +// 1) We can use a proper mutex purely to protect the init +// of the CriticalSection. This in turns requires us to protect +// the Mutex creation, which we can do using the snappily +// named InterlockedCompareExchangePointer API (present on +// windows XP and above). +// +// 2) In cases where we want to work on pre-Windows XP, we +// can use an even more horrible hack described below. +// +// So why wouldn't we always use 2)? Because not calling +// the init function for a critical section means it fails +// testing with ApplicationVerifier (and presumably similar +// tools). +// +// We therefore default to 1, and people who want to be able +// to run on pre-Windows XP boxes can build with: +// CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +// defined. This is automatically set for builds using +// versions of MSVC that don't have this API available. +// +// From: http://locklessinc.com/articles/pthreads_on_windows/ +// The pthreads API has an initialization macro that has no correspondence to anything in +// the windows API. By investigating the internal definition of the critical section type, +// one may work out how to initialize one without calling InitializeCriticalSection(). +// The trick here is that InitializeCriticalSection() is not allowed to fail. It tries +// to allocate a critical section debug object, but if no memory is available, it sets +// the pointer to a specific value. (One would expect that value to be NULL, but it is +// actually (void *)-1 for some reason.) Thus we can use this special value for that +// pointer, and the critical section code will work. + +// The other important part of the critical section type to initialize is the number +// of waiters. This controls whether or not the mutex is locked. Fortunately, this +// part of the critical section is unlikely to change. Apparently, many programs +// already test critical sections to see if they are locked using this value, so +// Microsoft felt that it was necessary to keep it set at -1 for an unlocked critical +// section, even when they changed the underlying algorithm to be more scalable. +// The final parts of the critical section object are unimportant, and can be set +// to zero for their defaults. This yields to an initialization macro: + +typedef CRITICAL_SECTION _cmsMutex; + +#ifdef _MSC_VER +# if (_MSC_VER >= 1800) +# pragma warning(disable : 26135) +# endif +#endif + +#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +// If we are building with a version of MSVC smaller +// than 1400 (i.e. before VS2005) then we don't have +// the InterlockedCompareExchangePointer API, so use +// the old version. +# ifdef _MSC_VER +# if _MSC_VER < 1400 +# define CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +# endif +# endif +#endif + +#ifdef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT +# define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG) -1,-1,0,0,0,0} +#else +# define CMS_MUTEX_INITIALIZER {(PRTL_CRITICAL_SECTION_DEBUG)NULL,-1,0,0,0,0} +#endif + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + InitializeCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + DeleteCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +#else + +// Rest of the wide world +#include + +#define CMS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_init(m, NULL); +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_destroy(m); +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +#endif +#else + +#define CMS_MUTEX_INITIALIZER 0 +typedef int _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + cmsUNUSED_PARAMETER(m); + return 0; +} +#endif + +// Plug-In registration --------------------------------------------------------------- + +// Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once. +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size); + +// Memory management +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Interpolation +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Formatters management +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag type management +cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag management +cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Intent management +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Multi Process elements +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Optimization +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Transform +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Mutex +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// --------------------------------------------------------------------------------------------------------- + +// Suballocators. +typedef struct _cmsSubAllocator_chunk_st { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + struct _cmsSubAllocator_chunk_st* next; + +} _cmsSubAllocator_chunk; + + +typedef struct { + + cmsContext ContextID; + _cmsSubAllocator_chunk* h; + +} _cmsSubAllocator; + + +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial); +void _cmsSubAllocDestroy(_cmsSubAllocator* s); +void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size); +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size); + +// ---------------------------------------------------------------------------------- + +// The context clients. +typedef enum { + + UserPtr, // User-defined pointer + Logger, + AlarmCodesContext, + AdaptationStateContext, + MemPlugin, + InterpPlugin, + CurvesPlugin, + FormattersPlugin, + TagTypePlugin, + TagPlugin, + IntentPlugin, + MPEPlugin, + OptimizationPlugin, + TransformPlugin, + MutexPlugin, + + // Last in list + MemoryClientMax + +} _cmsMemoryClient; + + +// Container for memory management plug-in. +typedef struct { + + _cmsMallocFnPtrType MallocPtr; + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} _cmsMemPluginChunkType; + +// Copy memory management function pointers from plug-in to chunk, taking care of missing routines +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr); + +// Internal structure for context +struct _cmsContext_struct { + + struct _cmsContext_struct* Next; // Points to next context in the new style + _cmsSubAllocator* MemPool; // The memory pool that stores context data + + void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is hold in the suballocator. + // If NULL, then it reverts to global Context0 + + _cmsMemPluginChunkType DefaultMemoryManager; // The allocators used for creating the context itself. Cannot be overridden +}; + +// Returns a pointer to a valid context structure, including the global one if id is zero. +// Verifies the magic number. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID); + +// Returns the block assigned to the specific zone. +void* _cmsContextGetClientChunk(cmsContext id, _cmsMemoryClient mc); + + +// Chunks of context memory by plug-in client ------------------------------------------------------- + +// Those structures encapsulates all variables needed by the several context clients (mostly plug-ins) + +// Container for error logger -- not a plug-in +typedef struct { + + cmsLogErrorHandlerFunction LogErrorHandler; // Set to NULL for Context0 fallback + +} _cmsLogErrorChunkType; + +// The global Context0 storage for error logger +extern _cmsLogErrorChunkType _cmsLogErrorChunk; + +// Allocate and init error logger container. +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for alarm codes -- not a plug-in +typedef struct { + + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]; + +} _cmsAlarmCodesChunkType; + +// The global Context0 storage for alarm codes +extern _cmsAlarmCodesChunkType _cmsAlarmCodesChunk; + +// Allocate and init alarm codes container. +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for adaptation state -- not a plug-in +typedef struct { + + cmsFloat64Number AdaptationState; + +} _cmsAdaptationStateChunkType; + +// The global Context0 storage for adaptation state +extern _cmsAdaptationStateChunkType _cmsAdaptationStateChunk; + +// Allocate and init adaptation state container. +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + + +// The global Context0 storage for memory management +extern _cmsMemPluginChunkType _cmsMemPluginChunk; + +// Allocate and init memory management container. +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for interpolation plug-in +typedef struct { + + cmsInterpFnFactory Interpolators; + +} _cmsInterpPluginChunkType; + +// The global Context0 storage for interpolation plug-in +extern _cmsInterpPluginChunkType _cmsInterpPluginChunk; + +// Allocate and init interpolation container. +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for parametric curves plug-in +typedef struct { + + struct _cmsParametricCurvesCollection_st* ParametricCurves; + +} _cmsCurvesPluginChunkType; + +// The global Context0 storage for tone curves plug-in +extern _cmsCurvesPluginChunkType _cmsCurvesPluginChunk; + +// Allocate and init parametric curves container. +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for formatters plug-in +typedef struct { + + struct _cms_formatters_factory_list* FactoryList; + +} _cmsFormattersPluginChunkType; + +// The global Context0 storage for formatters plug-in +extern _cmsFormattersPluginChunkType _cmsFormattersPluginChunk; + +// Allocate and init formatters container. +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// This chunk type is shared by TagType plug-in and MPE Plug-in +typedef struct { + + struct _cmsTagTypeLinkedList_st* TagTypes; + +} _cmsTagTypePluginChunkType; + + +// The global Context0 storage for tag types plug-in +extern _cmsTagTypePluginChunkType _cmsTagTypePluginChunk; + + +// The global Context0 storage for mult process elements plug-in +extern _cmsTagTypePluginChunkType _cmsMPETypePluginChunk; + +// Allocate and init Tag types container. +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Allocate and init MPE container. +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Container for tag plug-in +typedef struct { + + struct _cmsTagLinkedList_st* Tag; + +} _cmsTagPluginChunkType; + + +// The global Context0 storage for tag plug-in +extern _cmsTagPluginChunkType _cmsTagPluginChunk; + +// Allocate and init Tag container. +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for intents plug-in +typedef struct { + + struct _cms_intents_list* Intents; + +} _cmsIntentsPluginChunkType; + + +// The global Context0 storage for intents plug-in +extern _cmsIntentsPluginChunkType _cmsIntentsPluginChunk; + +// Allocate and init intents container. +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for optimization plug-in +typedef struct { + + struct _cmsOptimizationCollection_st* OptimizationCollection; + +} _cmsOptimizationPluginChunkType; + + +// The global Context0 storage for optimizers plug-in +extern _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk; + +// Allocate and init optimizers container. +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for transform plug-in +typedef struct { + + struct _cmsTransformCollection_st* TransformCollection; + +} _cmsTransformPluginChunkType; + +// The global Context0 storage for full-transform replacement plug-in +extern _cmsTransformPluginChunkType _cmsTransformPluginChunk; + +// Allocate and init transform container. +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for mutex plug-in +typedef struct { + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} _cmsMutexPluginChunkType; + +// The global Context0 storage for mutex plug-in +extern _cmsMutexPluginChunkType _cmsMutexPluginChunk; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// ---------------------------------------------------------------------------------- +// MLU internal representation +typedef struct { + + cmsUInt16Number Language; + cmsUInt16Number Country; + + cmsUInt32Number StrW; // Offset to current unicode string + cmsUInt32Number Len; // Length in bytes + +} _cmsMLUentry; + +struct _cms_MLU_struct { + + cmsContext ContextID; + + // The directory + cmsUInt32Number AllocatedEntries; + cmsUInt32Number UsedEntries; + _cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool + + // The Pool + cmsUInt32Number PoolSize; // The maximum allocated size + cmsUInt32Number PoolUsed; // The used size + void* MemPool; // Pointer to begin of memory pool +}; + +// Named color list internal representation +typedef struct { + + char Name[cmsMAX_PATH]; + cmsUInt16Number PCS[3]; + cmsUInt16Number DeviceColorant[cmsMAXCHANNELS]; + +} _cmsNAMEDCOLOR; + +struct _cms_NAMEDCOLORLIST_struct { + + cmsUInt32Number nColors; + cmsUInt32Number Allocated; + cmsUInt32Number ColorantCount; + + char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most + char Suffix[33]; + + _cmsNAMEDCOLOR* List; + + cmsContext ContextID; +}; + + +// ---------------------------------------------------------------------------------- + +// This is the internal struct holding profile details. + +// Maximum supported tags in a profile +#define MAX_TABLE_TAG 100 + +typedef struct _cms_iccprofile_struct { + + // I/O handler + cmsIOHANDLER* IOhandler; + + // The thread ID + cmsContext ContextID; + + // Creation time + struct tm Created; + + // Only most important items found in ICC profiles + cmsUInt32Number Version; + cmsProfileClassSignature DeviceClass; + cmsColorSpaceSignature ColorSpace; + cmsColorSpaceSignature PCS; + cmsUInt32Number RenderingIntent; + + cmsUInt32Number flags; + cmsUInt32Number manufacturer, model; + cmsUInt64Number attributes; + cmsUInt32Number creator; + + cmsProfileID ProfileID; + + // Dictionary + cmsUInt32Number TagCount; + cmsTagSignature TagNames[MAX_TABLE_TAG]; + cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to which is linked (0=none) + cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk + cmsUInt32Number TagOffsets[MAX_TABLE_TAG]; + cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked + void * TagPtrs[MAX_TABLE_TAG]; + cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types + // depending on profile version, so we keep track of the + // type handler for each tag in the list. + // Special + cmsBool IsWrite; + + // Keep a mutex for cmsReadTag -- Note that this only works if the user includes a mutex plugin + void * UsrMutex; + +} _cmsICCPROFILE; + +// IO helpers for profiles +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc); +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace); +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); + +// Tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig); +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig); + +// Error logging --------------------------------------------------------------------------------------------------------- + +void _cmsTagSignature2String(char String[5], cmsTagSignature sig); + +// Interpolation --------------------------------------------------------------------------------------------------------- + +CMSCHECKPOINT cmsInterpParams* CMSEXPORT _cmsComputeInterpParams(cmsContext ContextID, cmsUInt32Number nSamples, cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags); +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], cmsUInt32Number InputChan, cmsUInt32Number OutputChan, const void* Table, cmsUInt32Number dwFlags); +CMSCHECKPOINT void CMSEXPORT _cmsFreeInterpParams(cmsInterpParams* p); +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p); + +// Curves ---------------------------------------------------------------------------------------------------------------- + +// This struct holds information about a segment, plus a pointer to the function that implements the evaluation. +// In the case of table-based, Eval pointer is set to NULL + +// The gamma function main structure +struct _cms_curve_struct { + + cmsInterpParams* InterpParams; // Private optimizations for interpolation + + cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables + cmsCurveSegment* Segments; // The segments + cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments + + cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment) + + // 16 bit Table-based representation follows + cmsUInt32Number nEntries; // Number of table elements + cmsUInt16Number* Table16; // The table itself. +}; + + +// Pipelines & Stages --------------------------------------------------------------------------------------------- + +// A single stage +struct _cmsStage_struct { + + cmsContext ContextID; + + cmsStageSignature Type; // Identifies the stage + cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations) + + cmsUInt32Number InputChannels; // Input channels -- for optimization purposes + cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes + + _cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point) + _cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage + _cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free + + // A generic pointer to whatever memory needed by the stage + void* Data; + + // Maintains linked list (used internally) + struct _cmsStage_struct* Next; +}; + + +// Special Stages (cannot be saved) +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID); +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID); +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels); +CMSCHECKPOINT cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan); +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID); +cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels); + + +// For curve set only +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe); + +struct _cmsPipeline_struct { + + cmsStage* Elements; // Points to elements chain + cmsUInt32Number InputChannels, OutputChannels; + + // Data & evaluators + void *Data; + + _cmsPipelineEval16Fn Eval16Fn; + _cmsPipelineEvalFloatFn EvalFloatFn; + _cmsFreeUserDataFn FreeDataFn; + _cmsDupUserDataFn DupDataFn; + + cmsContext ContextID; // Environment + + cmsBool SaveAs8Bits; // Implementation-specific: save as 8 bits if possible +}; + +// LUT reading & creation ------------------------------------------------------------------------------------------- + +// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy +// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources. + +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); +CMSCHECKPOINT cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent); + +// Special values +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile); +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile); + +// Profile linker -------------------------------------------------------------------------------------------------- + +// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point +// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS +// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +// Sequence -------------------------------------------------------------------------------------------------------- + +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile); +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq); +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]); + + +// LUT optimization ------------------------------------------------------------------------------------------------ + +CMSCHECKPOINT cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples); + +CMSAPI cmsUInt32Number CMSEXPORT _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags); + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs); + +CMSAPI cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags ); + + +// Hi level LUT building ---------------------------------------------------------------------------------------------- + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut); + + +// Formatters ------------------------------------------------------------------------------------------------------------ + +#define cmsFLAGS_CAN_CHANGE_FORMATTER 0x02000000 // Allow change buffer format + +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type); + +CMSCHECKPOINT cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); + + +#ifndef CMS_NO_HALF_SUPPORT + +// Half float +CMSCHECKPOINT cmsFloat32Number CMSEXPORT _cmsHalf2Float(cmsUInt16Number h); +CMSCHECKPOINT cmsUInt16Number CMSEXPORT _cmsFloat2Half(cmsFloat32Number flt); + +#endif + +// Transform logic ------------------------------------------------------------------------------------------------------ + +struct _cmstransform_struct; + +typedef struct { + + // 1-pixel cache (16 bits only) + cmsUInt16Number CacheIn[cmsMAXCHANNELS]; + cmsUInt16Number CacheOut[cmsMAXCHANNELS]; + +} _cmsCACHE; + + + +// Transformation +typedef struct _cmstransform_struct { + + cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference + + // Points to transform code + _cmsTransform2Fn xform; + + // Formatters, cannot be embedded into LUT because cache + cmsFormatter16 FromInput; + cmsFormatter16 ToOutput; + + cmsFormatterFloat FromInputFloat; + cmsFormatterFloat ToOutputFloat; + + // 1-pixel cache seed for zero as input (16 bits, read only) + _cmsCACHE Cache; + + // A Pipeline holding the full (optimized) transform + cmsPipeline* Lut; + + // A Pipeline holding the gamut check. It goes from the input space to bilevel + cmsPipeline* GamutCheck; + + // Colorant tables + cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table + cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK) + + // Informational only + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + + // White points (informative only) + cmsCIEXYZ EntryWhitePoint; + cmsCIEXYZ ExitWhitePoint; + + // Profiles used to create the transform + cmsSEQ* Sequence; + + cmsUInt32Number dwOriginalFlags; + cmsFloat64Number AdaptationState; + + // The intent of this transform. That is usually the last intent in the profilechain, but may differ + cmsUInt32Number RenderingIntent; + + // An id that uniquely identifies the running context. May be null. + cmsContext ContextID; + + // A user-defined pointer that can be used to store data for transform plug-ins + void* UserData; + _cmsFreeUserDataFn FreeUserData; + + // A way to provide backwards compatibility with full xform plugins + _cmsTransformFn OldXform; + +} _cmsTRANSFORM; + +// Copies extra channels from input to output if the original flags in the transform structure +// instructs to do so. This function is called on all standard transform functions. +void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in, + void* out, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride); + +// ----------------------------------------------------------------------------------------------------------------------- + +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll); + +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries); + + +#define _lcms_internal_H +#endif diff -Nru openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2_plugin.h openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2_plugin.h --- openjdk-17-17.0.2+8/src/java.desktop/share/native/liblcms/lcms2_plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/share/native/liblcms/lcms2_plugin.h 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,709 @@ +/* + * 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. + */ + +// This file is available under and governed by the GNU General Public +// License version 2 only, as published by the Free Software Foundation. +// However, the following notice accompanied the original version of this +// file: +// +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2020 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +// This is the plug-in header file. Normal LittleCMS clients should not use it. +// It is provided for plug-in writters that may want to access the support +// functions to do low level operations. All plug-in related structures +// are defined here. Including this file forces to include the standard API too. + +#ifndef _lcms_plugin_H + +// Deal with Microsoft's attempt at deprecating C standard runtime functions +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +# endif +# endif +#endif + +#ifndef _lcms2_H +#include "lcms2.h" +#endif + +// We need some standard C functions. +#include +#include +#include +#include +#include + + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Vector & Matrix operations ----------------------------------------------------------------------- + +// Axis of the matrix/array. No specific meaning at all. +#define VX 0 +#define VY 1 +#define VZ 2 + +// Vectors +typedef struct { + cmsFloat64Number n[3]; + + } cmsVEC3; + +// 3x3 Matrix +typedef struct { + cmsVEC3 v[3]; + + } cmsMAT3; + +CMSAPI void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z); +CMSAPI void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b); + +CMSAPI void CMSEXPORT _cmsMAT3identity(cmsMAT3* a); +CMSAPI cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a); +CMSAPI void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v); + + +// MD5 low level ------------------------------------------------------------------------------------- + +CMSAPI cmsHANDLE CMSEXPORT cmsMD5alloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsMD5add(cmsHANDLE Handle, const cmsUInt8Number* buf, cmsUInt32Number len); +CMSAPI void CMSEXPORT cmsMD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle); + +// Error logging ------------------------------------------------------------------------------------- + +CMSAPI void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...); + +// Memory management ---------------------------------------------------------------------------------- + +CMSAPI void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); +CMSAPI void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr); +CMSAPI void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size); + +// I/O handler ---------------------------------------------------------------------------------- + +struct _cms_io_handler { + + void* stream; // Associated stream, which is implemented differently depending on media. + + cmsContext ContextID; + cmsUInt32Number UsedSpace; + cmsUInt32Number ReportedSize; + char PhysicalFile[cmsMAX_PATH]; + + cmsUInt32Number (* Read)(struct _cms_io_handler* iohandler, void *Buffer, + cmsUInt32Number size, + cmsUInt32Number count); + cmsBool (* Seek)(struct _cms_io_handler* iohandler, cmsUInt32Number offset); + cmsBool (* Close)(struct _cms_io_handler* iohandler); + cmsUInt32Number (* Tell)(struct _cms_io_handler* iohandler); + cmsBool (* Write)(struct _cms_io_handler* iohandler, cmsUInt32Number size, + const void* Buffer); +}; + +// Endianness adjust functions +CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word); +CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value); +CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord); + +// Helper IO functions +CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array); + +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array); + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} _cmsTagBase; + +// Type base helper functions +CMSAPI cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig); + +// Alignment functions +CMSAPI cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io); + +// To deal with text streams. 2K at most +CMSAPI cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...); + +// Fixed point helper functions +CMSAPI cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8); +CMSAPI cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val); + +CMSAPI cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32); +CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v); + +// Date/time helper functions +CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source); +CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest); + +//---------------------------------------------------------------------------------------------------------- + +// Shared callbacks for user data +typedef void (* _cmsFreeUserDataFn)(cmsContext ContextID, void* Data); +typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); + +//---------------------------------------------------------------------------------------------------------- + +// Plug-in foundation +#define cmsPluginMagicNumber 0x61637070 // 'acpp' + +#define cmsPluginMemHandlerSig 0x6D656D48 // 'memH' +#define cmsPluginInterpolationSig 0x696E7048 // 'inpH' +#define cmsPluginParametricCurveSig 0x70617248 // 'parH' +#define cmsPluginFormattersSig 0x66726D48 // 'frmH +#define cmsPluginTagTypeSig 0x74797048 // 'typH' +#define cmsPluginTagSig 0x74616748 // 'tagH' +#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH' +#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' +#define cmsPluginOptimizationSig 0x6F707448 // 'optH' +#define cmsPluginTransformSig 0x7A666D48 // 'xfmH' +#define cmsPluginMutexSig 0x6D747A48 // 'mtxH' + +typedef struct _cmsPluginBaseStruct { + + cmsUInt32Number Magic; // 'acpp' signature + cmsUInt32Number ExpectedVersion; // Expected version of LittleCMS + cmsUInt32Number Type; // Type of plug-in + struct _cmsPluginBaseStruct* Next; // For multiple plugin definition. NULL for end of list. + +} cmsPluginBase; + +// Maximum number of types in a plugin array +#define MAX_TYPES_IN_LCMS_PLUGIN 20 + +//---------------------------------------------------------------------------------------------------------- + +// Memory handler. Each new plug-in type replaces current behaviour + +typedef void* (* _cmsMallocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void (* _cmsFreeFnPtrType)(cmsContext ContextID, void *Ptr); +typedef void* (* _cmsReallocFnPtrType)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); + +typedef void* (* _cmsMalloZerocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void* (* _cmsCallocFnPtrType)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +typedef void* (* _cmsDupFnPtrType)(cmsContext ContextID, const void* Org, cmsUInt32Number size); + +typedef struct { + + cmsPluginBase base; + + // Required + _cmsMallocFnPtrType MallocPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + + // Optional + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} cmsPluginMemHandler; + + +// ------------------------------------------------------------------------------------------------------------------ + +// Interpolation. 16 bits and floating point versions. +struct _cms_interp_struc; + +// Interpolation callbacks + +// 16 bits forward interpolation. This function performs precision-limited linear interpolation +// and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFn16)(CMSREGISTER const cmsUInt16Number Input[], + CMSREGISTER cmsUInt16Number Output[], + CMSREGISTER const struct _cms_interp_struc* p); + +// Floating point forward interpolation. Full precision interpolation using floats. This is not a +// time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFnFloat)(cmsFloat32Number const Input[], + cmsFloat32Number Output[], + const struct _cms_interp_struc* p); + + + +// This type holds a pointer to an interpolator that can be either 16 bits or float +typedef union { + _cmsInterpFn16 Lerp16; // Forward interpolation in 16 bits + _cmsInterpFnFloat LerpFloat; // Forward interpolation in floating point +} cmsInterpFunction; + +// Flags for interpolator selection +#define CMS_LERP_FLAGS_16BITS 0x0000 // The default +#define CMS_LERP_FLAGS_FLOAT 0x0001 // Requires different implementation +#define CMS_LERP_FLAGS_TRILINEAR 0x0100 // Hint only + + +#define MAX_INPUT_DIMENSIONS 15 + +typedef struct _cms_interp_struc { // Used on all interpolations. Supplied by lcms2 when calling the interpolation function + + cmsContext ContextID; // The calling thread + + cmsUInt32Number dwFlags; // Keep original flags + cmsUInt32Number nInputs; // != 1 only in 3D interpolation + cmsUInt32Number nOutputs; // != 1 only in 3D interpolation + + cmsUInt32Number nSamples[MAX_INPUT_DIMENSIONS]; // Valid on all kinds of tables + cmsUInt32Number Domain[MAX_INPUT_DIMENSIONS]; // Domain = nSamples - 1 + + cmsUInt32Number opta[MAX_INPUT_DIMENSIONS]; // Optimization for 3D CLUT. This is the number of nodes premultiplied for each + // dimension. For example, in 7 nodes, 7, 7^2 , 7^3, 7^4, etc. On non-regular + // Samplings may vary according of the number of nodes for each dimension. + + const void *Table; // Points to the actual interpolation table + cmsInterpFunction Interpolation; // Points to the function to do the interpolation + + } cmsInterpParams; + +// Interpolators factory +typedef cmsInterpFunction (* cmsInterpFnFactory)(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// The plug-in +typedef struct { + cmsPluginBase base; + + // Points to a user-supplied function which implements the factory + cmsInterpFnFactory InterpolatorsFactory; + +} cmsPluginInterpolation; + +//---------------------------------------------------------------------------------------------------------- + +// Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10 + +// Evaluator callback for user-supplied parametric curves. May implement more than one type +typedef cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R); + +// Plug-in may implement an arbitrary number of parametric curves +typedef struct { + cmsPluginBase base; + + cmsUInt32Number nFunctions; // Number of supported functions + cmsUInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + +} cmsPluginParametricCurves; +//---------------------------------------------------------------------------------------------------------- + +// Formatters. This plug-in adds new handlers, replacing them if they already exist. Formatters dealing with +// cmsFloat32Number (bps = 4) or double (bps = 0) types are requested via FormatterFloat callback. Others come across +// Formatter16 callback + +struct _cmstransform_struct; + +typedef cmsUInt8Number* (* cmsFormatter16)(CMSREGISTER struct _cmstransform_struct* CMMcargo, + CMSREGISTER cmsUInt16Number Values[], + CMSREGISTER cmsUInt8Number* Buffer, + CMSREGISTER cmsUInt32Number Stride); + +typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo, + cmsFloat32Number Values[], + cmsUInt8Number* Buffer, + cmsUInt32Number Stride); + +// This type holds a pointer to a formatter that can be either 16 bits or cmsFloat32Number +typedef union { + cmsFormatter16 Fmt16; + cmsFormatterFloat FmtFloat; + +} cmsFormatter; + +#define CMS_PACK_FLAGS_16BITS 0x0000 +#define CMS_PACK_FLAGS_FLOAT 0x0001 + +typedef enum { cmsFormatterInput=0, cmsFormatterOutput=1 } cmsFormatterDirection; + +typedef cmsFormatter (* cmsFormatterFactory)(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); // precision + +// Plug-in may implement an arbitrary number of formatters +typedef struct { + cmsPluginBase base; + cmsFormatterFactory FormattersFactory; + +} cmsPluginFormatters; + +//---------------------------------------------------------------------------------------------------------- + +// Tag type handler. Each type is free to return anything it wants, and it is up to the caller to +// know in advance what is the type contained in the tag. +typedef struct _cms_typehandler_struct { + + cmsTagTypeSignature Signature; // The signature of the type + + // Allocates and reads items + void * (* ReadPtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag); + + // Writes n Items + cmsBool (* WritePtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Ptr, + cmsUInt32Number nItems); + + // Duplicate an item or array of items + void* (* DupPtr)(struct _cms_typehandler_struct* self, + const void *Ptr, + cmsUInt32Number n); + + // Free all resources + void (* FreePtr)(struct _cms_typehandler_struct* self, + void *Ptr); + + // Additional parameters used by the calling thread + cmsContext ContextID; + cmsUInt32Number ICCVersion; + +} cmsTagTypeHandler; + +// Each plug-in implements a single type +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginTagType; + +//---------------------------------------------------------------------------------------------------------- + +// This is the tag plugin, which identifies tags. For writing, a pointer to function is provided. +// This function should return the desired type for this tag, given the version of profile +// and the data being serialized. +typedef struct { + + cmsUInt32Number ElemCount; // If this tag needs an array, how many elements should keep + + // For reading. + cmsUInt32Number nSupportedTypes; // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum) + cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN]; + + // For writing + cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data); + +} cmsTagDescriptor; + +// Plug-in implements a single tag +typedef struct { + cmsPluginBase base; + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + +} cmsPluginTag; + +//---------------------------------------------------------------------------------------------------------- + +// Custom intents. This function should join all profiles specified in the array in +// a single LUT. Any custom intent in the chain redirects to custom function. If more than +// one custom intent is found, the one located first is invoked. Usually users should use only one +// custom intent, so mixing custom intents in same multiprofile transform is not supported. + +typedef cmsPipeline* (* cmsIntentFn)( cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +// Each plug-in defines a single intent number. +typedef struct { + cmsPluginBase base; + cmsUInt32Number Intent; + cmsIntentFn Link; + char Description[256]; + +} cmsPluginRenderingIntent; + + +// The default ICC intents (perceptual, saturation, rel.col and abs.col) +CMSAPI cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +//---------------------------------------------------------------------------------------------------------- + +// Pipelines, Multi Process Elements. + +typedef void (* _cmsStageEvalFn) (const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage* mpe); +typedef void*(* _cmsStageDupElemFn) (cmsStage* mpe); +typedef void (* _cmsStageFreeElemFn) (cmsStage* mpe); + + +// This function allocates a generic MPE +CMSAPI cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, // Points to fn that evaluates the element (always in floating point) + _cmsStageDupElemFn DupElemPtr, // Points to a fn that duplicates the stage + _cmsStageFreeElemFn FreePtr, // Points to a fn that sets the element free + void* Data); // A generic pointer to whatever memory needed by the element +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginMultiProcessElement; + + +// Data kept in "Element" member of cmsStage + +// Curves +typedef struct { + cmsUInt32Number nCurves; + cmsToneCurve** TheCurves; + +} _cmsStageToneCurvesData; + +// Matrix +typedef struct { + cmsFloat64Number* Double; // floating point for the matrix + cmsFloat64Number* Offset; // The offset + +} _cmsStageMatrixData; + +// CLUT +typedef struct { + + union { // Can have only one of both representations at same time + cmsUInt16Number* T; // Points to the table 16 bits table + cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table + + } Tab; + + cmsInterpParams* Params; + cmsUInt32Number nEntries; + cmsBool HasFloatValues; + +} _cmsStageCLutData; + + +//---------------------------------------------------------------------------------------------------------- +// Optimization. Using this plug-in, additional optimization strategies may be implemented. +// The function should return TRUE if any optimization is done on the LUT, this terminates +// the optimization search. Or FALSE if it is unable to optimize and want to give a chance +// to the rest of optimizers. + +typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + +// Pipeline Evaluator (in 16 bits) +typedef void (* _cmsPipelineEval16Fn)(CMSREGISTER const cmsUInt16Number In[], + CMSREGISTER cmsUInt16Number Out[], + const void* Data); + +// Pipeline Evaluator (in floating point) +typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const void* Data); + + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. + +CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsPipelineEval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn); + +typedef struct { + cmsPluginBase base; + + // Optimize entry point + _cmsOPToptimizeFn OptimizePtr; + +} cmsPluginOptimization; + +//---------------------------------------------------------------------------------------------------------- +// Full xform + +typedef struct { + cmsUInt32Number BytesPerLineIn; + cmsUInt32Number BytesPerLineOut; + cmsUInt32Number BytesPerPlaneIn; + cmsUInt32Number BytesPerPlaneOut; + +} cmsStride; + +typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo, // Legacy function, handles just ONE scanline. + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); // Stride in bytes to the next plana in planar formats + + +typedef void (*_cmsTransform2Fn)(struct _cmstransform_struct *CMMcargo, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number PixelsPerLine, + cmsUInt32Number LineCount, + const cmsStride* Stride); + +typedef cmsBool (* _cmsTransformFactory)(_cmsTransformFn* xform, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + +typedef cmsBool (* _cmsTransform2Factory)(_cmsTransform2Fn* xform, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + + +// Retrieve user data as specified by the factory +CMSAPI void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn); +CMSAPI void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo); + + +// Retrieve formatters +CMSAPI void CMSEXPORT _cmsGetTransformFormatters16 (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput); +CMSAPI void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput); + +// Retrieve original flags +CMSAPI cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo); + +typedef struct { + cmsPluginBase base; + + // Transform entry point + union { + _cmsTransformFactory legacy_xform; + _cmsTransform2Factory xform; + } factories; + +} cmsPluginTransform; + +//---------------------------------------------------------------------------------------------------------- +// Mutex + +typedef void* (* _cmsCreateMutexFnPtrType)(cmsContext ContextID); +typedef void (* _cmsDestroyMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef cmsBool (* _cmsLockMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef void (* _cmsUnlockMutexFnPtrType)(cmsContext ContextID, void* mtx); + +typedef struct { + cmsPluginBase base; + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} cmsPluginMutex; + +CMSAPI void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID); +CMSAPI void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx); +CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx); +CMSAPI void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx); + + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus + } +# endif +#endif + +#define _lcms_plugin_H +#endif diff -Nru openjdk-17-17.0.2+8/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c openjdk-17-17.0.3+7/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c --- openjdk-17-17.0.2+8/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/unix/native/libawt_xawt/awt/gtk2_interface.c 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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 @@ -147,10 +147,6 @@ GdkRectangle* area, GtkWidget* widget, const gchar* detail, GtkArrowType arrow_type, gboolean fill, gint x, gint y, gint width, gint height); -static void (*fp_gtk_paint_diamond)(GtkStyle* style, GdkWindow* window, - GtkStateType state_type, GtkShadowType shadow_type, - GdkRectangle* area, GtkWidget* widget, const gchar* detail, - gint x, gint y, gint width, gint height); static void (*fp_gtk_paint_box)(GtkStyle* style, GdkWindow* window, GtkStateType state_type, GtkShadowType shadow_type, GdkRectangle* area, GtkWidget* widget, const gchar* detail, @@ -595,7 +591,6 @@ fp_gtk_paint_vline = dl_symbol("gtk_paint_vline"); fp_gtk_paint_shadow = dl_symbol("gtk_paint_shadow"); fp_gtk_paint_arrow = dl_symbol("gtk_paint_arrow"); - fp_gtk_paint_diamond = dl_symbol("gtk_paint_diamond"); fp_gtk_paint_box = dl_symbol("gtk_paint_box"); fp_gtk_paint_flat_box = dl_symbol("gtk_paint_flat_box"); fp_gtk_paint_check = dl_symbol("gtk_paint_check"); @@ -1849,19 +1844,6 @@ shadow_type, NULL, gtk2_widget, detail, x, y, width, height); } - -static void gtk2_paint_diamond(WidgetType widget_type, GtkStateType state_type, - GtkShadowType shadow_type, const gchar *detail, - gint x, gint y, gint width, gint height) -{ - gtk2_widget = gtk2_get_widget(widget_type); - (*fp_gtk_paint_diamond)(gtk2_widget->style, gtk2_white_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); - (*fp_gtk_paint_diamond)(gtk2_widget->style, gtk2_black_pixmap, state_type, - shadow_type, NULL, gtk2_widget, detail, - x, y, width, height); -} static void gtk2_paint_expander(WidgetType widget_type, GtkStateType state_type, const gchar *detail, gint x, gint y, gint width, gint height, diff -Nru openjdk-17-17.0.2+8/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c openjdk-17-17.0.3+7/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c --- openjdk-17-17.0.2+8/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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,26 +58,6 @@ static GtkWidget *gtk3_widgets[_GTK_WIDGET_TYPE_SIZE]; -static void throw_exception(JNIEnv *env, const char* name, const char* message) -{ - jclass class = (*env)->FindClass(env, name); - - if (class != NULL) - (*env)->ThrowNew(env, class, message); - - (*env)->DeleteLocalRef(env, class); -} - -static void gtk3_add_state(GtkWidget *widget, GtkStateType state) { - GtkStateType old_state = fp_gtk_widget_get_state(widget); - fp_gtk_widget_set_state(widget, old_state | state); -} - -static void gtk3_remove_state(GtkWidget *widget, GtkStateType state) { - GtkStateType old_state = fp_gtk_widget_get_state(widget); - fp_gtk_widget_set_state(widget, old_state & ~state); -} - /* This is a workaround for the bug: * http://sourceware.org/bugzilla/show_bug.cgi?id=1814 * (dlsym/dlopen clears dlerror state) @@ -838,21 +818,6 @@ } } -/* GTK state_type filter */ -static GtkStateType get_gtk_state_type(WidgetType widget_type, gint synth_state) -{ - GtkStateType result = GTK_STATE_NORMAL; - - if ((synth_state & DISABLED) != 0) { - result = GTK_STATE_INSENSITIVE; - } else if ((synth_state & PRESSED) != 0) { - result = GTK_STATE_ACTIVE; - } else if ((synth_state & MOUSE_OVER) != 0) { - result = GTK_STATE_PRELIGHT; - } - return result; -} - static GtkStateFlags get_gtk_state_flags(gint synth_state) { GtkStateFlags flags = 0; @@ -897,19 +862,6 @@ return flags; } -/* GTK shadow_type filter */ -static GtkShadowType get_gtk_shadow_type(WidgetType widget_type, - gint synth_state) -{ - GtkShadowType result = GTK_SHADOW_OUT; - - if ((synth_state & SELECTED) != 0) { - result = GTK_SHADOW_IN; - } - return result; -} - - static GtkWidget* gtk3_get_arrow(GtkArrowType arrow_type, GtkShadowType shadow_type) { diff -Nru openjdk-17-17.0.2+8/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp openjdk-17-17.0.3+7/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp --- openjdk-17-17.0.2+8/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1056,38 +1056,57 @@ bmi.bmiHeader.biCompression = BI_RGB; // Extract the color bitmap int nBits = iconSize * iconSize; - long colorBits[MAX_ICON_SIZE * MAX_ICON_SIZE]; - GetDIBits(dc, iconInfo.hbmColor, 0, iconSize, colorBits, &bmi, DIB_RGB_COLORS); - // XP supports alpha in some icons, and depending on device. - // This should take precedence over the icon mask bits. - BOOL hasAlpha = FALSE; - if (IS_WINXP) { - for (int i = 0; i < nBits; i++) { - if ((colorBits[i] & 0xff000000) != 0) { - hasAlpha = TRUE; - break; + + long *colorBits = NULL; + long *maskBits = NULL; + + try { + entry_point(); + colorBits = (long*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(long)); + GetDIBits(dc, iconInfo.hbmColor, 0, iconSize, colorBits, &bmi, DIB_RGB_COLORS); + // XP supports alpha in some icons, and depending on device. + // This should take precedence over the icon mask bits. + BOOL hasAlpha = FALSE; + if (IS_WINXP) { + for (int i = 0; i < nBits; i++) { + if ((colorBits[i] & 0xff000000) != 0) { + hasAlpha = TRUE; + break; + } } } - } - if (!hasAlpha) { - // Extract the mask bitmap - long maskBits[MAX_ICON_SIZE * MAX_ICON_SIZE]; - GetDIBits(dc, iconInfo.hbmMask, 0, iconSize, maskBits, &bmi, DIB_RGB_COLORS); - // Copy the mask alphas into the color bits - for (int i = 0; i < nBits; i++) { - if (maskBits[i] == 0) { - colorBits[i] |= 0xff000000; + if (!hasAlpha) { + // Extract the mask bitmap + maskBits = (long*)safe_Malloc(MAX_ICON_SIZE * MAX_ICON_SIZE * sizeof(long)); + GetDIBits(dc, iconInfo.hbmMask, 0, iconSize, maskBits, &bmi, DIB_RGB_COLORS); + // Copy the mask alphas into the color bits + for (int i = 0; i < nBits; i++) { + if (maskBits[i] == 0) { + colorBits[i] |= 0xff000000; + } } } + // Create java array + iconBits = env->NewIntArray(nBits); + if (!(env->ExceptionCheck())) { + // Copy values to java array + env->SetIntArrayRegion(iconBits, 0, nBits, colorBits); + } + } catch(std::bad_alloc&) { + handle_bad_alloc(); } + // Release DC ReleaseDC(NULL, dc); - // Create java array - iconBits = env->NewIntArray(nBits); - if (!(env->ExceptionCheck())) { - // Copy values to java array - env->SetIntArrayRegion(iconBits, 0, nBits, colorBits); - } + + // Free bitmap buffers if they were allocated + if (colorBits != NULL) { + free(colorBits); + } + + if (maskBits != NULL) { + free(maskBits); + } } // Fix 4745575 GDI Resource Leak // MSDN diff -Nru openjdk-17-17.0.2+8/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp openjdk-17-17.0.3+7/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp --- openjdk-17-17.0.2+8/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.desktop/windows/native/libawt/windows/awt_Component.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2021, 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,6 +47,7 @@ #include "awt_Win32GraphicsDevice.h" #include "Hashtable.h" #include "ComCtl32Util.h" +#include "math.h" #include @@ -2234,8 +2235,8 @@ */ RECT* r = (RECT*)(buffer + rgndata->rdh.dwSize); RECT* un[2] = {0, 0}; - DWORD i; - for (i = 0; i < rgndata->rdh.nCount; i++, r++) { + DWORD i; + for (i = 0; i < rgndata->rdh.nCount; i++, r++) { int width = r->right-r->left; int height = r->bottom-r->top; if (width > 0 && height > 0) { @@ -2247,13 +2248,22 @@ } } } + // The Windows may request to update the small region of pixels that + // cannot be represented in the user's space, in this case, we will + // request to repaint the smallest non-empty bounding box in the user's + // space + int screen = GetScreenImOn(); + Devices::InstanceAccess devices; + AwtWin32GraphicsDevice* device = devices->GetDevice(screen); + float scaleX = (device == NULL) ? 1 : device->GetScaleX(); + float scaleY = (device == NULL) ? 1 : device->GetScaleY(); for(i = 0; i < 2; i++) { if (un[i] != 0) { - DoCallback("handleExpose", "(IIII)V", - ScaleDownX(un[i]->left), - ScaleDownY(un[i]->top), - ScaleDownX(un[i]->right - un[i]->left), - ScaleDownY(un[i]->bottom - un[i]->top)); + int x1 = floor(un[i]->left / scaleX); + int y1 = floor(un[i]->top / scaleY); + int x2 = ceil(un[i]->right / scaleX); + int y2 = ceil(un[i]->bottom / scaleY); + DoCallback("handleExpose", "(IIII)V", x1, y1, x2 - x1, y2 - y1); } } delete [] buffer; diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/LdapClientFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -65,7 +65,23 @@ connTimeout, readTimeout, trace, pcb); } + public PooledConnection createPooledConnection(PoolCallback pcb, long timeout) + throws NamingException { + return new LdapClient(host, port, socketFactory, + guardedIntegerCast(timeout), + readTimeout, trace, pcb); + } + public String toString() { return host + ":" + port; } + + private int guardedIntegerCast(long timeout) { + if (timeout < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } else if (timeout > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + return (int) timeout; + } } diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/LdapURL.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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,10 @@ import javax.naming.*; import java.net.MalformedURLException; import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; import com.sun.jndi.toolkit.url.UrlUtil; @@ -64,6 +68,25 @@ public final class LdapURL extends Uri { + private static final String PARSE_MODE_PROP = "com.sun.jndi.ldapURLParsing"; + private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT; + + public static final ParseMode PARSE_MODE; + static { + PrivilegedAction action = () -> + System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); + ParseMode parseMode = DEFAULT_PARSE_MODE; + try { + @SuppressWarnings("removal") + String mode = AccessController.doPrivileged(action); + parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); + } catch (Throwable t) { + parseMode = DEFAULT_PARSE_MODE; + } finally { + PARSE_MODE = parseMode; + } + } + private boolean useSsl = false; private String DN = null; private String attributes = null; @@ -83,7 +106,7 @@ useSsl = scheme.equalsIgnoreCase("ldaps"); if (! (scheme.equalsIgnoreCase("ldap") || useSsl)) { - throw new MalformedURLException("Not an LDAP URL: " + url); + throw newInvalidURISchemeException(url); } parsePathAndQuery(); // DN, attributes, scope, filter, extensions @@ -99,6 +122,21 @@ } } + @Override + protected MalformedURLException newInvalidURISchemeException(String uri) { + return new MalformedURLException("Not an LDAP URL: " + uri); + } + + @Override + protected boolean isSchemeOnly(String uri) { + return isLdapSchemeOnly(uri); + } + + @Override + protected ParseMode parseMode() { + return PARSE_MODE; + } + /** * Returns true if the URL is an LDAPS URL. */ @@ -151,13 +189,33 @@ StringTokenizer st = new StringTokenizer(urlList, " "); while (st.hasMoreTokens()) { - urls[i++] = st.nextToken(); + // we don't accept scheme-only URLs here + urls[i++] = validateURI(st.nextToken()); } String[] trimmed = new String[i]; System.arraycopy(urls, 0, trimmed, 0, i); return trimmed; } + public static boolean isLdapSchemeOnly(String uri) { + return "ldap:".equals(uri) || "ldaps:".equals(uri); + } + + public static String validateURI(String uri) { + // no validation in legacy mode parsing + if (PARSE_MODE == ParseMode.LEGACY) { + return uri; + } + + // special case of scheme-only URIs + if (isLdapSchemeOnly(uri)) { + return uri; + } + + // use java.net.URI to validate the uri syntax + return URI.create(uri).toString(); + } + /** * Determines whether an LDAP URL has query components. */ @@ -181,7 +239,8 @@ String p = (port != -1) ? (":" + port) : ""; String d = (dn != null) ? ("/" + UrlUtil.encode(dn, "UTF8")) : ""; - return useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d; + String uri = useSsl ? "ldaps://" + h + p + d : "ldap://" + h + p + d; + return validateURI(uri); } catch (UnsupportedEncodingException e) { // UTF8 should always be supported throw new IllegalStateException("UTF-8 encoding unavailable"); diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Connections.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,6 +27,9 @@ import java.util.ArrayList; // JDK 1.2 import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; import java.lang.ref.Reference; import java.lang.ref.SoftReference; @@ -68,13 +71,19 @@ com.sun.jndi.ldap.LdapPoolManager.trace; private static final int DEFAULT_SIZE = 10; + final private int initSize; private final int maxSize; private final int prefSize; private final List conns; + final private PooledConnectionFactory factory; private boolean closed = false; // Closed for business private Reference ref; // maintains reference to id to prevent premature GC + private boolean initialized = false; + private final ReentrantLock lock; + private final Condition connectionsAvailable; + /** * @param id the identity (connection request) of the connections in the list * @param initSize the number of connections to create initially @@ -87,105 +96,73 @@ * when one is removed. * @param factory The factory responsible for creating a connection */ - Connections(Object id, int initSize, int prefSize, int maxSize, - PooledConnectionFactory factory) throws NamingException { - + Connections(Object id, int initSize, int prefSize, int maxSize, PooledConnectionFactory factory, + ReentrantLock lock) throws NamingException { this.maxSize = maxSize; + this.lock = lock; + this.connectionsAvailable = lock.newCondition(); + this.factory = factory; + if (maxSize > 0) { // prefSize and initSize cannot exceed specified maxSize this.prefSize = Math.min(prefSize, maxSize); - initSize = Math.min(initSize, maxSize); + this.initSize = Math.min(initSize, maxSize); } else { this.prefSize = prefSize; + this.initSize = initSize; } - conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE); + this.conns = new ArrayList<>(maxSize > 0 ? maxSize : DEFAULT_SIZE); + this.initialized = initSize <= 0; // Maintain soft ref to id so that this Connections' entry in // Pool doesn't get GC'ed prematurely - ref = new SoftReference<>(id); + this.ref = new SoftReference<>(id); d("init size=", initSize); d("max size=", maxSize); d("preferred size=", prefSize); + } - // Create initial connections - PooledConnection conn; - for (int i = 0; i < initSize; i++) { - conn = factory.createPooledConnection(this); - td("Create ", conn ,factory); - conns.add(new ConnectionDesc(conn)); // Add new idle conn to pool + void waitForAvailableConnection() throws InterruptedNamingException { + try { + d("get(): waiting"); + connectionsAvailable.await(); + } catch (InterruptedException e) { + throw new InterruptedNamingException( + "Interrupted while waiting for a connection"); } } - /** - * Retrieves a PooledConnection from this list of connections. - * Use an existing one if one is idle, or create one if the list's - * max size hasn't been reached. If max size has been reached, wait - * for a PooledConnection to be returned, or one to be removed (thus - * not reaching the max size any longer). - * - * @param timeout if > 0, msec to wait until connection is available - * @param factory creates the PooledConnection if one needs to be created - * - * @return A non-null PooledConnection - * @throws NamingException PooledConnection cannot be created, because this - * thread was interrupted while it waited for an available connection, - * or if it timed out while waiting, or the creation of a connection - * resulted in an error. - */ - synchronized PooledConnection get(long timeout, - PooledConnectionFactory factory) throws NamingException { - PooledConnection conn; - long start = (timeout > 0 ? System.currentTimeMillis() : 0); - long waittime = timeout; - - d("get(): before"); - while ((conn = getOrCreateConnection(factory)) == null) { - if (timeout > 0 && waittime <= 0) { - throw new CommunicationException( - "Timeout exceeded while waiting for a connection: " + - timeout + "ms"); - } - try { - d("get(): waiting"); - if (waittime > 0) { - wait(waittime); // Wait until one is released or removed - } else { - wait(); - } - } catch (InterruptedException e) { - throw new InterruptedNamingException( + void waitForAvailableConnection(long waitTime) throws InterruptedNamingException { + try { + d("get(): waiting"); + connectionsAvailable.await(waitTime, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new InterruptedNamingException( "Interrupted while waiting for a connection"); - } - // Check whether we timed out - if (timeout > 0) { - long now = System.currentTimeMillis(); - waittime = timeout - (now - start); - } } - - d("get(): after"); - return conn; } /** * Retrieves an idle connection from this list if one is available. - * If none is available, create a new one if maxSize hasn't been reached. - * If maxSize has been reached, return null. - * Always called from a synchronized method. */ - private PooledConnection getOrCreateConnection( - PooledConnectionFactory factory) throws NamingException { - + PooledConnection getAvailableConnection(long timeout) throws NamingException { + if (!initialized) { + PooledConnection conn = createConnection(factory, timeout); + if (conns.size() >= initSize) { + this.initialized = true; + } + return conn; + } int size = conns.size(); // Current number of idle/nonidle conns - PooledConnection conn = null; if (prefSize <= 0 || size >= prefSize) { // If no prefSize specified, or list size already meets or // exceeds prefSize, then first look for an idle connection ConnectionDesc entry; - for (int i = 0; i < size; i++) { - entry = conns.get(i); + for (ConnectionDesc connectionDesc : conns) { + PooledConnection conn; + entry = connectionDesc; if ((conn = entry.tryUse()) != null) { d("get(): use ", conn); td("Use ", conn); @@ -193,17 +170,25 @@ } } } + return null; + } - // Check if list size already at maxSize specified - if (maxSize > 0 && size >= maxSize) { - return null; // List size is at limit; cannot create any more + /* + * Creates a new Connection if maxSize hasn't been reached. + * If maxSize has been reached, return null. + * Caller must hold the ReentrantLock. + */ + PooledConnection createConnection(PooledConnectionFactory factory, long timeout) + throws NamingException { + int size = conns.size(); // Current number of idle/non-idle connections + if (maxSize == 0 || size < maxSize) { + PooledConnection conn = factory.createPooledConnection(this, timeout); + td("Create and use ", conn, factory); + conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool + return conn; } - conn = factory.createPooledConnection(this); - td("Create and use ", conn, factory); - conns.add(new ConnectionDesc(conn, true)); // Add new conn to pool - - return conn; + return null; } /** @@ -211,43 +196,45 @@ * If the list size is below prefSize, the connection may be reused. * If the list size exceeds prefSize, then the connection is closed * and removed from the list. - * + *

    * public because implemented as part of PoolCallback. */ - public synchronized boolean releasePooledConnection(PooledConnection conn) { - ConnectionDesc entry; - int loc = conns.indexOf(entry=new ConnectionDesc(conn)); - - d("release(): ", conn); - - if (loc >= 0) { - // Found entry + public boolean releasePooledConnection(PooledConnection conn) { + lock.lock(); + try { + ConnectionDesc entry; + int loc = conns.indexOf(entry = new ConnectionDesc(conn)); - if (closed || (prefSize > 0 && conns.size() > prefSize)) { - // If list size exceeds prefSize, close connection + d("release(): ", conn); - d("release(): closing ", conn); - td("Close ", conn); + if (loc >= 0) { + // Found entry - // size must be >= 2 so don't worry about empty list - conns.remove(entry); - conn.closeConnection(); + if (closed || (prefSize > 0 && conns.size() > prefSize)) { + // If list size exceeds prefSize, close connection - } else { - d("release(): release ", conn); - td("Release ", conn); + d("release(): closing ", conn); + td("Close ", conn); - // Get ConnectionDesc from list to get correct state info - entry = conns.get(loc); - // Return connection to list, ready for reuse - entry.release(); + // size must be >= 2 so don't worry about empty list + conns.remove(entry); + conn.closeConnection(); + } else { + d("release(): release ", conn); + td("Release ", conn); + // Get ConnectionDesc from list to get correct state info + entry = conns.get(loc); + // Return connection to list, ready for reuse + entry.release(); + } + connectionsAvailable.signalAll(); + d("release(): notify"); + return true; } - notifyAll(); - d("release(): notify"); - return true; - } else { - return false; + } finally { + lock.unlock(); } + return false; } /** @@ -257,29 +244,34 @@ * when using the connection and wants it removed from the pool. * * @return true if conn removed; false if it was not in pool - * + *

    * public because implemented as part of PoolCallback. */ - public synchronized boolean removePooledConnection(PooledConnection conn) { - if (conns.remove(new ConnectionDesc(conn))) { - d("remove(): ", conn); - - notifyAll(); - - d("remove(): notify"); - td("Remove ", conn); - - if (conns.isEmpty()) { - // Remove softref to make pool entry eligible for GC. - // Once ref has been removed, it cannot be reinstated. - ref = null; - } + public boolean removePooledConnection(PooledConnection conn) { + lock.lock(); + try { + if (conns.remove(new ConnectionDesc(conn))) { + d("remove(): ", conn); + + connectionsAvailable.signalAll(); + + d("remove(): notify"); + td("Remove ", conn); + + if (conns.isEmpty()) { + // Remove softref to make pool entry eligible for GC. + // Once ref has been removed, it cannot be reinstated. + ref = null; + } - return true; - } else { - d("remove(): not found ", conn); - return false; + return true; + } else { + d("remove(): not found ", conn); + } + } finally { + lock.unlock(); } + return false; } /** @@ -291,8 +283,11 @@ */ boolean expire(long threshold) { List clonedConns; - synchronized(this) { + lock.lock(); + try { clonedConns = new ArrayList<>(conns); + } finally { + lock.unlock(); } List expired = new ArrayList<>(); @@ -304,12 +299,15 @@ } } - synchronized (this) { + lock.lock(); + try { conns.removeAll(expired); // Don't need to call notify() because we're // removing only idle connections. If there were // idle connections, then there should be no waiters. return conns.isEmpty(); // whether whole list has 'expired' + } finally { + lock.unlock(); } } @@ -355,6 +353,29 @@ + "; idle=" + idle + "; expired=" + expired; } + boolean grabLock(long timeout) throws InterruptedNamingException { + final long start = System.nanoTime(); + long current = start; + long remaining = timeout; + boolean locked = false; + while (!locked && remaining > 0) { + try { + locked = lock.tryLock(remaining, TimeUnit.MILLISECONDS); + remaining -= TimeUnit.NANOSECONDS.toMillis(current - start); + } catch (InterruptedException ignore) { + throw new InterruptedNamingException( + "Interrupted while waiting for the connection pool lock"); + } + current = System.nanoTime(); + remaining -= TimeUnit.NANOSECONDS.toMillis(current - start); + } + return locked; + } + + void unlock() { + lock.unlock(); + } + private void d(String msg, Object o1) { if (debug) { d(msg + o1); diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/Pool.java 2022-04-19 19:55:43.000000000 +0000 @@ -31,10 +31,13 @@ import java.util.Collection; import java.util.Collections; import java.util.LinkedList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import java.io.PrintStream; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; +import javax.naming.CommunicationException; import javax.naming.NamingException; /** @@ -117,45 +120,109 @@ public PooledConnection getPooledConnection(Object id, long timeout, PooledConnectionFactory factory) throws NamingException { + final long start = System.nanoTime(); + long remaining = timeout; + d("get(): ", id); if (debug) { synchronized (map) { d("size: ", map.size()); } + remaining = checkRemaining(start, remaining); } expungeStaleConnections(); + Connections conns = getOrCreateConnections(factory, id); + d("get(): size after: ", map.size()); + remaining = checkRemaining(start, remaining); + + if (!conns.grabLock(remaining)) { + throw new CommunicationException("Timed out waiting for lock"); + } + + try { + remaining = checkRemaining(start, remaining); + PooledConnection conn = null; + while (remaining > 0 && conn == null) { + conn = getOrCreatePooledConnection(factory, conns, start, remaining); + // don't loop if the timeout has expired + remaining = checkRemaining(start, timeout); + } + return conn; + } finally { + conns.unlock(); + } + } + + private Connections getOrCreateConnections(PooledConnectionFactory factory, Object id) + throws NamingException { + Connections conns; synchronized (map) { - conns = getConnections(id); - if (conns == null) { - d("get(): creating new connections list for ", id); - - // No connections for this id so create a new list - conns = new Connections(id, initSize, prefSize, maxSize, - factory); - ConnectionsRef connsRef = new ConnectionsRef(conns); - map.put(id, connsRef); - - // Create a weak reference to ConnectionsRef - Reference weakRef = - new ConnectionsWeakRef(connsRef, queue); - - // Keep the weak reference through the element of a linked list - weakRefs.add(weakRef); + ConnectionsRef ref = map.get(id); + if (ref != null) { + return ref.getConnections(); } - d("get(): size after: ", map.size()); + + d("get(): creating new connections list for ", id); + + // No connections for this id so create a new list + conns = new Connections(id, initSize, prefSize, maxSize, + factory, new ReentrantLock()); + + ConnectionsRef connsRef = new ConnectionsRef(conns); + map.put(id, connsRef); + + // Create a weak reference to ConnectionsRef + Reference weakRef = new ConnectionsWeakRef(connsRef, queue); + + // Keep the weak reference through the element of a linked list + weakRefs.add(weakRef); } + return conns; + } - return conns.get(timeout, factory); // get one connection from list + private PooledConnection getOrCreatePooledConnection( + PooledConnectionFactory factory, Connections conns, long start, long timeout) + throws NamingException { + PooledConnection conn = conns.getAvailableConnection(timeout); + if (conn != null) { + return conn; + } + // no available cached connection + // check if list size already at maxSize before creating a new one + conn = conns.createConnection(factory, timeout); + if (conn != null) { + return conn; + } + // max number of connections already created, + // try waiting around for one to become available + if (timeout <= 0) { + conns.waitForAvailableConnection(); + } else { + long remaining = checkRemaining(start, timeout); + conns.waitForAvailableConnection(remaining); + } + return null; } - private Connections getConnections(Object id) { - ConnectionsRef ref = map.get(id); - return (ref != null) ? ref.getConnections() : null; + // Check whether we timed out + private long checkRemaining(long start, long timeout) throws CommunicationException { + if (timeout > 0) { + long current = System.nanoTime(); + long remaining = timeout - TimeUnit.NANOSECONDS.toMillis(current - start); + if (remaining <= 0) { + throw new CommunicationException( + "Timeout exceeded while waiting for a connection: " + + timeout + "ms"); + } + return remaining; + } + return Long.MAX_VALUE; } + /** * Goes through the connections in this Pool and expires ones that * have been idle before 'threshold'. An expired connection is closed diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/ldap/pool/PooledConnectionFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -44,4 +44,13 @@ */ public abstract PooledConnection createPooledConnection(PoolCallback pcb) throws NamingException; + + /** + * Creates a pooled connection. + * @param pcb callback responsible for removing and releasing the pooled + * connection from the pool. + * @param timeout the connection timeout + */ + public abstract PooledConnection createPooledConnection(PoolCallback pcb, long timeout) + throws NamingException; }; diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/toolkit/url/GenericURLContext.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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,6 +32,8 @@ import java.util.Hashtable; import java.net.MalformedURLException; +import com.sun.jndi.toolkit.url.Uri.ParseMode; + /** * This abstract class is a generic URL context that accepts as the * name argument either a string URL or a Name whose first component @@ -48,6 +50,7 @@ * @author Rosanna Lee */ public abstract class GenericURLContext implements Context { + protected Hashtable myEnv = null; @SuppressWarnings("unchecked") // Expect Hashtable @@ -161,8 +164,18 @@ if (url.startsWith("//", start)) { start += 2; // skip double slash - // find last slash - int posn = url.indexOf('/', start); + // find where the authority component ends + // and the rest of the URL starts + int slash = url.indexOf('/', start); + int qmark = url.indexOf('?', start); + int fmark = url.indexOf('#', start); + if (fmark > -1 && qmark > fmark) qmark = -1; + if (fmark > -1 && slash > fmark) slash = -1; + if (qmark > -1 && slash > qmark) slash = -1; + int posn = slash > -1 ? slash + : (qmark > -1 ? qmark + : (fmark > -1 ? fmark + : url.length())); if (posn >= 0) { start = posn; } else { diff -Nru openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java --- openjdk-17-17.0.2+8/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.naming/share/classes/com/sun/jndi/toolkit/url/Uri.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2001, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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,8 @@ import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; /** @@ -36,15 +38,17 @@ * *

    The java.net.URL class cannot be used to parse URIs since it * requires the installation of URL stream handlers that may not be - * available. The hack of getting around this by temporarily - * replacing the scheme part of a URI is not appropriate here: JNDI - * service providers must work on older Java platforms, and we want - * new features and bug fixes that are not available in old versions - * of the URL class. - * - *

    It may be appropriate to drop this code in favor of the - * java.net.URI class. The changes would need to be written so as to - * still run on pre-1.4 platforms not containing that class. + * available. + * + *

    The {@linkplain ParseMode#STRICT strict} parsing mode uses + * the java.net.URI class to syntactically validate URI strings. + * The {@linkplain ParseMode#COMPAT compat} mode validate the + * URI authority and rejects URI fragments, but doesn't perform any + * additional validation on path and query, other than that + * which may be implemented in the concrete the Uri subclasses. + * The {@linkplain ParseMode#LEGACY legacy} mode should not be + * used unless the application is capable of validating all URI + * strings before any constructors of this class is invoked. * *

    The format of an absolute URI (see the RFCs mentioned above) is: *

    {@code
    @@ -105,6 +109,28 @@
     
     public class Uri {
     
    +    // three parsing modes
    +    public enum ParseMode {
    +        /**
    +         * Strict validation mode.
    +         * Validate the URI syntactically using {@link java.net.URI}.
    +         * Rejects URI fragments unless explicitly supported by the
    +         * subclass.
    +         */
    +        STRICT,
    +        /**
    +         * Compatibility mode. The URI authority is syntactically validated.
    +         * Rejects URI fragments unless explicitly supported by the
    +         * subclass.
    +         * This is the default.
    +         */
    +        COMPAT,
    +        /**
    +         * Legacy mode. In this mode, no validation is performed.
    +         */
    +        LEGACY
    +     }
    +
         protected String uri;
         protected String scheme;
         protected String host = null;
    @@ -112,6 +138,7 @@
         protected boolean hasAuthority;
         protected String path;
         protected String query = null;
    +    protected String fragment;
     
     
         /**
    @@ -129,13 +156,22 @@
         }
     
         /**
    +     * The parse mode for parsing this URI.
    +     * The default is {@link ParseMode#COMPAT}.
    +     * @return the parse mode for parsing this URI.
    +     */
    +    protected ParseMode parseMode() {
    +        return ParseMode.COMPAT;
    +    }
    +
    +    /**
          * Initializes a Uri object given a URI string.
          * This method must be called exactly once, and before any other Uri
          * methods.
          */
         protected void init(String uri) throws MalformedURLException {
             this.uri = uri;
    -        parse(uri);
    +        parse(uri, parseMode());
         }
     
         /**
    @@ -188,10 +224,229 @@
             return uri;
         }
     
    +    private void parse(String uri, ParseMode mode) throws MalformedURLException {
    +        switch (mode) {
    +            case STRICT -> parseStrict(uri);
    +            case COMPAT -> parseCompat(uri);
    +            case LEGACY -> parseLegacy(uri);
    +        }
    +    }
    +
    +    /*
    +     * Parses a URI string and sets this object's fields accordingly.
    +     * Use java.net.URI to validate the uri string syntax
    +     */
    +    private void parseStrict(String uri) throws MalformedURLException {
    +        try {
    +            if (!isSchemeOnly(uri)) {
    +                URI u = new URI(uri);
    +                scheme = u.getScheme();
    +                if (scheme == null) throw new MalformedURLException("Invalid URI: " + uri);
    +                var auth = u.getRawAuthority();
    +                hasAuthority = auth != null;
    +                if (hasAuthority) {
    +                    var host = u.getHost();
    +                    var port = u.getPort();
    +                    if (host != null) this.host = host;
    +                    if (port != -1) this.port = port;
    +                    String hostport = (host == null ? "" : host)
    +                            + (port == -1 ? "" : (":" + port));
    +                    if (!hostport.equals(auth)) {
    +                        // throw if we have user info or regname
    +                        throw new MalformedURLException("unsupported authority: " + auth);
    +                    }
    +                }
    +                path = u.getRawPath();
    +                if (u.getRawQuery() != null) {
    +                    query = "?" + u.getRawQuery();
    +                }
    +                if (u.getRawFragment() != null) {
    +                    if (!acceptsFragment()) {
    +                        throw new MalformedURLException("URI fragments not supported: " + uri);
    +                    }
    +                    fragment = "#" + u.getRawFragment();
    +                }
    +            } else {
    +                // scheme-only URIs are not supported by java.net.URI
    +                // validate the URI by appending "/" to the uri string.
    +                var s = uri.substring(0, uri.indexOf(':'));
    +                URI u = new URI(uri + "/");
    +                if (!s.equals(u.getScheme())
    +                        || !checkSchemeOnly(uri, u.getScheme())) {
    +                    throw newInvalidURISchemeException(uri);
    +                }
    +                scheme = s;
    +                path = "";
    +            }
    +        } catch (URISyntaxException e) {
    +            var mue =  new MalformedURLException(e.getMessage());
    +            mue.initCause(e);
    +            throw mue;
    +        }
    +    }
    +
    +
    +    /*
    +     * Parses a URI string and sets this object's fields accordingly.
    +     * Compatibility mode. Use java.net.URI to validate the syntax of
    +     * the uri string authority.
    +     */
    +    private void parseCompat(String uri) throws MalformedURLException {
    +        int i;  // index into URI
    +
    +        i = uri.indexOf(':');                           // parse scheme
    +        int slash = uri.indexOf('/');
    +        int qmark = uri.indexOf('?');
    +        int fmark = uri.indexOf('#');
    +        if (i < 0 || slash > 0 && i > slash || qmark > 0 && i > qmark || fmark > 0 && i > fmark) {
    +            throw new MalformedURLException("Invalid URI: " + uri);
    +        }
    +        if (fmark > -1) {
    +            if (!acceptsFragment()) {
    +                throw new MalformedURLException("URI fragments not supported: " + uri);
    +            }
    +        }
    +        if (i == uri.length() - 1) {
    +            if (!isSchemeOnly(uri)) {
    +                throw newInvalidURISchemeException(uri);
    +            }
    +        }
    +        scheme = uri.substring(0, i);
    +        i++;                                            // skip past ":"
    +
    +        hasAuthority = uri.startsWith("//", i);
    +        if (fmark > -1 && qmark > fmark) qmark = -1;
    +        int endp = qmark > -1 ? qmark : fmark > -1 ? fmark : uri.length();
    +        if (hasAuthority) {                             // parse "//host:port"
    +            i += 2;                                     // skip past "//"
    +            int starta = i;
    +            // authority ends at the first appearance of /, ?, or #
    +            int enda = uri.indexOf('/', i);
    +            if (enda == -1 || qmark > -1 && qmark < enda) enda = qmark;
    +            if (enda == -1 || fmark > -1 && fmark < enda) enda = fmark;
    +            if (enda < 0) {
    +                enda = uri.length();
    +            }
    +            if (uri.startsWith(":", i)) {
    +                // LdapURL supports empty host.
    +                i++;
    +                host = "";
    +                if (enda > i) {
    +                    port = Integer.parseInt(uri.substring(i, enda));
    +                }
    +            } else {
    +                // Use URI to parse authority
    +                try {
    +                    // URI requires at least one char after authority:
    +                    // we use "/" and expect that the resulting URI path
    +                    // will be exactly "/".
    +                    URI u = new URI(uri.substring(0, enda) + "/");
    +                    String auth = uri.substring(starta, enda);
    +                    host = u.getHost();
    +                    port = u.getPort();
    +                    String p = u.getRawPath();
    +                    String q = u.getRawQuery();
    +                    String f = u.getRawFragment();
    +                    String ui = u.getRawUserInfo();
    +                    if (ui != null) {
    +                        throw new MalformedURLException("user info not supported in authority: " + ui);
    +                    }
    +                    if (!"/".equals(p)) {
    +                        throw new MalformedURLException("invalid authority: " + auth);
    +                    }
    +                    if (q != null) {
    +                        throw new MalformedURLException("invalid trailing characters in authority: ?" + q);
    +                    }
    +                    if (f != null) {
    +                        throw new MalformedURLException("invalid trailing characters in authority: #" + f);
    +                    }
    +                    String hostport = (host == null ? "" : host)
    +                            + (port == -1?"":(":" + port));
    +                    if (!auth.equals(hostport)) {
    +                        // throw if we have user info or regname
    +                        throw new MalformedURLException("unsupported authority: " + auth);
    +                    }
    +                } catch (URISyntaxException e) {
    +                    var mue = new MalformedURLException(e.getMessage());
    +                    mue.initCause(e);
    +                    throw mue;
    +                }
    +            }
    +            i = enda;
    +        }
    +        path = uri.substring(i, endp);
    +        // look for query
    +        if (qmark > -1) {
    +            if (fmark > -1) {
    +                query = uri.substring(qmark, fmark);
    +            } else {
    +                query = uri.substring(qmark);
    +            }
    +        }
    +        if (fmark > -1) {
    +            fragment = uri.substring(fmark);
    +        }
    +    }
    +
    +    /**
    +     * A subclass of {@code Uri} that supports scheme only
    +     * URIs can override this method and return true in the
    +     * case where the URI string is a scheme-only URI that
    +     * the subclass supports.
    +     * @implSpec
    +     * The default implementation of this method returns false,
    +     * always.
    +     * @param uri An URI string
    +     * @return if this is a scheme-only URI supported by the subclass
    +     */
    +    protected boolean isSchemeOnly(String uri) {
    +        return false;
    +    }
    +
    +    /**
    +     * Checks whether the given uri string should be considered
    +     * as a scheme-only URI. For some protocols - e.g. DNS, we
    +     * might accept "dns://" as a valid URL denoting default DNS.
    +     * For others - we might only accept "scheme:".
    +     * @implSpec
    +     * The default implementation of this method returns true if
    +     * the URI is of the form {@code ":"} with nothing
    +     * after the scheme delimiter.
    +     * @param uri the URI
    +     * @param scheme the scheme
    +     * @return true if the URI should be considered as a scheme-only
    +     *         URI supported by this URI scheme.
    +     */
    +    protected boolean checkSchemeOnly(String uri, String scheme) {
    +        return uri.equals(scheme + ":");
    +    }
    +
    +    /**
    +     * Creates a {@code MalformedURLException} to be thrown when the
    +     * URI scheme is not supported.
    +     *
    +     * @param uri the URI string
    +     * @return a {@link MalformedURLException}
    +     */
    +    protected MalformedURLException newInvalidURISchemeException(String uri) {
    +        return new MalformedURLException("Invalid URI scheme: " + uri);
    +    }
    +
    +    /**
    +     * Whether fragments are supported.
    +     * @implSpec
    +     * The default implementation of this method retturns false, always.
    +     * @return true if fragments are supported.
    +     */
    +    protected boolean acceptsFragment() {
    +        return parseMode() == ParseMode.LEGACY;
    +    }
    +
         /*
          * Parses a URI string and sets this object's fields accordingly.
    +     * Legacy parsing mode.
          */
    -    private void parse(String uri) throws MalformedURLException {
    +    private void parseLegacy(String uri) throws MalformedURLException {
             int i;  // index into URI
     
             i = uri.indexOf(':');                           // parse scheme
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/java_cup/internal/runtime/lr_parser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2003, 2022, 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 com.sun.java_cup.internal.runtime;
     
    +import com.sun.org.apache.xalan.internal.xsltc.compiler.sym;
    +import java.util.Arrays;
     import java.util.Stack;
     
     /** This class implements a skeleton table driven LR parser.  In general,
    @@ -134,9 +136,19 @@
      * @see     com.sun.java_cup.internal.runtime.Symbol
      * @see     com.sun.java_cup.internal.runtime.virtual_parse_stack
      * @author  Frank Flannery
    + *
    + * @LastModified: Jan 2022
      */
     
     public abstract class lr_parser {
    +    public static final int ID_GROUP = 1;
    +    public static final int ID_OPERATOR = 2;
    +    public static final int ID_TOTAL_OPERATOR = 3;
    +
    +    private boolean isLiteral = false;
    +    private int grpCount = 0;
    +    private int opCount = 0;
    +    private int totalOpCount = 0;
     
       /*-----------------------------------------------------------*/
       /*--- Constructor(s) ----------------------------------------*/
    @@ -355,8 +367,29 @@
        *  the "scan with" clause.  Do not recycle objects; every call to
        *  scan() should return a fresh object.
        */
    -  public Symbol scan() throws java.lang.Exception {
    -    return getScanner().next_token();
    +  public Symbol scan() throws Exception {
    +      Symbol s = getScanner().next_token();
    +
    +      if (s.sym == sym.LPAREN) {
    +          if (!isLiteral) {
    +            grpCount++;
    +          }
    +          opCount++; // function
    +          isLiteral = false;
    +      } else if (contains(sym.OPERATORS, s.sym)) {
    +          opCount++;
    +          isLiteral = false;
    +      }
    +
    +      if (s.sym == sym.Literal || s.sym == sym.QNAME) {
    +          isLiteral = true;
    +      }
    +
    +    return s;
    +  }
    +
    +  private boolean contains(final int[] arr, final int key) {
    +    return Arrays.stream(arr).anyMatch(i -> i == key);
       }
     
       /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
    @@ -552,6 +585,9 @@
     
           /* do user initialization */
           user_init();
    +      isLiteral = false;
    +      grpCount = 0;
    +      opCount = 0;
     
           /* get the first token */
           cur_token = scan();
    @@ -630,9 +666,29 @@
                     }
                 }
             }
    +
    +      totalOpCount += opCount;
           return lhs_sym;
         }
     
    +    /**
    +     * Returns the count of operators in XPath expressions.
    +     *
    +     * @param id the ID of the count
    +     * @return the count associated with the ID
    +     */
    +    public int getCount(int id) {
    +        switch (id) {
    +            case ID_GROUP:
    +                return grpCount;
    +            case ID_OPERATOR:
    +                return opCount;
    +            case ID_TOTAL_OPERATOR:
    +                return totalOpCount;
    +        }
    +        return 0;
    +    }
    +
       /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/
     
       /** Write a debugging message to System.err for the debugging version
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java	1970-01-01 00:00:00.000000000 +0000
    @@ -1,480 +0,0 @@
    -/*
    - * Copyright (c) 2013, 2021, 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 com.sun.org.apache.xalan.internal.utils;
    -
    -import java.util.concurrent.CopyOnWriteArrayList;
    -import jdk.xml.internal.JdkConstants;
    -import jdk.xml.internal.JdkProperty.ImplPropMap;
    -import jdk.xml.internal.JdkProperty.State;
    -import jdk.xml.internal.SecuritySupport;
    -import org.xml.sax.SAXException;
    -
    -
    -/**
    - * This class is not the same as that in Xerces. It is used to manage the
    - * state of corresponding Xerces properties and pass the values over to
    - * the Xerces Security Manager.
    - *
    - * @author Joe Wang Oracle Corp.
    - *
    - */
    -public final class XMLSecurityManager {
    -
    -    /**
    -     * Limits managed by the security manager
    -     */
    -    @SuppressWarnings("deprecation")
    -    public static enum Limit {
    -
    -        ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", JdkConstants.JDK_ENTITY_EXPANSION_LIMIT,
    -                JdkConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000),
    -        MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", JdkConstants.JDK_MAX_OCCUR_LIMIT,
    -                JdkConstants.SP_MAX_OCCUR_LIMIT, 0, 5000),
    -        ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", JdkConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT,
    -                JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000),
    -        TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", JdkConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT,
    -                JdkConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000),
    -        GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT,
    -                JdkConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0),
    -        PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT,
    -                JdkConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000),
    -        MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", JdkConstants.JDK_MAX_ELEMENT_DEPTH,
    -                JdkConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
    -        MAX_NAME_LIMIT("MaxXMLNameLimit", JdkConstants.JDK_XML_NAME_LIMIT,
    -                JdkConstants.SP_XML_NAME_LIMIT, 1000, 1000),
    -        ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", JdkConstants.JDK_ENTITY_REPLACEMENT_LIMIT,
    -                JdkConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
    -
    -        final String key;
    -        final String apiProperty;
    -        final String systemProperty;
    -        final int defaultValue;
    -        final int secureValue;
    -
    -        Limit(String key, String apiProperty, String systemProperty, int value, int secureValue) {
    -            this.key = key;
    -            this.apiProperty = apiProperty;
    -            this.systemProperty = systemProperty;
    -            this.defaultValue = value;
    -            this.secureValue = secureValue;
    -        }
    -
    -        /**
    -         * Checks whether the specified name is a limit. Checks both the
    -         * property and System Property which is now the new property name.
    -         *
    -         * @param name the specified name
    -         * @return true if there is a match, false otherwise
    -         */
    -        public boolean is(String name) {
    -            // current spec: new property name == systemProperty
    -            return (systemProperty != null && systemProperty.equals(name)) ||
    -                   // current spec: apiProperty is legacy
    -                   (apiProperty.equals(name));
    -        }
    -
    -        /**
    -         * Returns the state of a property name. By the specification as of JDK 17,
    -         * the "jdk.xml." prefixed System property name is also the current API
    -         * name. The URI-based qName is legacy.
    -         *
    -         * @param name the property name
    -         * @return the state of the property name, null if no match
    -         */
    -        public State getState(String name) {
    -            if (systemProperty != null && systemProperty.equals(name)) {
    -                return State.APIPROPERTY;
    -            } else if (apiProperty.equals(name)) {
    -                //the URI-style qName is legacy
    -                return State.LEGACY_APIPROPERTY;
    -            }
    -            return null;
    -        }
    -
    -        public String key() {
    -            return key;
    -        }
    -
    -        public String apiProperty() {
    -            return apiProperty;
    -        }
    -
    -        public String systemProperty() {
    -            return systemProperty;
    -        }
    -
    -        public int defaultValue() {
    -            return defaultValue;
    -        }
    -
    -        int secureValue() {
    -            return secureValue;
    -        }
    -    }
    -
    -    /**
    -     * Map old property names with the new ones
    -     */
    -    public static enum NameMap {
    -
    -        ENTITY_EXPANSION_LIMIT(JdkConstants.SP_ENTITY_EXPANSION_LIMIT,
    -                JdkConstants.ENTITY_EXPANSION_LIMIT),
    -        MAX_OCCUR_NODE_LIMIT(JdkConstants.SP_MAX_OCCUR_LIMIT,
    -                JdkConstants.MAX_OCCUR_LIMIT),
    -        ELEMENT_ATTRIBUTE_LIMIT(JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT,
    -                JdkConstants.ELEMENT_ATTRIBUTE_LIMIT);
    -        final String newName;
    -        final String oldName;
    -
    -        NameMap(String newName, String oldName) {
    -            this.newName = newName;
    -            this.oldName = oldName;
    -        }
    -
    -        String getOldName(String newName) {
    -            if (newName.equals(this.newName)) {
    -                return oldName;
    -            }
    -            return null;
    -        }
    -    }
    -    /**
    -     * Values of the properties
    -     */
    -    private final int[] values;
    -    /**
    -     * States of the settings for each property
    -     */
    -    private State[] states;
    -    /**
    -     * States that determine if properties are set explicitly
    -     */
    -    private boolean[] isSet;
    -
    -
    -    /**
    -     * Index of the special entityCountInfo property
    -     */
    -    private final int indexEntityCountInfo = 10000;
    -    private String printEntityCountInfo = "";
    -
    -    /**
    -     * Default constructor. Establishes default values for known security
    -     * vulnerabilities.
    -     */
    -    public XMLSecurityManager() {
    -        this(false);
    -    }
    -
    -    /**
    -     * Instantiate Security Manager in accordance with the status of
    -     * secure processing
    -     * @param secureProcessing
    -     */
    -    public XMLSecurityManager(boolean secureProcessing) {
    -        values = new int[Limit.values().length];
    -        states = new State[Limit.values().length];
    -        isSet = new boolean[Limit.values().length];
    -        for (Limit limit : Limit.values()) {
    -            if (secureProcessing) {
    -                values[limit.ordinal()] = limit.secureValue();
    -                states[limit.ordinal()] = State.FSP;
    -            } else {
    -                values[limit.ordinal()] = limit.defaultValue();
    -                states[limit.ordinal()] = State.DEFAULT;
    -            }
    -        }
    -        //read system properties or jaxp.properties
    -        readSystemProperties();
    -    }
    -
    -    /**
    -     * Setting FEATURE_SECURE_PROCESSING explicitly
    -     */
    -    public void setSecureProcessing(boolean secure) {
    -        for (Limit limit : Limit.values()) {
    -            if (secure) {
    -                setLimit(limit.ordinal(), State.FSP, limit.secureValue());
    -            } else {
    -                setLimit(limit.ordinal(), State.FSP, limit.defaultValue());
    -            }
    -        }
    -    }
    -
    -    /**
    -     * Set limit by property name and state
    -     * @param propertyName property name
    -     * @param state the state of the property
    -     * @param value the value of the property
    -     * @return true if the property is managed by the security manager; false
    -     *              if otherwise.
    -     */
    -    public boolean setLimit(String propertyName, State state, Object value) {
    -        int index = getIndex(propertyName);
    -        if (index > -1) {
    -            State pState = state;
    -            if (index != indexEntityCountInfo && state == State.APIPROPERTY) {
    -                pState = (Limit.values()[index]).getState(propertyName);
    -            }
    -            setLimit(index, pState, value);
    -            return true;
    -        }
    -        return false;
    -    }
    -
    -    /**
    -     * Set the value for a specific limit.
    -     *
    -     * @param limit the limit
    -     * @param state the state of the property
    -     * @param value the value of the property
    -     */
    -    public void setLimit(Limit limit, State state, int value) {
    -        setLimit(limit.ordinal(), state, value);
    -    }
    -
    -    /**
    -     * Set the value of a property by its index
    -     *
    -     * @param index the index of the property
    -     * @param state the state of the property
    -     * @param value the value of the property
    -     */
    -    public void setLimit(int index, State state, Object value) {
    -        if (index == indexEntityCountInfo) {
    -            //if it's explicitly set, it's treated as yes no matter the value
    -            printEntityCountInfo = (String)value;
    -        } else {
    -            int temp;
    -            if (value instanceof Integer) {
    -                temp = (Integer)value;
    -            } else {
    -                temp = Integer.parseInt((String) value);
    -                if (temp < 0) {
    -                    temp = 0;
    -                }
    -            }
    -            setLimit(index, state, temp);
    -        }
    -    }
    -
    -    /**
    -     * Set the value of a property by its index
    -     *
    -     * @param index the index of the property
    -     * @param state the state of the property
    -     * @param value the value of the property
    -     */
    -    public void setLimit(int index, State state, int value) {
    -        if (index == indexEntityCountInfo) {
    -            //if it's explicitly set, it's treated as yes no matter the value
    -            printEntityCountInfo = JdkConstants.JDK_YES;
    -        } else {
    -            //only update if it shall override
    -            if (state.compareTo(states[index]) >= 0) {
    -                values[index] = value;
    -                states[index] = state;
    -                isSet[index] = true;
    -            }
    -        }
    -    }
    -
    -
    -    /**
    -     * Return the value of the specified property.
    -     *
    -     * @param propertyName the property name
    -     * @return the value of the property as a string. If a property is managed
    -     * by this manager, its value shall not be null.
    -     */
    -    public String getLimitAsString(String propertyName) {
    -        int index = getIndex(propertyName);
    -        if (index > -1) {
    -            return getLimitValueByIndex(index);
    -        }
    -
    -        return null;
    -    }
    -
    -    /**
    -     * Return the value of a property by its ordinal
    -     *
    -     * @param limit the property
    -     * @return value of a property
    -     */
    -    public String getLimitValueAsString(Limit limit) {
    -        return Integer.toString(values[limit.ordinal()]);
    -    }
    -
    -    /**
    -     * Return the value of the specified property
    -     *
    -     * @param limit the property
    -     * @return the value of the property
    -     */
    -    public int getLimit(Limit limit) {
    -        return values[limit.ordinal()];
    -    }
    -
    -    /**
    -     * Return the value of a property by its ordinal
    -     *
    -     * @param index the index of a property
    -     * @return value of a property
    -     */
    -    public int getLimitByIndex(int index) {
    -        return values[index];
    -    }
    -    /**
    -     * Return the value of a property by its index
    -     *
    -     * @param index the index of a property
    -     * @return limit of a property as a string
    -     */
    -    public String getLimitValueByIndex(int index) {
    -        if (index == indexEntityCountInfo) {
    -            return printEntityCountInfo;
    -        }
    -
    -        return Integer.toString(values[index]);
    -    }
    -    /**
    -     * Return the state of the limit property
    -     *
    -     * @param limit the limit
    -     * @return the state of the limit property
    -     */
    -    public State getState(Limit limit) {
    -        return states[limit.ordinal()];
    -    }
    -
    -    /**
    -     * Return the state of the limit property
    -     *
    -     * @param limit the limit
    -     * @return the state of the limit property
    -     */
    -    public String getStateLiteral(Limit limit) {
    -        return states[limit.ordinal()].literal();
    -    }
    -
    -    /**
    -     * Get the index by property name
    -     *
    -     * @param propertyName property name
    -     * @return the index of the property if found; return -1 if not
    -     */
    -    public int getIndex(String propertyName) {
    -        for (Limit limit : Limit.values()) {
    -            // see JDK-8265248, accept both the URL and jdk.xml as prefix
    -            if (limit.is(propertyName)) {
    -                //internally, ordinal is used as index
    -                return limit.ordinal();
    -            }
    -        }
    -        //special property to return entity count info
    -        if (ImplPropMap.ENTITYCOUNT.is(propertyName)) {
    -            return indexEntityCountInfo;
    -        }
    -        return -1;
    -    }
    -
    -    /**
    -     * Indicate if a property is set explicitly
    -     * @param index
    -     * @return
    -     */
    -    public boolean isSet(int index) {
    -        return isSet[index];
    -    }
    -
    -    public boolean printEntityCountInfo() {
    -        return printEntityCountInfo.equals(JdkConstants.JDK_YES);
    -    }
    -    /**
    -     * Read from system properties, or those in jaxp.properties
    -     */
    -    private void readSystemProperties() {
    -
    -        for (Limit limit : Limit.values()) {
    -            if (!getSystemProperty(limit, limit.systemProperty())) {
    -                //if system property is not found, try the older form if any
    -                for (NameMap nameMap : NameMap.values()) {
    -                    String oldName = nameMap.getOldName(limit.systemProperty());
    -                    if (oldName != null) {
    -                        getSystemProperty(limit, oldName);
    -                    }
    -                }
    -            }
    -        }
    -
    -    }
    -
    -    // Array list to store printed warnings for each SAX parser used
    -    private static final CopyOnWriteArrayList printedWarnings = new CopyOnWriteArrayList<>();
    -
    -    /**
    -     * Prints out warnings if a parser does not support the specified feature/property.
    -     *
    -     * @param parserClassName the name of the parser class
    -     * @param propertyName the property name
    -     * @param exception the exception thrown by the parser
    -     */
    -    public static void printWarning(String parserClassName, String propertyName, SAXException exception) {
    -        String key = parserClassName+":"+propertyName;
    -        if (printedWarnings.addIfAbsent(key)) {
    -            System.err.println( "Warning: "+parserClassName+": "+exception.getMessage());
    -        }
    -    }
    -
    -    /**
    -     * Read from system properties, or those in jaxp.properties
    -     *
    -     * @param property the type of the property
    -     * @param sysPropertyName the name of system property
    -     */
    -    private boolean getSystemProperty(Limit limit, String sysPropertyName) {
    -        try {
    -            String value = SecuritySupport.getSystemProperty(sysPropertyName);
    -            if (value != null && !value.equals("")) {
    -                values[limit.ordinal()] = Integer.parseInt(value);
    -                states[limit.ordinal()] = State.SYSTEMPROPERTY;
    -                return true;
    -            }
    -
    -            value = SecuritySupport.readJAXPProperty(sysPropertyName);
    -            if (value != null && !value.equals("")) {
    -                values[limit.ordinal()] = Integer.parseInt(value);
    -                states[limit.ordinal()] = State.JAXPDOTPROPERTIES;
    -                return true;
    -            }
    -        } catch (NumberFormatException e) {
    -            //invalid setting
    -            throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty());
    -        }
    -        return false;
    -    }
    -}
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Parser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -22,7 +22,6 @@
     
     import com.sun.java_cup.internal.runtime.Symbol;
     import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodType;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
    @@ -46,6 +45,7 @@
     import jdk.xml.internal.JdkXmlFeatures;
     import jdk.xml.internal.JdkXmlUtils;
     import jdk.xml.internal.SecuritySupport;
    +import jdk.xml.internal.XMLSecurityManager;
     import org.xml.sax.Attributes;
     import org.xml.sax.ContentHandler;
     import org.xml.sax.InputSource;
    @@ -62,7 +62,7 @@
      * @author G. Todd Miller
      * @author Morten Jorgensen
      * @author Erwin Bolwidt 
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     public class Parser implements Constants, ContentHandler {
     
    @@ -504,8 +504,10 @@
                     XMLSecurityManager securityManager =
                             (XMLSecurityManager)_xsltc.getProperty(JdkConstants.SECURITY_MANAGER);
                     for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
    -                    lastProperty = limit.apiProperty();
    -                    reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit));
    +                    if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) {
    +                        lastProperty = limit.apiProperty();
    +                        reader.setProperty(lastProperty, securityManager.getLimitValueAsString(limit));
    +                    }
                     }
                     if (securityManager.printEntityCountInfo()) {
                         lastProperty = JdkConstants.JDK_DEBUG_LIMIT;
    @@ -1169,6 +1171,9 @@
                                                 expression, parent));
             }
             catch (Exception e) {
    +            if (ErrorMsg.XPATH_LIMIT.equals(e.getMessage())) {
    +                throw new RuntimeException(ErrorMsg.XPATH_LIMIT);
    +            }
                 if (_xsltc.debug()) e.printStackTrace();
                 reportError(ERROR, new ErrorMsg(ErrorMsg.XPATH_PARSER_ERR,
                                                 expression, parent));
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XPathParser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2005, 2022, 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,14 +34,21 @@
     import java.util.ArrayList;
     import java.util.List;
     import java.util.Stack;
    +import jdk.xml.internal.JdkConstants;
    +import jdk.xml.internal.XMLLimitAnalyzer;
    +import jdk.xml.internal.XMLSecurityManager;
    +import jdk.xml.internal.XMLSecurityManager.Limit;
     
     /**
      * CUP v0.11b generated parser.
      * This class was generated by CUP v0.11b on Nov 12, 2019.
      *
    - * @LastModified: Nov 2019
    + * @LastModified: Jan 2022
      */
     public class XPathParser extends lr_parser {
    +    private int grpLimit = 0;
    +    private int opLimit = 0;
    +    private int totalOpLimit = 0;
     
         /**
          * Default constructor.
    @@ -953,10 +960,19 @@
          */
         public SymbolTable _symbolTable;
     
    +    private XMLSecurityManager _xmlSM;
    +    private XMLLimitAnalyzer _limitAnalyzer = null;
    +
         public XPathParser(Parser parser) {
             _parser = parser;
             _xsltc = parser.getXSLTC();
             _symbolTable = parser.getSymbolTable();
    +        _xmlSM = (XMLSecurityManager)_xsltc.getProperty(JdkConstants.SECURITY_MANAGER);
    +        _limitAnalyzer = new XMLLimitAnalyzer();
    +        // no limits if _xmlSM is null
    +        grpLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_GROUP_LIMIT) : 0;
    +        opLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_OP_LIMIT) : 0;
    +        totalOpLimit = (_xmlSM != null) ? _xmlSM.getLimit(Limit.XPATH_TOTALOP_LIMIT) : 0;
         }
     
         public int getLineNumber() {
    @@ -1101,7 +1117,32 @@
             try {
                 _expression = expression;
                 _lineNumber = lineNumber;
    -            return super.parse();
    +            Symbol s = super.parse();
    +            int grpCount = getCount(ID_GROUP);
    +            int opCount = getCount(ID_OPERATOR);
    +            int totalOpCount = getCount(ID_TOTAL_OPERATOR);
    +
    +            String errCode = null;
    +            Object[] params = null;
    +            if (grpLimit > 0 && grpCount > grpLimit) {
    +                errCode = ErrorMsg.XPATH_GROUP_LIMIT;
    +                params = new Object[]{grpCount, grpLimit,
    +                    _xmlSM.getStateLiteral(Limit.XPATH_GROUP_LIMIT)};
    +            } else if (opLimit > 0 && opCount > opLimit) {
    +                errCode = ErrorMsg.XPATH_OPERATOR_LIMIT;
    +                params = new Object[]{opCount, opLimit,
    +                    _xmlSM.getStateLiteral(Limit.XPATH_OP_LIMIT)};
    +            } else if (totalOpLimit > 0 && totalOpCount > totalOpLimit) {
    +                errCode = ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT;
    +                params = new Object[]{totalOpCount, totalOpLimit,
    +                    _xmlSM.getStateLiteral(Limit.XPATH_TOTALOP_LIMIT)};
    +            }
    +            if (errCode != null) {
    +                _parser.reportError(Constants.FATAL,
    +                        new ErrorMsg(errCode, lineNumber, params));
    +                throw new RuntimeException(ErrorMsg.XPATH_LIMIT);
    +            }
    +            return s;
             } catch (IllegalCharException e) {
                 ErrorMsg err = new ErrorMsg(ErrorMsg.ILLEGAL_CHAR_ERR,
                         lineNumber, e.getMessage());
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -21,7 +21,6 @@
     package com.sun.org.apache.xalan.internal.xsltc.compiler;
     
     import com.sun.org.apache.bcel.internal.classfile.JavaClass;
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
     import com.sun.org.apache.xml.internal.dtm.DTM;
    @@ -47,8 +46,8 @@
     import javax.xml.catalog.CatalogFeatures;
     import jdk.xml.internal.JdkConstants;
     import jdk.xml.internal.JdkXmlFeatures;
    -import jdk.xml.internal.JdkXmlUtils;
     import jdk.xml.internal.SecuritySupport;
    +import jdk.xml.internal.XMLSecurityManager;
     import org.xml.sax.InputSource;
     import org.xml.sax.XMLReader;
     
    @@ -58,7 +57,7 @@
      * @author G. Todd Miller
      * @author Morten Jorgensen
      * @author John Howard (johnh@schemasoft.com)
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     public final class XSLTC {
     
    @@ -505,7 +504,10 @@
                 }
             }
             catch (Exception e) {
    -            /*if (_debug)*/ e.printStackTrace();
    +            if (_debug) e.printStackTrace();
    +            if (ErrorMsg.XPATH_LIMIT.equals(e.getMessage())) {
    +                return !_parser.errorsFound();
    +            }
                 _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
             }
             catch (Error e) {
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/sym.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2004, 2022, 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,9 +25,13 @@
     
     package com.sun.org.apache.xalan.internal.xsltc.compiler;
     
    +import java.util.Arrays;
    +
     /**
      * CUP generated class containing symbol constants.
      * This class was generated by CUP v0.10j on Fri Feb 27 13:01:50 PST 2004.
    + *
    + * @LastModified: Jan 2022
      */
     public class sym {
       /* terminals */
    @@ -85,4 +89,12 @@
       public static final int ATTRIBUTE = 41;
       public static final int GT = 19;
       public static final int NODE = 31;
    +  /*
    +    AXES: count once at DCOLON,
    +          these axes names are therefore not counted:
    +            NAMESPACE, FOLLOWINGSIBLING, CHILD, DESCENDANTORSELF, DESCENDANT
    +          , PRECEDINGSIBLING, SELF, ANCESTORORSELF, PRECEDING, ANCESTOROR, PARENT, FOLLOWING, ATTRIBUTE
    +  */
    +  public static final int[] OPERATORS = {GE, SLASH, ATSIGN, LPAREN, DCOLON,
    +      MINUS, STAR, LT, OR, DIV, PLUS, LE, VBAR, MOD, EQ, LBRACK, DOLLAR, NE, GT};
     }
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMessages.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -24,6 +24,7 @@
     
     /**
      * @author Morten Jorgensen
    + * @LastModified: Jan 2022
      */
     public class ErrorMessages extends ListResourceBundle {
     
    @@ -1027,12 +1028,22 @@
              "smaller templates."
             },
     
    -         {ErrorMsg.DESERIALIZE_TRANSLET_ERR, "When Java security is enabled, " +
    -                        "support for deserializing TemplatesImpl is disabled." +
    -                        "This can be overridden by setting the jdk.xml.enableTemplatesImplDeserialization" +
    -                        " system property to true."}
    -
    -    };
    +        {ErrorMsg.DESERIALIZE_TRANSLET_ERR, "When Java security is enabled, "
    +              + "support for deserializing TemplatesImpl is disabled. This can be "
    +              + "overridden by setting the jdk.xml.enableTemplatesImplDeserialization"
    +              + " system property to true."},
    +
    +        {ErrorMsg.XPATH_GROUP_LIMIT,
    +            "JAXP0801001: the compiler encountered an XPath expression containing "
    +              + "''{0}'' groups that exceeds the ''{1}'' limit set by ''{2}''."},
    +
    +        {ErrorMsg.XPATH_OPERATOR_LIMIT,
    +            "JAXP0801002: the compiler encountered an XPath expression containing "
    +              + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."},
    +        {ErrorMsg.XPATH_TOTAL_OPERATOR_LIMIT,
    +            "JAXP0801003: the compiler encountered XPath expressions with an accumulated "
    +              + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."},
    +      };
     
         }
     }
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/util/ErrorMsg.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -33,7 +33,7 @@
      * @author G. Todd Miller
      * @author Erwin Bolwidt 
      * @author Morten Jorgensen
    - * @LastModified: Sep 2017
    + * @LastModified: Jan 2022
      */
     public final class ErrorMsg {
     
    @@ -171,6 +171,11 @@
     
         public static final String DESERIALIZE_TRANSLET_ERR = "DESERIALIZE_TEMPLATES_ERR";
     
    +    public static final String XPATH_LIMIT = "XPATH_LIMIT";
    +    public static final String XPATH_GROUP_LIMIT = "XPATH_GROUP_LIMIT";
    +    public static final String XPATH_OPERATOR_LIMIT = "XPATH_OPERATOR_LIMIT";
    +    public static final String XPATH_TOTAL_OPERATOR_LIMIT = "XPATH_TOTAL_OPERATOR_LIMIT";
    +
         // All error messages are localized and are stored in resource bundles.
         // This array and the following 4 strings are read from that bundle.
         private static ResourceBundle _bundle;
    @@ -207,7 +212,11 @@
         public ErrorMsg(String code, int line, Object param) {
             _code = code;
             _line = line;
    -        _params = new Object[] { param };
    +        if (param instanceof Object[]) {
    +            _params = (Object[])param;
    +        } else {
    +            _params = new Object[] { param };
    +        }
         }
     
         public ErrorMsg(String code, Object param) {
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerFactoryImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -21,9 +21,9 @@
     package com.sun.org.apache.xalan.internal.xsltc.trax;
     
     import jdk.xml.internal.JdkConstants;
    +import jdk.xml.internal.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.utils.FeaturePropertyBase;
     import com.sun.org.apache.xalan.internal.utils.ObjectFactory;
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.utils.XMLSecurityPropertyManager.Property;
     import com.sun.org.apache.xalan.internal.utils.XMLSecurityPropertyManager;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.Constants;
    @@ -88,7 +88,7 @@
      * @author G. Todd Miller
      * @author Morten Jorgensen
      * @author Santiago Pericas-Geertsen
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     public class TransformerFactoryImpl
         extends SAXTransformerFactory implements SourceLoader
    @@ -286,7 +286,8 @@
             _xmlSecurityManager = new XMLSecurityManager(true);
             //Unmodifiable hash map with loaded external extension functions
             _xsltcExtensionFunctions = null;
    -        _extensionClassLoader = new JdkProperty<>(ImplPropMap.EXTCLSLOADER, null, State.DEFAULT);
    +        _extensionClassLoader = new JdkProperty<>(ImplPropMap.EXTCLSLOADER,
    +                ClassLoader.class, null, State.DEFAULT);
         }
     
         public Map> getExternalExtensionsMap() {
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TransformerImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -20,7 +20,6 @@
     
     package com.sun.org.apache.xalan.internal.xsltc.trax;
     
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.xsltc.DOM;
     import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
     import com.sun.org.apache.xalan.internal.xsltc.StripFilter;
    @@ -89,6 +88,7 @@
     import jdk.xml.internal.JdkXmlUtils;
     import jdk.xml.internal.JdkProperty.ImplPropMap;
     import jdk.xml.internal.JdkProperty.State;
    +import jdk.xml.internal.XMLSecurityManager;
     import jdk.xml.internal.SecuritySupport;
     import jdk.xml.internal.TransformErrorListener;
     import org.xml.sax.ContentHandler;
    @@ -101,7 +101,7 @@
      * @author Morten Jorgensen
      * @author G. Todd Miller
      * @author Santiago Pericas-Geertsen
    - * @LastModified: Sept 2021
    + * @LastModified: Jan 2022
      */
     public final class TransformerImpl extends Transformer
         implements DOMCache
    @@ -288,10 +288,8 @@
                 _translet.setMessageHandler(new MessageHandler(_errorListener));
             }
             _properties = createOutputProperties(outputProperties);
    -        String isStandalone = SecuritySupport.getJAXPSystemProperty(
    -                String.class, SP_XSLTC_IS_STANDALONE, "no");
             _xsltcIsStandalone = new JdkProperty<>(ImplPropMap.XSLTCISSTANDALONE,
    -                isStandalone, State.DEFAULT);
    +                String.class, "no", State.DEFAULT);
             _propertiesClone = (Properties) _properties.clone();
             _indentNumber = indentNumber;
             _tfactory = tfactory;
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/Util.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -20,7 +20,6 @@
     
     package com.sun.org.apache.xalan.internal.xsltc.trax;
     
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.XSLTC;
     import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
     import java.io.InputStream;
    @@ -39,6 +38,7 @@
     import jdk.xml.internal.JdkConstants;
     import jdk.xml.internal.JdkXmlFeatures;
     import jdk.xml.internal.JdkXmlUtils;
    +import jdk.xml.internal.XMLSecurityManager;
     import org.w3c.dom.Document;
     import org.xml.sax.InputSource;
     import org.xml.sax.SAXException;
    @@ -51,7 +51,7 @@
      *
      * Added Catalog Support for URI resolution
      *
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     @SuppressWarnings("deprecation") //org.xml.sax.helpers.XMLReaderFactory
     public final class Util {
    @@ -113,9 +113,11 @@
                                     (XMLSecurityManager)xsltc.getProperty(JdkConstants.SECURITY_MANAGER);
                             if (securityManager != null) {
                                 for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
    -                                lastProperty = limit.apiProperty();
    -                                reader.setProperty(lastProperty,
    -                                        securityManager.getLimitValueAsString(limit));
    +                                if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) {
    +                                    lastProperty = limit.apiProperty();
    +                                    reader.setProperty(lastProperty,
    +                                            securityManager.getLimitValueAsString(limit));
    +                                }
                                 }
                                 if (securityManager.printEntityCountInfo()) {
                                     lastProperty = JdkConstants.JDK_DEBUG_LIMIT;
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/parsers/AbstractDOMParser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -56,6 +56,7 @@
     import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
     import java.util.Locale;
     import java.util.Stack;
    +import jdk.xml.internal.JdkXmlUtils;
     import org.w3c.dom.Attr;
     import org.w3c.dom.CDATASection;
     import org.w3c.dom.Comment;
    @@ -84,7 +85,7 @@
      * @author Andy Clark, IBM
      * @author Elena Litani, IBM
      *
    - * @LastModified: Jan 2019
    + * @LastModified: July 2021
      */
     public class AbstractDOMParser extends AbstractXMLDocumentParser {
     
    @@ -2041,17 +2042,8 @@
                 else {
                     fInternalSubset.append (name);
                 }
    -            fInternalSubset.append (' ');
    -            if (publicId != null) {
    -                fInternalSubset.append ("PUBLIC '");
    -                fInternalSubset.append (publicId);
    -                fInternalSubset.append ("' '");
    -            }
    -            else {
    -                fInternalSubset.append ("SYSTEM '");
    -            }
    -            fInternalSubset.append (literalSystemId);
    -            fInternalSubset.append ("'>\n");
    +            fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId));
    +            fInternalSubset.append (">\n");
             }
     
             // NOTE: We only know how to create these nodes for the Xerces
    @@ -2181,20 +2173,8 @@
             if (fInternalSubset != null && !fInDTDExternalSubset) {
                 fInternalSubset.append ("\n");
             }
    @@ -2261,19 +2241,8 @@
             if (fInternalSubset != null && !fInDTDExternalSubset) {
                 fInternalSubset.append ("\n");
    +            fInternalSubset.append (JdkXmlUtils.getDTDExternalDecl(publicId, literalSystemId));
    +            fInternalSubset.append (">\n");
             }
     
             // NOTE: We only know how to create these nodes for the Xerces
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToHTMLStream.java	2022-04-19 19:55:43.000000000 +0000
    @@ -31,6 +31,7 @@
     import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
     import com.sun.org.apache.xml.internal.serializer.utils.Utils;
     import javax.xml.transform.ErrorListener;
    +import jdk.xml.internal.JdkXmlUtils;
     
     /**
      * This serializer takes a series of SAX or
    @@ -41,7 +42,7 @@
      * because it is used from another package.
      *
      * @xsl.usage internal
    - * @LastModified: June 2021
    + * @LastModified: July 2021
      */
     public final class ToHTMLStream extends ToStream
     {
    @@ -679,28 +680,10 @@
                     final java.io.Writer writer = m_writer;
                     try
                     {
    -                writer.write("');
    -                outputLineSep();
    +                    writer.write("');
    +                    outputLineSep();
                     }
                     catch(IOException e)
                     {
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java	2022-04-19 19:55:43.000000000 +0000
    @@ -54,7 +54,7 @@
      * serializers (xml, html, text ...) that write output to a stream.
      *
      * @xsl.usage internal
    - * @LastModified: June 2021
    + * @LastModified: July 2021
      */
     abstract public class ToStream extends SerializerBase {
     
    @@ -895,16 +895,8 @@
     
                 m_writer.write("");
    +            m_writer.write(JdkXmlUtils.getDTDExternalDecl(publicId, systemId));
    +            m_writer.write(">");
                 m_writer.write(m_lineSep, 0, m_lineSepLen);
             } catch (IOException e) {
                 // TODO Auto-generated catch block
    @@ -1967,27 +1959,11 @@
                 final Writer writer = m_writer;
                 writer.write("");
    @@ -1995,17 +1971,6 @@
                         closeDecl = false; // done closing
                     }
                 }
    -            boolean dothis = false;
    -            if (dothis)
    -            {
    -                // at one point this code seemed right,
    -                // but not anymore - Brian M.
    -                if (closeDecl)
    -                {
    -                    writer.write('>');
    -                    writer.write(m_lineSep, 0, m_lineSepLen);
    -                }
    -            }
             }
             catch (IOException e)
             {
    @@ -3570,16 +3535,8 @@
     
                 m_writer.write("");
    +            m_writer.write(JdkXmlUtils.getDTDExternalDecl(pubID, sysID));
    +            m_writer.write(">");
                 m_writer.write(m_lineSep, 0, m_lineSepLen);
             } catch (IOException e) {
                 // TODO Auto-generated catch block
    @@ -3600,16 +3557,8 @@
     
                 m_writer.write("");
                 m_writer.write(m_lineSep, 0, m_lineSepLen);
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/dom3/DOM3TreeWalker.java	2022-04-19 19:55:43.000000000 +0000
    @@ -63,7 +63,7 @@
      * parameters and filters if any during serialization.
      *
      * @xsl.usage internal
    - * @LastModified: Apr 2021
    + * @LastModified: July 2021
      */
     final class DOM3TreeWalker {
     
    @@ -506,23 +506,7 @@
     
                         dtd.append("(ImplPropMap.ISSTANDALONE, isStandalone, State.DEFAULT);
    +        // JDK specific property isStandalone, the default value is false
    +        fIsStandalone = new JdkProperty<>(ImplPropMap.ISSTANDALONE, Boolean.class, false, State.DEFAULT);
             // the system property is true only if it is "true" and false otherwise
    -        if (isStandalone) {
    +        if (fIsStandalone.getValue()) {
                 fFeatures |= IS_STANDALONE;
                 fDOMConfigProperties.setProperty(DOMConstants.NS_IS_STANDALONE,
                         DOMConstants.DOM3_EXPLICIT_TRUE);
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/utils/SystemIDResolver.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,6 +1,5 @@
     /*
    - * reserved comment block
    - * DO NOT REMOVE OR ALTER!
    + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -42,6 +41,8 @@
      * used in com.sun.org.apache.xml.internal.serializer.
      *
      * @xsl.usage internal
    + *
    + * @LastModified: Jan 2022
      */
     public final class SystemIDResolver
     {
    @@ -282,7 +283,7 @@
       public static String getAbsoluteURI(String urlString, String base)
               throws TransformerException
       {
    -    if (base == null)
    +    if (base == null || base.length() == 0)
           return getAbsoluteURI(urlString);
     
         String absoluteBase = getAbsoluteURI(base);
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xml/internal/utils/XMLReaderManager.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -20,7 +20,6 @@
     
     package com.sun.org.apache.xml.internal.utils;
     
    -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager;
     import java.util.HashMap;
     import javax.xml.XMLConstants;
     import javax.xml.catalog.CatalogFeatures;
    @@ -28,6 +27,7 @@
     import jdk.xml.internal.JdkXmlFeatures;
     import jdk.xml.internal.JdkXmlUtils;
     import jdk.xml.internal.SecuritySupport;
    +import jdk.xml.internal.XMLSecurityManager;
     import org.xml.sax.SAXException;
     import org.xml.sax.SAXNotRecognizedException;
     import org.xml.sax.SAXNotSupportedException;
    @@ -37,7 +37,7 @@
      * Creates XMLReader objects and caches them for re-use.
      * This class follows the singleton pattern.
      *
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     public class XMLReaderManager {
     
    @@ -143,9 +143,11 @@
             try {
                 if (_xmlSecurityManager != null) {
                     for (XMLSecurityManager.Limit limit : XMLSecurityManager.Limit.values()) {
    -                    lastProperty = limit.apiProperty();
    -                    reader.setProperty(lastProperty,
    -                            _xmlSecurityManager.getLimitValueAsString(limit));
    +                    if (limit.isSupported(XMLSecurityManager.Processor.PARSER)) {
    +                        lastProperty = limit.apiProperty();
    +                        reader.setProperty(lastProperty,
    +                                _xmlSecurityManager.getLimitValueAsString(limit));
    +                    }
                     }
                     if (_xmlSecurityManager.printEntityCountInfo()) {
                         lastProperty = JdkConstants.JDK_DEBUG_LIMIT;
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/XPath.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -22,6 +22,7 @@
     
     import com.sun.org.apache.xalan.internal.res.XSLMessages;
     import com.sun.org.apache.xml.internal.dtm.DTM;
    +import com.sun.org.apache.xml.internal.utils.DefaultErrorHandler;
     import com.sun.org.apache.xml.internal.utils.PrefixResolver;
     import com.sun.org.apache.xml.internal.utils.QName;
     import com.sun.org.apache.xml.internal.utils.SAXSourceLocator;
    @@ -35,12 +36,13 @@
     import javax.xml.transform.ErrorListener;
     import javax.xml.transform.SourceLocator;
     import javax.xml.transform.TransformerException;
    +import jdk.xml.internal.XMLSecurityManager;
     
     /**
      * The XPath class wraps an expression object and provides general services
      * for execution of that expression.
      * @xsl.usage advanced
    - * @LastModified: May 2019
    + * @LastModified: Jan 2022
      */
     public class XPath implements Serializable, ExpressionOwner
     {
    @@ -160,40 +162,11 @@
        *
        * @throws javax.xml.transform.TransformerException if syntax or other error.
        */
    -  public XPath(
    -          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
    -          ErrorListener errorListener)
    -            throws javax.xml.transform.TransformerException
    +  public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver,
    +          int type, ErrorListener errorListener)
    +            throws TransformerException
       {
    -    initFunctionTable();
    -    if(null == errorListener)
    -      errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
    -
    -    m_patternString = exprString;
    -
    -    XPathParser parser = new XPathParser(errorListener, locator);
    -    Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
    -
    -    if (SELECT == type)
    -      parser.initXPath(compiler, exprString, prefixResolver);
    -    else if (MATCH == type)
    -      parser.initMatchPattern(compiler, exprString, prefixResolver);
    -    else
    -      throw new RuntimeException(XSLMessages.createXPATHMessage(
    -              XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
    -              new Object[]{Integer.toString(type)}));
    -
    -    // System.out.println("----------------");
    -    Expression expr = compiler.compileExpression(0);
    -
    -    // System.out.println("expr: "+expr);
    -    this.setExpression(expr);
    -
    -    if((null != locator) && locator instanceof ExpressionNode)
    -    {
    -        expr.exprSetParent((ExpressionNode)locator);
    -    }
    -
    +    this(exprString, locator, prefixResolver, type, errorListener, null);
       }
     
       /**
    @@ -207,22 +180,27 @@
        *                       namespace URIs.
        * @param type one of {@link #SELECT} or {@link #MATCH}.
        * @param errorListener The error listener, or null if default should be used.
    +   * @param funcTable the function table
    +   * @param xmlSecMgr the XML security manager
        *
        * @throws javax.xml.transform.TransformerException if syntax or other error.
        */
    -  public XPath(
    -          String exprString, SourceLocator locator,
    -          PrefixResolver prefixResolver, int type,
    -          ErrorListener errorListener, FunctionTable aTable)
    -            throws javax.xml.transform.TransformerException
    +  public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver,
    +          int type, ErrorListener errorListener, FunctionTable funcTable,
    +          XMLSecurityManager xmlSecMgr)
    +            throws TransformerException
       {
    -    m_funcTable = aTable;
    +    if (funcTable == null) {
    +        initFunctionTable();
    +    } else {
    +        m_funcTable = funcTable;
    +    }
         if(null == errorListener)
    -      errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
    +      errorListener = new DefaultErrorHandler();
     
         m_patternString = exprString;
     
    -    XPathParser parser = new XPathParser(errorListener, locator);
    +    XPathParser parser = new XPathParser(errorListener, locator, xmlSecMgr);
         Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
     
         if (SELECT == type)
    @@ -261,14 +239,33 @@
        *
        * @throws javax.xml.transform.TransformerException if syntax or other error.
        */
    -  public XPath(
    -          String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
    -            throws javax.xml.transform.TransformerException
    +  public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver,
    +          int type)
    +            throws TransformerException
       {
         this(exprString, locator, prefixResolver, type, null);
       }
     
       /**
    +   * Constructs an XPath object.
    +   *
    +   * @param exprString The XPath expression.
    +   * @param locator The location of the expression, may be null.
    +   * @param prefixResolver A prefix resolver to use to resolve prefixes to
    +   *                       namespace URIs.
    +   * @param type one of {@link #SELECT} or {@link #MATCH}.
    +   * @param errorListener The error listener, or null if default should be used.
    +   * @param funcTable the function table
    +   * @throws TransformerException
    +   */
    +  public XPath(String exprString, SourceLocator locator, PrefixResolver prefixResolver,
    +          int type, ErrorListener errorListener, FunctionTable funcTable)
    +            throws TransformerException
    +  {
    +    this(exprString, locator, prefixResolver, type, errorListener, funcTable, null);
    +  }
    +
    +  /**
        * Construct an XPath object.
        *
        * @param expr The Expression object.
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Lexer.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -20,15 +20,19 @@
     
     package com.sun.org.apache.xpath.internal.compiler;
     
    +import com.sun.org.apache.xalan.internal.res.XSLMessages;
     import com.sun.org.apache.xml.internal.utils.PrefixResolver;
     import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
     import java.util.List;
    +import javax.xml.transform.TransformerException;
    +import jdk.xml.internal.XMLSecurityManager;
    +import jdk.xml.internal.XMLSecurityManager.Limit;
     
     /**
      * This class is in charge of lexical processing of the XPath
      * expression into tokens.
      *
    - * @LastModified: Nov 2017
    + * @LastModified: Jan 2022
      */
     class Lexer
     {
    @@ -70,6 +74,24 @@
        */
       private int m_patternMapSize;
     
    +  // XML security manager
    +  XMLSecurityManager m_xmlSecMgr;
    +
    +  // operator limit
    +  private int m_opCountLimit;
    +
    +  // group limit
    +  private int m_grpCountLimit;
    +
    +  // count of operators
    +  private int m_opCount;
    +
    +  // count of groups
    +  private int m_grpCount;
    +
    +  // indicate whether the current token is a literal
    +  private boolean isLiteral = false;
    +
       /**
        * Create a Lexer object.
        *
    @@ -77,14 +99,22 @@
        * @param resolver The prefix resolver for mapping qualified name prefixes
        *                 to namespace URIs.
        * @param xpathProcessor The parser that is processing strings to opcodes.
    +   * @param xmlSecMgr the XML security manager
        */
       Lexer(Compiler compiler, PrefixResolver resolver,
    -        XPathParser xpathProcessor)
    +        XPathParser xpathProcessor, XMLSecurityManager xmlSecMgr)
       {
    -
         m_compiler = compiler;
         m_namespaceContext = resolver;
         m_processor = xpathProcessor;
    +    m_xmlSecMgr = xmlSecMgr;
    +    /**
    +     * No limits if XML Security Manager is null. Applications using XPath through
    +     * the public API always have a XMLSecurityManager. Applications invoking
    +     * the internal XPath API shall consider using the public API instead.
    +     */
    +    m_opCountLimit = (xmlSecMgr != null) ? xmlSecMgr.getLimit(Limit.XPATH_OP_LIMIT) : 0;
    +    m_grpCountLimit = (xmlSecMgr != null) ? xmlSecMgr.getLimit(Limit.XPATH_GROUP_LIMIT) : 0;
       }
     
       /**
    @@ -92,7 +122,7 @@
        * elements.
        * @param pat XSLT Expression.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       void tokenize(String pat) throws javax.xml.transform.TransformerException
       {
    @@ -105,13 +135,14 @@
        * @param pat XSLT Expression.
        * @param targetStrings a list to hold Strings, may be null.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       @SuppressWarnings("fallthrough") // on purpose at case '-', '(' and default
       void tokenize(String pat, List targetStrings)
    -          throws javax.xml.transform.TransformerException
    +          throws TransformerException
       {
     
    +    boolean isGroup = false;
         m_compiler.m_currentPattern = pat;
         m_patternMapSize = 0;
     
    @@ -136,7 +167,7 @@
     
           switch (c)
           {
    -      case '\"' :
    +      case Token.DQ :
           {
             if (startSubstring != -1)
             {
    @@ -171,7 +202,7 @@
             }
           }
           break;
    -      case '\'' :
    +      case Token.SQ :
             if (startSubstring != -1)
             {
               isNum = false;
    @@ -190,9 +221,9 @@
     
             startSubstring = i;
     
    -        for (i++; (i < nChars) && ((c = pat.charAt(i)) != '\''); i++);
    +        for (i++; (i < nChars) && ((c = pat.charAt(i)) != Token.SQ); i++);
     
    -        if (c == '\'' && i < nChars)
    +        if (c == Token.SQ && i < nChars)
             {
               addToTokenQueue(pat.substring(startSubstring, i + 1));
     
    @@ -220,18 +251,24 @@
               }
               else
               {
    -            addToTokenQueue(pat.substring(startSubstring, i));
    +            // check operator symbol
    +            String s = pat.substring(startSubstring, i);
    +            if (Token.contains(s)) {
    +                m_opCount++;
    +                isLiteral = false;
    +            }
    +            addToTokenQueue(s);
               }
     
               startSubstring = -1;
             }
             break;
    -      case '@' :
    +      case Token.AT :
             isAttrName = true;
     
           // fall-through on purpose
    -      case '-' :
    -        if ('-' == c)
    +      case Token.MINUS :
    +        if (Token.MINUS == c)
             {
               if (!(isNum || (startSubstring == -1)))
               {
    @@ -242,22 +279,22 @@
             }
     
           // fall-through on purpose
    -      case '(' :
    -      case '[' :
    -      case ')' :
    -      case ']' :
    -      case '|' :
    -      case '/' :
    -      case '*' :
    -      case '+' :
    -      case '=' :
    -      case ',' :
    +      case Token.LPAREN :
    +      case Token.LBRACK :
    +      case Token.RPAREN :
    +      case Token.RBRACK :
    +      case Token.VBAR :
    +      case Token.SLASH :
    +      case Token.STAR :
    +      case Token.PLUS :
    +      case Token.EQ :
    +      case Token.COMMA :
           case '\\' :  // Unused at the moment
           case '^' :  // Unused at the moment
    -      case '!' :  // Unused at the moment
    -      case '$' :
    -      case '<' :
    -      case '>' :
    +      case Token.EM :  // Unused at the moment
    +      case Token.DOLLAR :
    +      case Token.LT :
    +      case Token.GT :
             if (startSubstring != -1)
             {
               isNum = false;
    @@ -275,11 +312,11 @@
     
               startSubstring = -1;
             }
    -        else if (('/' == c) && isStartOfPat)
    +        else if ((Token.SLASH == c) && isStartOfPat)
             {
               isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
             }
    -        else if ('*' == c)
    +        else if (Token.STAR == c)
             {
               isStartOfPat = mapPatternElemPos(nesting, isStartOfPat, isAttrName);
               isAttrName = false;
    @@ -287,7 +324,7 @@
     
             if (0 == nesting)
             {
    -          if ('|' == c)
    +          if (Token.VBAR == c)
               {
                 if (null != targetStrings)
                 {
    @@ -298,18 +335,32 @@
               }
             }
     
    -        if ((')' == c) || (']' == c))
    +        if ((Token.RPAREN == c) || (Token.RBRACK == c))
             {
               nesting--;
             }
    -        else if (('(' == c) || ('[' == c))
    +        else if ((Token.LPAREN == c) || (Token.LBRACK == c))
             {
               nesting++;
    +          if (!isLiteral && (Token.LPAREN == c)) {
    +            m_grpCount++;
    +            m_opCount++;
    +            isLiteral = false;
    +          }
    +        }
    +
    +        if ((Token.GT == c || Token.LT == c || Token.EQ == c) && Token.EQ != peekNext(pat, i)) {
    +            m_opCount++;
    +            isLiteral = false;
    +        }
    +        else if ((Token.LPAREN != c) && (Token.RPAREN != c) && (Token.RBRACK != c)) {
    +            m_opCount++;
    +            isLiteral = false;
             }
     
             addToTokenQueue(pat.substring(i, i + 1));
             break;
    -      case ':' :
    +      case Token.COLON_CHAR:
             if (i>0)
             {
               if (posOfNSSep == (i - 1))
    @@ -324,7 +375,7 @@
                 isAttrName = false;
                 startSubstring = -1;
                 posOfNSSep = -1;
    -
    +            m_opCount++;
                 addToTokenQueue(pat.substring(i - 1, i + 1));
     
                 break;
    @@ -337,6 +388,7 @@
     
           // fall through on purpose
           default :
    +        isLiteral = true;
             if (-1 == startSubstring)
             {
               startSubstring = i;
    @@ -347,6 +399,20 @@
               isNum = Character.isDigit(c);
             }
           }
    +      if (m_grpCountLimit > 0 && m_grpCount > m_grpCountLimit) {
    +          throw new TransformerException(XSLMessages.createXPATHMessage(
    +            XPATHErrorResources.ER_XPATH_GROUP_LIMIT,
    +            new Object[]{Integer.toString(m_grpCount),
    +                Integer.toString(m_grpCountLimit),
    +                m_xmlSecMgr.getStateLiteral(Limit.XPATH_GROUP_LIMIT)}));
    +      }
    +      if (m_opCountLimit > 0 && m_opCount > m_opCountLimit) {
    +          throw new TransformerException(XSLMessages.createXPATHMessage(
    +            XPATHErrorResources.ER_XPATH_OPERATOR_LIMIT,
    +            new Object[]{Integer.toString(m_opCount),
    +                Integer.toString(m_opCountLimit),
    +                m_xmlSecMgr.getStateLiteral(Limit.XPATH_OP_LIMIT)}));
    +      }
         }
     
         if (startSubstring != -1)
    @@ -378,6 +444,19 @@
       }
     
       /**
    +   * Peeks at the next character without advancing the index.
    +   * @param s the input string
    +   * @param index the current index
    +   * @return the next char
    +   */
    +  private char peekNext(String s, int index) {
    +      if (index >= 0 && index < s.length() - 1) {
    +          return s.charAt(index + 1);
    +      }
    +      return 0;
    +  }
    +
    +  /**
        * Record the current position on the token queue as long as
        * this is a top-level element.  Must be called before the
        * next token is added to the m_tokenQueue.
    @@ -499,7 +578,7 @@
     
         resetTokenMark(tokPos + 1);
     
    -    if (m_processor.lookahead('(', 1))
    +    if (m_processor.lookahead(Token.LPAREN, 1))
         {
           int tok = getKeywordToken(m_processor.m_token);
     
    @@ -529,14 +608,14 @@
         }
         else
         {
    -      if (m_processor.tokenIs('@'))
    +      if (m_processor.tokenIs(Token.AT))
           {
             tokPos++;
     
             resetTokenMark(tokPos + 1);
           }
     
    -      if (m_processor.lookahead(':', 1))
    +      if (m_processor.lookahead(Token.COLON_CHAR, 1))
           {
             tokPos += 2;
           }
    @@ -565,13 +644,13 @@
        * @param posOfNSSep The position of the namespace seperator (':').
        * @param posOfScan The end of the name index.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        *
        * @return -1 always.
        */
       private int mapNSTokens(String pat, int startSubstring, int posOfNSSep,
                               int posOfScan)
    -           throws javax.xml.transform.TransformerException
    +           throws TransformerException
      {
     
         String prefix = "";
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java	1970-01-01 00:00:00.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/Token.java	2022-04-19 19:55:43.000000000 +0000
    @@ -0,0 +1,76 @@
    +/*
    + * Copyright (c) 2022, 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 com.sun.org.apache.xpath.internal.compiler;
    +
    +import java.util.Arrays;
    +
    +/**
    + * XPath tokens
    + */
    +public final class Token {
    +    static final char EM = '!';
    +    static final char EQ = '=';
    +    static final char LT = '<';
    +    static final char GT = '>';
    +    static final char PLUS = '+';
    +    static final char MINUS = '-';
    +    static final char STAR = '*';
    +    static final char VBAR = '|';
    +    static final char SLASH = '/';
    +    static final char LBRACK = '[';
    +    static final char RBRACK = ']';
    +    static final char LPAREN = '(';
    +    static final char RPAREN = ')';
    +    static final char COMMA = ',';
    +    static final char AT = '@';
    +    static final char US = '_';
    +    static final char COLON_CHAR = ':';
    +    static final char SQ = '\'';
    +    static final char DQ = '"';
    +    static final char DOLLAR = '$';
    +
    +    static final String OR = "or";
    +    static final String AND = "and";
    +    static final String DIV = "div";
    +    static final String MOD = "mod";
    +    static final String QUO = "quo";
    +    static final String DOT = ".";
    +    static final String DDOT = "..";
    +    static final String DCOLON = "::";
    +    static final String ATTR = "attribute";
    +    static final String CHILD = "child";
    +
    +    static final String[] OPERATORS = {OR, AND, DIV, MOD, QUO,
    +        DDOT, DCOLON, ATTR, CHILD};
    +
    +    public static boolean contains(String str) {
    +        return Arrays.stream(OPERATORS).anyMatch(str::equals);
    +    }
    +
    +    private Token() {
    +        //to prevent instantiation
    +    }
    +}
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/compiler/XPathParser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -20,21 +20,22 @@
     
     package com.sun.org.apache.xpath.internal.compiler;
     
    -import javax.xml.transform.ErrorListener;
    -import javax.xml.transform.TransformerException;
    -
     import com.sun.org.apache.xalan.internal.res.XSLMessages;
     import com.sun.org.apache.xml.internal.utils.PrefixResolver;
     import com.sun.org.apache.xpath.internal.XPathProcessorException;
     import com.sun.org.apache.xpath.internal.objects.XNumber;
     import com.sun.org.apache.xpath.internal.objects.XString;
     import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
    +import javax.xml.transform.ErrorListener;
    +import javax.xml.transform.SourceLocator;
    +import javax.xml.transform.TransformerException;
    +import jdk.xml.internal.XMLSecurityManager;
     
     /**
      * Tokenizes and parses XPath expressions. This should really be named
      * XPathParserImpl, and may be renamed in the future.
      * @xsl.usage general
    - * @LastModified: May 2019
    + * @LastModified: Jan 2022
      */
     public class XPathParser
     {
    @@ -75,13 +76,18 @@
       // counts open predicates
       private int countPredicate;
     
    +  // XML security manager
    +  XMLSecurityManager m_xmlSecMgr;
    +
       /**
        * The parser constructor.
        */
    -  public XPathParser(ErrorListener errorListener, javax.xml.transform.SourceLocator sourceLocator)
    +  public XPathParser(ErrorListener errorListener, SourceLocator sourceLocator,
    +          XMLSecurityManager xmlSecMgr)
       {
         m_errorListener = errorListener;
         m_sourceLocator = sourceLocator;
    +    m_xmlSecMgr = xmlSecMgr;
       }
     
       /**
    @@ -99,18 +105,18 @@
        * @param namespaceContext An object that is able to resolve prefixes in
        * the XPath to namespaces.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       public void initXPath(
               Compiler compiler, String expression, PrefixResolver namespaceContext)
    -            throws javax.xml.transform.TransformerException
    +            throws TransformerException
       {
     
         m_ops = compiler;
         m_namespaceContext = namespaceContext;
         m_functionTable = compiler.getFunctionTable();
     
    -    Lexer lexer = new Lexer(compiler, namespaceContext, this);
    +    Lexer lexer = new Lexer(compiler, namespaceContext, this, m_xmlSecMgr);
     
         lexer.tokenize(expression);
     
    @@ -178,18 +184,18 @@
        * @param namespaceContext An object that is able to resolve prefixes in
        * the XPath to namespaces.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       public void initMatchPattern(
               Compiler compiler, String expression, PrefixResolver namespaceContext)
    -            throws javax.xml.transform.TransformerException
    +            throws TransformerException
       {
     
         m_ops = compiler;
         m_namespaceContext = namespaceContext;
         m_functionTable = compiler.getFunctionTable();
     
    -    Lexer lexer = new Lexer(compiler, namespaceContext, this);
    +    Lexer lexer = new Lexer(compiler, namespaceContext, this, m_xmlSecMgr);
     
         lexer.tokenize(expression);
     
    @@ -382,9 +388,9 @@
         if ((m_queueMark - n) > 0)
         {
           String lookbehind = (String) m_ops.m_tokenQueue.elementAt(m_queueMark - (n - 1));
    -      char c0 = (lookbehind == null) ? '|' : lookbehind.charAt(0);
    +      char c0 = (lookbehind == null) ? Token.VBAR : lookbehind.charAt(0);
     
    -      hasToken = (c0 == '|') ? false : true;
    +      hasToken = (c0 == Token.VBAR) ? false : true;
         }
         else
         {
    @@ -496,10 +502,10 @@
        *
        * @param expected The string to be expected.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       private final void consumeExpected(String expected)
    -          throws javax.xml.transform.TransformerException
    +          throws TransformerException
       {
     
         if (tokenIs(expected))
    @@ -524,10 +530,10 @@
        *
        * @param expected the character to be expected.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       private final void consumeExpected(char expected)
    -          throws javax.xml.transform.TransformerException
    +          throws TransformerException
       {
     
         if (tokenIs(expected))
    @@ -749,9 +755,9 @@
        * Expr  ::=  OrExpr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Expr() throws javax.xml.transform.TransformerException
    +  protected void Expr() throws TransformerException
       {
            OrExpr();
       }
    @@ -763,16 +769,16 @@
        * | OrExpr 'or' AndExpr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void OrExpr() throws javax.xml.transform.TransformerException
    +  protected void OrExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
         AndExpr();
     
    -    if ((null != m_token) && tokenIs("or"))
    +    if ((null != m_token) && tokenIs(Token.OR))
         {
           nextToken();
           insertOp(opPos, 2, OpCodes.OP_OR);
    @@ -790,16 +796,16 @@
        * | AndExpr 'and' EqualityExpr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void AndExpr() throws javax.xml.transform.TransformerException
    +  protected void AndExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
         EqualityExpr(-1);
     
    -    if ((null != m_token) && tokenIs("and"))
    +    if ((null != m_token) && tokenIs(Token.AND))
         {
           nextToken();
           insertOp(opPos, 2, OpCodes.OP_AND);
    @@ -823,9 +829,9 @@
        *
        * @return the position at the end of the equality expression.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int EqualityExpr(int addPos) throws javax.xml.transform.TransformerException
    +  protected int EqualityExpr(int addPos) throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -837,7 +843,7 @@
     
         if (null != m_token)
         {
    -      if (tokenIs('!') && lookahead('=', 1))
    +      if (tokenIs(Token.EM) && lookahead(Token.EQ, 1))
           {
             nextToken();
             nextToken();
    @@ -850,7 +856,7 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs('='))
    +      else if (tokenIs(Token.EQ))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_EQUALS);
    @@ -883,9 +889,9 @@
        *
        * @return the position at the end of the relational expression.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int RelationalExpr(int addPos) throws javax.xml.transform.TransformerException
    +  protected int RelationalExpr(int addPos) throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -897,11 +903,11 @@
     
         if (null != m_token)
         {
    -      if (tokenIs('<'))
    +      if (tokenIs(Token.LT))
           {
             nextToken();
     
    -        if (tokenIs('='))
    +        if (tokenIs(Token.EQ))
             {
               nextToken();
               insertOp(addPos, 2, OpCodes.OP_LTE);
    @@ -918,11 +924,11 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs('>'))
    +      else if (tokenIs(Token.GT))
           {
             nextToken();
     
    -        if (tokenIs('='))
    +        if (tokenIs(Token.EQ))
             {
               nextToken();
               insertOp(addPos, 2, OpCodes.OP_GTE);
    @@ -958,9 +964,9 @@
        *
        * @return the position at the end of the equality expression.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int AdditiveExpr(int addPos) throws javax.xml.transform.TransformerException
    +  protected int AdditiveExpr(int addPos) throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -972,7 +978,7 @@
     
         if (null != m_token)
         {
    -      if (tokenIs('+'))
    +      if (tokenIs(Token.PLUS))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_PLUS);
    @@ -984,7 +990,7 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs('-'))
    +      else if (tokenIs(Token.MINUS))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_MINUS);
    @@ -1016,9 +1022,9 @@
        *
        * @return the position at the end of the equality expression.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int MultiplicativeExpr(int addPos) throws javax.xml.transform.TransformerException
    +  protected int MultiplicativeExpr(int addPos) throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1030,7 +1036,7 @@
     
         if (null != m_token)
         {
    -      if (tokenIs('*'))
    +      if (tokenIs(Token.STAR))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_MULT);
    @@ -1042,7 +1048,7 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs("div"))
    +      else if (tokenIs(Token.DIV))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_DIV);
    @@ -1054,7 +1060,7 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs("mod"))
    +      else if (tokenIs(Token.MOD))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_MOD);
    @@ -1066,7 +1072,7 @@
               m_ops.getOp(addPos + opPlusLeftHandLen + 1) + opPlusLeftHandLen);
             addPos += 2;
           }
    -      else if (tokenIs("quo"))
    +      else if (tokenIs(Token.QUO))
           {
             nextToken();
             insertOp(addPos, 2, OpCodes.OP_QUO);
    @@ -1089,15 +1095,15 @@
        * | '-' UnaryExpr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void UnaryExpr() throws javax.xml.transform.TransformerException
    +  protected void UnaryExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
         boolean isNeg = false;
     
    -    if (m_tokenChar == '-')
    +    if (m_tokenChar == Token.MINUS)
         {
           nextToken();
           appendOp(2, OpCodes.OP_NEG);
    @@ -1117,9 +1123,9 @@
        * StringExpr  ::=  Expr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void StringExpr() throws javax.xml.transform.TransformerException
    +  protected void StringExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1137,9 +1143,9 @@
        * StringExpr  ::=  Expr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void BooleanExpr() throws javax.xml.transform.TransformerException
    +  protected void BooleanExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1163,9 +1169,9 @@
        * NumberExpr  ::=  Expr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void NumberExpr() throws javax.xml.transform.TransformerException
    +  protected void NumberExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1188,9 +1194,9 @@
        * | UnionExpr '|' PathExpr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void UnionExpr() throws javax.xml.transform.TransformerException
    +  protected void UnionExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1201,7 +1207,7 @@
         {
           PathExpr();
     
    -      if (tokenIs('|'))
    +      if (tokenIs(Token.VBAR))
           {
             if (false == foundUnion)
             {
    @@ -1234,9 +1240,9 @@
        * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
        * the error condition is severe enough to halt processing.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void PathExpr() throws javax.xml.transform.TransformerException
    +  protected void PathExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1249,7 +1255,7 @@
           // have been inserted.
           boolean locationPathStarted = (filterExprMatch==FILTER_MATCH_PREDICATES);
     
    -      if (tokenIs('/'))
    +      if (tokenIs(Token.SLASH))
           {
             nextToken();
     
    @@ -1299,9 +1305,9 @@
        *          FilterExpr that was just a PrimaryExpr; or
        *          FILTER_MATCH_FAILED, if this method did not match a FilterExpr
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int FilterExpr() throws javax.xml.transform.TransformerException
    +  protected int FilterExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1310,13 +1316,13 @@
     
         if (PrimaryExpr())
         {
    -      if (tokenIs('['))
    +      if (tokenIs(Token.LBRACK))
           {
     
             // int locationPathOpPos = opPos;
             insertOp(opPos, 2, OpCodes.OP_LOCATIONPATH);
     
    -        while (tokenIs('['))
    +        while (tokenIs(Token.LBRACK))
             {
               Predicate();
             }
    @@ -1354,16 +1360,16 @@
        *
        * @return true if this method successfully matched a PrimaryExpr
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        *
        */
    -  protected boolean PrimaryExpr() throws javax.xml.transform.TransformerException
    +  protected boolean PrimaryExpr() throws TransformerException
       {
     
         boolean matchFound;
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
    -    if ((m_tokenChar == '\'') || (m_tokenChar == '"'))
    +    if ((m_tokenChar == Token.SQ) || (m_tokenChar == Token.DQ))
         {
           appendOp(2, OpCodes.OP_LITERAL);
           Literal();
    @@ -1373,7 +1379,7 @@
     
           matchFound = true;
         }
    -    else if (m_tokenChar == '$')
    +    else if (m_tokenChar == Token.DOLLAR)
         {
           nextToken();  // consume '$'
           appendOp(2, OpCodes.OP_VARIABLE);
    @@ -1384,12 +1390,12 @@
     
           matchFound = true;
         }
    -    else if (m_tokenChar == '(')
    +    else if (m_tokenChar == Token.LPAREN)
         {
           nextToken();
           appendOp(2, OpCodes.OP_GROUP);
           Expr();
    -      consumeExpected(')');
    +      consumeExpected(Token.RPAREN);
     
           m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH,
             m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
    @@ -1407,7 +1413,7 @@
     
           matchFound = true;
         }
    -    else if (lookahead('(', 1) || (lookahead(':', 1) && lookahead('(', 3)))
    +    else if (lookahead(Token.LPAREN, 1) || (lookahead(Token.COLON_CHAR, 1) && lookahead(Token.LPAREN, 3)))
         {
           matchFound = FunctionCall();
         }
    @@ -1424,9 +1430,9 @@
        * Argument    ::=    Expr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Argument() throws javax.xml.transform.TransformerException
    +  protected void Argument() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1444,14 +1450,14 @@
        *
        * @return true if, and only if, a FunctionCall was matched
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected boolean FunctionCall() throws javax.xml.transform.TransformerException
    +  protected boolean FunctionCall() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
    -    if (lookahead(':', 1))
    +    if (lookahead(Token.COLON_CHAR, 1))
         {
           appendOp(4, OpCodes.OP_EXTFUNCTION);
     
    @@ -1491,22 +1497,23 @@
           nextToken();
         }
     
    -    consumeExpected('(');
    +    consumeExpected(Token.LPAREN);
     
    -    while (!tokenIs(')') && m_token != null)
    +    while (!tokenIs(Token.RPAREN) && m_token != null)
         {
    -      if (tokenIs(','))
    +      if (tokenIs(Token.COMMA))
           {
    -        error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null);  //"Found ',' but no preceding argument!");
    +        //"Found ',' but no preceding argument!");
    +        error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_PRECEDING_ARG, null);
           }
     
           Argument();
     
    -      if (!tokenIs(')'))
    +      if (!tokenIs(Token.RPAREN))
           {
    -        consumeExpected(',');
    +        consumeExpected(Token.COMMA);
     
    -        if (tokenIs(')'))
    +        if (tokenIs(Token.RPAREN))
             {
               error(XPATHErrorResources.ER_FOUND_COMMA_BUT_NO_FOLLOWING_ARG,
                     null);  //"Found ',' but no following argument!");
    @@ -1514,7 +1521,7 @@
           }
         }
     
    -    consumeExpected(')');
    +    consumeExpected(Token.RPAREN);
     
         // Terminate for safety.
         m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ENDOP);
    @@ -1533,9 +1540,9 @@
        * | AbsoluteLocationPath
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void LocationPath() throws javax.xml.transform.TransformerException
    +  protected void LocationPath() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1543,7 +1550,7 @@
         // int locationPathOpPos = opPos;
         appendOp(2, OpCodes.OP_LOCATIONPATH);
     
    -    boolean seenSlash = tokenIs('/');
    +    boolean seenSlash = tokenIs(Token.SLASH);
     
         if (seenSlash)
         {
    @@ -1584,17 +1591,17 @@
        *
        * @returns true if, and only if, a RelativeLocationPath was matched
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       protected boolean RelativeLocationPath()
    -               throws javax.xml.transform.TransformerException
    +               throws TransformerException
       {
         if (!Step())
         {
           return false;
         }
     
    -    while (tokenIs('/'))
    +    while (tokenIs(Token.SLASH))
         {
           nextToken();
     
    @@ -1616,13 +1623,13 @@
        *
        * @returns false if step was empty (or only a '/'); true, otherwise
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected boolean Step() throws javax.xml.transform.TransformerException
    +  protected boolean Step() throws TransformerException
       {
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
     
    -    boolean doubleSlash = tokenIs('/');
    +    boolean doubleSlash = tokenIs(Token.SLASH);
     
         // At most a single '/' before each Step is consumed by caller; if the
         // first thing is a '/', that means we had '//' and the Step must not
    @@ -1654,11 +1661,11 @@
           opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
         }
     
    -    if (tokenIs("."))
    +    if (tokenIs(Token.DOT))
         {
           nextToken();
     
    -      if (tokenIs('['))
    +      if (tokenIs(Token.LBRACK))
           {
             error(XPATHErrorResources.ER_PREDICATE_ILLEGAL_SYNTAX, null);  //"'..[predicate]' or '.[predicate]' is illegal syntax.  Use 'self::node()[predicate]' instead.");
           }
    @@ -1669,7 +1676,7 @@
           m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 2,4);
           m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH) - 1, OpCodes.NODETYPE_NODE);
         }
    -    else if (tokenIs(".."))
    +    else if (tokenIs(Token.DDOT))
         {
           nextToken();
           appendOp(4, OpCodes.FROM_PARENT);
    @@ -1682,12 +1689,12 @@
         // There is probably a better way to test for this
         // transition... but it gets real hairy if you try
         // to do it in basis().
    -    else if (tokenIs('*') || tokenIs('@') || tokenIs('_')
    +    else if (tokenIs(Token.STAR) || tokenIs(Token.AT) || tokenIs(Token.US)
                  || (m_token!= null && Character.isLetter(m_token.charAt(0))))
         {
           Basis();
     
    -      while (tokenIs('['))
    +      while (tokenIs(Token.LBRACK))
           {
             Predicate();
           }
    @@ -1716,23 +1723,23 @@
        * Basis    ::=    AxisName '::' NodeTest
        * | AbbreviatedBasis
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Basis() throws javax.xml.transform.TransformerException
    +  protected void Basis() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
         int axesType;
     
         // The next blocks guarantee that a FROM_XXX will be added.
    -    if (lookahead("::", 1))
    +    if (lookahead(Token.DCOLON, 1))
         {
           axesType = AxisName();
     
           nextToken();
           nextToken();
         }
    -    else if (tokenIs('@'))
    +    else if (tokenIs(Token.AT))
         {
           axesType = OpCodes.FROM_ATTRIBUTES;
     
    @@ -1763,9 +1770,9 @@
        *
        * @return FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected int AxisName() throws javax.xml.transform.TransformerException
    +  protected int AxisName() throws TransformerException
       {
     
         Object val = Keywords.getAxisName(m_token);
    @@ -1791,12 +1798,12 @@
        *
        * @param axesType FROM_XXX axes type, found in {@link com.sun.org.apache.xpath.internal.compiler.Keywords}.
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void NodeTest(int axesType) throws javax.xml.transform.TransformerException
    +  protected void NodeTest(int axesType) throws TransformerException
       {
     
    -    if (lookahead('(', 1))
    +    if (lookahead(Token.LPAREN, 1))
         {
           Object nodeTestOp = Keywords.getNodeType(m_token);
     
    @@ -1814,17 +1821,17 @@
             m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), nt);
             m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
     
    -        consumeExpected('(');
    +        consumeExpected(Token.LPAREN);
     
             if (OpCodes.NODETYPE_PI == nt)
             {
    -          if (!tokenIs(')'))
    +          if (!tokenIs(Token.RPAREN))
               {
                 Literal();
               }
             }
     
    -        consumeExpected(')');
    +        consumeExpected(Token.RPAREN);
           }
         }
         else
    @@ -1834,9 +1841,9 @@
           m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.NODENAME);
           m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
     
    -      if (lookahead(':', 1))
    +      if (lookahead(Token.COLON_CHAR, 1))
           {
    -        if (tokenIs('*'))
    +        if (tokenIs(Token.STAR))
             {
               m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
             }
    @@ -1846,7 +1853,7 @@
     
               // Minimalist check for an NCName - just check first character
               // to distinguish from other possible tokens
    -          if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
    +          if (!Character.isLetter(m_tokenChar) && !tokenIs(Token.US))
               {
                 // "Node test that matches either NCName:* or QName was expected."
                 error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
    @@ -1863,7 +1870,7 @@
     
           m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
     
    -      if (tokenIs('*'))
    +      if (tokenIs(Token.STAR))
           {
             m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), OpCodes.ELEMWILDCARD);
           }
    @@ -1873,7 +1880,7 @@
     
             // Minimalist check for an NCName - just check first character
             // to distinguish from other possible tokens
    -        if (!Character.isLetter(m_tokenChar) && !tokenIs('_'))
    +        if (!Character.isLetter(m_tokenChar) && !tokenIs(Token.US))
             {
               // "Node test that matches either NCName:* or QName was expected."
               error(XPATHErrorResources.ER_EXPECTED_NODE_TEST, null);
    @@ -1891,11 +1898,11 @@
        * Predicate ::= '[' PredicateExpr ']'
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Predicate() throws javax.xml.transform.TransformerException
    +  protected void Predicate() throws TransformerException
       {
    -    if (tokenIs('['))
    +    if (tokenIs(Token.LBRACK))
         {
           countPredicate++;
           nextToken();
    @@ -1910,9 +1917,9 @@
        * PredicateExpr ::= Expr
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void PredicateExpr() throws javax.xml.transform.TransformerException
    +  protected void PredicateExpr() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -1932,12 +1939,12 @@
        * Prefix ::=  NCName
        * LocalPart ::=  NCName
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void QName() throws javax.xml.transform.TransformerException
    +  protected void QName() throws TransformerException
       {
         // Namespace
    -    if(lookahead(':', 1))
    +    if(lookahead(Token.COLON_CHAR, 1))
         {
           m_ops.setOp(m_ops.getOp(OpMap.MAPINDEX_LENGTH), m_queueMark - 1);
           m_ops.setOp(OpMap.MAPINDEX_LENGTH, m_ops.getOp(OpMap.MAPINDEX_LENGTH) + 1);
    @@ -1979,16 +1986,16 @@
        * | "'" [^']* "'"
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Literal() throws javax.xml.transform.TransformerException
    +  protected void Literal() throws TransformerException
       {
     
         int last = m_token.length() - 1;
         char c0 = m_tokenChar;
         char cX = m_token.charAt(last);
     
    -    if (((c0 == '\"') && (cX == '\"')) || ((c0 == '\'') && (cX == '\'')))
    +    if (((c0 == Token.DQ) && (cX == Token.DQ)) || ((c0 == Token.SQ) && (cX == Token.SQ)))
         {
     
           // Mutate the token to remove the quotes and have the XString object
    @@ -2019,9 +2026,9 @@
        * Number ::= [0-9]+('.'[0-9]+)? | '.'[0-9]+
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Number() throws javax.xml.transform.TransformerException
    +  protected void Number() throws TransformerException
       {
     
         if (null != m_token)
    @@ -2062,16 +2069,16 @@
        * | Pattern '|' LocationPathPattern
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void Pattern() throws javax.xml.transform.TransformerException
    +  protected void Pattern() throws TransformerException
       {
     
         while (true)
         {
           LocationPathPattern();
     
    -      if (tokenIs('|'))
    +      if (tokenIs(Token.VBAR))
           {
             nextToken();
           }
    @@ -2090,9 +2097,9 @@
        * | '//'? RelativePathPattern
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void LocationPathPattern() throws javax.xml.transform.TransformerException
    +  protected void LocationPathPattern() throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -2105,17 +2112,17 @@
     
         appendOp(2, OpCodes.OP_LOCATIONPATHPATTERN);
     
    -    if (lookahead('(', 1)
    +    if (lookahead(Token.LPAREN, 1)
                 && (tokenIs(Keywords.FUNC_ID_STRING)
                     || tokenIs(Keywords.FUNC_KEY_STRING)))
         {
           IdKeyPattern();
     
    -      if (tokenIs('/'))
    +      if (tokenIs(Token.SLASH))
           {
             nextToken();
     
    -        if (tokenIs('/'))
    +        if (tokenIs(Token.SLASH))
             {
               appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
     
    @@ -2133,9 +2140,9 @@
             relativePathStatus = RELATIVE_PATH_REQUIRED;
           }
         }
    -    else if (tokenIs('/'))
    +    else if (tokenIs(Token.SLASH))
         {
    -      if (lookahead('/', 1))
    +      if (lookahead(Token.SLASH, 1))
           {
             appendOp(4, OpCodes.MATCH_ANY_ANCESTOR);
     
    @@ -2168,7 +2175,7 @@
     
         if (relativePathStatus != RELATIVE_PATH_NOT_PERMITTED)
         {
    -      if (!tokenIs('|') && (null != m_token))
    +      if (!tokenIs(Token.VBAR) && (null != m_token))
           {
             RelativePathPattern();
           }
    @@ -2193,9 +2200,9 @@
        * (Also handle doc())
        *
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
    -  protected void IdKeyPattern() throws javax.xml.transform.TransformerException
    +  protected void IdKeyPattern() throws TransformerException
       {
         FunctionCall();
       }
    @@ -2206,17 +2213,17 @@
        * | RelativePathPattern '/' StepPattern
        * | RelativePathPattern '//' StepPattern
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       protected void RelativePathPattern()
    -              throws javax.xml.transform.TransformerException
    +              throws TransformerException
       {
     
         // Caller will have consumed any '/' or '//' preceding the
         // RelativePathPattern, so let StepPattern know it can't begin with a '/'
         boolean trailingSlashConsumed = StepPattern(false);
     
    -    while (tokenIs('/'))
    +    while (tokenIs(Token.SLASH))
         {
           nextToken();
     
    @@ -2236,10 +2243,10 @@
        *
        * @return boolean indicating whether a slash following the step was consumed
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       protected boolean StepPattern(boolean isLeadingSlashPermitted)
    -            throws javax.xml.transform.TransformerException
    +            throws TransformerException
       {
         return AbbreviatedNodeTestStep(isLeadingSlashPermitted);
       }
    @@ -2253,10 +2260,10 @@
        *
        * @return boolean indicating whether a slash following the step was consumed
        *
    -   * @throws javax.xml.transform.TransformerException
    +   * @throws TransformerException
        */
       protected boolean AbbreviatedNodeTestStep(boolean isLeadingSlashPermitted)
    -            throws javax.xml.transform.TransformerException
    +            throws TransformerException
       {
     
         int opPos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
    @@ -2265,22 +2272,22 @@
         // The next blocks guarantee that a MATCH_XXX will be added.
         int matchTypePos = -1;
     
    -    if (tokenIs('@'))
    +    if (tokenIs(Token.AT))
         {
           axesType = OpCodes.MATCH_ATTRIBUTE;
     
           appendOp(2, axesType);
           nextToken();
         }
    -    else if (this.lookahead("::", 1))
    +    else if (this.lookahead(Token.DCOLON, 1))
         {
    -      if (tokenIs("attribute"))
    +      if (tokenIs(Token.ATTR))
           {
             axesType = OpCodes.MATCH_ATTRIBUTE;
     
             appendOp(2, axesType);
           }
    -      else if (tokenIs("child"))
    +      else if (tokenIs(Token.CHILD))
           {
             matchTypePos = m_ops.getOp(OpMap.MAPINDEX_LENGTH);
             axesType = OpCodes.MATCH_IMMEDIATE_ANCESTOR;
    @@ -2298,7 +2305,7 @@
           nextToken();
           nextToken();
         }
    -    else if (tokenIs('/'))
    +    else if (tokenIs(Token.SLASH))
         {
           if (!isLeadingSlashPermitted)
           {
    @@ -2327,7 +2334,7 @@
         m_ops.setOp(opPos + OpMap.MAPINDEX_LENGTH + 1,
           m_ops.getOp(OpMap.MAPINDEX_LENGTH) - opPos);
     
    -    while (tokenIs('['))
    +    while (tokenIs(Token.LBRACK))
         {
           Predicate();
         }
    @@ -2346,7 +2353,7 @@
         // If current step is on the attribute axis (e.g., "@x//b"), we won't
         // change the current step, and let following step be marked as
         // MATCH_ANY_ANCESTOR on next call instead.
    -    if ((matchTypePos > -1) && tokenIs('/') && lookahead('/', 1))
    +    if ((matchTypePos > -1) && tokenIs(Token.SLASH) && lookahead(Token.SLASH, 1))
         {
           m_ops.setOp(matchTypePos, OpCodes.MATCH_ANY_ANCESTOR);
     
    diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java
    --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathFactoryImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
      */
     /*
      * Licensed to the Apache Software Foundation (ASF) under one or more
    @@ -30,13 +30,14 @@
     import jdk.xml.internal.JdkConstants;
     import jdk.xml.internal.JdkProperty;
     import jdk.xml.internal.JdkXmlFeatures;
    +import jdk.xml.internal.XMLSecurityManager;
     
     /**
      * The XPathFactory builds XPaths.
      *
      * @author  Ramesh Mandava
      *
    - * @LastModified: May 2021
    + * @LastModified: Jan 2022
      */
     public  class XPathFactoryImpl extends XPathFactory {
     
    @@ -70,6 +71,11 @@
             private final JdkXmlFeatures _featureManager;
     
             /**
    +         * The XML security manager
    +         */
    +        private XMLSecurityManager _xmlSecMgr;
    +
    +        /**
              * javax.xml.xpath.XPathFactory implementation.
              */
             @SuppressWarnings("removal")
    @@ -79,7 +85,9 @@
                     _isNotSecureProcessing = false;
                 }
                 _featureManager = new JdkXmlFeatures(!_isNotSecureProcessing);
    +            _xmlSecMgr = new XMLSecurityManager(true);
             }
    +
             /**
              * 

    Is specified object model supported by this * XPathFactory?

    @@ -126,9 +134,8 @@ * @return New XPath */ public javax.xml.xpath.XPath newXPath() { - return new com.sun.org.apache.xpath.internal.jaxp.XPathImpl( - xPathVariableResolver, xPathFunctionResolver, - !_isNotSecureProcessing, _featureManager ); + return new XPathImpl(xPathVariableResolver, xPathFunctionResolver, + !_isNotSecureProcessing, _featureManager, _xmlSecMgr); } /** diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -32,6 +32,7 @@ import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; import jdk.xml.internal.JdkXmlFeatures; +import jdk.xml.internal.XMLSecurityManager; import org.w3c.dom.Document; import org.xml.sax.InputSource; @@ -45,6 +46,8 @@ * Updated 12/04/2014: * New methods: evaluateExpression * Refactored to share code with XPathExpressionImpl. + * + * @LastModified: Jan 2022 */ public class XPathImpl extends XPathImplUtil implements javax.xml.xpath.XPath { @@ -54,18 +57,19 @@ private NamespaceContext namespaceContext=null; XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr) { - this(vr, fr, false, new JdkXmlFeatures(false)); + this(vr, fr, false, new JdkXmlFeatures(false), new XMLSecurityManager(true)); } XPathImpl(XPathVariableResolver vr, XPathFunctionResolver fr, - boolean featureSecureProcessing, JdkXmlFeatures featureManager) { + boolean featureSecureProcessing, JdkXmlFeatures featureManager, + XMLSecurityManager xmlSecMgr) { this.origVariableResolver = this.variableResolver = vr; this.origFunctionResolver = this.functionResolver = fr; this.featureSecureProcessing = featureSecureProcessing; this.featureManager = featureManager; overrideDefaultParser = featureManager.getFeature( JdkXmlFeatures.XmlFeature.JDK_OVERRIDE_PARSER); - + this.xmlSecMgr = xmlSecMgr; } @@ -113,8 +117,8 @@ private XObject eval(String expression, Object contextItem) throws TransformerException { requireNonNull(expression, "XPath expression"); - com.sun.org.apache.xpath.internal.XPath xpath = new com.sun.org.apache.xpath.internal.XPath(expression, - null, prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT); + XPath xpath = new XPath(expression, null, prefixResolver, XPath.SELECT, + null, null, xmlSecMgr); return eval(contextItem, xpath); } @@ -159,8 +163,8 @@ throws XPathExpressionException { requireNonNull(expression, "XPath expression"); try { - com.sun.org.apache.xpath.internal.XPath xpath = new XPath (expression, null, - prefixResolver, com.sun.org.apache.xpath.internal.XPath.SELECT); + XPath xpath = new XPath(expression, null, prefixResolver, XPath.SELECT, + null, null, xmlSecMgr); // Can have errorListener XPathExpressionImpl ximpl = new XPathExpressionImpl (xpath, prefixResolver, functionResolver, variableResolver, diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathImplUtil.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, 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 @@ -43,6 +43,7 @@ import javax.xml.xpath.XPathVariableResolver; import jdk.xml.internal.JdkXmlFeatures; import jdk.xml.internal.JdkXmlUtils; +import jdk.xml.internal.XMLSecurityManager; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.traversal.NodeIterator; @@ -52,6 +53,8 @@ /** * This class contains several utility methods used by XPathImpl and * XPathExpressionImpl + * + * @LastModified: Jan 2022 */ class XPathImplUtil { XPathFunctionResolver functionResolver; @@ -63,6 +66,7 @@ // extensions function need to throw XPathFunctionException boolean featureSecureProcessing = false; JdkXmlFeatures featureManager; + XMLSecurityManager xmlSecMgr; /** * Evaluate an XPath context using the internal XPath engine diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/jaxp/XPathResultImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, 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 @@ -171,7 +171,11 @@ case XObject.CLASS_RTREEFRAG: //NODE NodeIterator ni = resultObject.nodeset(); //Return the first node, or null - return type.cast(ni.nextNode()); + try { + return type.cast(ni.nextNode()); + } catch (RuntimeException e) { + throw new TransformerException(e.getMessage(), e.getCause()); + } } return null; diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/org/apache/xpath/internal/res/XPATHErrorResources.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -31,7 +31,7 @@ * Also you need to update the count of messages(MAX_CODE)or * the count of warnings(MAX_WARNING) [ Information purpose only] * @xsl.usage advanced - * @LastModified: May 2019 + * @LastModified: Jan 2022 */ public class XPATHErrorResources extends ListResourceBundle { @@ -322,6 +322,9 @@ public static final String ER_SECUREPROCESSING_FEATURE = "ER_SECUREPROCESSING_FEATURE"; public static final String ER_NULL_XPATH_FUNCTION_RESOLVER = "ER_NULL_XPATH_FUNCTION_RESOLVER"; public static final String ER_NULL_XPATH_VARIABLE_RESOLVER = "ER_NULL_XPATH_VARIABLE_RESOLVER"; + public static final String ER_XPATH_GROUP_LIMIT = "XPATH_GROUP_LIMIT"; + public static final String ER_XPATH_OPERATOR_LIMIT = "XPATH_OPERATOR_LIMIT"; + //END: Keys needed for exception messages of JAXP 1.3 XPath API implementation public static final String WG_LOCALE_NAME_NOT_HANDLED = @@ -836,6 +839,14 @@ { ER_NULL_XPATH_VARIABLE_RESOLVER, "Attempting to set a null XPathVariableResolver:{0}#setXPathVariableResolver(null)"}, + { ER_XPATH_GROUP_LIMIT, + "JAXP0801001: the compiler encountered an XPath expression containing " + + "''{0}'' groups that exceeds the ''{1}'' limit set by ''{2}''."}, + + { ER_XPATH_OPERATOR_LIMIT, + "JAXP0801002: the compiler encountered an XPath expression containing " + + "''{0}'' operators that exceeds the ''{1}'' limit set by ''{2}''."}, + //END: Definitions of error keys used in exception messages of JAXP 1.3 XPath API implementation // Warnings... diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/xml/internal/stream/events/EntityDeclarationImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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 @@ import javax.xml.stream.events.EntityDeclaration; import javax.xml.stream.events.XMLEvent; import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier; +import jdk.xml.internal.JdkXmlUtils; /** * @@ -129,18 +130,12 @@ //escape quotes, lt and amps writer.write(" \""); charEncode(writer, fReplacementText); + writer.write("\""); } else { //external entity - String pubId = getPublicId(); - if (pubId != null) { - writer.write(" PUBLIC \""); - writer.write(pubId); - } else { - writer.write(" SYSTEM \""); - writer.write(getSystemId()); - } + writer.write(JdkXmlUtils.getDTDExternalDecl(getPublicId(), getSystemId())); } - writer.write("\""); + if (fNotationName != null) { writer.write(" NDATA "); writer.write(fNotationName); diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/com/sun/xml/internal/stream/events/NotationDeclarationImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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 @@ import javax.xml.stream.events.NotationDeclaration; import javax.xml.stream.events.XMLEvent; import com.sun.xml.internal.stream.dtd.nonvalidating.XMLNotationDecl; +import jdk.xml.internal.JdkXmlUtils; /** * Implementation of NotationDeclaration event. @@ -88,16 +89,7 @@ { writer.write("'); } } diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkConstants.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2022, 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 @@ -206,6 +206,21 @@ public static final String SP_MAX_ELEMENT_DEPTH = "jdk.xml.maxElementDepth"; /** + * JDK XPath Expression group limit + */ + public static final String XPATH_GROUP_LIMIT = "jdk.xml.xpathExprGrpLimit"; + + /** + * JDK XPath Expression operators limit + */ + public static final String XPATH_OP_LIMIT = "jdk.xml.xpathExprOpLimit"; + + /** + * JDK XSL XPath limit or Total Number of Operators Permitted in an XSL Stylesheet + */ + public static final String XPATH_TOTALOP_LIMIT = "jdk.xml.xpathTotalOpLimit"; + + /** * JDK TransformerFactory and Transformer attribute that specifies a class * loader that will be used for extension functions class loading * Value: a "null", the default value, means that the default EF class loading diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkProperty.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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 @@ -51,20 +51,43 @@ */ public final class JdkProperty { - private ImplPropMap pName; + private final ImplPropMap pName; + private final Class pType; private T pValue; private State pState = State.DEFAULT; /** * Constructs a JDkProperty. * @param name the name of the property + * @param type the type of the value * @param value the initial value * @param state the state of the property */ - public JdkProperty(ImplPropMap name, T value, State state) { + public JdkProperty(ImplPropMap name, Class type, T value, State state) { this.pName = name; + this.pType = type; this.pValue = value; this.pState = state; + readSystemProperty(); + } + + /** + * Read from system properties, or those in jaxp.properties + */ + private void readSystemProperty() { + if (pState == State.DEFAULT) { + T value = null; + if (pName.systemProperty() != null) { + value = SecuritySupport.getJAXPSystemProperty(pType, pName.systemProperty(), null); + } + if (value == null && pName.systemPropertyOld() != null) { + value = SecuritySupport.getJAXPSystemProperty(pType, pName.systemPropertyOld(), null); + } + if (value != null) { + pValue = value; + pState = State.SYSTEMPROPERTY; + } + } } /** diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/JdkXmlUtils.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, 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,6 @@ */ package jdk.xml.internal; -import com.sun.org.apache.xalan.internal.utils.XMLSecurityManager; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xerces.internal.impl.Constants; import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl; @@ -367,19 +366,40 @@ } /** - * Returns the character to be used to quote the input content. Between - * single and double quotes, this method returns the one that is not found - * in the input. Returns double quote by default. - * - * @param s the input string - * @return returns the quote not found in the input - */ - public static char getQuoteChar(String s) { - if (s != null && s.indexOf('"') > -1) { - return '\''; - } else { - return '"'; + * Returns the external declaration for a DTD construct. + * + * @param publicId the public identifier + * @param systemId the system identifier + * @return a DTD external declaration + */ + public static String getDTDExternalDecl(String publicId, String systemId) { + StringBuilder sb = new StringBuilder(); + if (null != publicId) { + sb.append(" PUBLIC "); + sb.append(quoteString(publicId)); + } + + if (null != systemId) { + if (null == publicId) { + sb.append(" SYSTEM "); + } else { + sb.append(" "); + } + + sb.append(quoteString(systemId)); } + return sb.toString(); + } + + /** + * Returns the input string quoted with double quotes or single ones if + * there is a double quote in the string. + * @param s the input string, can not be null + * @return the quoted string + */ + private static String quoteString(String s) { + char c = (s.indexOf('"') > -1) ? '\'' : '"'; + return c + s + c; } private static XMLReader getXMLReaderWSAXFactory(boolean overrideDefaultParser) { diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/XMLLimitAnalyzer.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2013, 2022, 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.xml.internal; + +import java.util.Formatter; +import java.util.HashMap; +import java.util.Map; +import jdk.xml.internal.XMLSecurityManager.Limit; + + +/** + * A helper for analyzing entity expansion limits + * + */ +public final class XMLLimitAnalyzer { + + /** + * Map old property names with the new ones + */ + public static enum NameMap { + ENTITY_EXPANSION_LIMIT(JdkConstants.SP_ENTITY_EXPANSION_LIMIT, JdkConstants.ENTITY_EXPANSION_LIMIT), + MAX_OCCUR_NODE_LIMIT(JdkConstants.SP_MAX_OCCUR_LIMIT, JdkConstants.MAX_OCCUR_LIMIT), + ELEMENT_ATTRIBUTE_LIMIT(JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, JdkConstants.ELEMENT_ATTRIBUTE_LIMIT); + + final String newName; + final String oldName; + + NameMap(String newName, String oldName) { + this.newName = newName; + this.oldName = oldName; + } + + String getOldName(String newName) { + if (newName.equals(this.newName)) { + return oldName; + } + return null; + } + } + + /** + * Max value accumulated for each property + */ + private final int[] values; + /** + * Names of the entities corresponding to their max values + */ + private final String[] names; + /** + * Total value of accumulated entities + */ + private final int[] totalValue; + + /** + * Maintain values of the top 10 elements in the process of parsing + */ + private final Map[] caches; + + private String entityStart, entityEnd; + /** + * Default constructor. Establishes default values for known security + * vulnerabilities. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public XMLLimitAnalyzer() { + values = new int[Limit.values().length]; + totalValue = new int[Limit.values().length]; + names = new String[Limit.values().length]; + caches = new Map[Limit.values().length]; + } + + /** + * Add the value to the current max count for the specified property + * To find the max value of all entities, set no limit + * + * @param limit the type of the property + * @param entityName the name of the entity + * @param value the value of the entity + */ + public void addValue(Limit limit, String entityName, int value) { + addValue(limit.ordinal(), entityName, value); + } + + /** + * Add the value to the current count by the index of the property + * @param index the index of the property + * @param entityName the name of the entity + * @param value the value of the entity + */ + public void addValue(int index, String entityName, int value) { + if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || + index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() || + index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || + index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() + ) { + totalValue[index] += value; + return; + } + if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || + index == Limit.MAX_NAME_LIMIT.ordinal()) { + values[index] = value; + totalValue[index] = value; + return; + } + + Map cache; + if (caches[index] == null) { + cache = new HashMap<>(10); + caches[index] = cache; + } else { + cache = caches[index]; + } + + int accumulatedValue = value; + if (cache.containsKey(entityName)) { + accumulatedValue += cache.get(entityName); + cache.put(entityName, accumulatedValue); + } else { + cache.put(entityName, value); + } + + if (accumulatedValue > values[index]) { + values[index] = accumulatedValue; + names[index] = entityName; + } + + + if (index == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.PARAMETER_ENTITY_SIZE_LIMIT.ordinal()) { + totalValue[Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()] += value; + } + } + + /** + * Return the value of the current max count for the specified property + * + * @param limit the property + * @return the value of the property + */ + public int getValue(Limit limit) { + return getValue(limit.ordinal()); + } + + public int getValue(int index) { + if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) { + return totalValue[index]; + } + return values[index]; + } + /** + * Return the total value accumulated so far + * + * @param limit the property + * @return the accumulated value of the property + */ + public int getTotalValue(Limit limit) { + return totalValue[limit.ordinal()]; + } + + public int getTotalValue(int index) { + return totalValue[index]; + } + /** + * Return the current max value (count or length) by the index of a property + * @param index the index of a property + * @return count of a property + */ + public int getValueByIndex(int index) { + return values[index]; + } + + public void startEntity(String name) { + entityStart = name; + } + + public boolean isTracking(String name) { + if (entityStart == null) { + return false; + } + return entityStart.equals(name); + } + /** + * Stop tracking the entity + * @param limit the limit property + * @param name the name of an entity + */ + public void endEntity(Limit limit, String name) { + entityStart = ""; + Map cache = caches[limit.ordinal()]; + if (cache != null) { + cache.remove(name); + } + } + + /** + * Resets the current value of the specified limit. + * @param limit The limit to be reset. + */ + public void reset(Limit limit) { + if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) { + totalValue[limit.ordinal()] = 0; + } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) { + names[limit.ordinal()] = null; + values[limit.ordinal()] = 0; + caches[limit.ordinal()] = null; + totalValue[limit.ordinal()] = 0; + } + } + + public void debugPrint(XMLSecurityManager securityManager) { + Formatter formatter = new Formatter(); + System.out.println(formatter.format("%30s %15s %15s %15s %30s", + "Property","Limit","Total size","Size","Entity Name")); + + for (Limit limit : Limit.values()) { + formatter = new Formatter(); + System.out.println(formatter.format("%30s %15d %15d %15d %30s", + limit.name(), + securityManager.getLimit(limit), + totalValue[limit.ordinal()], + values[limit.ordinal()], + names[limit.ordinal()])); + } + } +} diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/jdk/xml/internal/XMLSecurityManager.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,633 @@ +/* + * Copyright (c) 2013, 2022, 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.xml.internal; + + +import com.sun.org.apache.xerces.internal.util.SecurityManager; +import java.util.concurrent.CopyOnWriteArrayList; +import jdk.xml.internal.JdkProperty.State; +import jdk.xml.internal.JdkProperty.ImplPropMap; +import org.xml.sax.SAXException; + +/** + * This class manages standard and implementation-specific limitations. + * + */ +public final class XMLSecurityManager { + + /** + * Limits managed by the security manager + */ + @SuppressWarnings("deprecation") + public static enum Limit { + ENTITY_EXPANSION_LIMIT("EntityExpansionLimit", JdkConstants.JDK_ENTITY_EXPANSION_LIMIT, + JdkConstants.SP_ENTITY_EXPANSION_LIMIT, 0, 64000, Processor.PARSER), + MAX_OCCUR_NODE_LIMIT("MaxOccurLimit", JdkConstants.JDK_MAX_OCCUR_LIMIT, + JdkConstants.SP_MAX_OCCUR_LIMIT, 0, 5000, Processor.PARSER), + ELEMENT_ATTRIBUTE_LIMIT("ElementAttributeLimit", JdkConstants.JDK_ELEMENT_ATTRIBUTE_LIMIT, + JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, 0, 10000, Processor.PARSER), + TOTAL_ENTITY_SIZE_LIMIT("TotalEntitySizeLimit", JdkConstants.JDK_TOTAL_ENTITY_SIZE_LIMIT, + JdkConstants.SP_TOTAL_ENTITY_SIZE_LIMIT, 0, 50000000, Processor.PARSER), + GENERAL_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_GENERAL_ENTITY_SIZE_LIMIT, + JdkConstants.SP_GENERAL_ENTITY_SIZE_LIMIT, 0, 0, Processor.PARSER), + PARAMETER_ENTITY_SIZE_LIMIT("MaxEntitySizeLimit", JdkConstants.JDK_PARAMETER_ENTITY_SIZE_LIMIT, + JdkConstants.SP_PARAMETER_ENTITY_SIZE_LIMIT, 0, 1000000, Processor.PARSER), + MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", JdkConstants.JDK_MAX_ELEMENT_DEPTH, + JdkConstants.SP_MAX_ELEMENT_DEPTH, 0, 0, Processor.PARSER), + MAX_NAME_LIMIT("MaxXMLNameLimit", JdkConstants.JDK_XML_NAME_LIMIT, + JdkConstants.SP_XML_NAME_LIMIT, 1000, 1000, Processor.PARSER), + ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", JdkConstants.JDK_ENTITY_REPLACEMENT_LIMIT, + JdkConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000, Processor.PARSER), + XPATH_GROUP_LIMIT("XPathGroupLimit", JdkConstants.XPATH_GROUP_LIMIT, + JdkConstants.XPATH_GROUP_LIMIT, 10, 10, Processor.XPATH), + XPATH_OP_LIMIT("XPathExprOpLimit", JdkConstants.XPATH_OP_LIMIT, + JdkConstants.XPATH_OP_LIMIT, 100, 100, Processor.XPATH), + XPATH_TOTALOP_LIMIT("XPathTotalOpLimit", JdkConstants.XPATH_TOTALOP_LIMIT, + JdkConstants.XPATH_TOTALOP_LIMIT, 10000, 10000, Processor.XPATH) + ; + + final String key; + final String apiProperty; + final String systemProperty; + final int defaultValue; + final int secureValue; + final Processor processor; + + Limit(String key, String apiProperty, String systemProperty, int value, + int secureValue, Processor processor) { + this.key = key; + this.apiProperty = apiProperty; + this.systemProperty = systemProperty; + this.defaultValue = value; + this.secureValue = secureValue; + this.processor = processor; + } + + /** + * Checks whether the specified name is a limit. Checks both the + * property and System Property which is now the new property name. + * + * @param name the specified name + * @return true if there is a match, false otherwise + */ + public boolean is(String name) { + // current spec: new property name == systemProperty + return (systemProperty != null && systemProperty.equals(name)) || + // current spec: apiProperty is legacy + (apiProperty.equals(name)); + } + + /** + * Returns the state of a property name. By the specification as of JDK 17, + * the "jdk.xml." prefixed System property name is also the current API + * name. The URI-based qName is legacy. + * + * @param name the property name + * @return the state of the property name, null if no match + */ + public State getState(String name) { + if (systemProperty != null && systemProperty.equals(name)) { + return State.APIPROPERTY; + } else if (apiProperty.equals(name)) { + //the URI-style qName is legacy + return State.LEGACY_APIPROPERTY; + } + return null; + } + + public String key() { + return key; + } + + public String apiProperty() { + return apiProperty; + } + + public String systemProperty() { + return systemProperty; + } + + public int defaultValue() { + return defaultValue; + } + + public boolean isSupported(Processor p) { + return processor == p; + } + + int secureValue() { + return secureValue; + } + } + + /** + * Map old property names with the new ones + */ + public static enum NameMap { + + ENTITY_EXPANSION_LIMIT(JdkConstants.SP_ENTITY_EXPANSION_LIMIT, JdkConstants.ENTITY_EXPANSION_LIMIT), + MAX_OCCUR_NODE_LIMIT(JdkConstants.SP_MAX_OCCUR_LIMIT, JdkConstants.MAX_OCCUR_LIMIT), + ELEMENT_ATTRIBUTE_LIMIT(JdkConstants.SP_ELEMENT_ATTRIBUTE_LIMIT, JdkConstants.ELEMENT_ATTRIBUTE_LIMIT); + final String newName; + final String oldName; + + NameMap(String newName, String oldName) { + this.newName = newName; + this.oldName = oldName; + } + + String getOldName(String newName) { + if (newName.equals(this.newName)) { + return oldName; + } + return null; + } + } + + /** + * Supported processors + */ + public static enum Processor { + PARSER, + XPATH, + } + + private static final int NO_LIMIT = 0; + + /** + * Values of the properties + */ + private final int[] values; + + /** + * States of the settings for each property + */ + private State[] states; + + /** + * Flag indicating if secure processing is set + */ + boolean secureProcessing; + + /** + * States that determine if properties are set explicitly + */ + private boolean[] isSet; + + + /** + * Index of the special entityCountInfo property + */ + private final int indexEntityCountInfo = 10000; + private String printEntityCountInfo = ""; + + /** + * Default constructor. Establishes default values for known security + * vulnerabilities. + */ + public XMLSecurityManager() { + this(false); + } + + /** + * Instantiate Security Manager in accordance with the status of + * secure processing + * @param secureProcessing + */ + public XMLSecurityManager(boolean secureProcessing) { + values = new int[Limit.values().length]; + states = new State[Limit.values().length]; + isSet = new boolean[Limit.values().length]; + this.secureProcessing = secureProcessing; + for (Limit limit : Limit.values()) { + if (secureProcessing) { + values[limit.ordinal()] = limit.secureValue; + states[limit.ordinal()] = State.FSP; + } else { + values[limit.ordinal()] = limit.defaultValue(); + states[limit.ordinal()] = State.DEFAULT; + } + } + //read system properties or jaxp.properties + readSystemProperties(); + } + + /** + * Setting FEATURE_SECURE_PROCESSING explicitly + */ + public void setSecureProcessing(boolean secure) { + secureProcessing = secure; + for (Limit limit : Limit.values()) { + if (secure) { + setLimit(limit.ordinal(), State.FSP, limit.secureValue()); + } else { + setLimit(limit.ordinal(), State.FSP, limit.defaultValue()); + } + } + } + + /** + * Return the state of secure processing + * @return the state of secure processing + */ + public boolean isSecureProcessing() { + return secureProcessing; + } + + /** + * Finds a limit's new name with the given property name. + * @param propertyName the property name specified + * @return the limit's new name if found, null otherwise + */ + public String find(String propertyName) { + for (Limit limit : Limit.values()) { + if (limit.is(propertyName)) { + // current spec: new property name == systemProperty + return limit.systemProperty(); + } + } + //ENTITYCOUNT's new name is qName + if (ImplPropMap.ENTITYCOUNT.is(propertyName)) { + return ImplPropMap.ENTITYCOUNT.qName(); + } + return null; + } + + /** + * Set limit by property name and state + * @param propertyName property name + * @param state the state of the property + * @param value the value of the property + * @return true if the property is managed by the security manager; false + * if otherwise. + */ + public boolean setLimit(String propertyName, State state, Object value) { + int index = getIndex(propertyName); + if (index > -1) { + State pState = state; + if (index != indexEntityCountInfo && state == State.APIPROPERTY) { + pState = (Limit.values()[index]).getState(propertyName); + } + setLimit(index, pState, value); + return true; + } + return false; + } + + /** + * Set the value for a specific limit. + * + * @param limit the limit + * @param state the state of the property + * @param value the value of the property + */ + public void setLimit(Limit limit, State state, int value) { + setLimit(limit.ordinal(), state, value); + } + + /** + * Set the value of a property by its index + * + * @param index the index of the property + * @param state the state of the property + * @param value the value of the property + */ + public void setLimit(int index, State state, Object value) { + if (index == indexEntityCountInfo) { + printEntityCountInfo = (String)value; + } else { + int temp; + if (value instanceof Integer) { + temp = (Integer)value; + } else { + temp = Integer.parseInt((String) value); + if (temp < 0) { + temp = 0; + } + } + setLimit(index, state, temp); + } + } + + /** + * Set the value of a property by its index + * + * @param index the index of the property + * @param state the state of the property + * @param value the value of the property + */ + public void setLimit(int index, State state, int value) { + if (index == indexEntityCountInfo) { + //if it's explicitly set, it's treated as yes no matter the value + printEntityCountInfo = JdkConstants.JDK_YES; + } else { + //only update if it shall override + if (state.compareTo(states[index]) >= 0) { + values[index] = value; + states[index] = state; + isSet[index] = true; + } + } + } + + /** + * Return the value of the specified property + * + * @param propertyName the property name + * @return the value of the property as a string. If a property is managed + * by this manager, its value shall not be null. + */ + public String getLimitAsString(String propertyName) { + int index = getIndex(propertyName); + if (index > -1) { + return getLimitValueByIndex(index); + } + + return null; + } + /** + * Return the value of the specified property + * + * @param limit the property + * @return the value of the property + */ + public int getLimit(Limit limit) { + return values[limit.ordinal()]; + } + + /** + * Return the value of a property by its ordinal + * + * @param limit the property + * @return value of a property + */ + public String getLimitValueAsString(Limit limit) { + return Integer.toString(values[limit.ordinal()]); + } + + /** + * Return the value of a property by its ordinal + * + * @param index the index of a property + * @return limit of a property as a string + */ + public String getLimitValueByIndex(int index) { + if (index == indexEntityCountInfo) { + return printEntityCountInfo; + } + + return Integer.toString(values[index]); + } + + /** + * Return the state of the limit property + * + * @param limit the limit + * @return the state of the limit property + */ + public State getState(Limit limit) { + return states[limit.ordinal()]; + } + + /** + * Return the state of the limit property + * + * @param limit the limit + * @return the state of the limit property + */ + public String getStateLiteral(Limit limit) { + return states[limit.ordinal()].literal(); + } + + /** + * Get the index by property name + * + * @param propertyName property name + * @return the index of the property if found; return -1 if not + */ + public int getIndex(String propertyName) { + for (Limit limit : Limit.values()) { + // see JDK-8265248, accept both the URL and jdk.xml as prefix + if (limit.is(propertyName)) { + //internally, ordinal is used as index + return limit.ordinal(); + } + } + //special property to return entity count info + if (ImplPropMap.ENTITYCOUNT.is(propertyName)) { + return indexEntityCountInfo; + } + return -1; + } + + /** + * Check if there's no limit defined by the Security Manager + * @param limit + * @return + */ + public boolean isNoLimit(int limit) { + return limit == NO_LIMIT; + } + /** + * Check if the size (length or count) of the specified limit property is + * over the limit + * + * @param limit the type of the limit property + * @param entityName the name of the entity + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(Limit limit, String entityName, int size, + XMLLimitAnalyzer limitAnalyzer) { + return isOverLimit(limit.ordinal(), entityName, size, limitAnalyzer); + } + + /** + * Check if the value (length or count) of the specified limit property is + * over the limit + * + * @param index the index of the limit property + * @param entityName the name of the entity + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(int index, String entityName, int size, + XMLLimitAnalyzer limitAnalyzer) { + if (values[index] == NO_LIMIT) { + return false; + } + if (size > values[index]) { + limitAnalyzer.addValue(index, entityName, size); + return true; + } + return false; + } + + /** + * Check against cumulated value + * + * @param limit the type of the limit property + * @param size the size (count or length) of the entity + * @return true if the size is over the limit, false otherwise + */ + public boolean isOverLimit(Limit limit, XMLLimitAnalyzer limitAnalyzer) { + return isOverLimit(limit.ordinal(), limitAnalyzer); + } + + public boolean isOverLimit(int index, XMLLimitAnalyzer limitAnalyzer) { + if (values[index] == NO_LIMIT) { + return false; + } + + if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() || + index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() || + index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() || + index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() || + index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() || + index == Limit.MAX_NAME_LIMIT.ordinal() + ) { + return (limitAnalyzer.getTotalValue(index) > values[index]); + } else { + return (limitAnalyzer.getValue(index) > values[index]); + } + } + + public void debugPrint(XMLLimitAnalyzer limitAnalyzer) { + if (printEntityCountInfo.equals(JdkConstants.JDK_YES)) { + limitAnalyzer.debugPrint(this); + } + } + + + /** + * Indicate if a property is set explicitly + * @param index + * @return + */ + public boolean isSet(int index) { + return isSet[index]; + } + + public boolean printEntityCountInfo() { + return printEntityCountInfo.equals(JdkConstants.JDK_YES); + } + + /** + * Read from system properties, or those in jaxp.properties + */ + private void readSystemProperties() { + + for (Limit limit : Limit.values()) { + if (!getSystemProperty(limit, limit.systemProperty())) { + //if system property is not found, try the older form if any + for (NameMap nameMap : NameMap.values()) { + String oldName = nameMap.getOldName(limit.systemProperty()); + if (oldName != null) { + getSystemProperty(limit, oldName); + } + } + } + } + + } + + // Array list to store printed warnings for each SAX parser used + private static final CopyOnWriteArrayList printedWarnings = new CopyOnWriteArrayList<>(); + + /** + * Prints out warnings if a parser does not support the specified feature/property. + * + * @param parserClassName the name of the parser class + * @param propertyName the property name + * @param exception the exception thrown by the parser + */ + public static void printWarning(String parserClassName, String propertyName, SAXException exception) { + String key = parserClassName+":"+propertyName; + if (printedWarnings.addIfAbsent(key)) { + System.err.println( "Warning: "+parserClassName+": "+exception.getMessage()); + } + } + + /** + * Read from system properties, or those in jaxp.properties + * + * @param property the type of the property + * @param sysPropertyName the name of system property + */ + private boolean getSystemProperty(Limit limit, String sysPropertyName) { + try { + String value = SecuritySupport.getSystemProperty(sysPropertyName); + if (value != null && !value.equals("")) { + values[limit.ordinal()] = Integer.parseInt(value); + states[limit.ordinal()] = State.SYSTEMPROPERTY; + return true; + } + + value = SecuritySupport.readJAXPProperty(sysPropertyName); + if (value != null && !value.equals("")) { + values[limit.ordinal()] = Integer.parseInt(value); + states[limit.ordinal()] = State.JAXPDOTPROPERTIES; + return true; + } + } catch (NumberFormatException e) { + //invalid setting + throw new NumberFormatException("Invalid setting for system property: " + limit.systemProperty()); + } + return false; + } + + + /** + * Convert a value set through setProperty to XMLSecurityManager. + * If the value is an instance of XMLSecurityManager, use it to override the default; + * If the value is an old SecurityManager, convert to the new XMLSecurityManager. + * + * @param value user specified security manager + * @param securityManager an instance of XMLSecurityManager + * @return an instance of the new security manager XMLSecurityManager + */ + public static XMLSecurityManager convert(Object value, XMLSecurityManager securityManager) { + if (value == null) { + if (securityManager == null) { + securityManager = new XMLSecurityManager(true); + } + return securityManager; + } + if (value instanceof XMLSecurityManager) { + return (XMLSecurityManager)value; + } else { + if (securityManager == null) { + securityManager = new XMLSecurityManager(true); + } + if (value instanceof SecurityManager) { + SecurityManager origSM = (SecurityManager)value; + securityManager.setLimit(Limit.MAX_OCCUR_NODE_LIMIT, State.APIPROPERTY, origSM.getMaxOccurNodeLimit()); + securityManager.setLimit(Limit.ENTITY_EXPANSION_LIMIT, State.APIPROPERTY, origSM.getEntityExpansionLimit()); + securityManager.setLimit(Limit.ELEMENT_ATTRIBUTE_LIMIT, State.APIPROPERTY, origSM.getElementAttrLimit()); + } + return securityManager; + } + } +} diff -Nru openjdk-17-17.0.2+8/src/java.xml/share/classes/module-info.java openjdk-17-17.0.3+7/src/java.xml/share/classes/module-info.java --- openjdk-17-17.0.2+8/src/java.xml/share/classes/module-info.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml/share/classes/module-info.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, 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 @@ -407,7 +407,7 @@ * * XPath * XPath - * + * N/A * * * {@code XPathFactory factory = XPathFactory.newInstance();}
    @@ -432,12 +432,13 @@ * Full Name (prefix + name) * [1] * Description - * System Property [2] - * jaxp.properties [2] - * Value [3] - * Security [4] - * Supported Processor [5] - * Since [6] + * API Property [2] + * System Property [3] + * jaxp.properties [3] + * Value [4] + * Security [5] + * Supported Processor [6] + * Since [7] * * * Type @@ -455,6 +456,7 @@ * * yes * yes + * yes * Integer * * A positive integer. A value less than or equal to 0 indicates no limit. @@ -544,6 +546,7 @@ * * yes * yes + * yes * boolean * true/false * false @@ -567,6 +570,7 @@ * * yes * yes + * yes * String * yes/no * no @@ -584,6 +588,7 @@ * * yes * yes + * yes * Integer * A positive integer. A value less than * or equal to 0 indicates that the property is not specified. If the value is not @@ -599,6 +604,7 @@ * Sets a non-null ClassLoader instance to be used for loading XSLTC java * extension functions. * + * yes * no * no * Object @@ -609,6 +615,46 @@ * Transform * 9 * + * + * jdk.xml.xpathExprGrpLimit + * Limits the number of groups an XPath expression can contain. + * + * + * Transform:yes
    + * XPath:no + * + * yes + * yes + * Integer + * A positive integer. A value less than or equal to 0 indicates no limit. + * If the value is not an integer, a NumberFormatException is thrown. + * 10 + * 10 + * Yes + * + * Transform
    + * XPath + * + * 19 + * + * + * jdk.xml.xpathExprOpLimit + * Limits the number of operators an XPath expression can contain. + * + * 100 + * 100 + * + * + * jdk.xml.xpathTotalOpLimit + * Limits the total number of XPath operators in an XSL Stylesheet. + * + * yes + * 10000 + * 10000 + * + * Transform
    + * + * * * *

    @@ -622,12 +668,13 @@ * Full Name (prefix + name) * [1] * Description - * System Property [2] - * jaxp.properties [2] - * Value [3] - * Security [4] - * Supported Processor [5] - * Since [6] + * API Property [2] + * System Property [3] + * jaxp.properties [3] + * Value [4] + * Security [5] + * Supported Processor [6] + * Since [7] * * * Type @@ -643,6 +690,7 @@ * * yes * yes + * yes * Boolean * * true or false. True indicates that extension functions are allowed; False otherwise. @@ -700,11 +748,14 @@ *

    * [1] The full name of a property should be used to set the property. *

    - * [2] A value "yes" indicates there is a corresponding System Property + * [2] A value "yes" indicates that the property can be set through the + * processor or its factory, "no" otherwise. + *

    + * [3] A value "yes" indicates there is a corresponding System Property * for the property, "no" otherwise. * - *

    - * [3] The value must be exactly as listed in this table, case-sensitive. + *

    + * [4] The value must be exactly as listed in this table, case-sensitive. * The value of the corresponding System Property is the String representation of * the property value. If the type is boolean, the system property is true only * if it is "true"; If the type is String, the system property is true only if @@ -713,15 +764,15 @@ * is Integer, the value of the System Property is the String representation of * the value (e.g. "64000" for {@code entityExpansionLimit}). * - *

    - * [4] A value "yes" indicates the property is a Security Property. Refer + *

    + * [5] A value "yes" indicates the property is a Security Property. Refer * to the Scope and Order on how secure processing * may affect the value of a Security Property. - *

    - * [5] One or more processors that support the property. The values of the - * field are IDs described in table Processors. *

    - * [6] Indicates the initial release the property is introduced. + * [6] One or more processors that support the property. The values of the + * field are IDs described in table Processors. + *

    + * [7] Indicates the initial release the property is introduced. * *

    Legacy Property Names (deprecated)

    * JDK releases prior to JDK 17 support the use of URI style prefix for properties. diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/Init.java 2022-04-19 19:55:43.000000000 +0000 @@ -84,7 +84,7 @@ } @SuppressWarnings("removal") - InputStream is = + InputStream is = //NOPMD AccessController.doPrivileged( (PrivilegedAction) () -> { @@ -351,6 +351,9 @@ * @param callingClass The Class object of the calling object */ public static URL getResource(String resourceName, Class callingClass) { + if (resourceName == null) { + throw new NullPointerException(); + } URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName); if (url == null && resourceName.charAt(0) == '/') { //certain classloaders need it without the leading / @@ -404,6 +407,9 @@ * @param callingClass The Class object of the calling object */ private static List getResources(String resourceName, Class callingClass) { + if (resourceName == null) { + throw new NullPointerException(); + } List ret = new ArrayList<>(); Enumeration urls = new Enumeration() { public boolean hasMoreElements() { @@ -479,7 +485,7 @@ } - if (ret.isEmpty() && resourceName != null && resourceName.charAt(0) != '/') { + if (ret.isEmpty() && resourceName.charAt(0) != '/') { return getResources('/' + resourceName, callingClass); } return ret; diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/algorithms/implementations/IntegrityHmac.java 2022-04-19 19:55:43.000000000 +0000 @@ -309,7 +309,7 @@ Node n = XMLUtils.selectDsNode(element.getFirstChild(), Constants._TAG_HMACOUTPUTLENGTH, 0); if (n != null) { String hmacLength = XMLUtils.getFullTextChildrenFromNode(n); - if (hmacLength != null && !"".equals(hmacLength)) { + if (hmacLength != null && hmacLength.length() != 0) { this.hmacOutputLength = new HMACOutputLength(Integer.parseInt(hmacLength)); } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/Canonicalizer20010315.java 2022-04-19 19:55:43.000000000 +0000 @@ -125,7 +125,7 @@ * Output the Attr[]s for the given element. *
    * The code of this method is a copy of - * {@link #outputAttributes(Element, NameSpaceSymbTable, Map)}, + * {@link #outputAttributes(Element, NameSpaceSymbTable, Map, OutputStream)}, * whereas it takes into account that subtree-c14n is -- well -- subtree-based. * So if the element in question isRoot of c14n, it's parent is not in the * node set, as well as all other ancestors. diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerBase.java 2022-04-19 19:55:43.000000000 +0000 @@ -211,7 +211,7 @@ Node sibling = null; Node parentNode = null; Map cache = new HashMap<>(); - do { + do { //NOPMD switch (currentNode.getNodeType()) { case Node.ENTITY_NODE : @@ -338,7 +338,7 @@ Node parentNode = null; int documentLevel = NODE_BEFORE_DOCUMENT_ELEMENT; Map cache = new HashMap<>(); - do { + do { //NOPMD switch (currentNode.getNodeType()) { case Node.ENTITY_NODE : @@ -560,7 +560,7 @@ } parents.clear(); Attr nsprefix = ns.getMappingWithoutRendered(XMLNS); - if (nsprefix != null && "".equals(nsprefix.getValue())) { + if (nsprefix != null && nsprefix.getValue().length() == 0) { ns.addMappingAndRender( XMLNS, "", getNullNode(nsprefix.getOwnerDocument())); } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/CanonicalizerPhysical.java 2022-04-19 19:55:43.000000000 +0000 @@ -110,7 +110,7 @@ * Output the Attr[]s for the given element. *
    * The code of this method is a copy of - * {@link #outputAttributes(Element, NameSpaceSymbTable, Map)}, + * {@link #outputAttributes(Element, NameSpaceSymbTable, Map, OutputStream)}, * whereas it takes into account that subtree-c14n is -- well -- subtree-based. * So if the element in question isRoot of c14n, it's parent is not in the * node set, as well as all other ancestors. diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/c14n/implementations/NameSpaceSymbTable.java 2022-04-19 19:55:43.000000000 +0000 @@ -348,7 +348,7 @@ List entrySet() { List a = new ArrayList<>(); for (int i = 0;i < entries.length;i++) { - if (entries[i] != null && !"".equals(entries[i].uri)) { + if (entries[i] != null && entries[i].uri.length() != 0) { a.add(entries[i]); } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/exceptions/XMLSecurityRuntimeException.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,181 +0,0 @@ -/* - * reserved comment block - * DO NOT REMOVE OR ALTER! - */ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package com.sun.org.apache.xml.internal.security.exceptions; - -import java.text.MessageFormat; - -import com.sun.org.apache.xml.internal.security.utils.Constants; -import com.sun.org.apache.xml.internal.security.utils.I18n; - -/** - * The mother of all runtime Exceptions in this bundle. It allows exceptions to have - * their messages translated to the different locales. - * - * The {@code xmlsecurity_en.properties} file contains this line: - *
    - * xml.WrongElement = Can't create a {0} from a {1} element
    - * 
    - * - * Usage in the Java source is: - *
    - * {
    - *    Object[] exArgs = { Constants._TAG_TRANSFORMS, "BadElement" };
    - *
    - *    throw new XMLSecurityException("xml.WrongElement", exArgs);
    - * }
    - * 
    - * - * Additionally, if another Exception has been caught, we can supply it, too - *
    - * try {
    - *    ...
    - * } catch (Exception oldEx) {
    - *    Object[] exArgs = { Constants._TAG_TRANSFORMS, "BadElement" };
    - *
    - *    throw new XMLSecurityException("xml.WrongElement", exArgs, oldEx);
    - * }
    - * 
    - * - * - */ -public class XMLSecurityRuntimeException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - /** Field msgID */ - protected String msgID; - - /** - * Constructor XMLSecurityRuntimeException - * - */ - public XMLSecurityRuntimeException() { - super("Missing message string"); - - this.msgID = null; - } - - /** - * Constructor XMLSecurityRuntimeException - * - * @param msgID - */ - public XMLSecurityRuntimeException(String msgID) { - super(I18n.getExceptionMessage(msgID)); - - this.msgID = msgID; - } - - /** - * Constructor XMLSecurityRuntimeException - * - * @param msgID - * @param exArgs - */ - public XMLSecurityRuntimeException(String msgID, Object[] exArgs) { - super(MessageFormat.format(I18n.getExceptionMessage(msgID), exArgs)); - - this.msgID = msgID; - } - - /** - * Constructor XMLSecurityRuntimeException - * - * @param originalException - */ - public XMLSecurityRuntimeException(Exception originalException) { - super("Missing message ID to locate message string in resource bundle \"" - + Constants.exceptionMessagesResourceBundleBase - + "\". Original Exception was a " - + originalException.getClass().getName() + " and message " - + originalException.getMessage(), originalException); - } - - /** - * Constructor XMLSecurityRuntimeException - * - * @param msgID - * @param originalException - */ - public XMLSecurityRuntimeException(String msgID, Exception originalException) { - super(I18n.getExceptionMessage(msgID, originalException), originalException); - - this.msgID = msgID; - } - - /** - * Constructor XMLSecurityRuntimeException - * - * @param msgID - * @param exArgs - * @param originalException - */ - public XMLSecurityRuntimeException(String msgID, Object[] exArgs, Exception originalException) { - super(MessageFormat.format(I18n.getExceptionMessage(msgID), exArgs), originalException); - - this.msgID = msgID; - } - - /** - * Method getMsgID - * - * @return the messageId - */ - public String getMsgID() { - if (msgID == null) { - return "Missing message ID"; - } - return msgID; - } - - /** {@inheritDoc} */ - public String toString() { - String s = this.getClass().getName(); - String message = super.getLocalizedMessage(); - - if (message != null) { - message = s + ": " + message; - } else { - message = s; - } - - if (this.getCause() != null) { - message = message + "\nOriginal Exception was " + this.getCause().toString(); - } - - return message; - } - - /** - * Method getOriginalException - * - * @return the original exception - */ - public Exception getOriginalException() { - if (this.getCause() instanceof Exception) { - return (Exception)this.getCause(); - } - return null; - } - -} diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/KeyResolver.java 2022-04-19 19:55:43.000000000 +0000 @@ -170,8 +170,8 @@ ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException { JavaUtils.checkRegisterPermission(); - KeyResolverSpi keyResolverSpi = - (KeyResolverSpi) JavaUtils.newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, KeyResolver.class)); + KeyResolverSpi keyResolverSpi = (KeyResolverSpi) + JavaUtils.newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, KeyResolver.class)); register(keyResolverSpi, false); } @@ -193,8 +193,8 @@ KeyResolverSpi keyResolverSpi = null; Exception ex = null; try { - keyResolverSpi = (KeyResolverSpi) JavaUtils.newInstanceWithEmptyConstructor( - ClassLoaderUtils.loadClass(className, KeyResolver.class)); + keyResolverSpi = (KeyResolverSpi) + JavaUtils.newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, KeyResolver.class)); register(keyResolverSpi, true); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException e) { ex = e; @@ -253,8 +253,8 @@ JavaUtils.checkRegisterPermission(); List keyResolverList = new ArrayList<>(classNames.size()); for (String className : classNames) { - KeyResolverSpi keyResolverSpi = (KeyResolverSpi)JavaUtils - .newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, KeyResolver.class)); + KeyResolverSpi keyResolverSpi = (KeyResolverSpi) + JavaUtils.newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, KeyResolver.class)); keyResolverList.add(keyResolverSpi); } resolverList.addAll(keyResolverList); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/keys/keyresolver/implementations/KeyInfoReferenceResolver.java 2022-04-19 19:55:43.000000000 +0000 @@ -162,6 +162,7 @@ validateReference(referentElement, secureValidation); KeyInfo referent = new KeyInfo(referentElement, baseURI); + referent.setSecureValidation(secureValidation); referent.addStorageResolver(storage); return referent; } @@ -181,7 +182,7 @@ } KeyInfo referent = new KeyInfo(referentElement, ""); - if (referent.containsKeyInfoReference()) { + if (referent.containsKeyInfoReference() || referent.containsRetrievalMethod()) { if (secureValidation) { throw new XMLSecurityException("KeyInfoReferenceResolver.InvalidReferentElement.ReferenceWithSecure"); } else { diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/resource/config.xml 2022-04-19 19:55:43.000000000 +0000 @@ -96,8 +96,6 @@ - all his descendants in the output. + * method included the node and all its descendants in the output. * * @param rootNode */ @@ -528,7 +527,7 @@ if (inputOctetStreamProxy == null) { return null; } - try { + try { //NOPMD bytes = JavaUtils.getBytesFromStream(inputOctetStreamProxy); } finally { inputOctetStreamProxy.close(); @@ -539,15 +538,9 @@ /** * @param filter */ - public void addNodeFilter(NodeFilter filter) { + public void addNodeFilter(NodeFilter filter) throws XMLParserException, IOException { if (isOctetStream()) { - try { - convertToNodes(); - } catch (Exception e) { - throw new XMLSecurityRuntimeException( - "signature.XMLSignatureInput.nodesetReference", e - ); - } + convertToNodes(); } nodeFilters.add(filter); } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14N.java 2022-04-19 19:55:43.000000000 +0000 @@ -60,7 +60,7 @@ Canonicalizer20010315 c14n = getCanonicalizer(); - if (os == null) { + if (os == null && (input.isOctetStream() || input.isElement() || input.isNodeSet())) { try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { c14n.engineCanonicalize(input, writer, secureValidation); writer.flush(); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformC14NExclusive.java 2022-04-19 19:55:43.000000000 +0000 @@ -82,7 +82,7 @@ Canonicalizer20010315Excl c14n = getCanonicalizer(); - if (os == null) { + if (os == null && (input.isOctetStream() || input.isElement() || input.isNodeSet())) { try (ByteArrayOutputStream writer = new ByteArrayOutputStream()) { c14n.engineCanonicalize(input, inclusiveNamespaces, writer, secureValidation); writer.flush(); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformEnvelopedSignature.java 2022-04-19 19:55:43.000000000 +0000 @@ -22,8 +22,10 @@ */ package com.sun.org.apache.xml.internal.security.transforms.implementations; +import java.io.IOException; import java.io.OutputStream; +import com.sun.org.apache.xml.internal.security.parser.XMLParserException; import com.sun.org.apache.xml.internal.security.signature.NodeFilter; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; import com.sun.org.apache.xml.internal.security.transforms.TransformSpi; @@ -71,7 +73,11 @@ Node signatureElement = searchSignatureElement(transformElement); input.setExcludeNode(signatureElement); - input.addNodeFilter(new EnvelopedNodeFilter(signatureElement)); + try { + input.addNodeFilter(new EnvelopedNodeFilter(signatureElement)); + } catch (XMLParserException | IOException ex) { + throw new TransformationException(ex); + } return input; } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/transforms/implementations/TransformXPath.java 2022-04-19 19:55:43.000000000 +0000 @@ -22,11 +22,12 @@ */ package com.sun.org.apache.xml.internal.security.transforms.implementations; +import java.io.IOException; import java.io.OutputStream; import javax.xml.transform.TransformerException; -import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityRuntimeException; +import com.sun.org.apache.xml.internal.security.parser.XMLParserException; import com.sun.org.apache.xml.internal.security.signature.NodeFilter; import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; import com.sun.org.apache.xml.internal.security.transforms.TransformSpi; @@ -51,6 +52,9 @@ */ public class TransformXPath extends TransformSpi { + private static final com.sun.org.slf4j.internal.Logger LOG = + com.sun.org.slf4j.internal.LoggerFactory.getLogger(TransformXPath.class); + /** * {@inheritDoc} */ @@ -102,7 +106,7 @@ input.addNodeFilter(new XPathNodeFilter(xpathElement, xpathnode, str, xpathAPIInstance)); input.setNodeSet(true); return input; - } catch (DOMException ex) { + } catch (XMLParserException | IOException | DOMException ex) { throw new TransformationException(ex); } } @@ -144,11 +148,8 @@ } return 0; } catch (TransformerException e) { - Object[] eArgs = {currentNode}; - throw new XMLSecurityRuntimeException("signature.Transform.node", eArgs, e); - } catch (Exception e) { - Object[] eArgs = {currentNode, currentNode.getNodeType()}; - throw new XMLSecurityRuntimeException("signature.Transform.nodeAndType",eArgs, e); + LOG.debug("Error evaluating XPath expression", e); + return 0; } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/Base64.java 2022-04-19 19:55:43.000000000 +0000 @@ -43,6 +43,7 @@ * @see com.sun.org.apache.xml.internal.security.transforms.implementations.TransformBase64Decode */ @Deprecated +@SuppressWarnings("PMD") public final class Base64 { /** Field BASE64DEFAULTLENGTH */ diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/DOMNamespaceContext.java 2022-04-19 19:55:43.000000000 +0000 @@ -127,11 +127,11 @@ return DEFAULT_NS_PREFIX; } } - if (namespaceURI == null) { + if (namespaceURI == null && context != null) { return context.lookupNamespaceURI(null) != null ? null : DEFAULT_NS_PREFIX; - } else if (namespaceURI.equals(XML_NS_URI)) { + } else if (XML_NS_URI.equals(namespaceURI)) { return XML_NS_PREFIX; - } else if (namespaceURI.equals(XMLNS_ATTRIBUTE_NS_URI)) { + } else if (XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI)) { return XMLNS_ATTRIBUTE; } return null; diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/RFC2253Parser.java 2022-04-19 19:55:43.000000000 +0000 @@ -190,20 +190,21 @@ if (value.startsWith("\"")) { StringBuilder sb = new StringBuilder(); - StringReader sr = new StringReader(value.substring(1, value.length() - 1)); - int i = 0; - char c; - - while ((i = sr.read()) > -1) { - c = (char) i; + try (StringReader sr = new StringReader(value.substring(1, value.length() - 1))) { + int i = 0; + char c; + + while ((i = sr.read()) > -1) { + c = (char) i; + + //the following char is defined at 4.Relationship with RFC1779 and LDAPv2 inrfc2253 + if (c == ',' || c == '=' || c == '+' || c == '<' + || c == '>' || c == '#' || c == ';') { + sb.append('\\'); + } - //the following char is defined at 4.Relationship with RFC1779 and LDAPv2 inrfc2253 - if (c == ',' || c == '=' || c == '+' || c == '<' - || c == '>' || c == '#' || c == ';') { - sb.append('\\'); + sb.append(c); } - - sb.append(c); } value = trim(sb.toString()); @@ -263,37 +264,38 @@ */ static String changeLess32toRFC(String string) throws IOException { StringBuilder sb = new StringBuilder(); - StringReader sr = new StringReader(string); int i = 0; char c; - while ((i = sr.read()) > -1) { - c = (char) i; + try (StringReader sr = new StringReader(string)) { + while ((i = sr.read()) > -1) { + c = (char) i; - if (c == '\\') { - sb.append(c); + if (c == '\\') { + sb.append(c); - char c1 = (char) sr.read(); - char c2 = (char) sr.read(); + char c1 = (char) sr.read(); + char c2 = (char) sr.read(); - //65 (A) 97 (a) - if ((c1 >= 48 && c1 <= 57 || c1 >= 65 && c1 <= 70 || c1 >= 97 && c1 <= 102) - && (c2 >= 48 && c2 <= 57 - || c2 >= 65 && c2 <= 70 - || c2 >= 97 && c2 <= 102)) { - try { - char ch = (char) Byte.parseByte("" + c1 + c2, 16); - - sb.append(ch); - } catch (NumberFormatException ex) { - throw new IOException(ex); + //65 (A) 97 (a) + if ((c1 >= 48 && c1 <= 57 || c1 >= 65 && c1 <= 70 || c1 >= 97 && c1 <= 102) + && (c2 >= 48 && c2 <= 57 + || c2 >= 65 && c2 <= 70 + || c2 >= 97 && c2 <= 102)) { + try { + char ch = (char) Byte.parseByte("" + c1 + c2, 16); + + sb.append(ch); + } catch (NumberFormatException ex) { + throw new IOException(ex); + } + } else { + sb.append(c1); + sb.append(c2); } } else { - sb.append(c1); - sb.append(c2); + sb.append(c); } - } else { - sb.append(c); } } @@ -309,15 +311,16 @@ */ static String changeLess32toXML(String string) throws IOException { StringBuilder sb = new StringBuilder(); - StringReader sr = new StringReader(string); int i = 0; - while ((i = sr.read()) > -1) { - if (i < 32) { - sb.append('\\'); - sb.append(Integer.toHexString(i)); - } else { - sb.append((char) i); + try (StringReader sr = new StringReader(string)) { + while ((i = sr.read()) > -1) { + if (i < 32) { + sb.append('\\'); + sb.append(Integer.toHexString(i)); + } else { + sb.append((char) i); + } } } @@ -333,28 +336,29 @@ */ static String changeWStoXML(String string) throws IOException { StringBuilder sb = new StringBuilder(); - StringReader sr = new StringReader(string); int i = 0; char c; - while ((i = sr.read()) > -1) { - c = (char) i; + try (StringReader sr = new StringReader(string)) { + while ((i = sr.read()) > -1) { + c = (char) i; - if (c == '\\') { - char c1 = (char) sr.read(); + if (c == '\\') { + char c1 = (char) sr.read(); - if (c1 == ' ') { - sb.append('\\'); + if (c1 == ' ') { + sb.append('\\'); - String s = "20"; + String s = "20"; - sb.append(s); + sb.append(s); + } else { + sb.append('\\'); + sb.append(c1); + } } else { - sb.append('\\'); - sb.append(c1); + sb.append(c); } - } else { - sb.append(c); } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/XMLUtils.java 2022-04-19 19:55:43.000000000 +0000 @@ -583,7 +583,7 @@ Node parent = null; Node sibling = null; final String namespaceNs = Constants.NamespaceSpecNS; - do { + do { //NOPMD switch (node.getNodeType()) { case Node.ELEMENT_NODE : Element element = (Element) node; diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolver.java 2022-04-19 19:55:43.000000000 +0000 @@ -122,8 +122,8 @@ List resourceResolversToAdd = new ArrayList<>(classNames.size()); for (String className : classNames) { - ResourceResolverSpi resourceResolverSpi = (ResourceResolverSpi)JavaUtils - .newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, ResourceResolver.class)); + ResourceResolverSpi resourceResolverSpi = (ResourceResolverSpi) + JavaUtils.newInstanceWithEmptyConstructor(ClassLoaderUtils.loadClass(className, ResourceResolver.class)); resourceResolversToAdd.add(resourceResolverSpi); } resolverList.addAll(resourceResolversToAdd); @@ -159,15 +159,6 @@ LOG.debug("check resolvability by class {}", resolver.getClass().getName()); if (resolver.engineCanResolveURI(context)) { - // Check to see whether the Resolver is allowed - if (context.secureValidation - && (resolver instanceof ResolverLocalFilesystem - || resolver instanceof ResolverDirectHTTP)) { - Object[] exArgs = { resolver.getClass().getName() }; - throw new ResourceResolverException( - "signature.Reference.ForbiddenResolver", exArgs, context.uriToResolve, context.baseUri - ); - } return resolver.engineResolveURI(context); } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/ResourceResolverContext.java 2022-04-19 19:55:43.000000000 +0000 @@ -54,5 +54,4 @@ public Map getProperties() { return properties; } - } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverDirectHTTP.java 2022-04-19 19:55:43.000000000 +0000 @@ -219,7 +219,8 @@ LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); if (context.uriToResolve.startsWith("http:") || - context.baseUri != null && context.baseUri.startsWith("http:")) { + context.uriToResolve.startsWith("https:") || + context.baseUri != null && (context.baseUri.startsWith("http:") || context.baseUri.startsWith("https:"))) { LOG.debug("I state that I can resolve {}", context.uriToResolve); return true; } @@ -231,7 +232,7 @@ private static URI getNewURI(String uri, String baseURI) throws URISyntaxException { URI newUri = null; - if (baseURI == null || "".equals(baseURI)) { + if (baseURI == null || baseURI.length() == 0) { newUri = new URI(uri); } else { newUri = new URI(baseURI).resolve(uri); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/com/sun/org/apache/xml/internal/security/utils/resolver/implementations/ResolverLocalFilesystem.java 2022-04-19 19:55:43.000000000 +0000 @@ -38,8 +38,6 @@ */ public class ResolverLocalFilesystem extends ResourceResolverSpi { - private static final int FILE_URI_LENGTH = "file:/".length(); - private static final com.sun.org.slf4j.internal.Logger LOG = com.sun.org.slf4j.internal.LoggerFactory.getLogger(ResolverLocalFilesystem.class); @@ -53,9 +51,7 @@ // calculate new URI URI uriNew = getNewURI(context.uriToResolve, context.baseUri); - String fileName = - ResolverLocalFilesystem.translateUriToFilename(uriNew.toString()); - InputStream inputStream = Files.newInputStream(Paths.get(fileName)); + InputStream inputStream = Files.newInputStream(Paths.get(uriNew)); //NOPMD XMLSignatureInput result = new XMLSignatureInput(inputStream); result.setSecureValidation(context.secureValidation); @@ -68,41 +64,6 @@ } /** - * Method translateUriToFilename - * - * @param uri - * @return the string of the filename - */ - private static String translateUriToFilename(String uri) { - - String subStr = uri.substring(FILE_URI_LENGTH); - - if (subStr.indexOf("%20") > -1) { - int offset = 0; - int index = 0; - StringBuilder temp = new StringBuilder(subStr.length()); - do { - index = subStr.indexOf("%20",offset); - if (index == -1) { - temp.append(subStr.substring(offset)); - } else { - temp.append(subStr.substring(offset, index)); - temp.append(' '); - offset = index + 3; - } - } while(index != -1); - subStr = temp.toString(); - } - - if (subStr.charAt(1) == ':') { - // we're running M$ Windows, so this works fine - return subStr; - } - // we're running some UNIX, so we have to prepend a slash - return "/" + subStr; - } - - /** * {@inheritDoc} */ public boolean engineCanResolveURI(ResourceResolverContext context) { @@ -111,7 +72,7 @@ } if (context.uriToResolve.isEmpty() || context.uriToResolve.charAt(0) == '#' || - context.uriToResolve.startsWith("http:")) { + context.uriToResolve.startsWith("http:") || context.uriToResolve.startsWith("https:")) { return false; } @@ -133,7 +94,7 @@ private static URI getNewURI(String uri, String baseURI) throws URISyntaxException { URI newUri = null; - if (baseURI == null || "".equals(baseURI)) { + if (baseURI == null || baseURI.length() == 0) { newUri = new URI(uri); } else { newUri = new URI(baseURI).resolve(uri); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/ApacheCanonicalizer.java 2022-04-19 19:55:43.000000000 +0000 @@ -239,9 +239,6 @@ try { in = apacheTransform.performTransform(in, os, secVal); - if (!in.isNodeSet() && !in.isElement()) { - return null; - } if (in.isOctetStream()) { return new ApacheOctetStreamData(in); } else { diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMReference.java 2022-04-19 19:55:43.000000000 +0000 @@ -447,7 +447,7 @@ } Data data = dereferencedData; XMLSignatureInput xi = null; - try (OutputStream os = new UnsyncBufferedOutputStream(dos)) { + try (OutputStream os = new UnsyncBufferedOutputStream(dos)) { //NOPMD for (int i = 0, size = transforms.size(); i < size; i++) { DOMTransform transform = (DOMTransform)transforms.get(i); if (i < size - 1) { diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMTransform.java 2022-04-19 19:55:43.000000000 +0000 @@ -116,7 +116,7 @@ Document ownerDoc = DOMUtils.getOwnerDocument(parent); Element transformElem = null; - if (parent.getLocalName().equals("Transforms")) { + if ("Transforms".equals(parent.getLocalName())) { transformElem = DOMUtils.createElement(ownerDoc, "Transform", XMLSignature.XMLNS, dsPrefix); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMURIDereferencer.java 2022-04-19 19:55:43.000000000 +0000 @@ -138,7 +138,7 @@ } try { - ResourceResolverContext resContext = new ResourceResolverContext(uriAttr, baseURI, false); + ResourceResolverContext resContext = new ResourceResolverContext(uriAttr, baseURI, secVal); XMLSignatureInput in = ResourceResolver.resolve(resContext); if (in.isOctetStream()) { return new ApacheOctetStreamData(in); diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Policy.java 2022-04-19 19:55:43.000000000 +0000 @@ -43,14 +43,13 @@ */ public final class Policy { - // all restrictions are initialized to be unconstrained - private static Set disallowedAlgs = new HashSet<>(); - private static int maxTrans = Integer.MAX_VALUE; - private static int maxRefs = Integer.MAX_VALUE; - private static Set disallowedRefUriSchemes = new HashSet<>(); - private static Map minKeyMap = new HashMap<>(); - private static boolean noDuplicateIds = false; - private static boolean noRMLoops = false; + private static Set disallowedAlgs; + private static int maxTrans; + private static int maxRefs; + private static Set disallowedRefUriSchemes; + private static Map minKeyMap; + private static boolean noDuplicateIds; + private static boolean noRMLoops; static { try { @@ -64,6 +63,16 @@ private Policy() {} private static void initialize() { + // First initialized to be unconstrained and then parse the + // security property "jdk.xml.dsig.secureValidationPolicy" + disallowedAlgs = new HashSet<>(); + maxTrans = Integer.MAX_VALUE; + maxRefs = Integer.MAX_VALUE; + disallowedRefUriSchemes = new HashSet<>(); + minKeyMap = new HashMap<>(); + noDuplicateIds = false; + noRMLoops = false; + @SuppressWarnings("removal") String prop = AccessController.doPrivileged((PrivilegedAction) () -> diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/Utils.java 2022-04-19 19:55:43.000000000 +0000 @@ -114,7 +114,7 @@ } private static boolean getBoolean(XMLCryptoContext xc, String name) { - Boolean value = (Boolean)xc.getProperty(name); + Boolean value = (Boolean) xc.getProperty(name); return value != null && value; } } diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/XMLDSigRI.java 2022-04-19 19:55:43.000000000 +0000 @@ -134,7 +134,7 @@ @SuppressWarnings("removal") public XMLDSigRI() { // This is the JDK XMLDSig provider, synced from - // Apache Santuario XML Security for Java, version 2.2.1 + // Apache Santuario XML Security for Java, version 2.3.0 super("XMLDSig", VER, INFO); final Provider p = this; diff -Nru openjdk-17-17.0.2+8/src/java.xml.crypto/share/legal/santuario.md openjdk-17-17.0.3+7/src/java.xml.crypto/share/legal/santuario.md --- openjdk-17-17.0.2+8/src/java.xml.crypto/share/legal/santuario.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/java.xml.crypto/share/legal/santuario.md 2022-04-19 19:55:43.000000000 +0000 @@ -1,4 +1,4 @@ -## Apache Santuario v2.2.1 +## Apache Santuario v2.3.0 ### Apache Santuario Notice
    diff -Nru openjdk-17-17.0.2+8/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java openjdk-17-17.0.3+7/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
    --- openjdk-17-17.0.2+8/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java	2022-04-19 19:55:43.000000000 +0000
    @@ -157,7 +157,7 @@
                             JCExpression clazz = copy(t.clazz, p);
                             List args = copy(t.args, p);
                             JCClassDecl def = null;
    -                        return make.at(t.pos).SpeculativeNewClass(encl, typeargs, clazz, args, def, t.def != null);
    +                        return make.at(t.pos).SpeculativeNewClass(encl, typeargs, clazz, args, def, t.def != null || t.classDeclRemoved());
                         } else {
                             return super.visitNewClass(node, p);
                         }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java openjdk-17-17.0.3+7/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java
    --- openjdk-17-17.0.2+8/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/wrapper/CK_MECHANISM.java	2022-04-19 19:55:43.000000000 +0000
    @@ -164,7 +164,6 @@
         // CK_MECHANISM(long) constructor and setParameter(CK_RSA_PKCS_PSS_PARAMS)
         // methods instead of creating yet another constructor
         public void setParameter(CK_RSA_PKCS_PSS_PARAMS params) {
    -        assert(this.mechanism == CKM_RSA_PKCS_PSS);
             assert(params != null);
             if (this.pParameter != null && this.pParameter.equals(params)) {
                 return;
    diff -Nru openjdk-17-17.0.2+8/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java openjdk-17-17.0.3+7/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java
    --- openjdk-17-17.0.2+8/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSAOperations.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 2018, 2021, 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 @@
     import sun.security.util.math.*;
     import static sun.security.ec.ECOperations.IntermediateValueException;
     
    +import java.math.BigInteger;
     import java.security.ProviderException;
     import java.security.spec.*;
     import java.util.Arrays;
    @@ -200,7 +201,8 @@
     
             IntegerFieldModuloP field = ecOps.getField();
             IntegerFieldModuloP orderField = ecOps.getOrderField();
    -        int length = (orderField.getSize().bitLength() + 7) / 8;
    +        BigInteger mod = orderField.getSize();
    +        int length = (mod.bitLength() + 7) / 8;
     
             byte[] r;
             byte[] s;
    @@ -218,6 +220,13 @@
                 System.arraycopy(sig, encodeLength, s, length - encodeLength, encodeLength);
             }
     
    +        BigInteger rb = new BigInteger(1, r);
    +        BigInteger sb = new BigInteger(1, s);
    +        if (rb.signum() == 0 || sb.signum() == 0
    +                || rb.compareTo(mod) >= 0 || sb.compareTo(mod) >= 0) {
    +            return false;
    +        }
    +
             ArrayUtil.reverse(r);
             ArrayUtil.reverse(s);
             IntegerModuloP ri = orderField.getElement(r);
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Candidate.java	2022-04-19 19:55:43.000000000 +0000
    @@ -137,4 +137,9 @@
         public int compareTo(Candidate o) {
             return value.compareTo(o.value);
         }
    +
    +    @Override
    +    public String toString() {
    +        return "Candidate{" + value + "}";
    +    }
     }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java	1970-01-01 00:00:00.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/CompletionMatcher.java	2022-04-19 19:55:43.000000000 +0000
    @@ -0,0 +1,48 @@
    +/*
    + * Copyright (c) 2002-2020, the original author or authors.
    + *
    + * This software is distributable under the BSD license. See the terms of the
    + * BSD license in the documentation provided with this software.
    + *
    + * https://opensource.org/licenses/BSD-3-Clause
    + */
    +package jdk.internal.org.jline.reader;
    +
    +import java.util.List;
    +import java.util.Map;
    +
    +public interface CompletionMatcher {
    +
    +    /**
    +     * Compiles completion matcher functions
    +     *
    +     * @param options LineReader options
    +     * @param prefix invoked by complete-prefix or expand-or-complete-prefix widget
    +     * @param line The parsed line within which completion has been requested
    +     * @param caseInsensitive if completion is case insensitive or not
    +     * @param errors number of errors accepted in matching
    +     * @param originalGroupName value of JLineReader variable original-group-name
    +     */
    +    void compile(Map options, boolean prefix, CompletingParsedLine line
    +            , boolean caseInsensitive, int errors, String originalGroupName);
    +
    +    /**
    +     *
    +     * @param candidates list of candidates
    +     * @return a map of candidates that completion matcher matches
    +     */
    +    List matches(List candidates);
    +
    +    /**
    +     *
    +     * @return a candidate that have exact match, null if no exact match found
    +     */
    +    Candidate exactMatch();
    +
    +    /**
    +     *
    +     * @return a common prefix of matched candidates
    +     */
    +    String getCommonPrefix();
    +
    +}
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ConfigurationPath.java	1970-01-01 00:00:00.000000000 +0000
    @@ -1,75 +0,0 @@
    -/*
    - * Copyright (c) 2002-2019, the original author or authors.
    - *
    - * This software is distributable under the BSD license. See the terms of the
    - * BSD license in the documentation provided with this software.
    - *
    - * https://opensource.org/licenses/BSD-3-Clause
    - */
    -package jdk.internal.org.jline.reader;
    -
    -import java.io.IOException;
    -import java.nio.file.Path;
    -
    -public class ConfigurationPath {
    -    private Path appConfig;
    -    private Path userConfig;
    -
    -    /**
    -     * Configuration class constructor.
    -     * @param appConfig   Application configuration directory
    -     * @param userConfig  User private configuration directory
    -     */
    -    public ConfigurationPath(Path appConfig, Path userConfig) {
    -        this.appConfig = appConfig;
    -        this.userConfig = userConfig;
    -    }
    -
    -    /**
    -     * Search configuration file first from userConfig and then appConfig directory. Returns null if file is not found.
    -     * @param  name    Configuration file name.
    -     * @return         Configuration file.
    -     *
    -     */
    -    public Path getConfig(String name) {
    -        Path out = null;
    -        if (userConfig != null && userConfig.resolve(name).toFile().exists()) {
    -            out = userConfig.resolve(name);
    -        } else if (appConfig != null && appConfig.resolve(name).toFile().exists()) {
    -            out = appConfig.resolve(name);
    -        }
    -        return out;
    -    }
    -
    -    /**
    -     * Search configuration file from userConfig directory. Returns null if file is not found.
    -     * @param  name    Configuration file name.
    -     * @return         Configuration file.
    -     * @throws         IOException   When we do not have read access to the file or directory.
    -     *
    -     */
    -    public Path getUserConfig(String name) throws IOException {
    -        return getUserConfig(name, false);
    -    }
    -
    -    /**
    -     * Search configuration file from userConfig directory. Returns null if file is not found.
    -     * @param  name    Configuration file name
    -     * @param  create  When true configuration file is created if not found.
    -     * @return         Configuration file.
    -     * @throws         IOException   When we do not have read/write access to the file or directory.
    -     */
    -    public Path getUserConfig(String name, boolean create) throws IOException {
    -        Path out = null;
    -        if (userConfig != null) {
    -            if (!userConfig.resolve(name).toFile().exists() && create) {
    -                userConfig.resolve(name).toFile().createNewFile();
    -            }
    -            if (userConfig.resolve(name).toFile().exists()) {
    -                out = userConfig.resolve(name);
    -            }
    -        }
    -        return out;
    -    }
    -
    -}
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/EndOfFileException.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002-2016, the original author or authors.
    + * Copyright (c) 2002-2020, the original author or authors.
      *
      * This software is distributable under the BSD license. See the terms of the
      * BSD license in the documentation provided with this software.
    @@ -15,6 +15,7 @@
     public class EndOfFileException extends RuntimeException {
     
         private static final long serialVersionUID = 528485360925144689L;
    +    private String partialLine;
     
         public EndOfFileException() {
         }
    @@ -34,4 +35,13 @@
         public EndOfFileException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
             super(message, cause, enableSuppression, writableStackTrace);
         }
    +
    +    public EndOfFileException partialLine(String partialLine) {
    +        this.partialLine = partialLine;
    +        return this;
    +    }
    +
    +    public String getPartialLine() {
    +        return partialLine;
    +    }
     }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReader.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002-2019, the original author or authors.
    + * Copyright (c) 2002-2021, the original author or authors.
      *
      * This software is distributable under the BSD license. See the terms of the
      * BSD license in the documentation provided with this software.
    @@ -294,7 +294,13 @@
         String COMMENT_BEGIN = "comment-begin";
         String BELL_STYLE = "bell-style";
         String PREFER_VISIBLE_BELL = "prefer-visible-bell";
    +    /** tab completion: if candidates are more than list-max a question will be asked before displaying them */
         String LIST_MAX = "list-max";
    +    /**
    +     * tab completion: if candidates are less than menu-list-max
    +     * they are displayed in a list below the field to be completed
    +     */
    +    String MENU_LIST_MAX = "menu-list-max";
         String DISABLE_HISTORY = "disable-history";
         String DISABLE_COMPLETION = "disable-completion";
         String EDITING_MODE = "editing-mode";
    @@ -303,6 +309,7 @@
         String WORDCHARS = "WORDCHARS";
         String REMOVE_SUFFIX_CHARS = "REMOVE_SUFFIX_CHARS";
         String SEARCH_TERMINATORS = "search-terminators";
    +    /** Number of matching errors that are accepted by the completion matcher */
         String ERRORS = "errors";
         /** Property for the "others" group name */
         String OTHERS_GROUP_NAME = "OTHERS_GROUP_NAME";
    @@ -310,12 +317,19 @@
         String ORIGINAL_GROUP_NAME = "ORIGINAL_GROUP_NAME";
         /** Completion style for displaying groups name */
         String COMPLETION_STYLE_GROUP = "COMPLETION_STYLE_GROUP";
    +    String COMPLETION_STYLE_LIST_GROUP = "COMPLETION_STYLE_LIST_GROUP";
         /** Completion style for displaying the current selected item */
         String COMPLETION_STYLE_SELECTION = "COMPLETION_STYLE_SELECTION";
    +    String COMPLETION_STYLE_LIST_SELECTION = "COMPLETION_STYLE_LIST_SELECTION";
         /** Completion style for displaying the candidate description */
         String COMPLETION_STYLE_DESCRIPTION = "COMPLETION_STYLE_DESCRIPTION";
    +    String COMPLETION_STYLE_LIST_DESCRIPTION = "COMPLETION_STYLE_LIST_DESCRIPTION";
         /** Completion style for displaying the matching part of candidates */
         String COMPLETION_STYLE_STARTING = "COMPLETION_STYLE_STARTING";
    +    String COMPLETION_STYLE_LIST_STARTING = "COMPLETION_STYLE_LIST_STARTING";
    +    /** Completion style for displaying the list */
    +    String COMPLETION_STYLE_BACKGROUND = "COMPLETION_STYLE_BACKGROUND";
    +    String COMPLETION_STYLE_LIST_BACKGROUND = "COMPLETION_STYLE_LIST_BACKGROUND";
         /**
          * Set the template for prompts for secondary (continuation) lines.
          * This is a prompt template as described in the class header.
    @@ -370,10 +384,20 @@
          */
         String FEATURES_MAX_BUFFER_SIZE = "features-max-buffer-size";
     
    +    /**
    +     * Min buffer size for tab auto-suggestions.
    +     * For shorter buffer sizes auto-suggestions are not resolved.
    +     */
    +    String SUGGESTIONS_MIN_BUFFER_SIZE = "suggestions-min-buffer-size";
    +
         Map> defaultKeyMaps();
     
         enum Option {
             COMPLETE_IN_WORD,
    +        /** use camel case completion matcher */
    +        COMPLETE_MATCHER_CAMELCASE,
    +        /** use type completion matcher */
    +        COMPLETE_MATCHER_TYPO(true),
             DISABLE_EVENT_EXPANSION,
             HISTORY_VERIFY,
             HISTORY_IGNORE_SPACE(true),
    @@ -386,9 +410,13 @@
             AUTO_GROUP(true),
             AUTO_MENU(true),
             AUTO_LIST(true),
    +        /** list candidates below the field to be completed */
    +        AUTO_MENU_LIST,
             RECOGNIZE_EXACT,
             /** display group name before each group (else display all group names first) */
             GROUP(true),
    +        /** when double tab to select candidate keep candidates grouped (else loose grouping) */
    +        GROUP_PERSIST,
             /** if completion is case insensitive or not */
             CASE_INSENSITIVE,
             LIST_AMBIGUOUS,
    @@ -414,7 +442,8 @@
             DELAY_LINE_WRAP,
             AUTO_PARAM_SLASH(true),
             AUTO_REMOVE_SLASH(true),
    -        USE_FORWARD_SLASH(false),
    +        /** FileNameCompleter: Use '/' character as a file directory separator */
    +        USE_FORWARD_SLASH,
             /** When hitting the <tab> key at the beginning of the line, insert a tabulation
              *  instead of completing.  This is mainly useful when {@link #BRACKETED_PASTE} is
              *  disabled, so that copy/paste of indented text does not trigger completion.
    @@ -450,6 +479,11 @@
                 this.def = def;
             }
     
    +        public final boolean isSet(Map options) {
    +            Boolean b = options.get(this);
    +            return b != null ? b : this.isDef();
    +        }
    +
             public boolean isDef() {
                 return def;
             }
    @@ -489,8 +523,9 @@
          * Equivalent to readLine(null, null, null).
          *
          * @return the line read
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
    +     * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
    +     * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
    +     * @throws java.io.IOError in case of other i/o errors
          */
         String readLine() throws UserInterruptException, EndOfFileException;
     
    @@ -502,8 +537,9 @@
          *
          * @param mask      The mask character, null or 0.
          * @return          A line that is read from the terminal, can never be null.
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
    +     * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
    +     * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
    +     * @throws java.io.IOError in case of other i/o errors
          */
         String readLine(Character mask) throws UserInterruptException, EndOfFileException;
     
    @@ -515,8 +551,9 @@
          *
          * @param prompt    The prompt to issue to the terminal, may be null.
          * @return          A line that is read from the terminal, can never be null.
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
    +     * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
    +     * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
    +     * @throws java.io.IOError in case of other i/o errors
          */
         String readLine(String prompt) throws UserInterruptException, EndOfFileException;
     
    @@ -529,8 +566,9 @@
          * @param prompt    The prompt to issue to the terminal, may be null.
          * @param mask      The mask character, null or 0.
          * @return          A line that is read from the terminal, can never be null.
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
    +     * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
    +     * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
    +     * @throws java.io.IOError in case of other i/o errors
          */
         String readLine(String prompt, Character mask) throws UserInterruptException, EndOfFileException;
     
    @@ -546,8 +584,9 @@
          * @param mask      The character mask, may be null.
          * @param buffer    The default value presented to the user to edit, may be null.
          * @return          A line that is read from the terminal, can never be null.
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
    +     * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
    +     * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
    +     * @throws java.io.IOError in case of other i/o errors
          */
         String readLine(String prompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException;
     
    @@ -568,8 +607,6 @@
          * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
          * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
          * @throws java.io.IOError in case of other i/o errors
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
          */
         String readLine(String prompt, String rightPrompt, Character mask, String buffer) throws UserInterruptException, EndOfFileException;
     
    @@ -590,8 +627,6 @@
          * @throws UserInterruptException if readLine was interrupted (using Ctrl-C for example)
          * @throws EndOfFileException if an EOF has been found (using Ctrl-D for example)
          * @throws java.io.IOError in case of other i/o errors
    -     * @throws UserInterruptException If the call was interrupted by the user.
    -     * @throws EndOfFileException     If the end of the input stream was reached.
          */
         String readLine(String prompt, String rightPrompt, MaskingCallback maskingCallback, String buffer) throws UserInterruptException, EndOfFileException;
     
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/LineReaderBuilder.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 2002-2018, the original author or authors.
    + * Copyright (c) 2002-2020, the original author or authors.
      *
      * This software is distributable under the BSD license. See the terms of the
      * BSD license in the documentation provided with this software.
    @@ -36,6 +36,7 @@
         Highlighter highlighter;
         Parser parser;
         Expander expander;
    +    CompletionMatcher completionMatcher;
     
         private LineReaderBuilder() {
         }
    @@ -103,6 +104,11 @@
             return this;
         }
     
    +    public LineReaderBuilder completionMatcher(CompletionMatcher completionMatcher) {
    +        this.completionMatcher = completionMatcher;
    +        return this;
    +    }
    +
         public LineReader build() {
             Terminal terminal = this.terminal;
             if (terminal == null) {
    @@ -133,6 +139,9 @@
             if (expander != null) {
                 reader.setExpander(expander);
             }
    +        if (completionMatcher != null) {
    +            reader.setCompletionMatcher(completionMatcher);
    +        }
             for (Map.Entry e : options.entrySet()) {
                 reader.option(e.getKey(), e.getValue());
             }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/Parser.java	2022-04-19 19:55:43.000000000 +0000
    @@ -35,16 +35,12 @@
     
         default String getCommand(final String line) {
             String out = "";
    -        Pattern  patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+.*|$)");
    +        Pattern  patternCommand = Pattern.compile("^\\s*" + REGEX_VARIABLE + "=(" + REGEX_COMMAND + ")(\\s+|$)");
             Matcher matcher = patternCommand.matcher(line);
             if (matcher.find()) {
                 out = matcher.group(1);
             } else {
                 out = line.trim().split("\\s+")[0];
    -            int idx = out.indexOf("=");
    -            if (idx > -1) {
    -                out = out.substring(idx + 1);
    -            }
                 if (!out.matches(REGEX_COMMAND)) {
                     out = "";
                 }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java	1970-01-01 00:00:00.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/PrintAboveWriter.java	2022-04-19 19:55:43.000000000 +0000
    @@ -0,0 +1,42 @@
    +/*
    + * Copyright (c) 2002-2021, the original author or authors.
    + *
    + * This software is distributable under the BSD license. See the terms of the
    + * BSD license in the documentation provided with this software.
    + *
    + * https://opensource.org/licenses/BSD-3-Clause
    + */
    +package jdk.internal.org.jline.reader;
    +
    +import java.io.StringWriter;
    +import java.io.Writer;
    +
    +/**
    + * Redirects a {@link Writer} to a {@link LineReader}'s {@link LineReader#printAbove(String)} method,
    + * which draws output above the current prompt / input line.
    + *
    + * 

    Example:

    + *
    + *     LineReader reader = LineReaderBuilder.builder().terminal(terminal).parser(parser).build();
    + *     PrintAboveWriter printAbove = new PrintAboveWriter(reader);
    + *     printAbove.write(new char[] { 'h', 'i', '!', '\n'});
    + * 
    + * + */ +public class PrintAboveWriter extends StringWriter { + private final LineReader reader; + + public PrintAboveWriter(LineReader reader) { + this.reader = reader; + } + + @Override + public void flush() { + StringBuffer buffer = getBuffer(); + int lastNewline = buffer.lastIndexOf("\n"); + if (lastNewline >= 0) { + reader.printAbove(buffer.substring(0, lastNewline + 1)); + buffer.delete(0, lastNewline + 1); + } + } +} diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/ScriptEngine.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2002-2020, the original author or authors. - * - * This software is distributable under the BSD license. See the terms of the - * BSD license in the documentation provided with this software. - * - * https://opensource.org/licenses/BSD-3-Clause - */ -package jdk.internal.org.jline.reader; - -import java.io.File; -import java.nio.file.Path; -import java.util.*; - -/** - * Manage scriptEngine variables, statements and script execution. - * - * @author Matti Rinta-Nikkola - */ -public interface ScriptEngine { - - /** - * - * @return scriptEngine name - */ - String getEngineName(); - - /** - * - * @return script file name extensions - */ - Collection getExtensions(); - - /** - * Tests if console variable exists - * @param name - * @return true if variable exists - */ - boolean hasVariable(String name); - - /** - * Creates variable - * @param name of the variable - * @param value of the variable - */ - void put(String name, Object value); - - /** - * Gets variable value - * @param name of the variable - * @return value of the variable - */ - Object get(String name); - - /** - * Gets all variables with values - * @return map of the variables - */ - default Map find() { - return find(null); - } - - /** - * Gets all the variables that match the name. Name can contain * wild cards. - * @param name of the variable - * @return map of the variables - */ - Map find(String name); - - /** - * Deletes variables. Variable name cab contain * wild cards. - * @param vars - */ - void del(String... vars); - - /** - * Converts object to JSON string. - * @param object object to convert to JSON - * @return formatted JSON string - */ - String toJson(Object object); - - /** - * Converts object to string. - * @param object object to convert to string - * @return object string value - */ - String toString(Object object); - - /** - * Substitute variable reference with its value. - * @param variable - * @return Substituted variable - * @throws Exception - */ - default Object expandParameter(String variable) { - return expandParameter(variable, ""); - } - - /** - * Substitute variable reference with its value. - * @param variable - * @param format serialization format - * @return Substituted variable - * @throws Exception - */ - Object expandParameter(String variable, String format); - - /** - * Persists object value to file. - * @param file - * @param object - */ - default void persist(Path file, Object object) { - persist(file, object, "JSON"); - } - - /** - * Persists object value to file. - * @param file - * @param object - * @param format - */ - void persist(Path file, Object object, String format); - - /** - * Executes scriptEngine statement - * @param statement - * @return result - * @throws Exception - */ - Object execute(String statement) throws Exception; - - /** - * Executes scriptEngine script - * @param script - * @return result - * @throws Exception - */ - default Object execute(File script) throws Exception { - return execute(script, null); - } - - /** - * Executes scriptEngine script - * @param script - * @param args - * @return - * @throws Exception - */ - Object execute(File script, Object[] args) throws Exception; - -} diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/CompletionMatcherImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2002-2021, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.reader.impl; + +import jdk.internal.org.jline.reader.Candidate; +import jdk.internal.org.jline.reader.CompletingParsedLine; +import jdk.internal.org.jline.reader.CompletionMatcher; +import jdk.internal.org.jline.reader.LineReader; +import jdk.internal.org.jline.utils.AttributedString; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class CompletionMatcherImpl implements CompletionMatcher { + protected Predicate exact; + protected List>, Map>>> matchers; + private Map> matching; + private boolean caseInsensitive; + + public CompletionMatcherImpl() { + } + + protected void reset(boolean caseInsensitive) { + this.caseInsensitive = caseInsensitive; + exact = s -> false; + matchers = new ArrayList<>(); + matching = null; + } + + @Override + public void compile(Map options, boolean prefix, CompletingParsedLine line + , boolean caseInsensitive, int errors, String originalGroupName) { + reset(caseInsensitive); + defaultMatchers(options, prefix, line, caseInsensitive, errors, originalGroupName); + } + + @Override + public List matches(List candidates) { + matching = Collections.emptyMap(); + Map> sortedCandidates = sort(candidates); + for (Function>, + Map>> matcher : matchers) { + matching = matcher.apply(sortedCandidates); + if (!matching.isEmpty()) { + break; + } + } + return !matching.isEmpty() ? matching.entrySet().stream().flatMap(e -> e.getValue().stream()).collect(Collectors.toList()) + : new ArrayList<>(); + } + + @Override + public Candidate exactMatch() { + if (matching == null) { + throw new IllegalStateException(); + } + return matching.values().stream().flatMap(Collection::stream) + .filter(Candidate::complete) + .filter(c -> exact.test(c.value())) + .findFirst().orElse(null); + } + + @Override + public String getCommonPrefix() { + if (matching == null) { + throw new IllegalStateException(); + } + String commonPrefix = null; + for (String key : matching.keySet()) { + commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive); + } + return commonPrefix; + } + + /** + * Default JLine matchers + */ + protected void defaultMatchers(Map options, boolean prefix, CompletingParsedLine line + , boolean caseInsensitive, int errors, String originalGroupName) { + // Find matchers + // TODO: glob completion + String wd = line.word(); + String wdi = caseInsensitive ? wd.toLowerCase() : wd; + String wp = wdi.substring(0, line.wordCursor()); + if (prefix) { + matchers = new ArrayList<>(Arrays.asList( + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wp)), + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp)) + )); + if (LineReader.Option.COMPLETE_MATCHER_TYPO.isSet(options)) { + matchers.add(typoMatcher(wp, errors, caseInsensitive, originalGroupName)); + } + exact = s -> caseInsensitive ? s.equalsIgnoreCase(wp) : s.equals(wp); + } else if (!LineReader.Option.EMPTY_WORD_OPTIONS.isSet(options) && wd.length() == 0) { + matchers = new ArrayList<>(Collections.singletonList(simpleMatcher(s -> !s.startsWith("-")))); + exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd); + } else { + if (LineReader.Option.COMPLETE_IN_WORD.isSet(options)) { + String ws = wdi.substring(line.wordCursor()); + Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); + Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); + matchers = new ArrayList<>(Arrays.asList( + simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s).matches()), + simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s).matches()) + )); + } else { + matchers = new ArrayList<>(Arrays.asList( + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wdi)), + simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi)) + )); + } + if (LineReader.Option.COMPLETE_MATCHER_CAMELCASE.isSet(options)) { + matchers.add(simpleMatcher(s -> camelMatch(wd, 0, s, 0))); + } + if (LineReader.Option.COMPLETE_MATCHER_TYPO.isSet(options)) { + matchers.add(typoMatcher(wdi, errors, caseInsensitive, originalGroupName)); + } + exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd); + } + } + + protected Function>, + Map>> simpleMatcher(Predicate predicate) { + return m -> m.entrySet().stream() + .filter(e -> predicate.test(e.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + protected Function>, + Map>> typoMatcher(String word, int errors, boolean caseInsensitive, String originalGroupName) { + return m -> { + Map> map = m.entrySet().stream() + .filter(e -> ReaderUtils.distance(word, caseInsensitive ? e.getKey().toLowerCase() : e.getKey()) < errors) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + if (map.size() > 1) { + map.computeIfAbsent(word, w -> new ArrayList<>()) + .add(new Candidate(word, word, originalGroupName, null, null, null, false)); + } + return map; + }; + } + + protected boolean camelMatch(String word, int i, String candidate, int j) { + if (word.length() <= i) { + return true; + } else if (candidate.length() <= j) { + return false; + } else { + char c = word.charAt(i); + if (c == candidate.charAt(j)) { + return camelMatch(word, i + 1, candidate, j + 1); + } else { + for (int j1 = j; j1 < candidate.length(); j1++) { + if (Character.isUpperCase(candidate.charAt(j1))) { + if (Character.toUpperCase(c) == candidate.charAt(j1)) { + if (camelMatch(word, i + 1, candidate, j1 + 1)) { + return true; + } + } + } + } + return false; + } + } + } + + private Map> sort(List candidates) { + // Build a list of sorted candidates + Map> sortedCandidates = new HashMap<>(); + for (Candidate candidate : candidates) { + sortedCandidates + .computeIfAbsent(AttributedString.fromAnsi(candidate.value()).toString(), s -> new ArrayList<>()) + .add(candidate); + } + return sortedCandidates; + } + + private String getCommonStart(String str1, String str2, boolean caseInsensitive) { + int[] s1 = str1.codePoints().toArray(); + int[] s2 = str2.codePoints().toArray(); + int len = 0; + while (len < Math.min(s1.length, s2.length)) { + int ch1 = s1[len]; + int ch2 = s2[len]; + if (ch1 != ch2 && caseInsensitive) { + ch1 = Character.toUpperCase(ch1); + ch2 = Character.toUpperCase(ch2); + if (ch1 != ch2) { + ch1 = Character.toLowerCase(ch1); + ch2 = Character.toLowerCase(ch2); + } + } + if (ch1 != ch2) { + break; + } + len++; + } + return new String(s1, 0, len); + } + +} \ No newline at end of file diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultHighlighter.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2021, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -19,8 +19,8 @@ import jdk.internal.org.jline.utils.WCWidth; public class DefaultHighlighter implements Highlighter { - private Pattern errorPattern; - private int errorIndex = -1; + protected Pattern errorPattern; + protected int errorIndex = -1; @Override public void setErrorPattern(Pattern errorPattern) { diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/DefaultParser.java 2022-04-19 19:55:43.000000000 +0000 @@ -24,7 +24,7 @@ ROUND, // () CURLY, // {} SQUARE, // [] - ANGLE; // <> + ANGLE // <> } private char[] quoteChars = {'\'', '"'}; @@ -39,8 +39,8 @@ private char[] closingBrackets = null; - private String regexVariable = "[a-zA-Z_]{1,}[a-zA-Z0-9_-]*((.|\\['|\\[\\\"|\\[)[a-zA-Z0-9_-]*(|'\\]|\\\"\\]|\\])){0,1}"; - private String regexCommand = "[:]{0,1}[a-zA-Z]{1,}[a-zA-Z0-9_-]*"; + private String regexVariable = "[a-zA-Z_]+[a-zA-Z0-9_-]*((\\.|\\['|\\[\"|\\[)[a-zA-Z0-9_-]*(|']|\"]|]))?"; + private String regexCommand = "[:]?[a-zA-Z]+[a-zA-Z0-9_-]*"; private int commandGroup = 4; // @@ -175,23 +175,25 @@ @Override public boolean validVariableName(String name) { - return name != null && name.matches(regexVariable); + return name != null && regexVariable != null && name.matches(regexVariable); } @Override public String getCommand(final String line) { String out = ""; - Pattern patternCommand = Pattern.compile("^\\s*" + regexVariable + "=(" + regexCommand + ")(\\s+.*|$)"); - Matcher matcher = patternCommand.matcher(line); - if (matcher.find()) { - out = matcher.group(commandGroup); - } else { - out = line.trim().split("\\s+")[0]; - int idx = out.indexOf("="); - if (idx > -1) { - out = out.substring(idx + 1); + boolean checkCommandOnly = regexVariable == null; + if (!checkCommandOnly) { + Pattern patternCommand = Pattern.compile("^\\s*" + regexVariable + "=(" + regexCommand + ")(\\s+|$)"); + Matcher matcher = patternCommand.matcher(line); + if (matcher.find()) { + out = matcher.group(commandGroup); + } else { + checkCommandOnly = true; } + } + if (checkCommandOnly) { + out = line.trim().split("\\s+")[0]; if (!out.matches(regexCommand)) { out = ""; } @@ -202,10 +204,12 @@ @Override public String getVariable(final String line) { String out = null; - Pattern patternCommand = Pattern.compile("^\\s*(" + regexVariable + ")\\s*=[^=~].*"); - Matcher matcher = patternCommand.matcher(line); - if (matcher.find()) { - out = matcher.group(1); + if (regexVariable != null) { + Pattern patternCommand = Pattern.compile("^\\s*(" + regexVariable + ")\\s*=[^=~].*"); + Matcher matcher = patternCommand.matcher(line); + if (matcher.find()) { + out = matcher.group(1); + } } return out; } @@ -289,7 +293,7 @@ rawWordLength = rawWordCursor; } - if (context != ParseContext.COMPLETE) { + if (context != ParseContext.COMPLETE && context != ParseContext.SPLIT_LINE) { if (eofOnEscapedNewLine && isEscapeChar(line, line.length() - 1)) { throw new EOFError(-1, -1, "Escaped new line", "newline"); } @@ -609,25 +613,28 @@ } } if (escapeChars != null) { - // Completion is protected by an opening quote: - // Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does. - // Also, close the quote at the end - if (openingQuote != null) { - needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote); - } - // Completion is protected by middle quotes: - // Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does. - else if (middleQuotes) { - needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)); - } - // No quote protection, need to escape everything: delimiter chars (spaces), quote chars - // and escapes themselves - else { - needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) || isRawQuoteChar(sb.charAt(i)); - } - for (int i = 0; i < sb.length(); i++) { - if (needToBeEscaped.test(i)) { - sb.insert(i++, escapeChars[0]); + if (escapeChars.length > 0) { + // Completion is protected by an opening quote: + // Delimiters (spaces) don't need to be escaped, nor do other quotes, but everything else does. + // Also, close the quote at the end + if (openingQuote != null) { + needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)) || String.valueOf(sb.charAt(i)).equals(openingQuote); + } + // Completion is protected by middle quotes: + // Delimiters (spaces) don't need to be escaped, nor do quotes, but everything else does. + else if (middleQuotes) { + needToBeEscaped = i -> isRawEscapeChar(sb.charAt(i)); + } + // No quote protection, need to escape everything: delimiter chars (spaces), quote chars + // and escapes themselves + else { + needToBeEscaped = i -> isDelimiterChar(sb, i) || isRawEscapeChar(sb.charAt(i)) + || isRawQuoteChar(sb.charAt(i)); + } + for (int i = 0; i < sb.length(); i++) { + if (needToBeEscaped.test(i)) { + sb.insert(i++, escapeChars[0]); + } } } } else if (openingQuote == null && !middleQuotes) { diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/LineReaderImpl.java 2022-04-19 19:55:43.000000000 +0000 @@ -20,7 +20,6 @@ import java.lang.reflect.Constructor; import java.time.Instant; import java.util.*; -import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; import java.util.function.*; @@ -46,9 +45,9 @@ import jdk.internal.org.jline.utils.Curses; import jdk.internal.org.jline.utils.Display; import jdk.internal.org.jline.utils.InfoCmp.Capability; -import jdk.internal.org.jline.utils.Levenshtein; import jdk.internal.org.jline.utils.Log; import jdk.internal.org.jline.utils.Status; +import jdk.internal.org.jline.utils.StyleResolver; import jdk.internal.org.jline.utils.WCWidth; import static jdk.internal.org.jline.keymap.KeyMap.alt; @@ -80,18 +79,26 @@ public static final String DEFAULT_SEARCH_TERMINATORS = "\033\012"; public static final String DEFAULT_BELL_STYLE = ""; public static final int DEFAULT_LIST_MAX = 100; + public static final int DEFAULT_MENU_LIST_MAX = Integer.MAX_VALUE; public static final int DEFAULT_ERRORS = 2; public static final long DEFAULT_BLINK_MATCHING_PAREN = 500L; public static final long DEFAULT_AMBIGUOUS_BINDING = 1000L; public static final String DEFAULT_SECONDARY_PROMPT_PATTERN = "%M> "; public static final String DEFAULT_OTHERS_GROUP_NAME = "others"; public static final String DEFAULT_ORIGINAL_GROUP_NAME = "original"; - public static final String DEFAULT_COMPLETION_STYLE_STARTING = "36"; // cyan - public static final String DEFAULT_COMPLETION_STYLE_DESCRIPTION = "90"; // dark gray - public static final String DEFAULT_COMPLETION_STYLE_GROUP = "35;1"; // magenta - public static final String DEFAULT_COMPLETION_STYLE_SELECTION = "7"; // inverted + public static final String DEFAULT_COMPLETION_STYLE_STARTING = "fg:cyan"; + public static final String DEFAULT_COMPLETION_STYLE_DESCRIPTION = "fg:bright-black"; + public static final String DEFAULT_COMPLETION_STYLE_GROUP = "fg:bright-magenta,bold"; + public static final String DEFAULT_COMPLETION_STYLE_SELECTION = "inverse"; + public static final String DEFAULT_COMPLETION_STYLE_BACKGROUND = "bg:default"; + public static final String DEFAULT_COMPLETION_STYLE_LIST_STARTING = DEFAULT_COMPLETION_STYLE_STARTING; + public static final String DEFAULT_COMPLETION_STYLE_LIST_DESCRIPTION = DEFAULT_COMPLETION_STYLE_DESCRIPTION; + public static final String DEFAULT_COMPLETION_STYLE_LIST_GROUP = "fg:black,bold"; + public static final String DEFAULT_COMPLETION_STYLE_LIST_SELECTION = DEFAULT_COMPLETION_STYLE_SELECTION; + public static final String DEFAULT_COMPLETION_STYLE_LIST_BACKGROUND = "bg:bright-magenta"; public static final int DEFAULT_INDENTATION = 0; public static final int DEFAULT_FEATURES_MAX_BUFFER_SIZE = 1000; + public static final int DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE = 1; private static final int MIN_ROWS = 3; @@ -162,6 +169,7 @@ protected Highlighter highlighter = new DefaultHighlighter(); protected Parser parser = new DefaultParser(); protected Expander expander = new DefaultExpander(); + protected CompletionMatcher completionMatcher = new CompletionMatcherImpl(); // // State variables @@ -270,6 +278,8 @@ */ protected List commandsBuffer = new ArrayList<>(); + int candidateStartPosition = 0; + public LineReaderImpl(Terminal terminal) throws IOException { this(terminal, null, null); } @@ -419,6 +429,10 @@ this.expander = expander; } + public void setCompletionMatcher(CompletionMatcher completionMatcher) { + this.completionMatcher = completionMatcher; + } + // // Line Reading // @@ -636,7 +650,7 @@ } Binding o = readBinding(getKeys(), local); if (o == null) { - throw new EndOfFileException(); + throw new EndOfFileException().partialLine(buf.length() > 0 ? buf.toString() : null); } Log.trace("Binding: ", o); if (buf.length() == 0 && getLastBinding().charAt(0) == originalAttributes.getControlChar(ControlChar.VEOF)) { @@ -741,11 +755,7 @@ size.copy(terminal.getBufferSize()); display = new Display(terminal, false); - if (size.getRows() == 0 || size.getColumns() == 0) { - display.resize(1, Integer.MAX_VALUE); - } else { - display.resize(size.getRows(), size.getColumns()); - } + display.resize(size.getRows(), size.getColumns()); if (isSet(Option.DELAY_LINE_WRAP)) display.setDelayLineWrap(true); } @@ -1049,8 +1059,7 @@ @Override public boolean isSet(Option option) { - Boolean b = options.get(option); - return b != null ? b : option.isDef(); + return option.isSet(options); } @Override @@ -1070,10 +1079,13 @@ @Override public void editAndAddInBuffer(File file) throws Exception { + if (isSet(Option.BRACKETED_PASTE)) { + terminal.writer().write(BRACKETED_PASTE_OFF); + } Constructor ctor = Class.forName("org.jline.builtins.Nano").getConstructor(Terminal.class, File.class); Editor editor = (Editor) ctor.newInstance(terminal, new File(file.getParent())); editor.setRestricted(true); - editor.open(Arrays.asList(file.getName())); + editor.open(Collections.singletonList(file.getName())); editor.run(); BufferedReader br = new BufferedReader(new FileReader(file)); String line; @@ -1344,7 +1356,7 @@ protected boolean viForwardWord() { if (count < 0) { - return callNeg(this::backwardWord); + return callNeg(this::viBackwardWord); } while (count-- > 0) { if (isViAlphaNum(buf.currChar())) { @@ -1395,21 +1407,7 @@ } protected boolean emacsForwardWord() { - if (count < 0) { - return callNeg(this::emacsBackwardWord); - } - while (count-- > 0) { - while (buf.cursor() < buf.length() && !isWord(buf.currChar())) { - buf.move(1); - } - if (isInViChangeOperation() && count == 0) { - return true; - } - while (buf.cursor() < buf.length() && isWord(buf.currChar())) { - buf.move(1); - } - } - return true; + return forwardWord(); } protected boolean viForwardBlankWordEnd() { @@ -1481,7 +1479,7 @@ protected boolean viBackwardWord() { if (count < 0) { - return callNeg(this::backwardWord); + return callNeg(this::viForwardWord); } while (count-- > 0) { int nl = 0; @@ -1584,24 +1582,7 @@ } protected boolean emacsBackwardWord() { - if (count < 0) { - return callNeg(this::emacsForwardWord); - } - while (count-- > 0) { - while (buf.cursor() > 0) { - buf.move(-1); - if (isWord(buf.currChar())) { - break; - } - } - while (buf.cursor() > 0) { - buf.move(-1); - if (!isWord(buf.currChar())) { - break; - } - } - } - return true; + return backwardWord(); } protected boolean backwardDeleteWord() { @@ -2557,7 +2538,7 @@ } terminal.puts(Capability.keypad_local); terminal.trackMouse(Terminal.MouseTracking.Off); - if (isSet(Option.BRACKETED_PASTE)) + if (isSet(Option.BRACKETED_PASTE) && !isTerminalDumb()) terminal.writer().write(BRACKETED_PASTE_OFF); flush(); } @@ -2720,7 +2701,7 @@ starts.add(new Pair<>(index, m.start())); } return starts; - } + } private String doGetSearchPattern() { StringBuilder sb = new StringBuilder(); @@ -3908,7 +3889,7 @@ } List newLinesToDisplay = new ArrayList<>(); - int displaySize = size.getRows() - (status != null ? status.size() : 0); + int displaySize = displayRows(status); if (newLines.size() > displaySize && !isTerminalDumb()) { StringBuilder sb = new StringBuilder(">...."); // blanks are needed when displaying command completion candidate list @@ -3964,13 +3945,12 @@ } History history = getHistory(); StringBuilder sb = new StringBuilder(); - char prev = '0'; - for (char c: buffer.toCharArray()) { - if ((c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^') && prev != '\\' ) { + for (char c: buffer.replace("\\", "\\\\").toCharArray()) { + if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '*' + || c == '$' || c == '.' || c == '?' || c == '+') { sb.append('\\'); } sb.append(c); - prev = c; } Pattern pattern = Pattern.compile(sb.toString() + ".*", Pattern.DOTALL); Iterator iter = history.reverseIterator(history.last()); @@ -4002,7 +3982,7 @@ AttributedStringBuilder full = new AttributedStringBuilder().tabs(TAB_WIDTH); full.append(prompt); full.append(tNewBuf); - if (doAutosuggestion) { + if (doAutosuggestion && !isTerminalDumb()) { String lastBinding = getLastBinding() != null ? getLastBinding() : ""; if (autosuggestion == SuggestionType.HISTORY) { AttributedStringBuilder sb = new AttributedStringBuilder(); @@ -4010,8 +3990,9 @@ sb.styled(AttributedStyle::faint, tailTip); full.append(sb.toAttributedString()); } else if (autosuggestion == SuggestionType.COMPLETER) { - if (buf.length() > 0 && buf.length() == buf.cursor() - && (!lastBinding.equals("\t") || buf.prevChar() == ' ' || buf.prevChar() == '=')) { + if (buf.length() >= getInt(SUGGESTIONS_MIN_BUFFER_SIZE, DEFAULT_SUGGESTIONS_MIN_BUFFER_SIZE) + && buf.length() == buf.cursor() + && (!lastBinding.equals("\t") || buf.prevChar() == ' ' || buf.prevChar() == '=')) { clearChoices(); listChoices(true); } else if (!lastBinding.equals("\t")) { @@ -4184,6 +4165,9 @@ List missings = new ArrayList<>(); if (computePrompts && secondaryPromptPattern.contains("%P")) { width = prompt.columnLength(); + if (width > size.getColumns() || prompt.contains('\n')) { + width = new TerminalLine(prompt.toString(), 0, size.getColumns()).getEndLine().length(); + } for (int line = 0; line < lines.size() - 1; line++) { AttributedString prompt; buf.append(lines.get(line)).append("\n"); @@ -4398,6 +4382,9 @@ } } catch (Exception e) { Log.info("Error while finding completion candidates", e); + if (Log.isDebugEnabled()) { + e.printStackTrace(); + } return false; } @@ -4423,79 +4410,17 @@ boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE); int errors = getInt(ERRORS, DEFAULT_ERRORS); - // Build a list of sorted candidates - Map> sortedCandidates = new HashMap<>(); - for (Candidate cand : candidates) { - sortedCandidates - .computeIfAbsent(AttributedString.fromAnsi(cand.value()).toString(), s -> new ArrayList<>()) - .add(cand); - } - - // Find matchers - // TODO: glob completion - List>, - Map>>> matchers; - Predicate exact; - if (prefix) { - String wd = line.word(); - String wdi = caseInsensitive ? wd.toLowerCase() : wd; - String wp = wdi.substring(0, line.wordCursor()); - matchers = Arrays.asList( - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wp)), - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wp)), - typoMatcher(wp, errors, caseInsensitive) - ); - exact = s -> caseInsensitive ? s.equalsIgnoreCase(wp) : s.equals(wp); - } else if (isSet(Option.COMPLETE_IN_WORD)) { - String wd = line.word(); - String wdi = caseInsensitive ? wd.toLowerCase() : wd; - String wp = wdi.substring(0, line.wordCursor()); - String ws = wdi.substring(line.wordCursor()); - Pattern p1 = Pattern.compile(Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); - Pattern p2 = Pattern.compile(".*" + Pattern.quote(wp) + ".*" + Pattern.quote(ws) + ".*"); - matchers = Arrays.asList( - simpleMatcher(s -> p1.matcher(caseInsensitive ? s.toLowerCase() : s).matches()), - simpleMatcher(s -> p2.matcher(caseInsensitive ? s.toLowerCase() : s).matches()), - typoMatcher(wdi, errors, caseInsensitive) - ); - exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd); - } else { - String wd = line.word(); - String wdi = caseInsensitive ? wd.toLowerCase() : wd; - if (isSet(Option.EMPTY_WORD_OPTIONS) || wd.length() > 0) { - matchers = Arrays.asList( - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).startsWith(wdi)), - simpleMatcher(s -> (caseInsensitive ? s.toLowerCase() : s).contains(wdi)), - typoMatcher(wdi, errors, caseInsensitive) - ); - } else { - matchers = Arrays.asList( - simpleMatcher(s -> !s.startsWith("-")) - ); - } - exact = s -> caseInsensitive ? s.equalsIgnoreCase(wd) : s.equals(wd); - } + completionMatcher.compile(options, prefix, line, caseInsensitive, errors, getOriginalGroupName()); // Find matching candidates - Map> matching = Collections.emptyMap(); - for (Function>, - Map>> matcher : matchers) { - matching = matcher.apply(sortedCandidates); - if (!matching.isEmpty()) { - break; - } - } - + List possible = completionMatcher.matches(candidates); // If we have no matches, bail out - if (matching.isEmpty()) { + if (possible.isEmpty()) { return false; } size.copy(terminal.getSize()); try { // If we only need to display the list, do it now if (lst == CompletionType.List) { - List possible = matching.entrySet().stream() - .flatMap(e -> e.getValue().stream()) - .collect(Collectors.toList()); doList(possible, line.word(), false, line::escape, forSuggestion); return !possible.isEmpty(); } @@ -4503,16 +4428,12 @@ // Check if there's a single possible match Candidate completion = null; // If there's a single possible completion - if (matching.size() == 1) { - completion = matching.values().stream().flatMap(Collection::stream) - .findFirst().orElse(null); + if (possible.size() == 1) { + completion = possible.get(0); } // Or if RECOGNIZE_EXACT is set, try to find an exact match else if (isSet(Option.RECOGNIZE_EXACT)) { - completion = matching.values().stream().flatMap(Collection::stream) - .filter(Candidate::complete) - .filter(c -> exact.test(c.value())) - .findFirst().orElse(null); + completion = completionMatcher.exactMatch(); } // Complete and exit if (completion != null && !completion.value().isEmpty()) { @@ -4531,6 +4452,9 @@ } } if (completion.suffix() != null) { + if (autosuggestion == SuggestionType.COMPLETER) { + listChoices(true); + } redisplay(); Binding op = readBinding(getKeys()); if (op != null) { @@ -4549,10 +4473,6 @@ return true; } - List possible = matching.entrySet().stream() - .flatMap(e -> e.getValue().stream()) - .collect(Collectors.toList()); - if (useMenu) { buf.move(line.word().length() - line.wordCursor()); buf.backspace(line.word().length()); @@ -4570,10 +4490,7 @@ } // Now, we need to find the unambiguous completion // TODO: need to find common suffix - String commonPrefix = null; - for (String key : matching.keySet()) { - commonPrefix = commonPrefix == null ? key : getCommonStart(commonPrefix, key, caseInsensitive); - } + String commonPrefix = completionMatcher.getCommonPrefix(); boolean hasUnambiguous = commonPrefix.startsWith(current) && !commonPrefix.equals(current); if (hasUnambiguous) { @@ -4641,7 +4558,7 @@ protected Comparator getCandidateComparator(boolean caseInsensitive, String word) { String wdi = caseInsensitive ? word.toLowerCase() : word; - ToIntFunction wordDistance = w -> distance(wdi, caseInsensitive ? w.toLowerCase() : w); + ToIntFunction wordDistance = w -> ReaderUtils.distance(wdi, caseInsensitive ? w.toLowerCase() : w); return Comparator .comparing(Candidate::value, Comparator.comparingInt(wordDistance)) .thenComparing(Comparator.naturalOrder()); @@ -4688,37 +4605,6 @@ } } - private Function>, - Map>> simpleMatcher(Predicate pred) { - return m -> m.entrySet().stream() - .filter(e -> pred.test(e.getKey())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - } - - private Function>, - Map>> typoMatcher(String word, int errors, boolean caseInsensitive) { - return m -> { - Map> map = m.entrySet().stream() - .filter(e -> distance(word, caseInsensitive ? e.getKey() : e.getKey().toLowerCase()) < errors) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - if (map.size() > 1) { - map.computeIfAbsent(word, w -> new ArrayList<>()) - .add(new Candidate(word, word, getOriginalGroupName(), null, null, null, false)); - } - return map; - }; - } - - private int distance(String word, String cand) { - if (word.length() < cand.length()) { - int d1 = Levenshtein.distance(word, cand.substring(0, Math.min(cand.length(), word.length()))); - int d2 = Levenshtein.distance(word, cand); - return Math.min(d1, d2); - } else { - return Levenshtein.distance(word, cand); - } - } - protected boolean nextBindingIsComplete() { redisplay(); KeyMap keyMap = keyMaps.get(MENU); @@ -4731,6 +4617,19 @@ } } + private int displayRows() { + return displayRows(Status.getStatus(terminal, false)); + } + + private int displayRows(Status status) { + return size.getRows() - (status != null ? status.size() : 0); + } + + private int promptLines() { + AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); + return text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); + } + private class MenuSupport implements Supplier { final List possible; final BiFunction escaper; @@ -4850,10 +4749,7 @@ // Compute displayed prompt PostResult pr = computePost(possible, completion(), null, completed); - AttributedString text = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); - int promptLines = text.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); - Status status = Status.getStatus(terminal, false); - int displaySize = size.getRows() - (status != null ? status.size() : 0) - promptLines; + int displaySize = displayRows() - promptLines(); if (pr.lines > displaySize) { int displayed = displaySize - 1; if (pr.selectedLine >= 0) { @@ -4902,7 +4798,13 @@ original.sort(getCandidateComparator(caseInsensitive, completed)); mergeCandidates(original); computePost(original, null, possible, completed); - + // candidate grouping is not supported by MenuSupport + boolean defaultAutoGroup = isSet(Option.AUTO_GROUP); + boolean defaultGroup = isSet(Option.GROUP); + if (!isSet(Option.GROUP_PERSIST)) { + option(Option.AUTO_GROUP, false); + option(Option.GROUP, false); + } // Build menu support MenuSupport menuSupport = new MenuSupport(original, completed, escaper); post = menuSupport; @@ -4959,17 +4861,21 @@ pushBackBinding(true); } post = null; + option(Option.AUTO_GROUP, defaultAutoGroup); + option(Option.GROUP, defaultGroup); return true; } } doAutosuggestion = false; callWidget(REDISPLAY); } + option(Option.AUTO_GROUP, defaultAutoGroup); + option(Option.GROUP, defaultGroup); return false; } protected boolean clearChoices() { - return doList(new ArrayList(), "", false, null, false); + return doList(new ArrayList<>(), "", false, null, false); } protected boolean doList(List possible @@ -5009,14 +4915,14 @@ boolean caseInsensitive = isSet(Option.CASE_INSENSITIVE); StringBuilder sb = new StringBuilder(); + candidateStartPosition = 0; while (true) { String current = completed + sb.toString(); List cands; if (sb.length() > 0) { - cands = possible.stream() - .filter(c -> caseInsensitive - ? c.value().toLowerCase().startsWith(current.toLowerCase()) - : c.value().startsWith(current)) + completionMatcher.compile(options, false, new CompletingWord(current), caseInsensitive, 0 + , null); + cands = completionMatcher.matches(possible).stream() .sorted(getCandidateComparator(caseInsensitive, current)) .collect(Collectors.toList()); } else { @@ -5024,6 +4930,9 @@ .sorted(getCandidateComparator(caseInsensitive, current)) .collect(Collectors.toList()); } + if (isSet(Option.AUTO_MENU_LIST) && candidateStartPosition == 0) { + candidateStartPosition = candidateStartPosition(cands); + } post = () -> { AttributedString t = insertSecondaryPrompts(AttributedStringBuilder.append(prompt, buf.toString()), new ArrayList<>()); int pl = t.columnSplitLength(size.getColumns(), false, display.delayLineWrap()).size(); @@ -5035,10 +4944,11 @@ redisplay(false); buf.cursor(oldCursor); println(); - List ls = postResult.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); + List ls = pr.post.columnSplitLength(size.getColumns(), false, display.delayLineWrap()); Display d = new Display(terminal, false); d.resize(size.getRows(), size.getColumns()); d.update(ls, -1); + println(); redrawLine(); return new AttributedString(""); } @@ -5089,6 +4999,59 @@ } } + private static class CompletingWord implements CompletingParsedLine { + private final String word; + + public CompletingWord(String word) { + this.word = word; + } + + @Override + public CharSequence escape(CharSequence candidate, boolean complete) { + return null; + } + + @Override + public int rawWordCursor() { + return word.length(); + } + + @Override + public int rawWordLength() { + return word.length(); + } + + @Override + public String word() { + return word; + } + + @Override + public int wordCursor() { + return word.length(); + } + + @Override + public int wordIndex() { + return 0; + } + + @Override + public List words() { + return null; + } + + @Override + public String line() { + return word; + } + + @Override + public int cursor() { + return word.length(); + } + } + protected static class PostResult { final AttributedString post; final int lines; @@ -5156,6 +5119,63 @@ private static final String DESC_SUFFIX = ")"; private static final int MARGIN_BETWEEN_DISPLAY_AND_DESC = 1; private static final int MARGIN_BETWEEN_COLUMNS = 3; + private static final int MENU_LIST_WIDTH = 25; + + private static class TerminalLine { + private String endLine; + private int startPos; + + public TerminalLine(String line, int startPos, int width) { + this.startPos = startPos; + endLine = line.substring(line.lastIndexOf('\n') + 1); + boolean first = true; + while (endLine.length() + (first ? startPos : 0) > width) { + if (first) { + endLine = endLine.substring(width - startPos); + } else { + endLine = endLine.substring(width); + } + first = false; + } + if (!first) { + this.startPos = 0; + } + } + + public int getStartPos() { + return startPos; + } + + public String getEndLine() { + return endLine; + } + } + + private int candidateStartPosition(List cands) { + List values = cands.stream().map(c -> AttributedString.stripAnsi(c.displ())) + .filter(c -> !c.matches("\\w+") && c.length() > 1).collect(Collectors.toList()); + Set notDelimiters = new HashSet<>(); + values.forEach(v -> v.substring(0, v.length() - 1).chars() + .filter(c -> !Character.isDigit(c) && !Character.isAlphabetic(c)) + .forEach(c -> notDelimiters.add(Character.toString((char)c)))); + int width = size.getColumns(); + int promptLength = prompt != null ? prompt.length() : 0; + if (promptLength > 0) { + TerminalLine tp = new TerminalLine(prompt.toString(), 0, width); + promptLength = tp.getEndLine().length(); + } + TerminalLine tl = new TerminalLine(buf.substring(0, buf.cursor()), promptLength, width); + int out = tl.getStartPos(); + String buffer = tl.getEndLine(); + for (int i = buffer.length(); i > 0; i--) { + if (buffer.substring(0, i).matches(".*\\W") + && !notDelimiters.contains(buffer.substring(i - 1, i))) { + out += i; + break; + } + } + return out; + } @SuppressWarnings("unchecked") protected PostResult toColumns(List items, Candidate selection, String completed, Function wcwidth, int width, boolean rowsFirst) { @@ -5163,6 +5183,7 @@ // TODO: support Option.LIST_PACKED // Compute column width int maxWidth = 0; + int listSize = 0; for (Object item : items) { if (item instanceof String) { int len = wcwidth.apply((String) item); @@ -5170,6 +5191,7 @@ } else if (item instanceof List) { for (Candidate cand : (List) item) { + listSize++; int len = wcwidth.apply(cand.displ()); if (cand.descr() != null) { len += MARGIN_BETWEEN_DISPLAY_AND_DESC; @@ -5183,8 +5205,33 @@ } // Build columns AttributedStringBuilder sb = new AttributedStringBuilder(); - for (Object list : items) { - toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, out); + if (listSize > 0) { + if (isSet(Option.AUTO_MENU_LIST) + && listSize < Math.min(getInt(MENU_LIST_MAX, DEFAULT_MENU_LIST_MAX), displayRows() - promptLines())) { + maxWidth = Math.max(maxWidth, MENU_LIST_WIDTH); + sb.tabs(Math.max(Math.min(candidateStartPosition, width - maxWidth - 1), 1)); + width = maxWidth + 2; + if (!isSet(Option.GROUP_PERSIST)) { + List list = new ArrayList<>(); + for (Object o : items) { + if (o instanceof Collection) { + list.addAll((Collection) o); + } + } + list = list.stream() + .sorted(getCandidateComparator(isSet(Option.CASE_INSENSITIVE), "")) + .collect(Collectors.toList()); + toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, true, out); + } else { + for (Object list : items) { + toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, true, out); + } + } + } else { + for (Object list : items) { + toColumns(list, width, maxWidth, sb, selection, completed, rowsFirst, false, out); + } + } } if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') { sb.setLength(sb.length() - 1); @@ -5193,16 +5240,29 @@ } @SuppressWarnings("unchecked") - protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed, boolean rowsFirst, int[] out) { + protected void toColumns(Object items, int width, int maxWidth, AttributedStringBuilder sb, Candidate selection, String completed + , boolean rowsFirst, boolean doMenuList, int[] out) { if (maxWidth <= 0 || width <= 0) { return; } // This is a group if (items instanceof String) { - sb.style(getCompletionStyleGroup()) + if (doMenuList) { + sb.style(AttributedStyle.DEFAULT); + sb.append('\t'); + } + AttributedStringBuilder asb = new AttributedStringBuilder(); + asb.style(getCompletionStyleGroup(doMenuList)) .append((String) items) - .style(AttributedStyle.DEFAULT) - .append("\n"); + .style(AttributedStyle.DEFAULT); + if (doMenuList) { + for (int k = ((String) items).length(); k < maxWidth + 1; k++) { + asb.append(' '); + } + } + sb.style(getCompletionStyleBackground(doMenuList)); + sb.append(asb); + sb.append("\n"); out[0]++; } // This is a Candidate list @@ -5224,6 +5284,11 @@ index = (i, j) -> j * lines + i; } for (int i = 0; i < lines; i++) { + if (doMenuList) { + sb.style(AttributedStyle.DEFAULT); + sb.append('\t'); + } + AttributedStringBuilder asb = new AttributedStringBuilder(); for (int j = 0; j < columns; j++) { int idx = index.applyAsInt(i, j); if (idx < candidates.size()) { @@ -5248,56 +5313,85 @@ } if (cand == selection) { out[1] = i; - sb.style(getCompletionStyleSelection()); + asb.style(getCompletionStyleSelection(doMenuList)); if (left.toString().regionMatches( isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { - sb.append(left.toString(), 0, completed.length()); - sb.append(left.toString(), completed.length(), left.length()); + asb.append(left.toString(), 0, completed.length()); + asb.append(left.toString(), completed.length(), left.length()); } else { - sb.append(left.toString()); + asb.append(left.toString()); } for (int k = 0; k < maxWidth - lw - rw; k++) { - sb.append(' '); + asb.append(' '); } if (right != null) { - sb.append(right); + asb.append(right); } - sb.style(AttributedStyle.DEFAULT); + asb.style(AttributedStyle.DEFAULT); } else { if (left.toString().regionMatches( isSet(Option.CASE_INSENSITIVE), 0, completed, 0, completed.length())) { - sb.style(getCompletionStyleStarting()); - sb.append(left, 0, completed.length()); - sb.style(AttributedStyle.DEFAULT); - sb.append(left, completed.length(), left.length()); + asb.style(getCompletionStyleStarting(doMenuList)); + asb.append(left, 0, completed.length()); + asb.style(AttributedStyle.DEFAULT); + asb.append(left, completed.length(), left.length()); } else { - sb.append(left); + asb.append(left); } if (right != null || hasRightItem) { for (int k = 0; k < maxWidth - lw - rw; k++) { - sb.append(' '); + asb.append(' '); } } if (right != null) { - sb.style(getCompletionStyleDescription()); - sb.append(right); - sb.style(AttributedStyle.DEFAULT); + asb.style(getCompletionStyleDescription(doMenuList)); + asb.append(right); + asb.style(AttributedStyle.DEFAULT); + } else if (doMenuList) { + for (int k = lw; k < maxWidth; k++) { + asb.append(' '); + } } } if (hasRightItem) { for (int k = 0; k < MARGIN_BETWEEN_COLUMNS; k++) { - sb.append(' '); + asb.append(' '); } } + if (doMenuList) { + asb.append(' '); + } } } + sb.style(getCompletionStyleBackground(doMenuList)); + sb.append(asb); sb.append('\n'); } out[0] += lines; } } - private AttributedStyle getCompletionStyleStarting() { + protected AttributedStyle getCompletionStyleStarting(boolean menuList) { + return menuList ? getCompletionStyleListStarting() : getCompletionStyleStarting(); + } + + protected AttributedStyle getCompletionStyleDescription(boolean menuList) { + return menuList ? getCompletionStyleListDescription() : getCompletionStyleDescription(); + } + + protected AttributedStyle getCompletionStyleGroup(boolean menuList) { + return menuList ? getCompletionStyleListGroup() : getCompletionStyleGroup(); + } + + protected AttributedStyle getCompletionStyleSelection(boolean menuList) { + return menuList ? getCompletionStyleListSelection() : getCompletionStyleSelection(); + } + + protected AttributedStyle getCompletionStyleBackground(boolean menuList) { + return menuList ? getCompletionStyleListBackground() : getCompletionStyleBackground(); + } + + protected AttributedStyle getCompletionStyleStarting() { return getCompletionStyle(COMPLETION_STYLE_STARTING, DEFAULT_COMPLETION_STYLE_STARTING); } @@ -5313,37 +5407,38 @@ return getCompletionStyle(COMPLETION_STYLE_SELECTION, DEFAULT_COMPLETION_STYLE_SELECTION); } + protected AttributedStyle getCompletionStyleBackground() { + return getCompletionStyle(COMPLETION_STYLE_BACKGROUND, DEFAULT_COMPLETION_STYLE_BACKGROUND); + } + + protected AttributedStyle getCompletionStyleListStarting() { + return getCompletionStyle(COMPLETION_STYLE_LIST_STARTING, DEFAULT_COMPLETION_STYLE_LIST_STARTING); + } + + protected AttributedStyle getCompletionStyleListDescription() { + return getCompletionStyle(COMPLETION_STYLE_LIST_DESCRIPTION, DEFAULT_COMPLETION_STYLE_LIST_DESCRIPTION); + } + + protected AttributedStyle getCompletionStyleListGroup() { + return getCompletionStyle(COMPLETION_STYLE_LIST_GROUP, DEFAULT_COMPLETION_STYLE_LIST_GROUP); + } + + protected AttributedStyle getCompletionStyleListSelection() { + return getCompletionStyle(COMPLETION_STYLE_LIST_SELECTION, DEFAULT_COMPLETION_STYLE_LIST_SELECTION); + } + + protected AttributedStyle getCompletionStyleListBackground() { + return getCompletionStyle(COMPLETION_STYLE_LIST_BACKGROUND, DEFAULT_COMPLETION_STYLE_LIST_BACKGROUND); + } + protected AttributedStyle getCompletionStyle(String name, String value) { - return buildStyle(getString(name, value)); + return new StyleResolver(s -> getString(s, null)).resolve("." + name, value); } protected AttributedStyle buildStyle(String str) { return AttributedString.fromAnsi("\u001b[" + str + "m ").styleAt(0); } - private String getCommonStart(String str1, String str2, boolean caseInsensitive) { - int[] s1 = str1.codePoints().toArray(); - int[] s2 = str2.codePoints().toArray(); - int len = 0; - while (len < Math.min(s1.length, s2.length)) { - int ch1 = s1[len]; - int ch2 = s2[len]; - if (ch1 != ch2 && caseInsensitive) { - ch1 = Character.toUpperCase(ch1); - ch2 = Character.toUpperCase(ch2); - if (ch1 != ch2) { - ch1 = Character.toLowerCase(ch1); - ch2 = Character.toLowerCase(ch2); - } - } - if (ch1 != ch2) { - break; - } - len++; - } - return new String(s1, 0, len); - } - /** * Used in "vi" mode for argumented history move, to move a specific * number of history entries forward or back. diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/ReaderUtils.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -9,6 +9,7 @@ package jdk.internal.org.jline.reader.impl; import jdk.internal.org.jline.reader.LineReader; +import jdk.internal.org.jline.utils.Levenshtein; public class ReaderUtils { @@ -67,4 +68,14 @@ return nb; } + public static int distance(String word, String cand) { + if (word.length() < cand.length()) { + int d1 = Levenshtein.distance(word, cand.substring(0, Math.min(cand.length(), word.length()))); + int d2 = Levenshtein.distance(word, cand); + return Math.min(d1, d2); + } else { + return Levenshtein.distance(word, cand); + } + } + } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/StringsCompleter.java 2022-04-19 19:55:43.000000000 +0000 @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -28,10 +29,11 @@ */ public class StringsCompleter implements Completer { - protected Collection candidates = new ArrayList<>(); + protected Collection candidates; protected Supplier> stringsSupplier; public StringsCompleter() { + this(Collections.emptyList()); } public StringsCompleter(Supplier> stringsSupplier) { @@ -46,6 +48,7 @@ public StringsCompleter(Iterable strings) { assert strings != null; + this.candidates = new ArrayList<>(); for (String string : strings) { candidates.add(new Candidate(AttributedString.stripAnsi(string), string, null, null, null, null, true)); } @@ -57,9 +60,10 @@ public StringsCompleter(Collection candidates) { assert candidates != null; - this.candidates.addAll(candidates); + this.candidates = new ArrayList<>(candidates); } + @Override public void complete(LineReader reader, final ParsedLine commandLine, final List candidates) { assert commandLine != null; assert candidates != null; @@ -72,4 +76,9 @@ } } + @Override + public String toString() { + String value = candidates != null ? candidates.toString() : "{" + stringsSupplier.toString() + "}"; + return "StringsCompleter" + value; + } } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/completer/SystemCompleter.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2002-2020, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.reader.impl.completer; + +import java.util.*; + +import jdk.internal.org.jline.reader.Candidate; +import jdk.internal.org.jline.reader.Completer; +import jdk.internal.org.jline.reader.LineReader; +import jdk.internal.org.jline.reader.ParsedLine; +import jdk.internal.org.jline.utils.AttributedString; + +/** + * Completer which contains multiple completers and aggregates them together. + * + * @author Matti Rinta-Nikkola + */ +public class SystemCompleter implements Completer { + private Map> completers = new HashMap<>(); + private Map aliasCommand = new HashMap<>(); + private StringsCompleter commands; + private boolean compiled = false; + + public SystemCompleter() {} + + @Override + public void complete(LineReader reader, ParsedLine commandLine, List candidates) { + if (!compiled) { + throw new IllegalStateException(); + } + assert commandLine != null; + assert candidates != null; + if (commandLine.words().size() > 0) { + if (commandLine.words().size() == 1) { + String buffer = commandLine.words().get(0); + int eq = buffer.indexOf('='); + if (eq < 0) { + commands.complete(reader, commandLine, candidates); + } else if (reader.getParser().validVariableName(buffer.substring(0, eq))) { + String curBuf = buffer.substring(0, eq + 1); + for (String c: completers.keySet()) { + candidates.add(new Candidate(AttributedString.stripAnsi(curBuf+c) + , c, null, null, null, null, true)); + } + } + } else { + String cmd = reader.getParser().getCommand(commandLine.words().get(0)); + if (command(cmd) != null) { + completers.get(command(cmd)).get(0).complete(reader, commandLine, candidates); + } + } + } + } + + public boolean isCompiled() { + return compiled; + } + + private String command(String cmd) { + String out = null; + if (cmd != null) { + if (completers.containsKey(cmd)) { + out = cmd; + } else if (aliasCommand.containsKey(cmd)) { + out = aliasCommand.get(cmd); + } + } + return out; + } + + public void add(String command, List completers) { + for (Completer c : completers) { + add(command, c); + } + } + + public void add(List commands, Completer completer) { + for (String c: commands) { + add(c, completer); + } + } + + public void add(String command, Completer completer) { + Objects.requireNonNull(command); + if (compiled) { + throw new IllegalStateException(); + } + if (!completers.containsKey(command)) { + completers.put(command, new ArrayList()); + } + if (completer instanceof ArgumentCompleter) { + ((ArgumentCompleter) completer).setStrictCommand(false); + } + completers.get(command).add(completer); + } + + public void add(SystemCompleter other) { + if (other.isCompiled()) { + throw new IllegalStateException(); + } + for (Map.Entry> entry: other.getCompleters().entrySet()) { + for (Completer c: entry.getValue()) { + add(entry.getKey(), c); + } + } + addAliases(other.getAliases()); + } + + public void addAliases(Map aliasCommand) { + if (compiled) { + throw new IllegalStateException(); + } + this.aliasCommand.putAll(aliasCommand); + } + + private Map getAliases() { + return aliasCommand; + } + + public void compile() { + if (compiled) { + return; + } + Map> compiledCompleters = new HashMap<>(); + for (Map.Entry> entry: completers.entrySet()) { + if (entry.getValue().size() == 1) { + compiledCompleters.put(entry.getKey(), entry.getValue()); + } else { + compiledCompleters.put(entry.getKey(), new ArrayList()); + compiledCompleters.get(entry.getKey()).add(new AggregateCompleter(entry.getValue())); + } + } + completers = compiledCompleters; + Set cmds = new HashSet<>(completers.keySet()); + cmds.addAll(aliasCommand.keySet()); + commands = new StringsCompleter(cmds); + compiled = true; + } + + public Map> getCompleters() { + return completers; + } + +} diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/reader/impl/history/DefaultHistory.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2021, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -84,7 +84,7 @@ try (BufferedReader reader = Files.newBufferedReader(path)) { internalClear(); reader.lines().forEach(line -> addHistoryLine(path, line)); - setHistoryFileData(path, new HistoryFileData(items.size(), items.size())); + setHistoryFileData(path, new HistoryFileData(items.size(), offset + items.size())); maybeResize(); } } @@ -105,7 +105,7 @@ Log.trace("Reading history from: ", path); try (BufferedReader reader = Files.newBufferedReader(path)) { reader.lines().forEach(line -> addHistoryLine(path, line, incremental)); - setHistoryFileData(path, new HistoryFileData(items.size(), items.size())); + setHistoryFileData(path, new HistoryFileData(items.size(), offset + items.size())); maybeResize(); } } @@ -136,11 +136,7 @@ private boolean isLineReaderHistory (Path path) throws IOException { Path lrp = getPath(); if (lrp == null) { - if (path != null) { - return false; - } else { - return true; - } + return path == null; } return Files.isSameFile(lrp, path); } @@ -226,7 +222,10 @@ private void internalWrite(Path path, int from) throws IOException { if (path != null) { Log.trace("Saving history to: ", path); - Files.createDirectories(path.toAbsolutePath().getParent()); + Path parent = path.toAbsolutePath().getParent(); + if (!Files.exists(parent)) { + Files.createDirectories(parent); + } // Append new items to the history file try (BufferedWriter writer = Files.newBufferedWriter(path.toAbsolutePath(), StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { @@ -258,11 +257,11 @@ }); } // Remove duplicates - doTrimHistory(allItems, max); + List trimmedItems = doTrimHistory(allItems, max); // Write history Path temp = Files.createTempFile(path.toAbsolutePath().getParent(), path.getFileName().toString(), ".tmp"); try (BufferedWriter writer = Files.newBufferedWriter(temp, StandardOpenOption.WRITE)) { - for (Entry entry : allItems) { + for (Entry entry : trimmedItems) { writer.append(format(entry)); } } @@ -270,8 +269,8 @@ // Keep items in memory if (isLineReaderHistory(path)) { internalClear(); - offset = allItems.get(0).index(); - items.addAll(allItems); + offset = trimmedItems.get(0).index(); + items.addAll(trimmedItems); setHistoryFileData(path, new HistoryFileData(items.size(), items.size())); } else { setEntriesInFile(path, allItems.size()); @@ -297,7 +296,7 @@ items.clear(); } - static void doTrimHistory(List allItems, int max) { + static List doTrimHistory(List allItems, int max) { int idx = 0; while (idx < allItems.size()) { int ridx = allItems.size() - idx - 1; @@ -314,6 +313,12 @@ while (allItems.size() > max) { allItems.remove(0); } + int index = allItems.get(allItems.size() - 1).index() - allItems.size() + 1; + List out = new ArrayList<>(); + for (Entry e : allItems) { + out.add(new EntryImpl(index++, e.time(), e.line())); + } + return out; } public int size() { @@ -338,7 +343,7 @@ private String format(Entry entry) { if (reader.isSet(LineReader.Option.HISTORY_TIMESTAMPED)) { - return Long.toString(entry.time().toEpochMilli()) + ":" + escape(entry.line()) + "\n"; + return entry.time().toEpochMilli() + ":" + escape(entry.line()) + "\n"; } return escape(entry.line()) + "\n"; } @@ -398,6 +403,8 @@ sb.append('|'); } else if (ch == '*') { sb.append('.').append('*'); + } else { + sb.append(ch); } } return line.matches(sb.toString()); @@ -441,7 +448,7 @@ } public void resetIndex() { - index = index > items.size() ? items.size() : index; + index = Math.min(index, items.size()); } protected static class EntryImpl implements Entry { @@ -622,7 +629,7 @@ return sb.toString(); } - private class HistoryFileData { + private static class HistoryFileData { private int lastLoaded = 0; private int entriesInFile = 0; diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/Terminal.java 2022-04-19 19:55:43.000000000 +0000 @@ -18,6 +18,7 @@ import java.util.function.IntSupplier; import jdk.internal.org.jline.terminal.impl.NativeSignalHandler; +import jdk.internal.org.jline.utils.ColorPalette; import jdk.internal.org.jline.utils.InfoCmp.Capability; import jdk.internal.org.jline.utils.NonBlockingReader; @@ -328,4 +329,10 @@ * @return true if focus tracking is supported */ boolean trackFocus(boolean tracking); + + /** + * Color support + */ + ColorPalette getPalette(); + } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/TerminalBuilder.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -88,6 +88,7 @@ } private static final AtomicReference SYSTEM_TERMINAL = new AtomicReference<>(); + private static final AtomicReference TERMINAL_OVERRIDE = new AtomicReference<>(); private String name; private InputStream in; @@ -100,12 +101,13 @@ private Boolean jansi; private Boolean exec; private Boolean dumb; + private Boolean color; private Attributes attributes; private Size size; private boolean nativeSignals = false; + private Function inputStreamWrapper = in -> in; private Terminal.SignalHandler signalHandler = Terminal.SignalHandler.SIG_DFL; private boolean paused = false; - private Function inputStreamWrapper = in -> in; private TerminalBuilder() { } @@ -151,6 +153,11 @@ return this; } + public TerminalBuilder color(boolean color) { + this.color = color; + return this; + } + /** * Set the encoding to use for reading/writing from the console. * If {@code null} (the default value), JLine will automatically select @@ -205,8 +212,8 @@ /** * Attributes to use when creating a non system terminal, * i.e. when the builder has been given the input and - * outut streams using the {@link #streams(InputStream, OutputStream)} method - * or when {@link #system(boolean)} has been explicitely called with + * output streams using the {@link #streams(InputStream, OutputStream)} method + * or when {@link #system(boolean)} has been explicitly called with * false. * * @param attributes the attributes to use @@ -222,8 +229,8 @@ /** * Initial size to use when creating a non system terminal, * i.e. when the builder has been given the input and - * outut streams using the {@link #streams(InputStream, OutputStream)} method - * or when {@link #system(boolean)} has been explicitely called with + * output streams using the {@link #streams(InputStream, OutputStream)} method + * or when {@link #system(boolean)} has been explicitly called with * false. * * @param size the initial size @@ -246,6 +253,11 @@ return this; } + public TerminalBuilder inputStreamWrapper(Function wrapper) { + this.inputStreamWrapper = wrapper; + return this; + } + /** * Initial paused state of the terminal (defaults to false). * By default, the terminal is started, but in some cases, @@ -261,13 +273,12 @@ return this; } - public TerminalBuilder inputStreamWrapper(Function wrapper) { - this.inputStreamWrapper = wrapper; - return this; - } - public Terminal build() throws IOException { - Terminal terminal = doBuild(); + Terminal override = TERMINAL_OVERRIDE.get(); + Terminal terminal = override != null ? override : doBuild(); + if (override != null) { + Log.debug(() -> "Overriding terminal with global value set by TerminalBuilder.setTerminalOverride"); + } Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName()); if (terminal instanceof AbstractPosixTerminal) { Log.debug(() -> "Using pty " + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName()); @@ -318,117 +329,132 @@ dumb = getBoolean(PROP_DUMB, null); } if ((system != null && system) || (system == null && in == null && out == null)) { - if (attributes != null || size != null) { - Log.warn("Attributes and size fields are ignored when creating a system terminal"); + if (system != null && ((in != null && !in.equals(System.in)) || (out != null && !out.equals(System.out)))) { + throw new IllegalArgumentException("Cannot create a system terminal using non System streams"); } - IllegalStateException exception = new IllegalStateException("Unable to create a system terminal"); Terminal terminal = null; - if (OSUtils.IS_WINDOWS) { - boolean cygwinTerm = "cygwin".equals(System.getenv("TERM")); - boolean ansiPassThrough = OSUtils.IS_CONEMU; - // - // Cygwin support - // - if ((OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) && exec && !cygwinTerm) { - try { - Pty pty = ExecPty.current(); - // Cygwin defaults to XTERM, but actually supports 256 colors, - // so if the value comes from the environment, change it to xterm-256color - if ("xterm".equals(type) && this.type == null && System.getProperty(PROP_TYPE) == null) { - type = "xterm-256color"; + IllegalStateException exception = new IllegalStateException("Unable to create a system terminal"); + TerminalBuilderSupport tbs = new TerminalBuilderSupport(jna, jansi); + if (tbs.isConsoleInput() && tbs.isConsoleOutput()) { + if (attributes != null || size != null) { + Log.warn("Attributes and size fields are ignored when creating a system terminal"); + } + if (OSUtils.IS_WINDOWS) { + if (!OSUtils.IS_CYGWIN && !OSUtils.IS_MSYSTEM) { + boolean ansiPassThrough = OSUtils.IS_CONEMU; + if (tbs.hasJnaSupport()) { + try { + terminal = tbs.getJnaSupport().winSysTerminal(name, type, ansiPassThrough, encoding, codepage + , nativeSignals, signalHandler, paused, inputStreamWrapper); + } catch (Throwable t) { + Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); + exception.addSuppressed(t); + } + } + if (terminal == null && tbs.hasJansiSupport()) { + try { + terminal = tbs.getJansiSupport().winSysTerminal(name, type, ansiPassThrough, encoding, codepage + , nativeSignals, signalHandler, paused); + } catch (Throwable t) { + Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); + exception.addSuppressed(t); + } + } + } else if (exec) { + // + // Cygwin support + // + try { + // Cygwin defaults to XTERM, but actually supports 256 colors, + // so if the value comes from the environment, change it to xterm-256color + if ("xterm".equals(type) && this.type == null && System.getProperty(PROP_TYPE) == null) { + type = "xterm-256color"; + } + Pty pty = tbs.getExecPty(); + terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); + } catch (IOException e) { + // Ignore if not a tty + Log.debug("Error creating EXEC based terminal: ", e.getMessage(), e); + exception.addSuppressed(e); } - terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); - } catch (IOException e) { - // Ignore if not a tty - Log.debug("Error creating EXEC based terminal: ", e.getMessage(), e); - exception.addSuppressed(e); } - } - if (jna) { - try { - terminal = load(JnaSupport.class).winSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused, inputStreamWrapper); - } catch (Throwable t) { - Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); - exception.addSuppressed(t); + if (terminal == null && !jna && !jansi && (dumb == null || !dumb)) { + throw new IllegalStateException("Unable to create a system terminal. On windows, either " + + "JNA or JANSI library is required. Make sure to add one of those in the classpath."); } - } - if (jansi) { - try { - terminal = load(JansiSupport.class).winSysTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused); - } catch (Throwable t) { - Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); - exception.addSuppressed(t); + } else { + if (tbs.hasJnaSupport()) { + try { + Pty pty = tbs.getJnaSupport().current(); + terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); + } catch (Throwable t) { + // ignore + Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); + exception.addSuppressed(t); + } } - } - } else { - if (jna) { - try { - Pty pty = load(JnaSupport.class).current(); - terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); - } catch (Throwable t) { - // ignore - Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); - exception.addSuppressed(t); + if (terminal == null && tbs.hasJansiSupport()) { + try { + Pty pty = tbs.getJansiSupport().current(); + terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); + } catch (Throwable t) { + Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); + exception.addSuppressed(t); + } } - } - if (jansi) { - try { - Pty pty = load(JansiSupport.class).current(); - terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); - } catch (Throwable t) { - Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); - exception.addSuppressed(t); + if (terminal == null && exec) { + try { + Pty pty = tbs.getExecPty(); + terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); + } catch (Throwable t) { + // Ignore if not a tty + Log.debug("Error creating EXEC based terminal: ", t.getMessage(), t); + exception.addSuppressed(t); + } } } - if (exec) { - try { - Pty pty = ExecPty.current(); - terminal = new PosixSysTerminal(name, type, pty, inputStreamWrapper.apply(pty.getSlaveInput()), pty.getSlaveOutput(), encoding, nativeSignals, signalHandler); - } catch (Throwable t) { - // Ignore if not a tty - Log.debug("Error creating EXEC based terminal: ", t.getMessage(), t); - exception.addSuppressed(t); + if (terminal instanceof AbstractTerminal) { + AbstractTerminal t = (AbstractTerminal) terminal; + if (SYSTEM_TERMINAL.compareAndSet(null, t)) { + t.setOnClose(() -> SYSTEM_TERMINAL.compareAndSet(t, null)); + } else { + exception.addSuppressed(new IllegalStateException("A system terminal is already running. " + + "Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " + + "or that previously created system Terminals have been correctly closed.")); + terminal.close(); + terminal = null; } } } - if (terminal instanceof AbstractTerminal) { - AbstractTerminal t = (AbstractTerminal) terminal; - if (SYSTEM_TERMINAL.compareAndSet(null, t)) { - t.setOnClose(new Runnable() { - @Override - public void run() { - SYSTEM_TERMINAL.compareAndSet(t, null); - } - }); - } else { - exception.addSuppressed(new IllegalStateException("A system terminal is already running. " + - "Make sure to use the created system Terminal on the LineReaderBuilder if you're using one " + - "or that previously created system Terminals have been correctly closed.")); - terminal.close(); - terminal = null; - } - } if (terminal == null && (dumb == null || dumb)) { // forced colored dumb terminal - boolean color = getBoolean(PROP_DUMB_COLOR, false); - // detect emacs using the env variable - if (!color) { - color = System.getenv("INSIDE_EMACS") != null; - } - // detect Intellij Idea - if (!color) { - String command = getParentProcessCommand(); - color = command != null && command.contains("idea"); - } - if (!color && dumb == null) { - if (Log.isDebugEnabled()) { - Log.warn("Creating a dumb terminal", exception); - } else { - Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)"); + Boolean color = this.color; + if (color == null) { + color = getBoolean(PROP_DUMB_COLOR, false); + // detect emacs using the env variable + if (!color) { + color = System.getenv("INSIDE_EMACS") != null; + } + // detect Intellij Idea + if (!color) { + String command = getParentProcessCommand(); + color = command != null && command.contains("idea"); + } + if (!color) { + color = tbs.isConsoleOutput() && System.getenv("TERM") != null; + } + if (!color && dumb == null) { + if (Log.isDebugEnabled()) { + Log.warn("input is tty: {}", tbs.isConsoleInput()); + Log.warn("output is tty: {}", tbs.isConsoleOutput()); + Log.warn("Creating a dumb terminal", exception); + } else { + Log.warn("Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)"); + } } } terminal = new DumbTerminal(name, color ? Terminal.TYPE_DUMB_COLOR : Terminal.TYPE_DUMB, - new FileInputStream(FileDescriptor.in), + inputStreamWrapper.apply(new FileInputStream(FileDescriptor.in)), new FileOutputStream(FileDescriptor.out), encoding, signalHandler); } @@ -440,7 +466,7 @@ if (jna) { try { Pty pty = load(JnaSupport.class).open(attributes, size); - return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused); + return new PosixPtyTerminal(name, type, pty, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused); } catch (Throwable t) { Log.debug("Error creating JNA based terminal: ", t.getMessage(), t); } @@ -448,12 +474,12 @@ if (jansi) { try { Pty pty = load(JansiSupport.class).open(attributes, size); - return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused); + return new PosixPtyTerminal(name, type, pty, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused); } catch (Throwable t) { Log.debug("Error creating JANSI based terminal: ", t.getMessage(), t); } } - return new ExternalTerminal(name, type, in, out, encoding, signalHandler, paused, attributes, size); + return new ExternalTerminal(name, type, inputStreamWrapper.apply(in), out, encoding, signalHandler, paused, attributes, size); } } @@ -482,7 +508,116 @@ return def; } - private S load(Class clazz) { + private static S load(Class clazz) { return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next(); } + + /** + * Allows an application to override the result of {@link #build()}. The + * intended use case is to allow a container or server application to control + * an embedded application that uses a LineReader that uses Terminal + * constructed with TerminalBuilder.build but provides no public api for setting + * the LineReader of the {@link Terminal}. For example, the sbt + * build tool uses a LineReader to implement an interactive shell. + * One of its supported commands is console which invokes + * the scala REPL. The scala REPL also uses a LineReader and it + * is necessary to override the {@link Terminal} used by the the REPL to + * share the same {@link Terminal} instance used by sbt. + * + *

    + * When this method is called with a non-null {@link Terminal}, all subsequent + * calls to {@link #build()} will return the provided {@link Terminal} regardless + * of how the {@link TerminalBuilder} was constructed. The default behavior + * of {@link TerminalBuilder} can be restored by calling setTerminalOverride + * with a null {@link Terminal} + *

    + * + *

    + * Usage of setTerminalOverride should be restricted to cases where it + * isn't possible to update the api of the nested application to accept + * a {@link Terminal instance}. + *

    + * + * @param terminal the {@link Terminal} to globally override + */ + @Deprecated + public static void setTerminalOverride(final Terminal terminal) { + TERMINAL_OVERRIDE.set(terminal); + } + + private static class TerminalBuilderSupport { + private JansiSupport jansiSupport = null; + private JnaSupport jnaSupport = null; + private Pty pty = null; + private boolean consoleOutput; + + TerminalBuilderSupport(boolean jna, boolean jansi) { + if (jna) { + try { + jnaSupport = load(JnaSupport.class); + consoleOutput = jnaSupport.isConsoleOutput(); + } catch (Throwable e) { + jnaSupport = null; + Log.debug("jnaSupport.isConsoleOutput(): ", e); + } + } + if (jansi) { + try { + jansiSupport = load(JansiSupport.class); + consoleOutput = jansiSupport.isConsoleOutput(); + } catch (Throwable e) { + jansiSupport = null; + Log.debug("jansiSupport.isConsoleOutput(): ", e); + } + } + if (jnaSupport == null && jansiSupport == null) { + try { + pty = ExecPty.current(); + consoleOutput = true; + } catch (Exception e) { + Log.debug("ExecPty.current(): ", e); + } + } + } + + public boolean isConsoleOutput() { + return consoleOutput; + } + + public boolean isConsoleInput() { + if (pty != null) { + return true; + } else if (hasJnaSupport()) { + return jnaSupport.isConsoleInput(); + } else if (hasJansiSupport()) { + return jansiSupport.isConsoleInput(); + } else { + return false; + } + } + + public boolean hasJnaSupport() { + return jnaSupport != null; + } + + public boolean hasJansiSupport() { + return jansiSupport != null; + } + + public JnaSupport getJnaSupport() { + return jnaSupport; + } + + public JansiSupport getJansiSupport() { + return jansiSupport; + } + + public Pty getExecPty() throws IOException { + if (pty == null) { + pty = ExecPty.current(); + } + return pty; + } + + } } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractTerminal.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2021, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -17,6 +17,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.IntConsumer; import java.util.function.IntSupplier; @@ -27,6 +28,7 @@ import jdk.internal.org.jline.terminal.Cursor; import jdk.internal.org.jline.terminal.MouseEvent; import jdk.internal.org.jline.terminal.Terminal; +import jdk.internal.org.jline.utils.ColorPalette; import jdk.internal.org.jline.utils.Curses; import jdk.internal.org.jline.utils.InfoCmp; import jdk.internal.org.jline.utils.InfoCmp.Capability; @@ -38,10 +40,11 @@ protected final String name; protected final String type; protected final Charset encoding; - protected final Map handlers = new HashMap<>(); + protected final Map handlers = new ConcurrentHashMap<>(); protected final Set bools = new HashSet<>(); protected final Map ints = new HashMap<>(); protected final Map strings = new HashMap<>(); + protected final ColorPalette palette = new ColorPalette(this); protected Status status; protected Runnable onClose; @@ -51,7 +54,7 @@ public AbstractTerminal(String name, String type, Charset encoding, SignalHandler signalHandler) throws IOException { this.name = name; - this.type = type; + this.type = type != null ? type : "ansi"; this.encoding = encoding != null ? encoding : Charset.defaultCharset(); for (Signal signal : Signal.values()) { handlers.put(signal, signalHandler); @@ -197,12 +200,10 @@ protected void parseInfoCmp() { String capabilities = null; - if (type != null) { - try { - capabilities = InfoCmp.getInfoCmp(type); - } catch (Exception e) { - Log.warn("Unable to retrieve infocmp for type " + type, e); - } + try { + capabilities = InfoCmp.getInfoCmp(type); + } catch (Exception e) { + Log.warn("Unable to retrieve infocmp for type " + type, e); } if (capabilities == null) { capabilities = InfoCmp.getLoadedInfoCmp("ansi"); @@ -241,7 +242,7 @@ @Override public boolean hasFocusSupport() { - return type != null && type.startsWith("xterm"); + return type.startsWith("xterm"); } @Override @@ -283,4 +284,8 @@ return false; } + @Override + public ColorPalette getPalette() { + return palette; + } } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/AbstractWindowsTerminal.java 2022-04-19 19:55:43.000000000 +0000 @@ -521,8 +521,6 @@ return true; } - protected abstract int getConsoleOutputCP(); - protected abstract int getConsoleMode(); protected abstract void setConsoleMode(int mode); diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JansiSupport.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -25,4 +25,9 @@ Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused) throws IOException; + boolean isWindowsConsole(); + + boolean isConsoleOutput(); + + boolean isConsoleInput(); } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/spi/JnaSupport.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -29,4 +29,9 @@ Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, Function inputStreamWrapper) throws IOException; + boolean isWindowsConsole(); + + boolean isConsoleOutput(); + + boolean isConsoleInput(); } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedCharSequence.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2019, the original author or authors. + * Copyright (c) 2002-2021, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -20,12 +20,16 @@ import static jdk.internal.org.jline.utils.AttributedStyle.FG_COLOR; import static jdk.internal.org.jline.utils.AttributedStyle.FG_COLOR_EXP; import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND; +import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND_IND; +import static jdk.internal.org.jline.utils.AttributedStyle.F_BACKGROUND_RGB; import static jdk.internal.org.jline.utils.AttributedStyle.F_BLINK; import static jdk.internal.org.jline.utils.AttributedStyle.F_BOLD; import static jdk.internal.org.jline.utils.AttributedStyle.F_CONCEAL; import static jdk.internal.org.jline.utils.AttributedStyle.F_CROSSED_OUT; import static jdk.internal.org.jline.utils.AttributedStyle.F_FAINT; import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND; +import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_IND; +import static jdk.internal.org.jline.utils.AttributedStyle.F_FOREGROUND_RGB; import static jdk.internal.org.jline.utils.AttributedStyle.F_INVERSE; import static jdk.internal.org.jline.utils.AttributedStyle.F_ITALIC; import static jdk.internal.org.jline.utils.AttributedStyle.F_UNDERLINE; @@ -35,6 +39,15 @@ public abstract class AttributedCharSequence implements CharSequence { + public static final int TRUE_COLORS = 0x1000000; + private static final int HIGH_COLORS = 0x7FFF; + + public enum ForceMode { + None, + Force256Colors, + ForceTrueColors + } + // cache the value here as we can't afford to get it each time static final boolean DISABLE_ALTERNATE_CHARSET = Boolean.getBoolean(PROP_DISABLE_ALTERNATE_CHARSET); @@ -55,33 +68,54 @@ return toString(); } int colors = 256; - boolean force256colors = false; + ForceMode forceMode = ForceMode.None; + ColorPalette palette = null; String alternateIn = null, alternateOut = null; if (terminal != null) { Integer max_colors = terminal.getNumericCapability(Capability.max_colors); if (max_colors != null) { colors = max_colors; } - force256colors = AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType()) - || AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType()); + if (AbstractWindowsTerminal.TYPE_WINDOWS_256_COLOR.equals(terminal.getType()) + || AbstractWindowsTerminal.TYPE_WINDOWS_CONEMU.equals(terminal.getType())) { + forceMode = ForceMode.Force256Colors; + } + palette = terminal.getPalette(); if (!DISABLE_ALTERNATE_CHARSET) { alternateIn = Curses.tputs(terminal.getStringCapability(Capability.enter_alt_charset_mode)); alternateOut = Curses.tputs(terminal.getStringCapability(Capability.exit_alt_charset_mode)); } } - return toAnsi(colors, force256colors, alternateIn, alternateOut); + return toAnsi(colors, forceMode, palette, alternateIn, alternateOut); } + @Deprecated public String toAnsi(int colors, boolean force256colors) { return toAnsi(colors, force256colors, null, null); } + @Deprecated public String toAnsi(int colors, boolean force256colors, String altIn, String altOut) { + return toAnsi(colors, force256colors ? ForceMode.Force256Colors : ForceMode.None, null, altIn, altOut); + } + + public String toAnsi(int colors, ForceMode force) { + return toAnsi(colors, force, null, null, null); + } + + public String toAnsi(int colors, ForceMode force, ColorPalette palette) { + return toAnsi(colors, force, palette, null, null); + } + + public String toAnsi(int colors, ForceMode force, ColorPalette palette, String altIn, String altOut) { StringBuilder sb = new StringBuilder(); - int style = 0; - int foreground = -1; - int background = -1; + long style = 0; + long foreground = 0; + long background = 0; boolean alt = false; + if (palette == null) { + palette = ColorPalette.DEFAULT; + } for (int i = 0; i < length(); i++) { char c = charAt(i); if (altIn != null && altOut != null) { @@ -105,14 +139,14 @@ sb.append(alt ? altIn : altOut); } } - int s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles + long s = styleCodeAt(i) & ~F_HIDDEN; // The hidden flag does not change the ansi styles if (style != s) { - int d = (style ^ s) & MASK; - int fg = (s & F_FOREGROUND) != 0 ? (s & FG_COLOR) >>> FG_COLOR_EXP : -1; - int bg = (s & F_BACKGROUND) != 0 ? (s & BG_COLOR) >>> BG_COLOR_EXP : -1; + long d = (style ^ s) & MASK; + long fg = (s & F_FOREGROUND) != 0 ? s & (FG_COLOR | F_FOREGROUND) : 0; + long bg = (s & F_BACKGROUND) != 0 ? s & (BG_COLOR | F_BACKGROUND) : 0; if (s == 0) { sb.append("\033[0m"); - foreground = background = -1; + foreground = background = 0; } else { sb.append("\033["); boolean first = true; @@ -135,18 +169,38 @@ first = attr(sb, (s & F_CROSSED_OUT) != 0 ? "9" : "29", first); } if (foreground != fg) { - if (fg >= 0) { - int rounded = Colors.roundColor(fg, colors); - if (rounded < 8 && !force256colors) { - first = attr(sb, "3" + Integer.toString(rounded), first); - // small hack to force setting bold again after a foreground color change - d |= (s & F_BOLD); - } else if (rounded < 16 && !force256colors) { - first = attr(sb, "9" + Integer.toString(rounded - 8), first); - // small hack to force setting bold again after a foreground color change - d |= (s & F_BOLD); - } else { - first = attr(sb, "38;5;" + Integer.toString(rounded), first); + if (fg > 0) { + int rounded = -1; + if ((fg & F_FOREGROUND_RGB) != 0) { + int r = (int)(fg >> (FG_COLOR_EXP + 16)) & 0xFF; + int g = (int)(fg >> (FG_COLOR_EXP + 8)) & 0xFF; + int b = (int)(fg >> FG_COLOR_EXP) & 0xFF; + if (colors >= HIGH_COLORS) { + first = attr(sb, "38;2;" + r + ";" + g + ";" + b, first); + } else { + rounded = palette.round(r, g, b); + } + } else if ((fg & F_FOREGROUND_IND) != 0) { + rounded = palette.round((int)(fg >> FG_COLOR_EXP) & 0xFF); + } + if (rounded >= 0) { + if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) { + int col = palette.getColor(rounded); + int r = (col >> 16) & 0xFF; + int g = (col >> 8) & 0xFF; + int b = col & 0xFF; + first = attr(sb, "38;2;" + r + ";" + g + ";" + b, first); + } else if (force == ForceMode.Force256Colors || rounded >= 16) { + first = attr(sb, "38;5;" + rounded, first); + } else if (rounded >= 8) { + first = attr(sb, "9" + (rounded - 8), first); + // small hack to force setting bold again after a foreground color change + d |= (s & F_BOLD); + } else { + first = attr(sb, "3" + rounded, first); + // small hack to force setting bold again after a foreground color change + d |= (s & F_BOLD); + } } } else { first = attr(sb, "39", first); @@ -154,14 +208,34 @@ foreground = fg; } if (background != bg) { - if (bg >= 0) { - int rounded = Colors.roundColor(bg, colors); - if (rounded < 8 && !force256colors) { - first = attr(sb, "4" + Integer.toString(rounded), first); - } else if (rounded < 16 && !force256colors) { - first = attr(sb, "10" + Integer.toString(rounded - 8), first); - } else { - first = attr(sb, "48;5;" + Integer.toString(rounded), first); + if (bg > 0) { + int rounded = -1; + if ((bg & F_BACKGROUND_RGB) != 0) { + int r = (int)(bg >> (BG_COLOR_EXP + 16)) & 0xFF; + int g = (int)(bg >> (BG_COLOR_EXP + 8)) & 0xFF; + int b = (int)(bg >> BG_COLOR_EXP) & 0xFF; + if (colors >= HIGH_COLORS) { + first = attr(sb, "48;2;" + r + ";" + g + ";" + b, first); + } else { + rounded = palette.round(r, g, b); + } + } else if ((bg & F_BACKGROUND_IND) != 0) { + rounded = palette.round((int)(bg >> BG_COLOR_EXP) & 0xFF); + } + if (rounded >= 0) { + if (colors >= HIGH_COLORS && force == ForceMode.ForceTrueColors) { + int col = palette.getColor(rounded); + int r = (col >> 16) & 0xFF; + int g = (col >> 8) & 0xFF; + int b = col & 0xFF; + first = attr(sb, "48;2;" + r + ";" + g + ";" + b, first); + } else if (force == ForceMode.Force256Colors || rounded >= 16) { + first = attr(sb, "48;5;" + rounded, first); + } else if (rounded >= 8) { + first = attr(sb, "10" + (rounded - 8), first); + } else { + first = attr(sb, "4" + rounded, first); + } } } else { first = attr(sb, "49", first); @@ -220,7 +294,7 @@ public abstract AttributedStyle styleAt(int index); - int styleCodeAt(int index) { + long styleCodeAt(int index) { return styleAt(index).getStyle(); } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedString.java 2022-04-19 19:55:43.000000000 +0000 @@ -25,7 +25,7 @@ public class AttributedString extends AttributedCharSequence { final char[] buffer; - final int[] style; + final long[] style; final int start; final int end; public static final AttributedString EMPTY = new AttributedString(""); @@ -78,7 +78,7 @@ for (int i = 0; i < l; i++) { buffer[i] = str.charAt(start + i); } - style = new int[l]; + style = new long[l]; if (s != null) { Arrays.fill(style, s.getStyle()); } @@ -87,7 +87,7 @@ } } - AttributedString(char[] buffer, int[] style, int start, int end) { + AttributedString(char[] buffer, long[] style, int start, int end) { this.buffer = buffer; this.style = style; this.start = start; @@ -142,7 +142,7 @@ } @Override - int styleCodeAt(int index) { + long styleCodeAt(int index) { return style[start + index]; } @@ -155,7 +155,7 @@ Matcher matcher = pattern.matcher(this); boolean result = matcher.find(); if (result) { - int[] newstyle = this.style.clone(); + long[] newstyle = this.style.clone(); do { for (int i = matcher.start(); i < matcher.end(); i++) { newstyle[this.start + i] = (newstyle[this.start + i] & ~style.getMask()) | style.getStyle(); @@ -185,7 +185,7 @@ } return true; } - private boolean arrEq(int[] a1, int[] a2, int s1, int s2, int l) { + private boolean arrEq(long[] a1, long[] a2, int s1, int s2, int l) { for (int i = 0; i < l; i++) { if (a1[s1+i] != a2[s2+i]) { return false; diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStringBuilder.java 2022-04-19 19:55:43.000000000 +0000 @@ -24,7 +24,7 @@ public class AttributedStringBuilder extends AttributedCharSequence implements Appendable { private char[] buffer; - private int[] style; + private long[] style; private int length; private TabStops tabs = new TabStops(0); private int lastLineLength = 0; @@ -44,7 +44,7 @@ public AttributedStringBuilder(int capacity) { buffer = new char[capacity]; - style = new int[capacity]; + style = new long[capacity]; length = 0; } @@ -64,7 +64,7 @@ } @Override - int styleCodeAt(int index) { + long styleCodeAt(int index) { return style[index]; } @@ -89,11 +89,17 @@ @Override public AttributedStringBuilder append(CharSequence csq) { + if (csq == null) { + csq = "null"; // Required by Appendable.append + } return append(new AttributedString(csq, current)); } @Override public AttributedStringBuilder append(CharSequence csq, int start, int end) { + if (csq == null) { + csq = "null"; // Required by Appendable.append + } return append(csq.subSequence(start, end)); } @@ -152,7 +158,7 @@ ensureCapacity(length + end - start); for (int i = start; i < end; i++) { char c = str.charAt(i); - int s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle(); + long s = str.styleCodeAt(i) & ~current.getMask() | current.getStyle(); if (tabs.defined() && c == '\t') { insertTab(new AttributedStyle(s, 0)); } else { @@ -286,12 +292,10 @@ int r = Integer.parseInt(params[++j]); int g = Integer.parseInt(params[++j]); int b = Integer.parseInt(params[++j]); - // convert to 256 colors - int col = 16 + (r >> 3) * 36 + (g >> 3) * 6 + (b >> 3); if (ansiParam == 38) { - current = current.foreground(col); + current = current.foreground(r, g, b); } else { - current = current.background(col); + current = current.background(r, g, b); } } } else if (ansiParam2 == 5) { diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/AttributedStyle.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2018, the original author or authors. + * Copyright (c) 2002-2021, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -26,24 +26,28 @@ public static final int BRIGHT = 8; - static final int F_BOLD = 0x00000001; - static final int F_FAINT = 0x00000002; - static final int F_ITALIC = 0x00000004; - static final int F_UNDERLINE = 0x00000008; - static final int F_BLINK = 0x00000010; - static final int F_INVERSE = 0x00000020; - static final int F_CONCEAL = 0x00000040; - static final int F_CROSSED_OUT = 0x00000080; - static final int F_FOREGROUND = 0x00000100; - static final int F_BACKGROUND = 0x00000200; - static final int F_HIDDEN = 0x00000400; - - static final int MASK = 0x000007FF; - - static final int FG_COLOR_EXP = 16; - static final int BG_COLOR_EXP = 24; - static final int FG_COLOR = 0xFF << FG_COLOR_EXP; - static final int BG_COLOR = 0xFF << BG_COLOR_EXP; + static final long F_BOLD = 0x00000001; + static final long F_FAINT = 0x00000002; + static final long F_ITALIC = 0x00000004; + static final long F_UNDERLINE = 0x00000008; + static final long F_BLINK = 0x00000010; + static final long F_INVERSE = 0x00000020; + static final long F_CONCEAL = 0x00000040; + static final long F_CROSSED_OUT = 0x00000080; + static final long F_FOREGROUND_IND = 0x00000100; + static final long F_FOREGROUND_RGB = 0x00000200; + static final long F_FOREGROUND = F_FOREGROUND_IND | F_FOREGROUND_RGB; + static final long F_BACKGROUND_IND = 0x00000400; + static final long F_BACKGROUND_RGB = 0x00000800; + static final long F_BACKGROUND = F_BACKGROUND_IND | F_BACKGROUND_RGB; + static final long F_HIDDEN = 0x00001000; + + static final long MASK = 0x00001FFF; + + static final int FG_COLOR_EXP = 15; + static final int BG_COLOR_EXP = 39; + static final long FG_COLOR = 0xFFFFFFL << FG_COLOR_EXP; + static final long BG_COLOR = 0xFFFFFFL << BG_COLOR_EXP; public static final AttributedStyle DEFAULT = new AttributedStyle(); public static final AttributedStyle BOLD = DEFAULT.bold(); @@ -53,8 +57,8 @@ public static final AttributedStyle HIDDEN = DEFAULT.hidden(); public static final AttributedStyle HIDDEN_OFF = DEFAULT.hiddenOff(); - final int style; - final int mask; + final long style; + final long mask; public AttributedStyle() { this(0, 0); @@ -64,7 +68,7 @@ this(s.style, s.mask); } - public AttributedStyle(int style, int mask) { + public AttributedStyle(long style, long mask) { this.style = style; this.mask = mask & MASK | ((style & F_FOREGROUND) != 0 ? FG_COLOR : 0) | ((style & F_BACKGROUND) != 0 ? BG_COLOR : 0); @@ -135,7 +139,7 @@ } public AttributedStyle inverseNeg() { - int s = (style & F_INVERSE) != 0 ? style & ~F_INVERSE : style | F_INVERSE; + long s = (style & F_INVERSE) != 0 ? style & ~F_INVERSE : style | F_INVERSE; return new AttributedStyle(s, mask | F_INVERSE); } @@ -172,7 +176,15 @@ } public AttributedStyle foreground(int color) { - return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND | ((color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND); + return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_IND | (((long) color << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_IND); + } + + public AttributedStyle foreground(int r, int g, int b) { + return foregroundRgb(r << 16 | g << 8 | b); + } + + public AttributedStyle foregroundRgb(int color) { + return new AttributedStyle(style & ~FG_COLOR | F_FOREGROUND_RGB | ((((long) color & 0xFFFFFF) << FG_COLOR_EXP) & FG_COLOR), mask | F_FOREGROUND_RGB); } public AttributedStyle foregroundOff() { @@ -184,7 +196,15 @@ } public AttributedStyle background(int color) { - return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND | ((color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND); + return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_IND | (((long) color << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_IND); + } + + public AttributedStyle background(int r, int g, int b) { + return backgroundRgb(r << 16 | g << 8 | b); + } + + public AttributedStyle backgroundRgb(int color) { + return new AttributedStyle(style & ~BG_COLOR | F_BACKGROUND_RGB | ((((long) color & 0xFFFFFF) << BG_COLOR_EXP) & BG_COLOR), mask | F_BACKGROUND_RGB); } public AttributedStyle backgroundOff() { @@ -214,11 +234,11 @@ return new AttributedStyle(style & ~F_HIDDEN, mask & ~F_HIDDEN); } - public int getStyle() { + public long getStyle() { return style; } - public int getMask() { + public long getMask() { return mask; } @@ -234,8 +254,22 @@ @Override public int hashCode() { - int result = style; - result = 31 * result + mask; - return result; + return 31 * Long.hashCode(style) + Long.hashCode(mask); + } + + public String toAnsi() { + AttributedStringBuilder sb = new AttributedStringBuilder(); + sb.styled(this, " "); + String s = sb.toAnsi(AttributedCharSequence.TRUE_COLORS, AttributedCharSequence.ForceMode.None); + return s.length() > 1 ? s.substring(2, s.indexOf('m')) : s; + } + + @Override + public String toString() { + return "AttributedStyle{" + + "style=" + style + + ", mask=" + mask + + ", ansi=" + toAnsi() + + '}'; } } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/ColorPalette.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2002-2020, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package jdk.internal.org.jline.utils; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import jdk.internal.org.jline.terminal.Terminal; + +/** + * Color palette + */ +public class ColorPalette { + + public static final String XTERM_INITC = "\\E]4;%p1%d;rgb\\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\\E\\\\"; + + public static final ColorPalette DEFAULT = new ColorPalette(); + + private final Terminal terminal; + private String distanceName; + private Colors.Distance distance; + private boolean osc4; + private int[] palette; + + public ColorPalette() { + this.terminal = null; + this.distanceName = null; + this.palette = Colors.DEFAULT_COLORS_256; + } + + public ColorPalette(Terminal terminal) throws IOException { + this(terminal, null); + } + + public ColorPalette(Terminal terminal, String distance) throws IOException { + this.terminal = terminal; + this.distanceName = distance; + loadPalette(false); + } + + /** + * Get the name of the distance to use for rounding colors. + * @return the name of the color distance + */ + public String getDistanceName() { + return distanceName; + } + + /** + * Set the name of the color distance to use when rounding RGB colors to the palette. + * @param name the name of the color distance + */ + public void setDistance(String name) { + this.distanceName = name; + } + + /** + * Check if the terminal has the capability to change colors. + * @return true if the terminal can change colors + */ + public boolean canChange() { + return terminal != null && terminal.getBooleanCapability(InfoCmp.Capability.can_change); + } + + /** + * Load the palette from the terminal. + * If the palette has already been loaded, subsequent calls will simply return true. + * + * @return true if the palette has been successfully loaded. + * @throws IOException + */ + public boolean loadPalette() throws IOException { + if (!osc4) { + loadPalette(true); + } + return osc4; + } + + protected void loadPalette(boolean doLoad) throws IOException { + if (terminal != null) { + int[] pal = doLoad ? doLoad(terminal) : null; + if (pal != null) { + this.palette = pal; + this.osc4 = true; + } else { + Integer cols = terminal.getNumericCapability(InfoCmp.Capability.max_colors); + if (cols != null) { + if (cols == Colors.DEFAULT_COLORS_88.length) { + this.palette = Colors.DEFAULT_COLORS_88; + } else { + this.palette = Arrays.copyOf(Colors.DEFAULT_COLORS_256, Math.min(cols, 256)); + } + } else { + this.palette = Arrays.copyOf(Colors.DEFAULT_COLORS_256, 256); + } + this.osc4 = false; + } + } else { + this.palette = Colors.DEFAULT_COLORS_256; + this.osc4 = false; + } + } + + /** + * Get the palette length + * @return the palette length + */ + public int getLength() { + return this.palette.length; + } + + /** + * Get a specific color in the palette + * @param index the index of the color + * @return the color at the given index + */ + public int getColor(int index) { + return palette[index]; + } + + /** + * Change the color of the palette + * @param index the index of the color + * @param color the new color value + */ + public void setColor(int index, int color) { + palette[index] = color; + if (canChange()) { + String initc = terminal.getStringCapability(InfoCmp.Capability.initialize_color); + if (initc != null || osc4) { + // initc expects color in 0..1000 range + int r = (((color >> 16) & 0xFF) * 1000) / 255 + 1; + int g = (((color >> 8) & 0xFF) * 1000) / 255 + 1; + int b = ((color & 0xFF) * 1000) / 255 + 1; + if (initc == null) { + // This is the xterm version + initc = XTERM_INITC; + } + Curses.tputs(terminal.writer(), initc, index, r, g, b); + terminal.writer().flush(); + } + } + } + + public boolean isReal() { + return osc4; + } + + public int round(int r, int g, int b) { + return Colors.roundColor((r << 16) + (g << 8) + b, palette, palette.length, getDist()); + } + + public int round(int col) { + if (col >= palette.length) { + col = Colors.roundColor(DEFAULT.getColor(col), palette, palette.length, getDist()); + } + return col; + } + + protected Colors.Distance getDist() { + if (distance == null) { + distance = Colors.getDistance(distanceName); + } + return distance; + } + + private static int[] doLoad(Terminal terminal) throws IOException { + PrintWriter writer = terminal.writer(); + NonBlockingReader reader = terminal.reader(); + + int[] palette = new int[256]; + for (int i = 0; i < 16; i++) { + StringBuilder req = new StringBuilder(1024); + req.append("\033]4"); + for (int j = 0; j < 16; j++) { + req.append(';').append(i * 16 + j).append(";?"); + } + req.append("\033\\"); + writer.write(req.toString()); + writer.flush(); + + boolean black = true; + for (int j = 0; j < 16; j++) { + if (reader.peek(50) < 0) { + break; + } + if (reader.read(10) != '\033' + || reader.read(10) != ']' + || reader.read(10) != '4' + || reader.read(10) != ';') { + return null; + } + int idx = 0; + int c; + while (true) { + c = reader.read(10); + if (c >= '0' && c <= '9') { + idx = idx * 10 + (c - '0'); + } else if (c == ';') { + break; + } else { + return null; + } + } + if (idx > 255) { + return null; + } + if (reader.read(10) != 'r' + || reader.read(10) != 'g' + || reader.read(10) != 'b' + || reader.read(10) != ':') { + return null; + } + StringBuilder sb = new StringBuilder(16); + List rgb = new ArrayList<>(); + while (true) { + c = reader.read(10); + if (c == '\007') { + rgb.add(sb.toString()); + break; + } else if (c == '\033') { + c = reader.read(10); + if (c == '\\') { + rgb.add(sb.toString()); + break; + } else { + return null; + } + } else if (c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') { + sb.append((char) c); + } else if (c == '/') { + rgb.add(sb.toString()); + sb.setLength(0); + } + } + if (rgb.size() != 3) { + return null; + } + double r = Integer.parseInt(rgb.get(0), 16) / ((1 << (4 * rgb.get(0).length())) - 1.0); + double g = Integer.parseInt(rgb.get(1), 16) / ((1 << (4 * rgb.get(1).length())) - 1.0); + double b = Integer.parseInt(rgb.get(2), 16) / ((1 << (4 * rgb.get(2).length())) - 1.0); + palette[idx] = (int)((Math.round(r * 255) << 16) + (Math.round(g * 255) << 8) + Math.round(b * 255)); + black &= palette[idx] == 0; + } + if (black) { + break; + } + } + int max = 256; + while (max > 0 && palette[--max] == 0); + return Arrays.copyOfRange(palette, 0, max + 1); + } +} diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Colors.java 2022-04-19 19:55:43.000000000 +0000 @@ -25,42 +25,92 @@ * Default 256 colors palette */ public static final int[] DEFAULT_COLORS_256 = { + // 16 ansi 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, - 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, - 0x005f87, 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, - 0x0087d7, 0x0087ff, 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, - 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, - 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, - 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, - 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, 0x5faf00, 0x5faf5f, - 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, - 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, - 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, - 0x875f87, 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, - 0x8787d7, 0x8787ff, 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, - 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, - 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, - 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, - 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, 0xafaf00, 0xafaf5f, - 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, - 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, - 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, - 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, - 0xd787d7, 0xd787ff, 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, - 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, - 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, 0xff0000, 0xff005f, 0xff0087, 0xff00af, - 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, - 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, 0xffaf00, 0xffaf5f, - 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, - 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + // 6x6x6 color cube + 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, + 0x005f00, 0x005f5f, 0x005f87, 0x005faf, 0x005fd7, 0x005fff, + 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, + 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, + 0x00d700, 0x00d75f, 0x00d787, 0x00d7af, 0x00d7d7, 0x00d7ff, + 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, + + 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, + 0x5f5f00, 0x5f5f5f, 0x5f5f87, 0x5f5faf, 0x5f5fd7, 0x5f5fff, + 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, + 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, + 0x5fd700, 0x5fd75f, 0x5fd787, 0x5fd7af, 0x5fd7d7, 0x5fd7ff, + 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, + + 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, + 0x875f00, 0x875f5f, 0x875f87, 0x875faf, 0x875fd7, 0x875fff, + 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, + 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, + 0x87d700, 0x87d75f, 0x87d787, 0x87d7af, 0x87d7d7, 0x87d7ff, + 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, + + 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, + 0xaf5f00, 0xaf5f5f, 0xaf5f87, 0xaf5faf, 0xaf5fd7, 0xaf5fff, + 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, + 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, + 0xafd700, 0xafd75f, 0xafd787, 0xafd7af, 0xafd7d7, 0xafd7ff, + 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, + + 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, + 0xd75f00, 0xd75f5f, 0xd75f87, 0xd75faf, 0xd75fd7, 0xd75fff, + 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, + 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, + 0xd7d700, 0xd7d75f, 0xd7d787, 0xd7d7af, 0xd7d7d7, 0xd7d7ff, + 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, + + 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, + 0xff5f00, 0xff5f5f, 0xff5f87, 0xff5faf, 0xff5fd7, 0xff5fff, + 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, + 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, + 0xffd700, 0xffd75f, 0xffd787, 0xffd7af, 0xffd7d7, 0xffd7ff, + 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, + // 24 grey ramp 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee, }; + /** + * Default 88 colors palette + */ + public static final int[] DEFAULT_COLORS_88 = { + // 16 ansi + 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xc0c0c0, + 0x808080, 0xff0000, 0x00ff00, 0xffff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff, + + // 4x4x4 color cube + 0x000000, 0x00008b, 0x0000cd, 0x0000ff, + 0x008b00, 0x008b8b, 0x008bcd, 0x008bff, + 0x00cd00, 0x00cd8b, 0x00cdcd, 0x00cdff, + 0x00ff00, 0x00ff8b, 0x00ffcd, 0x00ffff, + + 0x8b0000, 0x8b008b, 0x8b00cd, 0x8b00ff, + 0x8b8b00, 0x8b8b8b, 0x8b8bcd, 0x8b8bff, + 0x8bcd00, 0x8bcd8b, 0x8bcdcd, 0x8bcdff, + 0x8bff00, 0x8bff8b, 0x8bffcd, 0x8bffff, + + 0xcd0000, 0xcd008b, 0xcd00cd, 0xcd00ff, + 0xcd8b00, 0xcd8b8b, 0xcd8bcd, 0xcd8bff, + 0xcdcd00, 0xcdcd8b, 0xcdcdcd, 0xcdcdff, + 0xcdff00, 0xcdff8b, 0xcdffcd, 0xcdffff, + + 0xff0000, 0xff008b, 0xff00cd, 0xff00ff, + 0xff8b00, 0xff8b8b, 0xff8bcd, 0xff8bff, + 0xffcd00, 0xffcd8b, 0xffcdcd, 0xffcdff, + 0xffff00, 0xffff8b, 0xffffcd, 0xffffff, + + // 8 grey ramp + 0x2e2e2e, 0x5c5c5c, 0x737373, 0x8b8b8b, 0xa2a2a2, 0xb9b9b9, 0xd0d0d0, 0xe7e7e7, + }; + /** D50 illuminant for CAM color spaces */ public static final double[] D50 = new double[] { 96.422f, 100.0f, 82.521f }; /** D65 illuminant for CAM color spaces */ @@ -74,11 +124,11 @@ public static final double[] darkSurrounding = new double[] { 0.8, 0.525, 0.8 }; /** sRGB encoding environment */ - public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64/5, dimSurrounding); + public static final double[] sRGB_encoding_environment = vc(D50, 64.0, 64.0/5, dimSurrounding); /** sRGB typical environment */ - public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200/5, averageSurrounding); + public static final double[] sRGB_typical_environment = vc(D50, 200.0, 200.0/5, averageSurrounding); /** Adobe RGB environment */ - public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160/5, averageSurrounding); + public static final double[] AdobeRGB_environment = vc(D65, 160.0, 160.0/5, averageSurrounding); private static int[] COLORS_256 = DEFAULT_COLORS_256; @@ -130,15 +180,16 @@ return roundColor((r << 16) + (g << 8) + b, COLORS_256, max, (String) null); } - private static int roundColor(int color, int[] colors, int max, String dist) { + static int roundColor(int color, int[] colors, int max, String dist) { return roundColor(color, colors, max, getDistance(dist)); } - private interface Distance { + @FunctionalInterface + interface Distance { double compute(int c1, int c2); } - private static int roundColor(int color, int[] colors, int max, Distance distance) { + static int roundColor(int color, int[] colors, int max, Distance distance) { double best_distance = Integer.MAX_VALUE; int best_index = Integer.MAX_VALUE; for (int idx = 0; idx < max; idx++) { @@ -151,7 +202,7 @@ return best_index; } - private static Distance getDistance(String dist) { + static Distance getDistance(String dist) { if (dist == null) { dist = System.getProperty(PROP_COLOR_DISTANCE, "cie76"); } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Curses.java 2022-04-19 19:55:43.000000000 +0000 @@ -352,11 +352,82 @@ out.append(Integer.toString(toInteger(stack.pop()))); break; default: - throw new UnsupportedOperationException(); + if (ch == ':') { + ch = str.charAt(index++); + } + boolean alternate = false; + boolean left = false; + boolean space = false; + boolean plus = false; + int width = 0; + int prec = -1; + int cnv; + while ("-+# ".indexOf(ch) >= 0) { + switch (ch) { + case '-': left = true; break; + case '+': plus = true; break; + case '#': alternate = true; break; + case ' ': space = true; break; + } + ch = str.charAt(index++); + } + if ("123456789".indexOf(ch) >= 0) { + do { + width = width * 10 + (ch - '0'); + ch = str.charAt(index++); + } while ("0123456789".indexOf(ch) >= 0); + } + if (ch == '.') { + prec = 0; + ch = str.charAt(index++); + } + if ("0123456789".indexOf(ch) >= 0) { + do { + prec = prec * 10 + (ch - '0'); + ch = str.charAt(index++); + } while ("0123456789".indexOf(ch) >= 0); + } + if ("cdoxXs".indexOf(ch) < 0) { + throw new IllegalArgumentException(); + } + cnv = ch; + if (exec) { + String res; + if (cnv == 's') { + res = (String) stack.pop(); + if (prec >= 0) { + res = res.substring(0, prec); + } + } else { + int p = toInteger(stack.pop()); + StringBuilder fmt = new StringBuilder(16); + fmt.append('%'); + if (alternate) { + fmt.append('#'); + } + if (plus) { + fmt.append('+'); + } + if (space) { + fmt.append(' '); + } + if (prec >= 0) { + fmt.append('0'); + fmt.append(prec); + } + fmt.append((char) cnv); + res = String.format(fmt.toString(), p); + } + if (width > res.length()) { + res = String.format("%" + (left ? "-" : "") + width + "s", res); + } + out.append(res); + } + break; } break; case '$': - if (str.charAt(index) == '<') { + if (index < length && str.charAt(index) == '<') { // We don't honour delays, just skip int nb = 0; while ((ch = str.charAt(++index)) != '>') { diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Display.java 2022-04-19 19:55:43.000000000 +0000 @@ -72,6 +72,10 @@ public void setDelayLineWrap(boolean v) { delayLineWrap = v; } public void resize(int rows, int columns) { + if (rows == 0 || columns == 0) { + columns = Integer.MAX_VALUE - 1; + rows = 1; + } if (this.rows != rows || this.columns != columns) { this.rows = rows; this.columns = columns; diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/InfoCmp.java 2022-04-19 19:55:43.000000000 +0000 @@ -619,7 +619,7 @@ static { for (String s : Arrays.asList("dumb", "dumb-color", "ansi", "xterm", "xterm-256color", "windows", "windows-256color", "windows-conemu", "windows-vtp", - "screen", "screen-256color")) { + "screen", "screen-256color", "rxvt-unicode", "rxvt-unicode-256color", "rxvt-basic", "rxvt")) { setDefaultInfoCmp(s, () -> loadDefaultInfoCmp(s)); } } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Log.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -22,8 +22,9 @@ * @author Guillaume Nodet * @since 2.0 */ -public final class Log -{ +public final class Log { +// private static final Logger logger = Logger.getLogger("org.jline"); + public static void trace(final Object... messages) { // log(Level.FINEST, messages); } @@ -110,7 +111,6 @@ // } // // static void logr(final Level level, final Supplier record) { -// Logger logger = Logger.getLogger("org.jline"); // if (logger.isLoggable(level)) { // // inform record of the logger-name // LogRecord tmp = record.get(); @@ -120,7 +120,6 @@ // } // // static boolean isEnabled(Level level) { -// Logger logger = Logger.getLogger("org.jline"); // return logger.isLoggable(level); // } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpInputStream.java 2022-04-19 19:55:43.000000000 +0000 @@ -106,6 +106,20 @@ return res; } + @Override + public synchronized int readBuffered(byte[] b) throws IOException { + checkIoException(); + int res = wait(readBuffer, 0L); + if (res >= 0) { + res = 0; + while (res < b.length && readBuffer.hasRemaining()) { + b[res++] = (byte) (readBuffer.get() & 0x00FF); + } + } + rewind(readBuffer, writeBuffer); + return res; + } + public synchronized void setIoException(IOException exception) { this.ioException = exception; notifyAll(); diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/NonBlockingPumpReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -74,7 +74,11 @@ // Blocks until more input is available or the reader is closed. if (!closed && count == 0) { try { - notEmpty.await(timeout, TimeUnit.MILLISECONDS); + if (timeout > 0L) { + notEmpty.await(timeout, TimeUnit.MILLISECONDS); + } else { + notEmpty.await(); + } } catch (InterruptedException e) { throw (IOException) new InterruptedIOException().initCause(e); } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/PumpReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -183,7 +183,7 @@ } @Override - public int read(CharBuffer target) throws IOException { + public synchronized int read(CharBuffer target) throws IOException { if (!target.hasRemaining()) { return 0; } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/Signals.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016, the original author or authors. + * Copyright (c) 2002-2020, the original author or authors. * * This software is distributable under the BSD license. See the terms of the * BSD license in the documentation provided with this software. @@ -9,6 +9,7 @@ package jdk.internal.org.jline.utils; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.Objects; @@ -91,8 +92,12 @@ Object signal; try { signal = constructor.newInstance(name); - } catch (IllegalArgumentException e) { - Log.trace(() -> "Ignoring unsupported signal " + name); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof IllegalArgumentException) { + Log.trace(() -> "Ignoring unsupported signal " + name); + } else { + Log.debug("Error registering handler for signal ", name, e); + } return null; } Class signalHandlerClass = Class.forName("sun.misc.SignalHandler"); diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/StyleResolver.java 2022-04-19 19:55:43.000000000 +0000 @@ -33,6 +33,38 @@ } /** + * Returns the RGB color for the given name. + *

    + * Bright color can be specified with: {@code !} or {@code bright-}. + *

    + * Full xterm256 color can be specified with: {@code ~}. + * RGB colors can be specified with: {@code x} or {@code #} where {@code rgb} is + * a 24 bits hexadecimal color. + * + * @param name the name of the color + * @return color code, or {@code null} if unable to determine. + */ + private static Integer colorRgb(String name) { + name = name.toLowerCase(Locale.US); + // check hexadecimal color + if (name.charAt(0) == 'x' || name.charAt(0) == '#') { + try { + return Integer.parseInt(name.substring(1), 16); + } catch (NumberFormatException e) { +// log.warning("Invalid hexadecimal color: " + name); + return null; + } + } else { + // load indexed color + Integer color = color(name); + if (color != null && color != -1) { + color = Colors.DEFAULT_COLORS_256[color]; + } + return color; + } + } + + /** * Returns the color identifier for the given name. *

    * Bright color can be specified with: {@code !} or {@code bright-}. @@ -44,21 +76,20 @@ */ private static Integer color(String name) { int flags = 0; - name = name.toLowerCase(Locale.US); + if (name.equals("default")) { + return -1; + } // extract bright flag from color name - if (name.charAt(0) == '!') { - name = name.substring(1, name.length()); + else if (name.charAt(0) == '!') { + name = name.substring(1); flags = BRIGHT; } else if (name.startsWith("bright-")) { - name = name.substring(7, name.length()); + name = name.substring(7); flags = BRIGHT; } else if (name.charAt(0) == '~') { + name = name.substring(1); try { - // TODO: if the palette is not the default one, should be - // TODO: translate into 24-bits first and let the #toAnsi() call - // TODO: round with the current palette ? - name = name.substring(1, name.length()); return Colors.rgbColor(name); } catch (IllegalArgumentException e) { // log.warning("Invalid style-color name: " + name); @@ -297,25 +328,51 @@ String colorName = parts[1].trim(); // resolve the color-name - Integer color = color(colorName); - if (color == null) { -// log.warning("Invalid color-name: " + colorName); - } else { - // resolve and apply color-mode - switch (colorMode.toLowerCase(Locale.US)) { - case "foreground": - case "fg": - case "f": - return style.foreground(color); - - case "background": - case "bg": - case "b": - return style.background(color); + Integer color; + // resolve and apply color-mode + switch (colorMode.toLowerCase(Locale.US)) { + case "foreground": + case "fg": + case "f": + color = color(colorName); + if (color == null) { +// log.warning("Invalid color-name: " + colorName); + break; + } + return color >= 0 ? style.foreground(color) : style.foregroundDefault(); - default: -// log.warning("Invalid color-mode: " + colorMode); - } + case "background": + case "bg": + case "b": + color = color(colorName); + if (color == null) { +// log.warning("Invalid color-name: " + colorName); + break; + } + return color >= 0 ? style.background(color) : style.backgroundDefault(); + + case "foreground-rgb": + case "fg-rgb": + case "f-rgb": + color = colorRgb(colorName); + if (color == null) { +// log.warning("Invalid color-name: " + colorName); + break; + } + return color >= 0 ? style.foregroundRgb(color) : style.foregroundDefault(); + + case "background-rgb": + case "bg-rgb": + case "b-rgb": + color = colorRgb(colorName); + if (color == null) { +// log.warning("Invalid color-name: " + colorName); + break; + } + return color >= 0 ? style.backgroundRgb(color) : style.backgroundDefault(); + + default: +// log.warning("Invalid color-mode: " + colorMode); } return style; } diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-basic.caps 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,41 @@ +# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-basic +rxvt-basic|rxvt-m|rxvt terminal base (X Window System), + am, bce, eo, km, mir, msgr, xenl, xon, + cols#80, it#8, lines#24, + acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, + clear=\E[H\E[2J, cnorm=\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K, + enacs=\E(B\E)0, flash=\E[?5h\E[?5l, home=\E[H, + hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@, + il=\E[%p1%dL, il1=\E[L, ind=\n, is1=\E[?47l\E=\E[?1l, + is2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l, + kDC=\E[3$, kEND=\E[8$, kHOM=\E[7$, kLFT=\E[d, kNXT=\E[6$, + kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, ka3=\EOy, kb2=\EOu, kbs=^?, + kc1=\EOq, kc3=\EOs, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B, + kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kel=\E[8\^, + kend=\E[8~, kent=\EOM, kf1=\E[11~, kf10=\E[21~, + kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~, + kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~, + kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, kf21=\E[23$, + kf22=\E[24$, kf23=\E[11\^, kf24=\E[12\^, kf25=\E[13\^, + kf26=\E[14\^, kf27=\E[15\^, kf28=\E[17\^, kf29=\E[18\^, + kf3=\E[13~, kf30=\E[19\^, kf31=\E[20\^, kf32=\E[21\^, + kf33=\E[23\^, kf34=\E[24\^, kf35=\E[25\^, kf36=\E[26\^, + kf37=\E[28\^, kf38=\E[29\^, kf39=\E[31\^, kf4=\E[14~, + kf40=\E[32\^, kf41=\E[33\^, kf42=\E[34\^, kf43=\E[23@, + kf44=\E[24@, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, + kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~, + kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~, + rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, rmcup=\E[2J\E[?47l\E8, + rmir=\E[4l, rmkx=\E>, rmso=\E[27m, rmul=\E[24m, + rs1=\E>\E[1;3;4;5;6l\E[?7h\E[m\E[r\E[2J\E[H, + rs2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E=\E[?1000l\E[?25h, + s0ds=\E(B, s1ds=\E(0, sc=\E7, + sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;, + sgr0=\E[0m\017, smacs=^N, smcup=\E7\E[?47h, smir=\E[4h, + smkx=\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g, + vpa=\E[%i%p1%dd, diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode-256color.caps 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,44 @@ +# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-unicode-256color +rxvt-unicode-256color|rxvt-unicode terminal with 256 colors (X Window System), + am, bce, bw, ccc, eo, hs, km, mc5i, mir, msgr, npc, xenl, xon, + btns#5, colors#0x100, cols#80, it#8, lines#24, lm#0, ncv#0, + pairs#0x7fff, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, + clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, + dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX, ed=\E[J, el=\E[K, + el1=\E[1K, enacs=, flash=\E[?5h$<20/>\E[?5l, fsl=^G, + home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, + ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS, + initc=\E]4;%p1%d;rgb\:%p2%{65535}%*%{1000}%/%4.4X/%p3%{65535}%*%{1000}%/%4.4X/%p4%{65535}%*%{1000}%/%4.4X\E\\, + is1=\E[!p, + is2=\E[r\E[m\E[2J\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l, + kDC=\E[3$, kEND=\E[8$, kFND=\E[1$, kHOM=\E[7$, kIC=\E[2$, + kLFT=\E[d, kNXT=\E[6$, kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, + ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z, + kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, + kdch1=\E[3~, kel=\E[8\^, kend=\E[8~, kent=\EOM, kf1=\E[11~, + kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, + kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, + kf18=\E[32~, kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, + kf3=\E[13~, kf4=\E[14~, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, + kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~, + kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~, + mc0=\E[i, mc4=\E[4i, mc5=\E[5i, op=\E[39;49m, rc=\E8, + rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, + rmam=\E[?7l, rmcup=\E[r\E[?1049l, rmir=\E[4l, rmkx=\E>, + rmso=\E[27m, rmul=\E[24m, rs1=\Ec, + rs2=\E[r\E[m\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l, + s0ds=\E(B, s1ds=\E(0, s2ds=\E*B, s3ds=\E+B, sc=\E7, + setab=\E[48;5;%p1%dm, setaf=\E[38;5;%p1%dm, + setb=%?%p1%{7}%>%t\E[48;5;%p1%dm%e\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;, + setf=%?%p1%{7}%>%t\E[38;5;%p1%dm%e\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;, + sgr=\E[%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m%?%p9%t\E(0%e\E(B%;, + sgr0=\E[m\E(B, sitm=\E[3m, smacs=\E(0, smam=\E[?7h, + smcup=\E[?1049h, smir=\E[4h, smkx=\E=, smso=\E[7m, + smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n, + u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd, diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt-unicode.caps 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,44 @@ +# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt-unicode +rxvt-unicode|rxvt-unicode terminal (X Window System), + am, bce, bw, ccc, eo, hs, km, mc5i, mir, msgr, npc, xenl, xon, + btns#5, colors#88, cols#80, it#8, lines#24, lm#0, ncv#0, + pairs#7744, + acsc=+C\,D-A.B0E``aaffgghFiGjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, + clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dl=\E[%p1%dM, + dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX, ed=\E[J, el=\E[K, + el1=\E[1K, enacs=, flash=\E[?5h$<20/>\E[?5l, fsl=^G, + home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, + ich1=\E[@, il=\E[%p1%dL, il1=\E[L, ind=\n, indn=\E[%p1%dS, + initc=\E]4;%p1%d;rgb\:%p2%{65535}%*%{1000}%/%4.4X/%p3%{65535}%*%{1000}%/%4.4X/%p4%{65535}%*%{1000}%/%4.4X\E\\, + is1=\E[!p, + is2=\E[r\E[m\E[2J\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l, + kDC=\E[3$, kEND=\E[8$, kFND=\E[1$, kHOM=\E[7$, kIC=\E[2$, + kLFT=\E[d, kNXT=\E[6$, kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, + ka3=\EOy, kb2=\EOu, kbs=^?, kc1=\EOq, kc3=\EOs, kcbt=\E[Z, + kcub1=\E[D, kcud1=\E[B, kcuf1=\E[C, kcuu1=\E[A, + kdch1=\E[3~, kel=\E[8\^, kend=\E[8~, kent=\EOM, kf1=\E[11~, + kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, + kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, + kf18=\E[32~, kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, + kf3=\E[13~, kf4=\E[14~, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, + kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~, + kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~, + mc0=\E[i, mc4=\E[4i, mc5=\E[5i, op=\E[39;49m, rc=\E8, + rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, + rmam=\E[?7l, rmcup=\E[r\E[?1049l, rmir=\E[4l, rmkx=\E>, + rmso=\E[27m, rmul=\E[24m, rs1=\Ec, + rs2=\E[r\E[m\E[?7;25h\E[?1;3;4;5;6;9;66;1000;1001;1049l\E[4l, + s0ds=\E(B, s1ds=\E(0, s2ds=\E*B, s3ds=\E+B, sc=\E7, + setab=\E[48;5;%p1%dm, setaf=\E[38;5;%p1%dm, + setb=%?%p1%{7}%>%t\E[48;5;%p1%dm%e\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;, + setf=%?%p1%{7}%>%t\E[38;5;%p1%dm%e\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m%;, + sgr=\E[%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m%?%p9%t\E(0%e\E(B%;, + sgr0=\E[m\E(B, sitm=\E[3m, smacs=\E(0, smam=\E[?7h, + smcup=\E[?1049h, smir=\E[4h, smkx=\E=, smso=\E[7m, + smul=\E[4m, tbc=\E[3g, tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n, + u8=\E[?1;2c, u9=\E[c, vpa=\E[%i%p1%dd, diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/rxvt.caps 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,43 @@ +# Reconstructed via infocmp from file: /lib/terminfo/r/rxvt +rxvt|rxvt terminal emulator (X Window System), + am, bce, eo, km, mir, msgr, xenl, xon, + colors#8, cols#80, it#8, lines#24, ncv@, pairs#64, + acsc=``aaffggjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, blink=\E[5m, bold=\E[1m, civis=\E[?25l, + clear=\E[H\E[2J, cnorm=\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + dl=\E[%p1%dM, dl1=\E[M, ed=\E[J, el=\E[K, el1=\E[1K, + enacs=\E(B\E)0, flash=\E[?5h\E[?5l, home=\E[H, + hpa=\E[%i%p1%dG, ht=^I, hts=\EH, ich=\E[%p1%d@, ich1=\E[@, + il=\E[%p1%dL, il1=\E[L, ind=\n, is1=\E[?47l\E=\E[?1l, + is2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l, + kDC=\E[3$, kEND=\E[8$, kHOM=\E[7$, kLFT=\E[d, kNXT=\E[6$, + kPRV=\E[5$, kRIT=\E[c, ka1=\EOw, ka3=\EOy, kb2=\EOu, kbs=^?, + kc1=\EOq, kc3=\EOs, kcbt=\E[Z, kcub1=\E[D, kcud1=\E[B, + kcuf1=\E[C, kcuu1=\E[A, kdch1=\E[3~, kel=\E[8\^, + kend=\E[8~, kent=\EOM, kf1=\E[11~, kf10=\E[21~, + kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, kf14=\E[26~, + kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, kf18=\E[32~, + kf19=\E[33~, kf2=\E[12~, kf20=\E[34~, kf21=\E[23$, + kf22=\E[24$, kf23=\E[11\^, kf24=\E[12\^, kf25=\E[13\^, + kf26=\E[14\^, kf27=\E[15\^, kf28=\E[17\^, kf29=\E[18\^, + kf3=\E[13~, kf30=\E[19\^, kf31=\E[20\^, kf32=\E[21\^, + kf33=\E[23\^, kf34=\E[24\^, kf35=\E[25\^, kf36=\E[26\^, + kf37=\E[28\^, kf38=\E[29\^, kf39=\E[31\^, kf4=\E[14~, + kf40=\E[32\^, kf41=\E[33\^, kf42=\E[34\^, kf43=\E[23@, + kf44=\E[24@, kf5=\E[15~, kf6=\E[17~, kf7=\E[18~, + kf8=\E[19~, kf9=\E[20~, kfnd=\E[1~, khome=\E[7~, + kich1=\E[2~, kmous=\E[M, knp=\E[6~, kpp=\E[5~, kslt=\E[4~, + op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, rmacs=^O, + rmcup=\E[2J\E[?47l\E8, rmir=\E[4l, rmkx=\E>, rmso=\E[27m, + rmul=\E[24m, + rs1=\E>\E[1;3;4;5;6l\E[?7h\E[m\E[r\E[2J\E[H, + rs2=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E=\E[?1000l\E[?25h, + s0ds=\E(B, s1ds=\E(0, sc=\E7, setab=\E[4%p1%dm, + setaf=\E[3%p1%dm, + sgr=\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t\016%e\017%;, + sgr0=\E[m\017, smacs=^N, smcup=\E7\E[?47h, smir=\E[4h, + smkx=\E=, smso=\E[7m, smul=\E[4m, tbc=\E[3g, + vpa=\E[%i%p1%dd, diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/share/legal/jline.md openjdk-17-17.0.3+7/src/jdk.internal.le/share/legal/jline.md --- openjdk-17-17.0.2+8/src/jdk.internal.le/share/legal/jline.md 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.internal.le/share/legal/jline.md 2022-04-19 19:55:43.000000000 +0000 @@ -1,4 +1,4 @@ -## JLine v3.14.0 +## JLine v3.20.0 ### JLine License

    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java openjdk-17-17.0.3+7/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/JnaSupportImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -14,6 +14,7 @@
     import jdk.internal.org.jline.terminal.impl.jna.win.JnaWinSysTerminal;
     import jdk.internal.org.jline.terminal.spi.JnaSupport;
     import jdk.internal.org.jline.terminal.spi.Pty;
    +import jdk.internal.org.jline.utils.OSUtils;
     
     import java.io.IOException;
     import java.io.InputStream;
    @@ -47,4 +48,30 @@
         public Terminal winSysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding, int codepage, boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused, Function inputStreamWrapper) throws IOException {
             return JnaWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding, codepage, nativeSignals, signalHandler, paused, inputStreamWrapper);
         }
    +
    +    @Override
    +    public boolean isWindowsConsole() {
    +        return JnaWinSysTerminal.isWindowsConsole();
    +    }
    +
    +    @Override
    +    public boolean isConsoleOutput() {
    +        if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
    +            throw new UnsupportedOperationException();
    +        } else if (OSUtils.IS_WINDOWS) {
    +            return JnaWinSysTerminal.isConsoleOutput();
    +        }
    +        throw new UnsupportedOperationException();
    +    }
    +
    +    @Override
    +    public boolean isConsoleInput() {
    +        if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
    +            throw new UnsupportedOperationException();
    +        } else if (OSUtils.IS_WINDOWS) {
    +            return JnaWinSysTerminal.isConsoleInput();
    +        }
    +        throw new UnsupportedOperationException();
    +    }
    +
     }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java openjdk-17-17.0.3+7/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.le/windows/classes/jdk/internal/org/jline/terminal/impl/jna/win/JnaWinSysTerminal.java	2022-04-19 19:55:43.000000000 +0000
    @@ -70,17 +70,43 @@
             return terminal;
         }
     
    +    public static boolean isWindowsConsole() {
    +        try {
    +            IntByReference mode = new IntByReference();
    +            Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
    +            Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
    +            return true;
    +        } catch (LastErrorException e) {
    +            return false;
    +        }
    +    }
    +
    +    public static boolean isConsoleOutput() {
    +        try {
    +            IntByReference mode = new IntByReference();
    +            Kernel32.INSTANCE.GetConsoleMode(consoleOut, mode);
    +            return true;
    +        } catch (LastErrorException e) {
    +            return false;
    +        }
    +    }
    +
    +    public static boolean isConsoleInput() {
    +        try {
    +            IntByReference mode = new IntByReference();
    +            Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
    +            return true;
    +        } catch (LastErrorException e) {
    +            return false;
    +        }
    +    }
    +
         JnaWinSysTerminal(Writer writer, String name, String type, Charset encoding, int codepage, boolean nativeSignals, SignalHandler signalHandler, Function inputStreamWrapper) throws IOException {
             super(writer, name, type, encoding, codepage, nativeSignals, signalHandler, inputStreamWrapper);
             strings.put(InfoCmp.Capability.key_mouse, "\\E[M");
         }
     
         @Override
    -    protected int getConsoleOutputCP() {
    -        return Kernel32.INSTANCE.GetConsoleOutputCP();
    -    }
    -
    -    @Override
         protected int getConsoleMode() {
             IntByReference mode = new IntByReference();
             Kernel32.INSTANCE.GetConsoleMode(consoleIn, mode);
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java	2022-04-19 19:55:43.000000000 +0000
    @@ -783,17 +783,21 @@
     
         /**
          * Reads the current value of a static field. If {@code expectedType} is non-null, then the
    -     * object is exptected to be a subtype of {@code expectedType} and extra sanity checking is
    +     * object is expected to be a subtype of {@code expectedType} and extra sanity checking is
          * performed on the offset and kind of the read being performed.
    +     *
    +     * @throws IllegalArgumentException if any of the sanity checks fail
          */
    -    native JavaConstant readFieldValue(HotSpotResolvedObjectTypeImpl object, HotSpotResolvedObjectTypeImpl expectedType, long offset, boolean isVolatile, JavaKind kind);
    +    native JavaConstant readFieldValue(HotSpotResolvedObjectTypeImpl object, HotSpotResolvedObjectTypeImpl expectedType, long offset, JavaKind kind);
     
         /**
          * Reads the current value of an instance field. If {@code expectedType} is non-null, then the
    -     * object is exptected to be a subtype of {@code expectedType} and extra sanity checking is
    +     * object is expected to be a subtype of {@code expectedType} and extra sanity checking is
          * performed on the offset and kind of the read being performed.
    +     *
    +     * @throws IllegalArgumentException if any of the sanity checks fail
          */
    -    native JavaConstant readFieldValue(HotSpotObjectConstantImpl object, HotSpotResolvedObjectTypeImpl expectedType, long offset, boolean isVolatile, JavaKind kind);
    +    native JavaConstant readFieldValue(HotSpotObjectConstantImpl object, HotSpotResolvedObjectTypeImpl expectedType, long offset, JavaKind kind);
     
         /**
          * @see ResolvedJavaType#isInstance(JavaConstant)
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotCodeCacheProvider.java	2022-04-19 19:55:43.000000000 +0000
    @@ -109,22 +109,12 @@
             HotSpotCompiledCode hsCompiledCode = (HotSpotCompiledCode) compiledCode;
             String name = hsCompiledCode.getName();
             HotSpotCompiledNmethod hsCompiledNmethod = null;
    -        if (method == null) {
    -            // Must be a stub
    -            resultInstalledCode = new HotSpotRuntimeStub(name);
    -        } else {
    -            hsCompiledNmethod = (HotSpotCompiledNmethod) hsCompiledCode;
    -            HotSpotResolvedJavaMethodImpl hsMethod = (HotSpotResolvedJavaMethodImpl) method;
    -            resultInstalledCode = new HotSpotNmethod(hsMethod, name, isDefault, hsCompiledNmethod.id);
    -        }
    -
             HotSpotSpeculationLog speculationLog = null;
             if (log != null) {
                 if (log.hasSpeculations()) {
                     speculationLog = (HotSpotSpeculationLog) log;
                 }
             }
    -
             byte[] speculations;
             long failedSpeculationsAddress;
             if (speculationLog != null) {
    @@ -134,6 +124,18 @@
                 speculations = new byte[0];
                 failedSpeculationsAddress = 0L;
             }
    +
    +        if (method == null) {
    +            // Must be a stub
    +            resultInstalledCode = new HotSpotRuntimeStub(name);
    +        } else {
    +            hsCompiledNmethod = (HotSpotCompiledNmethod) hsCompiledCode;
    +            HotSpotResolvedJavaMethodImpl hsMethod = (HotSpotResolvedJavaMethodImpl) method;
    +            HotSpotNmethod nmethod = new HotSpotNmethod(hsMethod, name, isDefault, hsCompiledNmethod.id);
    +            nmethod.setSpeculationLog(speculationLog);
    +            resultInstalledCode = nmethod;
    +        }
    +
             int result = runtime.getCompilerToVM().installCode(target, (HotSpotCompiledCode) compiledCode, resultInstalledCode, failedSpeculationsAddress, speculations);
             if (result != config.codeInstallResultOk) {
                 String resultDesc = config.getCodeInstallResultDescription(result);
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantReflectionProvider.java	2022-04-19 19:55:43.000000000 +0000
    @@ -166,11 +166,11 @@
             if (hotspotField.isStatic()) {
                 HotSpotResolvedObjectTypeImpl holder = (HotSpotResolvedObjectTypeImpl) hotspotField.getDeclaringClass();
                 if (holder.isInitialized()) {
    -                return runtime().compilerToVm.readFieldValue(holder, (HotSpotResolvedObjectTypeImpl) hotspotField.getDeclaringClass(), hotspotField.getOffset(), field.isVolatile(),
    +                return runtime().compilerToVm.readFieldValue(holder, (HotSpotResolvedObjectTypeImpl) hotspotField.getDeclaringClass(), hotspotField.getOffset(),
                                     hotspotField.getType().getJavaKind());
                 }
             } else if (receiver instanceof HotSpotObjectConstantImpl) {
    -            return ((HotSpotObjectConstantImpl) receiver).readFieldValue(hotspotField, field.isVolatile());
    +            return ((HotSpotObjectConstantImpl) receiver).readFieldValue(hotspotField);
             } else if (receiver == null) {
                 throw new NullPointerException("receiver is null");
             }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotMemoryAccessProviderImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -77,7 +77,8 @@
                             throw new IllegalArgumentException(String.valueOf(bits));
                     }
                 }
    -            JavaConstant result = runtime().compilerToVm.readFieldValue((HotSpotObjectConstantImpl) baseConstant, null, initialDisplacement, true, readKind);
    +            HotSpotObjectConstantImpl baseObject = (HotSpotObjectConstantImpl) baseConstant;
    +            JavaConstant result = runtime().compilerToVm.readFieldValue(baseObject, null, initialDisplacement, readKind);
                 if (result != null && kind != readKind) {
                     return JavaConstant.forPrimitive(kind, result.asLong());
                 }
    @@ -108,7 +109,7 @@
         @Override
         public JavaConstant readObjectConstant(Constant base, long displacement) {
             if (base instanceof HotSpotObjectConstantImpl) {
    -            return runtime.getCompilerToVM().readFieldValue((HotSpotObjectConstantImpl) base, null, displacement, true, JavaKind.Object);
    +            return runtime.getCompilerToVM().readFieldValue((HotSpotObjectConstantImpl) base, null, displacement, JavaKind.Object);
             }
             if (base instanceof HotSpotMetaspaceConstant) {
                 MetaspaceObject metaspaceObject = HotSpotMetaspaceConstantImpl.getMetaspaceObject(base);
    @@ -130,7 +131,7 @@
         public JavaConstant readNarrowOopConstant(Constant base, long displacement) {
             if (base instanceof HotSpotObjectConstantImpl) {
                 assert runtime.getConfig().useCompressedOops;
    -            JavaConstant res = runtime.getCompilerToVM().readFieldValue((HotSpotObjectConstantImpl) base, null, displacement, true, JavaKind.Object);
    +            JavaConstant res = runtime.getCompilerToVM().readFieldValue((HotSpotObjectConstantImpl) base, null, displacement, JavaKind.Object);
                 if (res != null) {
                     return JavaConstant.NULL_POINTER.equals(res) ? HotSpotCompressedNullConstant.COMPRESSED_NULL : ((HotSpotObjectConstant) res).compress();
                 }
    @@ -147,7 +148,6 @@
             }
         }
     
    -
         @Override
         public Constant readKlassPointerConstant(Constant base, long displacement) {
             HotSpotResolvedObjectTypeImpl klass = readKlass(base, displacement, false);
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotNmethod.java	2022-04-19 19:55:43.000000000 +0000
    @@ -68,8 +68,8 @@
     
         /**
          * If this field is 0, this object is in the oops table of the nmethod. Otherwise, the value of
    -     * the field records the nmethod's compile identifier. This value is used to confirm an entry in
    -     * the code cache retrieved by {@link #address} is indeed the nmethod represented by this
    +     * the field records the nmethod's compile identifier. This value is used to confirm if an entry
    +     * in the code cache retrieved by {@link #address} is indeed the nmethod represented by this
          * object.
          *
          * @see #inOopsTable
    @@ -86,6 +86,23 @@
         }
     
         /**
    +     * Attaches {@code log} to this object. If {@code log.managesFailedSpeculations() == true}, this
    +     * ensures the failed speculation list lives at least as long as this object.
    +     */
    +    public void setSpeculationLog(HotSpotSpeculationLog log) {
    +        this.speculationLog = log;
    +    }
    +
    +    /**
    +     * The speculation log containing speculations embedded in the nmethod.
    +     *
    +     * If {@code speculationLog.managesFailedSpeculations() == true}, this field ensures the failed
    +     * speculation list lives at least as long as this object. This prevents deoptimization from
    +     * appending to an already freed list.
    +     */
    +    @SuppressWarnings("unused") private HotSpotSpeculationLog speculationLog;
    +
    +    /**
          * Determines if the nmethod associated with this object is the compiled entry point for
          * {@link #getMethod()}.
          */
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotObjectConstantImpl.java	2022-04-19 19:55:43.000000000 +0000
    @@ -72,7 +72,7 @@
             }
             // read ConstantCallSite.isFrozen as a volatile field
             HotSpotResolvedJavaField field = HotSpotMethodHandleAccessProvider.Internals.instance().constantCallSiteFrozenField;
    -        boolean isFrozen = readFieldValue(field, true /* volatile */).asBoolean();
    +        boolean isFrozen = readFieldValue(field).asBoolean();
             // isFrozen true implies fully-initialized
             return isFrozen;
         }
    @@ -80,7 +80,7 @@
         private HotSpotObjectConstantImpl readTarget() {
             // read CallSite.target as a volatile field
             HotSpotResolvedJavaField field = HotSpotMethodHandleAccessProvider.Internals.instance().callSiteTargetField;
    -        return (HotSpotObjectConstantImpl) readFieldValue(field, true /* volatile */);
    +        return (HotSpotObjectConstantImpl) readFieldValue(field);
         }
     
         @Override
    @@ -184,7 +184,7 @@
             return (compressed ? "NarrowOop" : getJavaKind().getJavaName()) + "[" + runtime().reflection.formatString(this) + "]";
         }
     
    -    public JavaConstant readFieldValue(HotSpotResolvedJavaField field, boolean isVolatile) {
    +    public JavaConstant readFieldValue(HotSpotResolvedJavaField field) {
             if (IS_IN_NATIVE_IMAGE && this instanceof DirectHotSpotObjectConstantImpl) {
                 // cannot read fields from objects due to lack of
                 // general reflection support in native image
    @@ -193,7 +193,7 @@
             if (field.isStatic()) {
                 return null;
             }
    -        return runtime().compilerToVm.readFieldValue(this, (HotSpotResolvedObjectTypeImpl) field.getDeclaringClass(), field.getOffset(), isVolatile, field.getType().getJavaKind());
    +        return runtime().compilerToVm.readFieldValue(this, (HotSpotResolvedObjectTypeImpl) field.getDeclaringClass(), field.getOffset(), field.getType().getJavaKind());
         }
     
         public ResolvedJavaType asJavaType() {
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java	2022-04-19 19:55:43.000000000 +0000
    @@ -337,7 +337,7 @@
         final int deoptReasonLoopLimitCheck = getConstant("Deoptimization::Reason_loop_limit_check", Integer.class);
         final int deoptReasonAliasing = getConstant("Deoptimization::Reason_aliasing", Integer.class);
         final int deoptReasonTransferToInterpreter = getConstant("Deoptimization::Reason_transfer_to_interpreter", Integer.class);
    -    final int deoptReasonOSROffset = getConstant("Deoptimization::Reason_LIMIT", Integer.class);
    +    final int deoptReasonOSROffset = getConstant("Deoptimization::Reason_TRAP_HISTORY_LENGTH", Integer.class);
     
         final int deoptActionNone = getConstant("Deoptimization::Action_none", Integer.class);
         final int deoptActionMaybeRecompile = getConstant("Deoptimization::Action_maybe_recompile", Integer.class);
    diff -Nru openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java
    --- openjdk-17-17.0.2+8/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.meta/src/jdk/vm/ci/meta/MemoryAccessProvider.java	2022-04-19 19:55:43.000000000 +0000
    @@ -37,7 +37,7 @@
          * @return the read value encapsulated in a {@link JavaConstant} object of {@link JavaKind} kind
          * @throws IllegalArgumentException if the read is out of bounds of the object or {@code kind}
          *             is {@link JavaKind#Void} or not {@linkplain JavaKind#isPrimitive() primitive}
    -     *             kind or {@code bits} is not 8, 16, 32 or 64
    +     *             kind or {@code bits} is not 8, 16, 32 or 64, or the read is unaligned
          */
         JavaConstant readPrimitiveConstant(JavaKind kind, Constant base, long displacement, int bits) throws IllegalArgumentException;
     
    diff -Nru openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java
    --- openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	2022-04-19 19:55:43.000000000 +0000
    @@ -34,12 +34,21 @@
     import java.util.regex.PatternSyntaxException;
     import jdk.internal.module.ModulePath;
     import jdk.internal.module.ModuleResolution;
    +import java.time.ZoneOffset;
    +import java.time.ZonedDateTime;
    +import java.time.LocalDateTime;
    +import java.time.format.DateTimeFormatter;
    +import java.time.format.DateTimeParseException;
     
     /**
      * Parser for GNU Style Options.
      */
     class GNUStyleOptions {
     
    +    // Valid --date range
    +    static final ZonedDateTime DATE_MIN = ZonedDateTime.parse("1980-01-01T00:00:02Z");
    +    static final ZonedDateTime DATE_MAX = ZonedDateTime.parse("2099-12-31T23:59:59Z");
    +
         static class BadArgs extends Exception {
             static final long serialVersionUID = 0L;
     
    @@ -188,6 +197,20 @@
                         jartool.flag0 = true;
                     }
                 },
    +            new Option(true, OptionType.CREATE_UPDATE_INDEX, "--date") {
    +                void process(Main jartool, String opt, String arg) throws BadArgs {
    +                    try {
    +                        ZonedDateTime date = ZonedDateTime.parse(arg, DateTimeFormatter.ISO_ZONED_DATE_TIME)
    +                                                             .withZoneSameInstant(ZoneOffset.UTC);
    +                        if (date.isBefore(DATE_MIN) || date.isAfter(DATE_MAX)) {
    +                            throw new BadArgs("error.date.out.of.range", arg);
    +                        }
    +                        jartool.date = date.toLocalDateTime();
    +                    } catch (DateTimeParseException x) {
    +                        throw new BadArgs("error.date.notvalid", arg);
    +                    }
    +                }
    +            },
     
                 // Hidden options
                 new Option(false, OptionType.OTHER, "-P") {
    diff -Nru openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/Main.java openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
    --- openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     /*
    - * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
    + * Copyright (c) 1996, 2022, 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,6 +59,7 @@
     import java.util.zip.ZipFile;
     import java.util.zip.ZipInputStream;
     import java.util.zip.ZipOutputStream;
    +import java.util.concurrent.TimeUnit;
     import jdk.internal.module.Checks;
     import jdk.internal.module.ModuleHashes;
     import jdk.internal.module.ModuleHashesBuilder;
    @@ -67,6 +68,8 @@
     import jdk.internal.module.ModuleResolution;
     import jdk.internal.module.ModuleTarget;
     import jdk.internal.util.jar.JarIndex;
    +import java.time.LocalDateTime;
    +import java.time.ZoneOffset;
     
     import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
     import static java.util.jar.JarFile.MANIFEST_NAME;
    @@ -127,7 +130,10 @@
         Map> pathsMap = new HashMap<>();
     
         // There's also a files array per version
    -    Map filesMap = new HashMap<>();
    +    // base version is the first entry and then follow with the version given
    +    // from the --release option in the command-line order.
    +    // The value of each entry is the files given in the command-line order.
    +    Map filesMap = new LinkedHashMap<>();
     
         // Do we think this is a multi-release jar?  Set to true
         // if --release option found followed by at least file
    @@ -170,6 +176,9 @@
         static final int VERSIONS_DIR_LENGTH = VERSIONS_DIR.length();
         private static ResourceBundle rsrc;
     
    +    /* Date option for entry timestamps resolved to UTC Local time */
    +    LocalDateTime date;
    +
         /**
          * If true, maintain compatibility with JDK releases prior to 6.0 by
          * timestamping extracted files with the time at which they are extracted.
    @@ -772,15 +781,17 @@
         private void expand(File dir, String[] files, Set cpaths, int version)
             throws IOException
         {
    -        if (files == null)
    +        if (files == null) {
                 return;
    +        }
     
             for (int i = 0; i < files.length; i++) {
                 File f;
    -            if (dir == null)
    +            if (dir == null) {
                     f = new File(files[i]);
    -            else
    +            } else {
                     f = new File(dir, files[i]);
    +            }
     
                 boolean isDir = f.isDirectory();
                 String name = toEntryName(f.getPath(), cpaths, isDir);
    @@ -802,18 +813,20 @@
                     Entry e = new Entry(f, name, false);
                     if (isModuleInfoEntry(name)) {
                         moduleInfos.putIfAbsent(name, Files.readAllBytes(f.toPath()));
    -                    if (uflag)
    +                    if (uflag) {
                             entryMap.put(name, e);
    +                    }
                     } else if (entries.add(e)) {
    -                    if (uflag)
    +                    if (uflag) {
                             entryMap.put(name, e);
    +                    }
                     }
                 } else if (isDir) {
                     Entry e = new Entry(f, name, true);
                     if (entries.add(e)) {
                         // utilize entryMap for the duplicate dir check even in
                         // case of cflag == true.
    -                    // dir name confilict/duplicate could happen with -C option.
    +                    // dir name conflict/duplicate could happen with -C option.
                         // just remove the last "e" from the "entries" (zos will fail
                         // with "duplicated" entries), but continue expanding the
                         // sub tree
    @@ -822,7 +835,12 @@
                         } else {
                             entryMap.put(name, e);
                         }
    -                    expand(f, f.list(), cpaths, version);
    +                    String[] dirFiles = f.list();
    +                    // Ensure files list is sorted for reproducible jar content
    +                    if (dirFiles != null) {
    +                        Arrays.sort(dirFiles);
    +                    }
    +                    expand(f, dirFiles, cpaths, version);
                     }
                 } else {
                     error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
    @@ -846,12 +864,12 @@
                         output(getMsg("out.added.manifest"));
                     }
                     ZipEntry e = new ZipEntry(MANIFEST_DIR);
    -                e.setTime(System.currentTimeMillis());
    +                setZipEntryTime(e);
                     e.setSize(0);
                     e.setCrc(0);
                     zos.putNextEntry(e);
                     e = new ZipEntry(MANIFEST_NAME);
    -                e.setTime(System.currentTimeMillis());
    +                setZipEntryTime(e);
                     if (flag0) {
                         crc32Manifest(e, manifest);
                     }
    @@ -951,7 +969,7 @@
                         // do our own compression
                         ZipEntry e2 = new ZipEntry(name);
                         e2.setMethod(e.getMethod());
    -                    e2.setTime(e.getTime());
    +                    setZipEntryTime(e2, e.getTime());
                         e2.setComment(e.getComment());
                         e2.setExtra(e.getExtra());
                         if (e.getMethod() == ZipEntry.STORED) {
    @@ -1017,7 +1035,7 @@
             throws IOException
         {
             ZipEntry e = new ZipEntry(INDEX_NAME);
    -        e.setTime(System.currentTimeMillis());
    +        setZipEntryTime(e);
             if (flag0) {
                 CRC32OutputStream os = new CRC32OutputStream();
                 index.write(os);
    @@ -1036,7 +1054,7 @@
                 String name = mi.getKey();
                 byte[] bytes = mi.getValue();
                 ZipEntry e = new ZipEntry(name);
    -            e.setTime(System.currentTimeMillis());
    +            setZipEntryTime(e);
                 if (flag0) {
                     crc32ModuleInfo(e, bytes);
                 }
    @@ -1061,7 +1079,7 @@
                 addMultiRelease(m);
             }
             ZipEntry e = new ZipEntry(MANIFEST_NAME);
    -        e.setTime(System.currentTimeMillis());
    +        setZipEntryTime(e);
             if (flag0) {
                 crc32Manifest(e, m);
             }
    @@ -1182,7 +1200,7 @@
                 out.print(formatMsg("out.adding", name));
             }
             ZipEntry e = new ZipEntry(name);
    -        e.setTime(file.lastModified());
    +        setZipEntryTime(e, file.lastModified());
             if (size == 0) {
                 e.setMethod(ZipEntry.STORED);
                 e.setSize(0);
    @@ -2261,4 +2279,18 @@
         static Comparator ENTRY_COMPARATOR =
             Comparator.comparing(ZipEntry::getName, ENTRYNAME_COMPARATOR);
     
    +    // Set the ZipEntry dostime using date if specified otherwise the current time
    +    private void setZipEntryTime(ZipEntry e) {
    +        setZipEntryTime(e, System.currentTimeMillis());
    +    }
    +
    +    // Set the ZipEntry dostime using the date if specified
    +    // otherwise the original time
    +    private void setZipEntryTime(ZipEntry e, long origTime) {
    +        if (date != null) {
    +            e.setTimeLocal(date);
    +        } else {
    +            e.setTime(origTime);
    +        }
    +    }
     }
    diff -Nru openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
    --- openjdk-17-17.0.2+8/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	2022-04-19 19:55:43.000000000 +0000
    @@ -1,5 +1,5 @@
     #
    -# Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
    +# Copyright (c) 1999, 2021, 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
    @@ -82,6 +82,10 @@
             release {0} not valid, must be >= 9
     error.release.unexpected.versioned.entry=\
             unexpected versioned entry {0} for release {1}
    +error.date.notvalid=\
    +        date {0} is not a valid ISO-8601 extended offset date-time with optional time-zone
    +error.date.out.of.range=\
    +        date {0} is not within the valid range 1980-01-01T00:00:02Z to 2099-12-31T23:59:59Z
     error.validator.jarfile.exception=\
             can not validate {0}: {1}
     error.validator.jarfile.invalid=\
    @@ -290,6 +294,10 @@
     \ Operation modifiers valid only in create, update, and generate-index mode:\n
     main.help.opt.create.update.index.no-compress=\
     \  -0, --no-compress          Store only; use no ZIP compression
    +main.help.opt.create.update.index.date=\
    +\      --date=TIMESTAMP       The timestamp in ISO-8601 extended offset date-time with\n\
    +\                             optional time-zone format, to use for the timestamps of\n\
    +\                             entries, e.g. "2022-02-12T12:30:00-05:00"
     main.help.opt.other=\
     \ Other options:\n
     main.help.opt.other.help=\
    diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java
    --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java	2021-12-07 18:03:22.000000000 +0000
    +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/consumer/EventStream.java	2022-04-19 19:55:43.000000000 +0000
    @@ -154,6 +154,8 @@
          * 

    * By default, the stream starts with the next event flushed by Flight * Recorder. + *

    + * Only trusted disk repositories should be opened. * * @param directory location of the disk repository, not {@code null} * @@ -184,6 +186,8 @@ * Creates an event stream from a file. *

    * By default, the stream starts with the first event in the file. + *

    + * Only recording files from trusted sources should be opened. * * @param file location of the file, not {@code null} * diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/consumer/RecordingFile.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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 @@ -70,6 +70,8 @@ /** * Creates a recording file. + *

    + * Only recording files from trusted sources should be used. * * @param file the path of the file to open, not {@code null} * @throws IOException if it's not a valid recording file, or an I/O error @@ -209,6 +211,8 @@ *

    * This method is intended for simple cases where it's convenient to read all * events in a single operation. It isn't intended for reading large files. + *

    + * Only recording files from trusted sources should be used. * * @param path the path to the file, not {@code null} * diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataReader.java 2022-04-19 19:55:43.000000000 +0000 @@ -66,6 +66,7 @@ public MetadataReader(RecordingInput input) throws IOException { this.input = input; int size = input.readInt(); + input.require(size, "Metadata string pool size %d exceeds available data" ); this.pool = new ArrayList<>(size); StringParser p = new StringParser(null, false); for (int i = 0; i < size; i++) { diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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 @@ -114,7 +114,7 @@ byte fileState1; input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) { - Utils.takeNap(1); + input.pollWait(); input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION); } input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION); @@ -184,7 +184,7 @@ finished = true; return; } - Utils.takeNap(1); + input.pollWait(); } } finally { input.position(pos); diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventDirectoryStream.java 2022-04-19 19:55:43.000000000 +0000 @@ -148,6 +148,7 @@ } currentChunkStartNanos = repositoryFiles.getTimestamp(path); try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) { + input.setStreamed(); currentParser = new ChunkParser(input, disp.parserConfiguration); long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration(); long filterStart = validStartTime ? disp.startNanos : segmentStart; diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/EventFileStream.java 2022-04-19 19:55:43.000000000 +0000 @@ -50,6 +50,7 @@ super(acc, null, Collections.emptyList()); Objects.requireNonNull(path); this.input = new RecordingInput(path.toFile(), FileAccess.UNPRIVILEGED); + this.input.setStreamed(); } @Override diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/OngoingStream.java 2022-04-19 19:55:43.000000000 +0000 @@ -214,6 +214,7 @@ return false; } input = new RecordingInput(path.toFile(), SecuritySupport.PRIVILEGED); + input.setStreamed(); header = new ChunkHeader(input); } return true; diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ParserFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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 @@ -296,6 +296,7 @@ @Override public Object parse(RecordingInput input) throws IOException { final int size = input.readInt(); + input.require(size, "Array size %d exceeds available data" ); final Object[] array = new Object[size]; for (int i = 0; i < size; i++) { array[i] = elementParser.parse(input); diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/RecordingInput.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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,6 +31,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.nio.file.Path; +import jdk.jfr.internal.Utils; public final class RecordingInput implements DataInput, AutoCloseable { @@ -66,6 +67,7 @@ } private final int blockSize; private final FileAccess fileAccess; + private long pollCount = 1000; private RandomAccessFile file; private String filename; private Block currentBlock = new Block(); @@ -418,6 +420,15 @@ return filename; } + // Purpose of this method is to prevent OOM by sanity check + // the minimum required number of bytes against what is available in + // segment/chunk/file + public void require(int minimumBytes, String errorMessage) throws IOException { + if (position + minimumBytes > size) { + throw new IOException(String.format(errorMessage, minimumBytes)); + } + } + // Purpose of this method is to reuse block cache from a // previous RecordingInput public void setFile(Path path) throws IOException { @@ -430,4 +441,18 @@ initialize(path.toFile()); } + // Marks that it is OK to poll indefinitely for file update + // By default, only 1000 polls are allowed + public void setStreamed() { + this.pollCount = Long.MAX_VALUE; + } + + // Wait for file to be updated + public void pollWait() throws IOException { + pollCount--; + if (pollCount < 0) { + throw new IOException("Recording file is stuck in locked stream state."); + } + Utils.takeNap(1); + } } diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/StringParser.java 2022-04-19 19:55:43.000000000 +0000 @@ -69,6 +69,7 @@ @Override public Object parse(RecordingInput input) throws IOException { int size = input.readInt(); + input.require(size, "String size %d exceeds available data"); ensureSize(size); if (lastSize == size) { boolean equalsLastString = true; @@ -114,6 +115,7 @@ @Override public Object parse(RecordingInput input) throws IOException { int size = input.readInt(); + input.require(size, "String size %d exceeds available data"); ensureSize(size); if (lastSize == size) { boolean equalsLastString = true; diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/JSONWriter.java 2022-04-19 19:55:43.000000000 +0000 @@ -185,7 +185,7 @@ private void printDataStructureName(String text) { printIndent(); print("\""); - print(text); + printEscaped(text); print("\": "); } diff -Nru openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java --- openjdk-17-17.0.2+8/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jfr/share/classes/jdk/jfr/internal/tool/XMLWriter.java 2022-04-19 19:55:43.000000000 +0000 @@ -83,7 +83,11 @@ } private void printAttribute(String name, String value) { - print(" ", name, "=\"", value, "\""); + print(" "); + print(name); // Only known strings + print("=\""); + printEscaped(value); + print("\""); } public void printObject(RecordedObject struct) { diff -Nru openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java --- openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodOutputStream.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import jdk.internal.jmod.JmodFile; +import java.time.LocalDateTime; import static jdk.internal.jmod.JmodFile.*; @@ -54,15 +55,17 @@ * This method creates (or overrides, if exists) the JMOD file, * returning the the output stream to write to the JMOD file. */ - static JmodOutputStream newOutputStream(Path file) throws IOException { + static JmodOutputStream newOutputStream(Path file, LocalDateTime date) throws IOException { OutputStream out = Files.newOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(out); - return new JmodOutputStream(bos); + return new JmodOutputStream(bos, date); } private final ZipOutputStream zos; - private JmodOutputStream(OutputStream out) { + private final LocalDateTime date; + private JmodOutputStream(OutputStream out, LocalDateTime date) { this.zos = new ZipOutputStream(out); + this.date = date; try { JmodFile.writeMagicNumber(out); } catch (IOException e) { @@ -104,7 +107,11 @@ // sun.tools.jar.Main.update() ZipEntry e2 = new ZipEntry(e1.getName()); e2.setMethod(e1.getMethod()); - e2.setTime(e1.getTime()); + if (date != null) { + e2.setTimeLocal(date); + } else { + e2.setTime(e1.getTime()); + } e2.setComment(e1.getComment()); e2.setExtra(e1.getExtra()); if (e1.getMethod() == ZipEntry.STORED) { @@ -124,7 +131,11 @@ String name = Paths.get(prefix, path).toString() .replace(File.separatorChar, '/'); entries.get(section).add(path); - return new ZipEntry(name); + ZipEntry zipEntry = new ZipEntry(name); + if (date != null) { + zipEntry.setTimeLocal(date); + } + return zipEntry; } public boolean contains(Section section, String path) { diff -Nru openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java --- openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -62,6 +62,11 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import jdk.internal.jmod.JmodFile; import jdk.internal.jmod.JmodFile.Section; @@ -160,8 +165,13 @@ boolean dryrun; List excludes; Path extractDir; + LocalDateTime date; } + // Valid --date range + static final ZonedDateTime DATE_MIN = ZonedDateTime.parse("1980-01-01T00:00:02Z"); + static final ZonedDateTime DATE_MAX = ZonedDateTime.parse("2099-12-31T23:59:59Z"); + public int run(String[] args) { try { @@ -427,7 +437,7 @@ Path target = options.jmodFile; Path tempTarget = jmodTempFilePath(target); try { - try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget)) { + try (JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date)) { jmod.write(jos); } Files.move(tempTarget, target); @@ -767,6 +777,10 @@ void processSection(JmodOutputStream out, Section section, Path path) throws IOException { + // Keep a sorted set of files to be processed, so that the jmod + // content is reproducible as Files.walkFileTree order is not defined + SortedMap filesToProcess = new TreeMap(); + Files.walkFileTree(path, Set.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor() { @Override @@ -782,14 +796,21 @@ if (out.contains(section, name)) { warning("warn.ignore.duplicate.entry", name, section); } else { - try (InputStream in = Files.newInputStream(file)) { - out.writeEntry(in, section, name); - } + filesToProcess.put(name, file); } } return FileVisitResult.CONTINUE; } }); + + // Process files in sorted order for deterministic jmod content + for (Map.Entry entry : filesToProcess.entrySet()) { + String name = entry.getKey(); + Path file = entry.getValue(); + try (InputStream in = Files.newInputStream(file)) { + out.writeEntry(in, section, name); + } + } } boolean matches(Path path, List matchers) { @@ -973,7 +994,11 @@ if (e.getName().equals(MODULE_INFO)) { // what about module-info.class in versioned entries? ZipEntry ze = new ZipEntry(e.getName()); - ze.setTime(System.currentTimeMillis()); + if (options.date != null) { + ze.setTimeLocal(options.date); + } else { + ze.setTime(System.currentTimeMillis()); + } jos.putNextEntry(ze); recordHashes(in, jos, moduleHashes); jos.closeEntry(); @@ -1001,7 +1026,7 @@ { try (JmodFile jf = new JmodFile(target); - JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget)) + JmodOutputStream jos = JmodOutputStream.newOutputStream(tempTarget, options.date)) { jf.stream().forEach(e -> { try (InputStream in = jf.getInputStream(e.section(), e.name())) { @@ -1136,6 +1161,26 @@ @Override public String valuePattern() { return "module-version"; } } + static class DateConverter implements ValueConverter { + @Override + public LocalDateTime convert(String value) { + try { + ZonedDateTime date = ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME) + .withZoneSameInstant(ZoneOffset.UTC); + if (date.isBefore(DATE_MIN) || date.isAfter(DATE_MAX)) { + throw new CommandException("err.date.out.of.range", value); + } + return date.toLocalDateTime(); + } catch (DateTimeParseException x) { + throw new CommandException("err.invalid.date", value, x.getMessage()); + } + } + + @Override public Class valueType() { return LocalDateTime.class; } + + @Override public String valuePattern() { return "date"; } + } + static class WarnIfResolvedReasonConverter implements ValueConverter { @@ -1371,6 +1416,11 @@ OptionSpec version = parser.accepts("version", getMessage("main.opt.version")); + OptionSpec date + = parser.accepts("date", getMessage("main.opt.date")) + .withRequiredArg() + .withValuesConvertedBy(new DateConverter()); + NonOptionArgumentSpec nonOptions = parser.nonOptions(); @@ -1414,6 +1464,8 @@ options.manPages = getLastElement(opts.valuesOf(manPages)); if (opts.has(legalNotices)) options.legalNotices = getLastElement(opts.valuesOf(legalNotices)); + if (opts.has(date)) + options.date = opts.valueOf(date); if (opts.has(modulePath)) { Path[] dirs = getLastElement(opts.valuesOf(modulePath)).toArray(new Path[0]); options.moduleFinder = ModulePath.of(Runtime.version(), true, dirs); diff -Nru openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties --- openjdk-17-17.0.2+8/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ # -# Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2015, 2021, 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 @@ -74,6 +74,9 @@ main.opt.do-not-resolve-by-default=Exclude from the default root set of modules main.opt.warn-if-resolved=Hint for a tool to issue a warning if the module \ is resolved. One of deprecated, deprecated-for-removal, or incubating +main.opt.date=Date and time for the timestamps of entries, specified in ISO-8601\ +\ extended offset date-time with optional time-zone format, e.g.\ +\ "2022-02-12T12:30:00-05:00" main.opt.cmdfile=Read options from the specified file @@ -106,6 +109,8 @@ err.missing.export.or.open.packages=Packages that are exported or open in {0} are not present: {1} err.module.resolution.fail=Resolution failed: {0} err.no.moduleToHash=No hashes recorded: no module matching {0} found to record hashes +err.invalid.date=--date {0} is not a valid ISO-8601 extended offset date-time with optional time-zone format: {1} +err.date.out.of.range=--date {0} is out of the valid range 1980-01-01T00:00:02Z to 2099-12-31T23:59:59Z warn.invalid.arg=Invalid classname or pathname not exist: {0} warn.no.module.hashes=No hashes recorded: no module specified for hashing depends on {0} warn.ignore.entry=ignoring entry {0}, in section {1} diff -Nru openjdk-17-17.0.2+8/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java openjdk-17-17.0.3+7/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java --- openjdk-17-17.0.2+8/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java 2022-04-19 19:55:43.000000000 +0000 @@ -254,7 +254,7 @@ Pattern.compile(OPTION_PRE_PATTERN.pattern() + "(?

    -??)(?-([a-z][a-z\\-]*)?)"); // match an option flag and a (possibly missing or incomplete) value private static final Pattern OPTION_VALUE_PATTERN = - Pattern.compile(OPTION_PATTERN.pattern() + "\\s+(?\\S*)"); + Pattern.compile(OPTION_PATTERN.pattern() + "\\s+(?(\\S|\\\\ )*)"); // Tool id (tid) mapping: the three name spaces NameSpace mainNamespace; @@ -1555,13 +1555,13 @@ private static CompletionProvider fileCompletions(Predicate accept) { return (code, cursor, anchor) -> { int lastSlash = code.lastIndexOf('/'); - String path = code.substring(0, lastSlash + 1); - String prefix = lastSlash != (-1) ? code.substring(lastSlash + 1) : code; + String path = code.substring(0, lastSlash + 1).replace("\\ ", " "); + String prefix = (lastSlash != (-1) ? code.substring(lastSlash + 1) : code).replace("\\ ", " "); Path current = toPathResolvingUserHome(path); List result = new ArrayList<>(); try (Stream dir = Files.list(current)) { dir.filter(f -> accept.test(f) && f.getFileName().toString().startsWith(prefix)) - .map(f -> new ArgSuggestion(f.getFileName() + (Files.isDirectory(f) ? "/" : ""))) + .map(f -> new ArgSuggestion(f.getFileName().toString().replace(" ", "\\ ") + (Files.isDirectory(f) ? "/" : ""))) .forEach(result::add); } catch (IOException ex) { //ignore... diff -Nru openjdk-17-17.0.2+8/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java openjdk-17-17.0.3+7/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java --- openjdk-17-17.0.2+8/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsClient.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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,10 +26,12 @@ package com.sun.jndi.dns; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.DatagramSocket; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.PortUnreachableException; import java.net.Socket; import java.net.SocketTimeoutException; import java.security.SecureRandom; @@ -275,7 +277,16 @@ } // servers } return new ResourceRecords(msg, msg.length, hdr, false); - + } catch (UncheckedIOException | PortUnreachableException ex) { + // DatagramSocket.connect in doUdpQuery can throw UncheckedIOException + // DatagramSocket.send in doUdpQuery can throw PortUnreachableException + if (debug) { + dprint("Caught Exception:" + ex); + } + if (caughtException == null) { + caughtException = ex; + } + doNotRetry[i] = true; } catch (IOException e) { if (debug) { dprint("Caught IOException:" + e); @@ -283,12 +294,6 @@ if (caughtException == null) { caughtException = e; } - // Use reflection to allow pre-1.4 compilation. - // This won't be needed much longer. - if (e.getClass().getName().equals( - "java.net.PortUnreachableException")) { - doNotRetry[i] = true; - } } catch (NameNotFoundException e) { // This is authoritative, so return immediately throw e; diff -Nru openjdk-17-17.0.2+8/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java openjdk-17-17.0.3+7/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java --- openjdk-17-17.0.2+8/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.naming.dns/share/classes/com/sun/jndi/dns/DnsUrl.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2022, 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,11 @@ import java.net.MalformedURLException; -import java.util.Hashtable; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; import java.util.StringTokenizer; import com.sun.jndi.toolkit.url.Uri; @@ -56,6 +60,24 @@ public class DnsUrl extends Uri { + private static final String PARSE_MODE_PROP = "com.sun.jndi.dnsURLParsing"; + private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT; + + public static final ParseMode PARSE_MODE; + static { + PrivilegedAction action = () -> + System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); + ParseMode parseMode = DEFAULT_PARSE_MODE; + try { + @SuppressWarnings("removal") + String mode = AccessController.doPrivileged(action); + parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); + } catch (Throwable t) { + parseMode = DEFAULT_PARSE_MODE; + } finally { + PARSE_MODE = parseMode; + } + } private String domain; // domain name of the context @@ -71,19 +93,58 @@ StringTokenizer st = new StringTokenizer(urlList, " "); while (st.hasMoreTokens()) { - urls[i++] = new DnsUrl(st.nextToken()); + try { + urls[i++] = new DnsUrl(validateURI(st.nextToken())); + } catch (URISyntaxException e) { + MalformedURLException mue = new MalformedURLException(e.getMessage()); + mue.initCause(e); + throw mue; + } } DnsUrl[] trimmed = new DnsUrl[i]; System.arraycopy(urls, 0, trimmed, 0, i); return trimmed; } + @Override + protected ParseMode parseMode() { + return PARSE_MODE; + } + + @Override + protected final boolean isSchemeOnly(String uri) { + return isDnsSchemeOnly(uri); + } + + @Override + protected boolean checkSchemeOnly(String uri, String scheme) { + return uri.equals(scheme + ":") || uri.equals(scheme + "://"); + } + + @Override + protected final MalformedURLException newInvalidURISchemeException(String uri) { + return new MalformedURLException( + uri + " is not a valid DNS pseudo-URL"); + } + + private static boolean isDnsSchemeOnly(String uri) { + return "dns:".equals(uri) || "dns://".equals(uri); + } + + private static String validateURI(String uri) throws URISyntaxException { + // no validation in legacy parsing mode + if (PARSE_MODE == ParseMode.LEGACY) return uri; + // special case of scheme-only URIs + if (isDnsSchemeOnly(uri)) return uri; + // use java.net.URI to validate the uri syntax + return new URI(uri).toString(); + } + public DnsUrl(String url) throws MalformedURLException { super(url); if (!scheme.equals("dns")) { - throw new MalformedURLException( - url + " is not a valid DNS pseudo-URL"); + throw newInvalidURISchemeException(url); } domain = path.startsWith("/") diff -Nru openjdk-17-17.0.2+8/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java openjdk-17-17.0.3+7/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java --- openjdk-17-17.0.2+8/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.naming.rmi/share/classes/com/sun/jndi/url/rmi/rmiURLContext.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2022, 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,12 +25,17 @@ package com.sun.jndi.url.rmi; +import java.net.URI; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Hashtable; +import java.util.Locale; import javax.naming.*; import javax.naming.spi.ResolveResult; import com.sun.jndi.toolkit.url.GenericURLContext; import com.sun.jndi.rmi.registry.RegistryContext; +import com.sun.jndi.toolkit.url.Uri.ParseMode; /** @@ -47,75 +52,263 @@ */ public class rmiURLContext extends GenericURLContext { - public rmiURLContext(Hashtable env) { - super(env); - } + private static final String PARSE_MODE_PROP = "com.sun.jndi.rmiURLParsing"; + private static final ParseMode DEFAULT_PARSE_MODE = ParseMode.COMPAT; - /** - * Resolves the registry portion of "url" to the corresponding - * RMI registry, and returns the atomic object name as the - * remaining name. - */ - protected ResolveResult getRootURLContext(String url, Hashtable env) - throws NamingException - { - if (!url.startsWith("rmi:")) { - throw (new IllegalArgumentException( - "rmiURLContext: name is not an RMI URL: " + url)); + public static final ParseMode PARSE_MODE; + static { + PrivilegedAction action = () -> + System.getProperty(PARSE_MODE_PROP, DEFAULT_PARSE_MODE.toString()); + ParseMode parseMode = DEFAULT_PARSE_MODE; + try { + @SuppressWarnings("removal") + String mode = AccessController.doPrivileged(action); + parseMode = ParseMode.valueOf(mode.toUpperCase(Locale.ROOT)); + } catch (Throwable t) { + parseMode = DEFAULT_PARSE_MODE; + } finally { + PARSE_MODE = parseMode; } + } - // Parse the URL. + public rmiURLContext(Hashtable env) { + super(env); + } + public static class Parser { + final String url; + final ParseMode mode; String host = null; int port = -1; String objName = null; + public Parser(String url) { + this(url, PARSE_MODE); + } + public Parser(String url, ParseMode mode) { + this.url = url; + this.mode = mode; + } + + public String url() {return url;} + public String host() {return host;} + public int port() {return port;} + public String objName() {return objName;} + public ParseMode mode() {return mode;} + + public void parse() throws NamingException { + if (!url.startsWith("rmi:")) { + throw (new IllegalArgumentException( + "rmiURLContext: name is not an RMI URL: " + url)); + } + + switch (mode) { + case STRICT -> parseStrict(); + case COMPAT -> parseCompat(); + case LEGACY -> parseLegacy(); + } + + } + + private void parseStrict() throws NamingException { + assert url.startsWith("rmi:"); + + if (url.equals("rmi:") || url.equals("rmi://")) return; + + // index into url, following the "rmi:" + int i = 4; + + if (url.startsWith("//", i)) { + i += 2; + try { + URI uri = URI.create(url); + host = uri.getHost(); + port = uri.getPort(); + String auth = uri.getRawAuthority(); + String hostport = (host == null ? "" : host) + + (port == -1 ? "" : ":" + port); + if (!hostport.equals(auth)) { + boolean failed = true; + if (hostport.equals("") && auth.startsWith(":")) { + // supports missing host + try { + port = Integer.parseInt(auth.substring(1)); + failed = false; + } catch (NumberFormatException x) { + failed = true; + } + } + if (failed) { + throw newNamingException(new IllegalArgumentException("invalid authority: " + + auth)); + } + } + i += auth.length(); + } catch (IllegalArgumentException iae) { + throw newNamingException(iae); + } + } + int fmark = url.indexOf('#', i); + if (fmark > -1) { + if (!acceptsFragment()) { + throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url)); + } + } - int i = 4; // index into url, following the "rmi:" + if ("".equals(host)) { + host = null; + } + if (url.startsWith("/", i)) { // skip "/" before object name + i++; + } + if (i < url.length()) { + objName = url.substring(i); + } + } - if (url.startsWith("//", i)) { // parse "//host:port" - i += 2; // skip past "//" + private void parseCompat() throws NamingException { + assert url.startsWith("rmi:"); + + int i = 4; // index into url, following the "rmi:" + boolean hasAuthority = url.startsWith("//", i); + if (hasAuthority) i += 2; // skip past "//" int slash = url.indexOf('/', i); - if (slash < 0) { - slash = url.length(); + int qmark = url.indexOf('?', i); + int fmark = url.indexOf('#', i); + if (fmark > -1 && qmark > fmark) qmark = -1; + if (fmark > -1 && slash > fmark) slash = -1; + if (qmark > -1 && slash > qmark) slash = -1; + + // The end of the authority component is either the + // slash (slash will be -1 if it doesn't come before + // query or fragment), or the question mark (qmark will + // be -1 if it doesn't come before the fragment), or + // the fragment separator, or the end of the URI + // string if there is no path, no query, and no fragment. + int enda = slash > -1 ? slash + : (qmark > -1 ? qmark + : (fmark > -1 ? fmark + : url.length())); + if (fmark > -1) { + if (!acceptsFragment()) { + throw newNamingException(new IllegalArgumentException("URI fragments not supported: " + url)); + } } - if (url.startsWith("[", i)) { // at IPv6 literal - int brac = url.indexOf(']', i + 1); - if (brac < 0 || brac > slash) { - throw new IllegalArgumentException( - "rmiURLContext: name is an Invalid URL: " + url); - } - host = url.substring(i, brac + 1); // include brackets - i = brac + 1; // skip past "[...]" - } else { // at host name or IPv4 - int colon = url.indexOf(':', i); - int hostEnd = (colon < 0 || colon > slash) - ? slash - : colon; - if (i < hostEnd) { - host = url.substring(i, hostEnd); - } - i = hostEnd; // skip past host - } - if ((i + 1 < slash)) { - if ( url.startsWith(":", i)) { // parse port - i++; // skip past ":" - port = Integer.parseInt(url.substring(i, slash)); + + if (hasAuthority && enda > i) { // parse "//host:port" + if (url.startsWith(":", i)) { + // LdapURL supports empty host. + i++; + host = ""; + if (enda > i) { + port = Integer.parseInt(url.substring(i, enda)); + } } else { - throw new IllegalArgumentException( - "rmiURLContext: name is an Invalid URL: " + url); + try { + URI uri = URI.create(url.substring(0, enda)); + host = uri.getHost(); + port = uri.getPort(); + String hostport = (host == null ? "" : host) + + (port == -1 ? "" : ":" + port); + if (!hostport.equals(uri.getRawAuthority())) { + throw newNamingException(new IllegalArgumentException("invalid authority: " + + uri.getRawAuthority())); + } + } catch (IllegalArgumentException iae) { + throw newNamingException(iae); + } } + i = enda; + } + if ("".equals(host)) { + host = null; } - i = slash; + if (url.startsWith("/", i)) { // skip "/" before object name + i++; + } + if (i < url.length()) { + objName = url.substring(i); + } + } - if ("".equals(host)) { - host = null; + + // The legacy parsing used to only throw IllegalArgumentException + // and continues to do so + private void parseLegacy() { + assert url.startsWith("rmi:"); + + // Parse the URL. + int i = 4; // index into url, following the "rmi:" + + if (url.startsWith("//", i)) { // parse "//host:port" + i += 2; // skip past "//" + int slash = url.indexOf('/', i); + if (slash < 0) { + slash = url.length(); + } + if (url.startsWith("[", i)) { // at IPv6 literal + int brac = url.indexOf(']', i + 1); + if (brac < 0 || brac > slash) { + throw new IllegalArgumentException( + "rmiURLContext: name is an Invalid URL: " + url); + } + host = url.substring(i, brac + 1); // include brackets + i = brac + 1; // skip past "[...]" + } else { // at host name or IPv4 + int colon = url.indexOf(':', i); + int hostEnd = (colon < 0 || colon > slash) + ? slash + : colon; + if (i < hostEnd) { + host = url.substring(i, hostEnd); + } + i = hostEnd; // skip past host + } + if ((i + 1 < slash)) { + if ( url.startsWith(":", i)) { // parse port + i++; // skip past ":" + port = Integer.parseInt(url.substring(i, slash)); + } else { + throw new IllegalArgumentException( + "rmiURLContext: name is an Invalid URL: " + url); + } + } + i = slash; + } + if ("".equals(host)) { + host = null; + } + if (url.startsWith("/", i)) { // skip "/" before object name + i++; + } + if (i < url.length()) { + objName = url.substring(i); + } } - if (url.startsWith("/", i)) { // skip "/" before object name - i++; + + NamingException newNamingException(Throwable cause) { + NamingException ne = new InvalidNameException(cause.getMessage()); + ne.initCause(cause); + return ne; } - if (i < url.length()) { - objName = url.substring(i); + + protected boolean acceptsFragment() { + return true; } + } + + /** + * Resolves the registry portion of "url" to the corresponding + * RMI registry, and returns the atomic object name as the + * remaining name. + */ + protected ResolveResult getRootURLContext(String url, Hashtable env) + throws NamingException + { + Parser parser = new Parser(url); + parser.parse(); + String host = parser.host; + int port = parser.port; + String objName = parser.objName; // Represent object name as empty or single-component composite name. CompositeName remaining = new CompositeName(); diff -Nru openjdk-17-17.0.2+8/src/jdk.random/share/classes/module-info.java openjdk-17-17.0.3+7/src/jdk.random/share/classes/module-info.java --- openjdk-17-17.0.2+8/src/jdk.random/share/classes/module-info.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/src/jdk.random/share/classes/module-info.java 2022-04-19 19:55:43.000000000 +0000 @@ -40,8 +40,7 @@ * @provides jdk.random.Xoroshiro128PlusPlus * @provides jdk.random.Xoshiro256PlusPlus * - * @use java.util.random.RandomGenerator - * @use jdk.internal.util.random.RandomSupport + * @uses java.util.random.RandomGenerator * * @moduleGraph * @since 16 diff -Nru openjdk-17-17.0.2+8/test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp openjdk-17-17.0.3+7/test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp --- openjdk-17-17.0.2+8/test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/gtest/gc/shared/test_ptrQueueBufferAllocator.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -180,12 +180,18 @@ {} virtual void main_run() { + bool shutdown_requested = false; while (true) { BufferNode* node = _cbl->pop(); if (node != NULL) { _allocator->release(node); - } else if (!Atomic::load_acquire(_continue_running)) { + } else if (shutdown_requested) { return; + } else if (!Atomic::load_acquire(_continue_running)) { + // To avoid a race that could leave buffers in the list after this + // thread has shut down, continue processing until the list is empty + // *after* the shut down request has been received. + shutdown_requested = true; } ThreadBlockInVM tbiv(this); // Safepoint check. } diff -Nru openjdk-17-17.0.2+8/test/hotspot/gtest/gtestMain.cpp openjdk-17-17.0.3+7/test/hotspot/gtest/gtestMain.cpp --- openjdk-17-17.0.2+8/test/hotspot/gtest/gtestMain.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/gtest/gtestMain.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -61,8 +61,7 @@ return strncmp(str + (str_len - suffix_len), suffix, suffix_len) == 0; } - -static int init_jvm(int argc, char **argv, bool disable_error_handling) { +static int init_jvm(int argc, char **argv, bool disable_error_handling, JavaVM** jvm_ptr) { // don't care about the program name argc--; argv++; @@ -90,10 +89,9 @@ args.options = options; args.ignoreUnrecognized = JNI_FALSE; - JavaVM* jvm; JNIEnv* env; - int ret = JNI_CreateJavaVM(&jvm, (void**)&env, &args); + int ret = JNI_CreateJavaVM(jvm_ptr, (void**)&env, &args); if (ret == JNI_OK) { // CreateJavaVM leaves WXExec context, while gtests // calls internal functions assuming running in WXWwrite. @@ -111,26 +109,31 @@ private: int _argc; char** _argv; - bool _is_initialized; - - void initialize_jvm() { - } + JavaVM* _jvm; public: JVMInitializerListener(int argc, char** argv) : - _argc(argc), _argv(argv), _is_initialized(false) { + _argc(argc), _argv(argv), _jvm(nullptr) { } virtual void OnTestStart(const ::testing::TestInfo& test_info) { const char* name = test_info.name(); - if (!_is_initialized && is_same_vm_test(name)) { + if (_jvm == nullptr && is_same_vm_test(name)) { // we want to have hs_err and core files when we execute regular tests - int ret_val = init_jvm(_argc, _argv, false); + int ret_val = init_jvm(_argc, _argv, false, &_jvm); if (ret_val != 0) { - ADD_FAILURE() << "Could not initialize the JVM"; + ADD_FAILURE() << "Could not initialize the JVM: " << ret_val; exit(1); } - _is_initialized = true; + } + } + + void destroy_jvm() { + if (_jvm != NULL) { + int ret = _jvm->DestroyJavaVM(); + if (ret != 0) { + fprintf(stderr, "Warning: DestroyJavaVM error %d\n", ret); + } } } }; @@ -208,6 +211,18 @@ return new_argv; } +// This is generally run once for a set of tests. But if that set includes a vm_assert or +// other_vm test, then a new process is forked, and runUnitTestsInner is called, passing +// just that test as the one to be executed. +// +// When we execute a vm_assert or other_vm test we create and initialize the JVM below. +// +// A vm_assert test crashes the VM so no cleanup is needed, but for other_vm we call +// DestroyJavaVM via the TEST_OTHER_VM macro prior to the call to exit(). +// +// For same_vm tests we use an event listener to create the JVM when the first same_vm +// test is executed. Once all tests are completed we can then call DestroyJavaVM on that +// JVM directly. static void runUnitTestsInner(int argc, char** argv) { ::testing::InitGoogleMock(&argc, argv); ::testing::GTEST_FLAG(death_test_style) = "threadsafe"; @@ -253,22 +268,38 @@ #endif // _WIN32 argv = remove_test_runner_arguments(&argc, argv); + + JVMInitializerListener* jvm_listener = NULL; + if (is_vmassert_test || is_othervm_test) { + JavaVM* jvm = NULL; // both vmassert and other vm tests require inited jvm // but only vmassert tests disable hs_err and core file generation - if (init_jvm(argc, argv, is_vmassert_test) != 0) { + int ret; + if ((ret = init_jvm(argc, argv, is_vmassert_test, &jvm)) != 0) { + fprintf(stderr, "ERROR: JNI_CreateJavaVM failed: %d\n", ret); abort(); } } else { ::testing::TestEventListeners& listeners = ::testing::UnitTest::GetInstance()->listeners(); - listeners.Append(new JVMInitializerListener(argc, argv)); + jvm_listener = new JVMInitializerListener(argc, argv); + listeners.Append(jvm_listener); } int result = RUN_ALL_TESTS(); + + // vm_assert and other_vm tests never reach this point as they either abort, or call + // exit() - see TEST_OTHER_VM macro. We will reach here when all same_vm tests have + // completed for this run, so we can terminate the VM used for that case. + if (result != 0) { fprintf(stderr, "ERROR: RUN_ALL_TESTS() failed. Error %d\n", result); exit(2); } + + if (jvm_listener != NULL) { + jvm_listener->destroy_jvm(); + } } // Thread support for -new-thread option diff -Nru openjdk-17-17.0.2+8/test/hotspot/gtest/runtime/test_os.cpp openjdk-17-17.0.3+7/test/hotspot/gtest/runtime/test_os.cpp --- openjdk-17-17.0.2+8/test/hotspot/gtest/runtime/test_os.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/gtest/runtime/test_os.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -715,8 +715,10 @@ #define LOG(...) // Invalid addresses + LOG("os::print_function_and_library_name(st, -1) expects FALSE."); address addr = (address)(intptr_t)-1; EXPECT_FALSE(os::print_function_and_library_name(&st, addr)); + LOG("os::print_function_and_library_name(st, NULL) expects FALSE."); addr = NULL; EXPECT_FALSE(os::print_function_and_library_name(&st, addr)); diff -Nru openjdk-17-17.0.2+8/test/hotspot/gtest/unittest.hpp openjdk-17-17.0.3+7/test/hotspot/gtest/unittest.hpp --- openjdk-17-17.0.2+8/test/hotspot/gtest/unittest.hpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/gtest/unittest.hpp 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2021, 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 @@ -88,6 +88,15 @@ static void child_ ## category ## _ ## name ## _() { \ ::testing::GTEST_FLAG(throw_on_failure) = true; \ test_ ## category ## _ ## name ## _(); \ + JavaVM* jvm[1]; \ + jsize nVMs = 0; \ + JNI_GetCreatedJavaVMs(&jvm[0], 1, &nVMs); \ + if (nVMs == 1) { \ + int ret = jvm[0]->DestroyJavaVM(); \ + if (ret != 0) { \ + fprintf(stderr, "Warning: DestroyJavaVM error %d\n", ret); \ + } \ + } \ fprintf(stderr, "OKIDOKI"); \ exit(0); \ } \ diff -Nru openjdk-17-17.0.2+8/test/hotspot/gtest/utilities/test_resourceHash.cpp openjdk-17-17.0.3+7/test/hotspot/gtest/utilities/test_resourceHash.cpp --- openjdk-17-17.0.2+8/test/hotspot/gtest/utilities/test_resourceHash.cpp 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/gtest/utilities/test_resourceHash.cpp 2022-04-19 19:55:43.000000000 +0000 @@ -60,6 +60,21 @@ } }; + class DeleterTestIter { + int _val; + public: + DeleterTestIter(int i) : _val(i) {} + + bool do_entry(K const& k, V const& v) { + if ((uintptr_t) k == (uintptr_t) _val) { + // Delete me! + return true; + } else { + return false; // continue iteration + } + } + }; + }; class SmallResourceHashtableTest : public CommonResourceHashtableTest { @@ -233,6 +248,15 @@ ASSERT_FALSE(rh.remove(as_K(index))); } rh.iterate(&et); + + // Add more entries in and then delete one. + for (uintptr_t i = 10; i > 0; --i) { + uintptr_t index = i - 1; + ASSERT_TRUE(rh.put(as_K(index), index)); + } + DeleterTestIter dt(5); + rh.unlink(&dt); + ASSERT_FALSE(rh.get(as_K(5))); } }; }; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/TEST.groups openjdk-17-17.0.3+7/test/hotspot/jtreg/TEST.groups --- openjdk-17-17.0.2+8/test/hotspot/jtreg/TEST.groups 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/TEST.groups 2022-04-19 19:55:43.000000000 +0000 @@ -172,6 +172,50 @@ -compiler/loopopts/Test7052494.java \ -compiler/runtime/Test6826736.java +tier2_compiler = \ + compiler/allocation/ \ + compiler/arguments/ \ + compiler/calls/ \ + compiler/cha/ \ + compiler/controldependency/ \ + compiler/conversions/ \ + compiler/codegen/ \ + compiler/linkage/ \ + compiler/loopstripmining/ \ + compiler/loopopts/Test7052494.java \ + compiler/longcountedloops/ \ + compiler/intrinsics/bmi \ + compiler/intrinsics/mathexact \ + compiler/intrinsics/sha \ + compiler/intrinsics/bigInteger/TestMultiplyToLen.java \ + compiler/intrinsics/zip/TestAdler32.java \ + compiler/membars/ \ + compiler/onSpinWait/ \ + compiler/parsing/ \ + compiler/rangechecks/ \ + compiler/reflection/ \ + compiler/rtm/ \ + compiler/runtime/Test6826736.java \ + compiler/stable/ \ + compiler/stringopts/ \ + -:tier1_compiler \ + -:hotspot_slow_compiler + +tier3_compiler = \ + compiler/c2/ \ + compiler/ciReplay/ \ + compiler/compilercontrol/ \ + compiler/debug/ \ + compiler/oracle/ \ + compiler/print/ \ + compiler/relocations/ \ + compiler/tiered/ \ + compiler/vectorapi/ \ + compiler/whitebox/ \ + :hotspot_slow_compiler \ + -:tier1_compiler \ + -:tier2_compiler + tier1_compiler_not_xcomp = \ compiler/profiling @@ -355,6 +399,7 @@ -runtime/cds/appcds/DumpClassList.java \ -runtime/cds/appcds/DumpClassListWithLF.java \ -runtime/cds/appcds/ExtraSymbols.java \ + -runtime/cds/appcds/LambdaContainsOldInf.java \ -runtime/cds/appcds/LambdaEagerInit.java \ -runtime/cds/appcds/LambdaProxyClasslist.java \ -runtime/cds/appcds/LambdaVerificationFailedDuringDump.java \ @@ -456,11 +501,13 @@ :hotspot_tier2_runtime \ :hotspot_tier2_runtime_platform_agnostic \ :hotspot_tier2_serviceability \ + :tier2_compiler \ :tier2_gc_epsilon \ :tier2_gc_shenandoah tier3 = \ :hotspot_tier3_runtime \ + :tier3_compiler \ :tier3_gc_shenandoah # Everything that is not in other tiers, but not apps diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/Test8271202.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/Test8271202.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/Test8271202.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/Test8271202.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021, 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 8271202 + * @requires vm.debug == true & vm.compiler1.enabled + * @run main/othervm -Xbatch -XX:TieredStopAtLevel=1 -XX:+DeoptimizeALot + * Test8271202 + */ + +public class Test8271202 { + public static void main(String[] strArr) { + try { + test(); + } catch (Exception e) { + // Expected + } + } + + static void test() { + long l6 = 10L; + int counter = 0; + int i2, i26, i29, iArr[] = new int[400]; + boolean b3 = true; + for (int smallinvoc = 0; smallinvoc < 139; smallinvoc++) { + } + for (i2 = 13; i2 < 1000; i2++) { + for (i26 = 2; i26 < 114; l6 += 2) { + // Infinite loop + if (b3) { + for (i29 = 1; i29 < 2; i29++) { + try { + iArr[i26] = 0; + } catch (ArithmeticException a_e) { + } + } + } + counter++; + if (counter == 100000) { + throw new RuntimeException("expected"); + } + } + } + } +} + diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathology.jasm 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021, 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. + * + */ +super public class TestC1PhiPlacementPathology + version 61:0 +{ + public static volatile Field sideEffect:I; + + public Method "":"()V" + stack 1 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + private static Method effect:"(I)V" + stack 2 locals 1 + { + getstatic Field sideEffect:"I"; + iload_0; + iadd; + putstatic Field sideEffect:"I"; + return; + } + public static Method test:"(I)V" + stack 2 locals 2 + { + iconst_0; + istore_1; + iload_0; + iconst_2; + irem; + ifne MODIFY_LOCAL; + iinc 1, 1; + goto LH2; + MODIFY_LOCAL: stack_frame_type append; + locals_map int; + iinc 1, 2; + iinc 0, 1; + goto LH1; + LH1: stack_frame_type same; + iinc 1, 1; + iload_1; + sipush 10000; + if_icmpge EXIT; + LH2: stack_frame_type same; + iinc 1, 1; + goto LH1; + EXIT: stack_frame_type same; + iload_1; + iload_0; + iadd; + invokestatic Method effect:"(I)V"; + return; + } +} // end Class TestC1PhiPlacementPathology diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c1/TestC1PhiPlacementPathologyMain.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, 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 8277447 + * @summary Test a pathological case for phi placement with an irreducible loop of a particular shape. + * + * @compile TestC1PhiPlacementPathology.jasm + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,TestC1PhiPlacementPathology::test + * -XX:CompilationMode=quick-only -XX:-UseOnStackReplacement TestC1PhiPlacementPathologyMain + */ + +public class TestC1PhiPlacementPathologyMain { + public static void main(String[] args) { + for (int i = 0; i < 11000; i++) { + TestC1PhiPlacementPathology.test(0); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/TestDeadDataLoopCmoveIdentity.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, 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 + * @key stress randomness + * @bug 8271056 + * @summary A dead data loop is created when applying an unsafe case of Cmov'ing identity. + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,compiler.c2.TestDeadDataLoopCmoveIdentity::* + * -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN -XX:StressSeed=359948366 compiler.c2.TestDeadDataLoopCmoveIdentity + * @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileCommand=compileonly,compiler.c2.TestDeadDataLoopCmoveIdentity::* + * -XX:+UnlockDiagnosticVMOptions -XX:+StressIGVN compiler.c2.TestDeadDataLoopCmoveIdentity + */ + +package compiler.c2; + +public class TestDeadDataLoopCmoveIdentity { + static boolean bFld; + + public static void main(String[] strArr) { + test(); + test2(); + } + + static void test() { + int i33 = 51925, iArr3[] = new int[10]; + if (bFld) { + ; + } else if (bFld) { + for (int i = 0; i < 100; i++) { } + do { + if (i33 != 0) { + } + int i34 = 1; + do { + switch (0) { + case 122: { } + } + } while (i34 < 1); + i33 += i33 + 3; + } while (i33 < 5); + } + } + + static void test2() { + int i33 = 51925, iArr3[] = new int[10]; + if (bFld) { + ; + } else if (bFld) { + do { + if (i33 != 0) { + } + int i34 = 1; + do { + switch (0) { + case 122: {} + } + } while (i34 < 1); + } while (++i33 < 5); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/TestSqrt.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/TestSqrt.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/TestSqrt.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/TestSqrt.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, 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 compiler.c2; + +/* + * @test + * @bug 8279076 + * @summary SqrtD/SqrtF should be matched only on supported platforms + * @requires vm.debug + * + * @run main/othervm -XX:-TieredCompilation -Xcomp + * -XX:CompileOnly=compiler/c2/TestSqrt + * -XX:CompileOnly=java/lang/Math + * compiler.c2.TestSqrt + */ +public class TestSqrt { + static float srcF = 42.0f; + static double srcD = 42.0d; + static float dstF; + static double dstD; + + public static void test() { + dstF = (float)Math.sqrt((double)srcF); + dstD = Math.sqrt(srcD); + } + + public static void main(String args[]) { + for (int i = 0; i < 20_000; i++) { + test(); + } + } +} + diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/c2/irTests/TestScheduleSmallMethod.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, Arm Limited. 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.c2.irTests; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; + +/* + * @test + * @bug 8275847 + * @requires vm.compiler2.enabled + * @summary Test that small method with runtime calls can be scheduled. + * @library /test/lib / + * @run driver compiler.c2.irTests.TestScheduleSmallMethod + */ +public class TestScheduleSmallMethod { + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + Scenario schedulerOn = new Scenario(0, "-XX:+OptoScheduling"); + Scenario schedulerOff = new Scenario(1, "-XX:-OptoScheduling"); + framework.addScenarios(schedulerOn, schedulerOff).start(); + } + + @Test + public double testSmallMethodTwoRuntimeCalls(double value) { + // The two intrinsified Math calls below caused the scheduler to + // bail out with "too many D-U pinch points". See bug 8275847. + return Math.log(Math.sin(value)); + } + + @Run(test = "testSmallMethodTwoRuntimeCalls") + public void checkTestSmallMethodTwoRuntimeCalls() throws Throwable { + Asserts.assertLessThan(testSmallMethodTwoRuntimeCalls(Math.PI/2), 0.00001); + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/ciReplay/TestVMNoCompLevel.java 2022-04-19 19:55:43.000000000 +0000 @@ -26,7 +26,8 @@ * @bug 8011675 * @library / /test/lib * @summary testing of ciReplay with using generated by VM replay.txt w/o comp_level - * @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.debug == true + * @requires vm.flightRecorder != true & vm.compMode != "Xint" & vm.debug == true & + * (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 1 | vm.opt.TieredStopAtLevel == 4) * @modules java.base/jdk.internal.misc * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/codecache/CheckSegmentedCodeCache.java 2022-04-19 19:55:43.000000000 +0000 @@ -58,7 +58,7 @@ out.shouldContain(NON_METHOD); } catch (RuntimeException e) { // Check if TieredCompilation is disabled (in a client VM) - if (!out.getOutput().contains("-XX:+TieredCompilation not supported in this VM")) { + if (Platform.isTieredSupported()) { // Code cache is not segmented throw new RuntimeException("No code cache segmentation."); } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderData.java 2022-04-19 19:55:43.000000000 +0000 @@ -80,6 +80,16 @@ } return result.toArray(new Object[result.size()][]); } + @DataProvider(name = "unalignedPrimitive") + public static Object[][] getUnalignedPrimitiveJavaKinds() { + List result = new ArrayList<>(); + for (KindData k : PRIMITIVE_KIND_DATA) { + if (k.unalignedInstanceFieldValue != null) { + result.add(new Object[] {k.kind, TEST_CONSTANT, k.instanceFieldOffset - 1, k.unalignedInstanceFieldValue, Math.max(8, k.kind.getBitCount())}); + } + } + return result.toArray(new Object[result.size()][]); + } @DataProvider(name = "outOfBoundsInstanceFields") public static Object[][] getOutOfBoundsStaticFieldReads() { @@ -170,6 +180,7 @@ final long staticFieldOffset; final JavaConstant instanceFieldValue; final JavaConstant staticFieldValue; + final JavaConstant unalignedInstanceFieldValue; KindData(JavaKind kind, Object testObject) { this.kind = kind; try { @@ -182,6 +193,17 @@ staticFieldOffset = UNSAFE.staticFieldOffset(staticField); instanceFieldValue = JavaConstant.forBoxedPrimitive(instanceField.get(testObject)); staticFieldValue = JavaConstant.forBoxedPrimitive(staticField.get(null)); + if (kind == JavaKind.Long) { + unalignedInstanceFieldValue = JavaConstant.forLong(UNSAFE.getLongUnaligned(testObject, instanceFieldOffset - 1)); + } else if (kind == JavaKind.Int) { + unalignedInstanceFieldValue = JavaConstant.forInt(UNSAFE.getIntUnaligned(testObject, instanceFieldOffset - 1)); + } else if (kind == JavaKind.Char) { + unalignedInstanceFieldValue = JavaConstant.forChar(UNSAFE.getCharUnaligned(testObject, instanceFieldOffset - 1)); + } else if (kind == JavaKind.Short) { + unalignedInstanceFieldValue = JavaConstant.forShort(UNSAFE.getShortUnaligned(testObject, instanceFieldOffset - 1)); + } else { + unalignedInstanceFieldValue = null; + } } catch (Exception e) { throw new Error("TESTBUG for kind " + kind, e); } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.hotspot.test/src/jdk/vm/ci/hotspot/test/MemoryAccessProviderTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -64,6 +64,11 @@ Assert.assertNull(PROVIDER.readPrimitiveConstant(kind, null, offset, bitsCount), "Unexpected value for null base"); } + @Test(dataProvider = "unalignedPrimitive", dataProviderClass = MemoryAccessProviderData.class, expectedExceptions = {IllegalArgumentException.class}) + public void testReadUnalignedConstantConstant(JavaKind kind, Constant base, Long offset, Object expected, int bitsCount) { + PROVIDER.readPrimitiveConstant(kind, null, offset, bitsCount); + } + @Test(dataProvider = "negative", dataProviderClass = MemoryAccessProviderData.class, expectedExceptions = {IllegalArgumentException.class}) public void testNegativeReadPrimitiveConstant(JavaKind kind, Constant base) { PROVIDER.readPrimitiveConstant(kind, base, 0L, kind == null ? 0 : kind.getByteCount() / 8); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestDeadPostLoopBecausePredicate.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, 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 8275330 + * @summary C2: assert(n->is_Root() || n->is_Region() || n->is_Phi() || n->is_MachMerge() || def_block->dominates(block)) failed: uses must be dominated by definitions + * + * @run main/othervm -Xmx512m -XX:+UnlockDiagnosticVMOptions -Xcomp -XX:CompileOnly=TestDeadPostLoopBecausePredicate TestDeadPostLoopBecausePredicate + * + */ + + +public class TestDeadPostLoopBecausePredicate { + + public static final int N = 400; + + public static int iFld=54270; + public static int iFld1=-4; + public int iFld2=201; + + public int mainTest(String[] strArr1) { + + int i=0, i17=8052, i19=22380, i20=60894, iArr[]=new int[N]; + init(iArr, 4); + + i = 1; + do { + for (i17 = 5; i17 < 114; i17++) { + switch ((i17 % 7) + 126) { + case 126: + for (i19 = 2; i19 > i; i19 -= 3) { + try { + i20 = (iFld2 % TestDeadPostLoopBecausePredicate.iFld1); + i20 = (iArr[i19 - 1] % TestDeadPostLoopBecausePredicate.iFld); + TestDeadPostLoopBecausePredicate.iFld = (TestDeadPostLoopBecausePredicate.iFld1 % iArr[i19]); + } catch (ArithmeticException a_e) {} + } + break; + } + } + } while (++i < 220); + + return i20; + } + + public static void init(int[] a, int seed) { + for (int j = 0; j < a.length; j++) { + a[j] = (j % 2 == 0) ? seed + j : seed - j; + } + } + + public static void main(String[] strArr) { + TestDeadPostLoopBecausePredicate _instance = new TestDeadPostLoopBecausePredicate(); + for (int i = 0; i < 10; i++ ) { + _instance.mainTest(strArr); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestEliminateNullCheckWithSplitIf.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2021, 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 + * @key stress randomness + * @bug 8275610 + * @summary Null check for field access of object floats above null check resulting in a segfault. + * @requires vm.compiler2.enabled + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.loopopts.TestEliminateNullCheckWithSplitIf::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:StressSeed=42 compiler.loopopts.TestEliminateNullCheckWithSplitIf + * @run main/othervm -Xbatch -XX:CompileCommand=compileonly,compiler.loopopts.TestEliminateNullCheckWithSplitIf::test + * -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+StressIGVN compiler.loopopts.TestEliminateNullCheckWithSplitIf + */ + +package compiler.loopopts; + +public class TestEliminateNullCheckWithSplitIf { + public static int[] iArrFld = new int[20]; + public static int[] iArrFld2 = new int[20]; + public static int iFld = 10; + public static MyClass obj; + + public static void main(String[] strArr) { + for (int i = 0; i < 10000; i++) { + obj = (i % 100 == 0 ? null : new MyClass()); + test(); + } + } + + // The field access obj.iFld requires a null check NC3 and adds a not-null CastPP node on the succeeded projection. + // In the first IGVN after parsing, the null check NC3 can be subsumed by the explicit null check NC2. + // (done in IfNode::simple_subsuming()). The Bool node of NC2 is also shared with the same null check NC1 earlier. + // However, C2 cannot remove the null check NC2, yet, because the IR in between the two checks are too complex + // (IfNode::search_identical() fails). + // Now, loopopts are applied: + // (1) First, the split if optimization is done. It recognizes that NC1 and NC2 are back to back null checks and removes + // the null check NC2 by splitting it through the region R which is removed afterwards. In this process, control dependent + // data nodes on the out projections of NC2 end up at the new regions R1/R2 created for each projection for R. They get + // the last nodes of the if and else block as input. For this example, R1 is a control input to the CastPP node which + // will merge both true projections. + // (2) Later in loop opts, the loop L is transformed into normal code and y will become a constant 1. + // After loopopts, another round of IGVN is done: + // (These steps also depend on the order in which they are applied in order to trigger the bug) + // (1) The region R is removed because one path is dead (a result of the split if optimization). + // (2) The new If node added by the above split if optimization is also folded. This rewires the CastPP node to + // the last control node in the If block which is the true projection of range check RC2. Up until now, the CastPP + // is still after the null check NC1. + // (3) The range check RC2 is removed because the range check RC1 already covers this range (see RangeCheck::Ideal()). + // All data nodes which are control dependent on RC2 will be rewired to the dominating range check RC1, including + // the non-null CastPP node - which now has a control input above the null check NC1. This also means that the field + // load obj.iFld now has the same early control as the CastPP (CastPP -> AddP -> LoadI). Using StressGCM can + // now schedule the obj.iFld load before the null check NC1 because the early control allows it which leads to a + // segmentation fault if obj is null. + public static void test() { + int x = iArrFld[17]; // Emits range check RC1 + if (obj != null) { // Null check NC1 + int y = 0; + for (int i = 0; i < 1; i++) { // Loop L + y++; + } + // Use additional loop to keep the rangecheck for iArrFld[y] in before loopopts. + // y will become constant 1 but only once the loop above is removed in loopopts. + x = iArrFld[y]; // Emits range check RC2 + } else { + x = iArrFld2[18]; + } + // Region R merging the if and else paths above. + if (obj != null) { // Null check NC2 + x = iArrFld2[obj.iFld]; // Emits Null check NC3 for obj.iFld + } + } +} + +class MyClass { + int iFld; +} + + + + diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/loopopts/TestSkeletonPredicateNegation.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (c) 2021, 2022, 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 8273277 + * @summary Skeleton predicates sometimes need to be negated + * @run main compiler.loopopts.TestSkeletonPredicateNegation + * + */ + +package compiler.loopopts; + +public class TestSkeletonPredicateNegation { + public static int in0 = 2; + + public static void main(String[] args) { + try { + TestSkeletonPredicateNegation instance = new TestSkeletonPredicateNegation(); + for (int i = 0; i < 10000; ++i) { + instance.mainTest(args); + } + } catch (Exception ex) { + System.out.println(ex.getClass().getCanonicalName()); + } catch (OutOfMemoryError e) { + System.out.println("OOM Error"); + } + } + + public void mainTest (String[] args){ + long loa11[] = new long[19]; + + for (long lo14 : loa11) { + TestSkeletonPredicateNegation.in0 = -128; + for (int i18 = 0; i18 < 13; i18++) { + try { + loa11[TestSkeletonPredicateNegation.in0] %= 2275269548L; + Math.ceil(1374905370.2785515599); + } catch (Exception a_e) { + TestSkeletonPredicateNegation.in0--; + } + } + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. 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 TestOnSpinWaitAArch64 + * @summary Checks that java.lang.Thread.onSpinWait is intrinsified with instructions specified with '-XX:OnSpinWaitInst' and '-XX:OnSpinWaitInstCount' + * @bug 8186670 + * @library /test/lib + * + * @requires vm.flagless + * @requires os.arch=="aarch64" + * + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 nop 7 + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 isb 3 + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c2 yield 1 + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 nop 7 + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 isb 3 + * @run driver compiler.onSpinWait.TestOnSpinWaitAArch64 c1 yield + */ + +package compiler.onSpinWait; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.ListIterator; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestOnSpinWaitAArch64 { + public static void main(String[] args) throws Exception { + String compiler = args[0]; + String spinWaitInst = args[1]; + String spinWaitInstCount = (args.length == 3) ? args[2] : "1"; + ArrayList command = new ArrayList(); + command.add("-XX:+IgnoreUnrecognizedVMOptions"); + command.add("-showversion"); + command.add("-XX:-BackgroundCompilation"); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+PrintAssembly"); + if (compiler.equals("c2")) { + command.add("-XX:-TieredCompilation"); + } else if (compiler.equals("c1")) { + command.add("-XX:+TieredCompilation"); + command.add("-XX:TieredStopAtLevel=1"); + } else { + throw new RuntimeException("Unknown compiler: " + compiler); + } + command.add("-Xbatch"); + command.add("-XX:OnSpinWaitInst=" + spinWaitInst); + command.add("-XX:OnSpinWaitInstCount=" + spinWaitInstCount); + command.add("-XX:CompileCommand=compileonly," + Launcher.class.getName() + "::" + "test"); + command.add(Launcher.class.getName()); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(command); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + System.out.println(analyzer.getOutput()); + + checkOutput(analyzer, spinWaitInst, Integer.parseInt(spinWaitInstCount)); + } + + private static String getSpinWaitInstHex(String spinWaitInst) { + if ("nop".equals(spinWaitInst)) { + return "1f20 03d5"; + } else if ("isb".equals(spinWaitInst)) { + return "df3f 03d5"; + } else if ("yield".equals(spinWaitInst)) { + return "3f20 03d5"; + } else { + throw new RuntimeException("Unknown spin wait instruction: " + spinWaitInst); + } + } + + private static void addInstrs(String line, ArrayList instrs) { + for (String instr : line.split("\\|")) { + instrs.add(instr.trim()); + } + } + + // The expected output of PrintAssembly for example for a spin wait with three NOPs: + // + // # {method} {0x0000ffff6ac00370} 'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher' + // # [sp+0x40] (sp of caller) + // 0x0000ffff9d557680: 1f20 03d5 | e953 40d1 | 3f01 00f9 | ff03 01d1 | fd7b 03a9 | 1f20 03d5 | 1f20 03d5 + // + // 0x0000ffff9d5576ac: ;*invokestatic onSpinWait {reexecute=0 rethrow=0 return_oop=0} + // ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0 (line 161) + // 0x0000ffff9d5576ac: 1f20 03d5 | fd7b 43a9 | ff03 0191 + // + // The checkOutput method adds hex instructions before 'invokestatic onSpinWait' and from the line after + // it to a list. The list is traversed from the end to count spin wait instructions. + // + // If JVM finds the hsdis library the output is like: + // + // # {method} {0x0000ffff63000370} 'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher' + // # [sp+0x20] (sp of caller) + // 0x0000ffffa409da80: nop + // 0x0000ffffa409da84: sub sp, sp, #0x20 + // 0x0000ffffa409da88: stp x29, x30, [sp, #16] ;*synchronization entry + // ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@-1 (line 187) + // 0x0000ffffa409da8c: nop + // 0x0000ffffa409da90: nop + // 0x0000ffffa409da94: nop + // 0x0000ffffa409da98: nop + // 0x0000ffffa409da9c: nop + // 0x0000ffffa409daa0: nop + // 0x0000ffffa409daa4: nop ;*invokestatic onSpinWait {reexecute=0 rethrow=0 return_oop=0} + // ; - compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0 (line 187) + private static void checkOutput(OutputAnalyzer output, String spinWaitInst, int spinWaitInstCount) { + Iterator iter = output.asLines().listIterator(); + + String match = skipTo(iter, "'test' '()V' in 'compiler/onSpinWait/TestOnSpinWaitAArch64$Launcher'"); + if (match == null) { + throw new RuntimeException("Missing compiler output for the method compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test"); + } + + ArrayList instrs = new ArrayList(); + String line = null; + boolean hasHexInstInOutput = false; + while (iter.hasNext()) { + line = iter.next(); + if (line.contains("*invokestatic onSpinWait")) { + break; + } + if (!hasHexInstInOutput) { + hasHexInstInOutput = line.contains("|"); + } + if (line.contains("0x") && !line.contains(";")) { + addInstrs(line, instrs); + } + } + + if (!iter.hasNext() || !iter.next().contains("- compiler.onSpinWait.TestOnSpinWaitAArch64$Launcher::test@0") || !iter.hasNext()) { + throw new RuntimeException("Missing compiler output for Thread.onSpinWait intrinsic"); + } + + String strToSearch = null; + if (!hasHexInstInOutput) { + instrs.add(line.split(";")[0].trim()); + strToSearch = spinWaitInst; + } else { + line = iter.next(); + if (!line.contains("0x") || line.contains(";")) { + throw new RuntimeException("Expected hex instructions"); + } + + addInstrs(line, instrs); + strToSearch = getSpinWaitInstHex(spinWaitInst); + } + + int foundInstCount = 0; + + ListIterator instrReverseIter = instrs.listIterator(instrs.size()); + while (instrReverseIter.hasPrevious()) { + if (instrReverseIter.previous().endsWith(strToSearch)) { + foundInstCount = 1; + break; + } + } + + while (instrReverseIter.hasPrevious()) { + if (!instrReverseIter.previous().endsWith(strToSearch)) { + break; + } + ++foundInstCount; + } + + if (foundInstCount != spinWaitInstCount) { + throw new RuntimeException("Wrong instruction " + strToSearch + " count " + foundInstCount + "!\n -- expecting " + spinWaitInstCount); + } + } + + private static String skipTo(Iterator iter, String substring) { + while (iter.hasNext()) { + String nextLine = iter.next(); + if (nextLine.contains(substring)) { + return nextLine; + } + } + return null; + } + + static class Launcher { + public static void main(final String[] args) throws Exception { + int end = 20_000; + + for (int i=0; i < end; i++) { + test(); + } + } + static void test() { + java.lang.Thread.onSpinWait(); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitAArch64DefaultFlags.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright Amazon.com Inc. 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 TestOnSpinWaitAArch64DefaultFlags + * @summary Check default values of '-XX:OnSpinWaitInst' and '-XX:OnSpinWaitInstCount' for AArch64 implementations. + * @bug 8277137 + * @library /test/lib / + * + * @requires os.arch=="aarch64" + * + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI + * compiler.onSpinWait.TestOnSpinWaitAArch64DefaultFlags + */ + +package compiler.onSpinWait; + +import java.util.Iterator; +import java.util.List; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import sun.hotspot.cpuinfo.CPUInfo; + +public class TestOnSpinWaitAArch64DefaultFlags { + private static boolean isCPUModelNeoverseN1(String cpuModel) { + return cpuModel.contains("0xd0c"); + } + + private static void checkFinalFlagsEqualTo(ProcessBuilder pb, String expectedOnSpinWaitInstValue, String expectedOnSpinWaitInstCountValue) throws Exception { + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + analyzer.shouldHaveExitValue(0); + + Iterator iter = analyzer.asLines().listIterator(); + String line = null; + boolean hasExpectedOnSpinWaitInstValue = false; + boolean hasExpectedOnSpinWaitInstCountValue = false; + while (iter.hasNext()) { + line = iter.next(); + if (!hasExpectedOnSpinWaitInstValue && line.contains("ccstr OnSpinWaitInst")) { + hasExpectedOnSpinWaitInstValue = line.contains("= " + expectedOnSpinWaitInstValue); + } + + if (!hasExpectedOnSpinWaitInstCountValue && line.contains("uint OnSpinWaitInstCount")) { + hasExpectedOnSpinWaitInstCountValue = line.contains("= " + expectedOnSpinWaitInstCountValue); + } + } + if (!hasExpectedOnSpinWaitInstValue) { + System.out.println(analyzer.getOutput()); + throw new RuntimeException("OnSpinWaitInst with the expected value '" + expectedOnSpinWaitInstValue + "' not found."); + } + if (!hasExpectedOnSpinWaitInstCountValue) { + System.out.println(analyzer.getOutput()); + throw new RuntimeException("OnSpinWaitInstCount with the expected value '" + expectedOnSpinWaitInstCountValue + "' not found."); + } + } + + public static void main(String[] args) throws Exception { + List cpuFeatures = CPUInfo.getFeatures(); + if (cpuFeatures.isEmpty()) { + System.out.println("Skip because no CPU features are available."); + return; + } + + final String cpuModel = cpuFeatures.get(0); + + if (isCPUModelNeoverseN1(cpuModel)) { + checkFinalFlagsEqualTo(ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintFlagsFinal", "-version"), + "isb", "1"); + checkFinalFlagsEqualTo(ProcessTools.createJavaProcessBuilder("-XX:+UnlockDiagnosticVMOptions", "-XX:OnSpinWaitInstCount=2", "-XX:+PrintFlagsFinal", "-version"), + "isb", "2"); + } else { + System.out.println("Skip because no defaults for CPU model: " + cpuModel); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/onSpinWait/TestOnSpinWaitNoneAArch64.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. 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 TestOnSpinWaitNoneAArch64 + * @summary Checks that java.lang.Thread.onSpinWait is not intrinsified when '-XX:OnSpinWaitInst=none' is used + * @bug 8186670 + * @library /test/lib + * + * @requires vm.flagless + * @requires os.arch=="aarch64" + * + * @run driver compiler.onSpinWait.TestOnSpinWaitNoneAArch64 + */ + +package compiler.onSpinWait; + +import java.util.ArrayList; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestOnSpinWaitNoneAArch64 { + + public static void main(String[] args) throws Exception { + ArrayList command = new ArrayList(); + command.add("-XX:+IgnoreUnrecognizedVMOptions"); + command.add("-showversion"); + command.add("-XX:-TieredCompilation"); + command.add("-Xbatch"); + command.add("-XX:+PrintCompilation"); + command.add("-XX:+UnlockDiagnosticVMOptions"); + command.add("-XX:+PrintInlining"); + command.add("-XX:OnSpinWaitInst=none"); + command.add(Launcher.class.getName()); + + // Test C2 compiler + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(command); + + OutputAnalyzer analyzer = new OutputAnalyzer(pb.start()); + + analyzer.shouldHaveExitValue(0); + + // The test is applicable only to C2 (present in Server VM). + if (analyzer.getStderr().contains("Server VM")) { + analyzer.shouldNotContain("java.lang.Thread::onSpinWait (1 bytes) (intrinsic)"); + } + } + + static class Launcher { + + public static void main(final String[] args) throws Exception { + int end = 20_000; + + for (int i=0; i < end; i++) { + test(); + } + } + static void test() { + java.lang.Thread.onSpinWait(); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/testlibrary/sha/predicate/IntrinsicPredicates.java 2022-04-19 19:55:43.000000000 +0000 @@ -60,10 +60,11 @@ }; public static final BooleanSupplier MD5_INSTRUCTION_AVAILABLE - = // x86 variants + = new OrPredicate(new CPUSpecificPredicate("aarch64.*", null, null), + // x86 variants new OrPredicate(new CPUSpecificPredicate("amd64.*", null, null), new OrPredicate(new CPUSpecificPredicate("i386.*", null, null), - new CPUSpecificPredicate("x86.*", null, null))); + new CPUSpecificPredicate("x86.*", null, null)))); public static final BooleanSupplier SHA1_INSTRUCTION_AVAILABLE = new OrPredicate(new CPUSpecificPredicate("aarch64.*", new String[] { "sha1" }, null), diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/compiler/vectorapi/TestLongVectorNeg.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, 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.vectorapi; + +import jdk.incubator.vector.LongVector; + +/* + * @test + * @bug 8275643 + * @summary Test that LongVector.neg is properly handled by the _VectorUnaryOp C2 intrinsic + * @modules jdk.incubator.vector + * @requires vm.debug + * @run main/othervm -XX:-TieredCompilation -XX:+AlwaysIncrementalInline -Xbatch + * -XX:CompileCommand=dontinline,compiler.vectorapi.TestLongVectorNeg::test + * compiler.vectorapi.TestLongVectorNeg + */ +public class TestLongVectorNeg { + + static LongVector test(LongVector v) { + return v.neg(); + } + + public static void main(String[] args) { + LongVector v = LongVector.zero(LongVector.SPECIES_128); + for (int i = 0; i < 50_000; ++i) { + v.abs(); // Warmup to make sure the !VO_SPECIAL code path is taken as well + test(v); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/cgroup/CgroupSubsystemFactory.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat Inc. + * Copyright (c) 2020, 2021, Red Hat Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,6 +105,7 @@ "devices 0 1 1\n" + "freezer 0 1 1\n" + "net_cls 0 1 1\n" + + "pids 0 1 1\n" + "blkio 0 1 1\n" + "perf_event 0 1 1 "; private String cgroupsNonZeroJoinControllers = @@ -168,7 +169,7 @@ "perf_event 4 1 1\n" + "net_prio 5 1 1\n" + "hugetlb 6 1 1\n" + - "pids 3 80 1"; + "pids 9 80 1"; // hierarchy has to match procSelfCgroupHybridContent private String mntInfoCgroupsV2Only = "28 21 0:25 / /sys/fs/cgroup rw,nosuid,nodev,noexec,relatime shared:4 - cgroup2 none rw,seclabel,nsdelegate"; private String mntInfoCgroupsV1SystemdOnly = diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/DockerBasicTest.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/DockerBasicTest.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/DockerBasicTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/DockerBasicTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -48,7 +48,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageNameAndTag, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageNameAndTag); try { testJavaVersion(); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestCPUAwareness.java 2022-04-19 19:55:43.000000000 +0000 @@ -52,7 +52,7 @@ } System.out.println("Test Environment: detected availableCPUs = " + availableCPUs); - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { // cpuset, period, shares, expected Active Processor Count diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestCPUSets.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestCPUSets.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestCPUSets.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestCPUSets.java 2022-04-19 19:55:43.000000000 +0000 @@ -57,7 +57,7 @@ Common.prepareWhiteBox(); - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { // Sanity test the cpu sets reader and parser diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFREvents.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFREvents.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFREvents.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFREvents.java 2022-04-19 19:55:43.000000000 +0000 @@ -58,7 +58,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFRNetworkEvents.java 2022-04-19 19:55:43.000000000 +0000 @@ -51,7 +51,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { runTest("jdk.SocketWrite"); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJFRWithJMX.java 2022-04-19 19:55:43.000000000 +0000 @@ -80,7 +80,7 @@ throw new SkippedException("test cannot be run under rootless podman configuration"); } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { test(); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestJcmdWithSideCar.java 2022-04-19 19:55:43.000000000 +0000 @@ -65,7 +65,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(IMAGE_NAME, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(IMAGE_NAME); try { // Start the loop process in the "main" container, then run test cases diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestMemoryAwareness.java 2022-04-19 19:55:43.000000000 +0000 @@ -50,7 +50,7 @@ } Common.prepareWhiteBox(); - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { testMemoryLimit("100m", "104857600"); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestMisc.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestMisc.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestMisc.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestMisc.java 2022-04-19 19:55:43.000000000 +0000 @@ -50,7 +50,7 @@ } Common.prepareWhiteBox(); - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { testMinusContainerSupport(); @@ -105,7 +105,9 @@ "Memory Soft Limit", "Memory Usage", "Maximum Memory Usage", - "memory_max_usage_in_bytes" + "memory_max_usage_in_bytes", + "maximum number of tasks", + "current number of tasks" }; for (String s : expectedToContain) { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestPids.java openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestPids.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/containers/docker/TestPids.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/containers/docker/TestPids.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 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 + * 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 + * @key cgroups + * @summary Test JVM's awareness of pids controller + * @requires docker.support + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @build sun.hotspot.WhiteBox PrintContainerInfo + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar sun.hotspot.WhiteBox + * @run driver TestPids + */ +import java.util.List; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.Asserts; +import jdk.test.lib.Container; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; + +public class TestPids { + private static final String imageName = Common.imageName("pids"); + private static final boolean IS_PODMAN = Container.ENGINE_COMMAND.contains("podman"); + private static final int UNLIMITED_PIDS_PODMAN = 0; + private static final int UNLIMITED_PIDS_DOCKER = -1; + + static final String warning_kernel_no_pids_support = "WARNING: Your kernel does not support pids limit capabilities"; + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + Common.prepareWhiteBox(); + DockerTestUtils.buildJdkContainerImage(imageName); + + try { + testPids(); + } finally { + if (!DockerTestUtils.RETAIN_IMAGE_AFTER_TEST) { + DockerTestUtils.removeDockerImage(imageName); + } + } + } + + private static void testPids() throws Exception { + System.out.println("Testing pids controller ..."); + testPids("400"); + testPids("800"); + testPids("2000"); + testPids("Unlimited"); + } + + private static DockerRunOptions commonOpts() { + DockerRunOptions opts = new DockerRunOptions(imageName, "/jdk/bin/java", "PrintContainerInfo"); + opts.addDockerOpts("--volume", Utils.TEST_CLASSES + ":/test-classes/"); + opts.addJavaOpts("-Xlog:os+container=trace", "-cp", "/test-classes/"); + Common.addWhiteBoxOpts(opts); + return opts; + } + + private static void checkResult(List lines, String lineMarker, String expectedValue) { + boolean lineMarkerFound = false; + + for (String line : lines) { + if (line.contains(warning_kernel_no_pids_support)) { + System.out.println("Docker pids limitation seems not to work, avoiding check"); + return; + } + + if (line.contains(lineMarker)) { + lineMarkerFound = true; + String[] parts = line.split(":"); + System.out.println("DEBUG: line = " + line); + System.out.println("DEBUG: parts.length = " + parts.length); + if (expectedValue.equals("any_integer")) { + Asserts.assertEquals(parts.length, 2); + String ivalue = parts[1].replaceAll("\\s",""); + try { + int ai = Integer.parseInt(ivalue); + System.out.println("Found " + lineMarker + " with value: " + ai + ". PASS."); + } catch (NumberFormatException ex) { + throw new RuntimeException("Could not convert " + ivalue + " to an integer, log line was " + line); + } + break; + } + + Asserts.assertEquals(parts.length, 2); + String actual = parts[1].replaceAll("\\s",""); + // Unlimited pids leads on some setups not to "max" in the output, but to a high number + if (expectedValue.equals("max")) { + if (actual.equals("max")) { + System.out.println("Found expected max for unlimited pids value."); + } else { + try { + int ai = Integer.parseInt(actual); + if (ai > 20000) { + System.out.println("Limit value " + ai + " got accepted as unlimited, log line was " + line); + } else { + throw new RuntimeException("Limit value " + ai + " is not accepted as unlimited, log line was " + line); + } + } catch (NumberFormatException ex) { + throw new RuntimeException("Could not convert " + actual + " to an integer, log line was " + line); + } + } + } else { + Asserts.assertEquals(actual, expectedValue); + } + break; + } + } + Asserts.assertTrue(lineMarkerFound); + } + + private static void testPids(String value) throws Exception { + Common.logNewTestCase("pids controller test, limiting value = " + value); + + DockerRunOptions opts = commonOpts(); + if (value.equals("Unlimited")) { + int unlimited = IS_PODMAN ? UNLIMITED_PIDS_PODMAN : UNLIMITED_PIDS_DOCKER; + opts.addDockerOpts("--pids-limit=" + unlimited); + } else { + opts.addDockerOpts("--pids-limit="+value); + } + + List lines = Common.run(opts).asLines(); + if (value.equals("Unlimited")) { + checkResult(lines, "Maximum number of tasks is: ", "max"); + } else { + checkResult(lines, "Maximum number of tasks is: ", value); + } + // current number of tasks value is hard to predict, so better expect no value + checkResult(lines, "Current number of tasks is: ", "any_integer"); + } + +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/g1/TestMixedGCLiveThreshold.java 2022-04-19 19:55:43.000000000 +0000 @@ -24,14 +24,36 @@ package gc.g1; /* - * @test TestMixedGCLiveThreshold - * @summary Test G1MixedGCLiveThresholdPercent. Fill up a region to at least 1/3 region-size, - * the region should not be selected for mixed GC cycle if liveness is above threshold. + * @test id=0percent + * @summary Test G1MixedGCLiveThresholdPercent=0. Fill up a region to at least 33 percent, + * the region should not be selected for mixed GC cycle. * @requires vm.gc.G1 * @library /test/lib * @build sun.hotspot.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox - * @run driver gc.g1.TestMixedGCLiveThreshold + * @run driver gc.g1.TestMixedGCLiveThreshold 0 false + */ + +/* + * @test id=25percent + * @summary Test G1MixedGCLiveThresholdPercent=25. Fill up a region to at least 33 percent, + * the region should not be selected for mixed GC cycle. + * @requires vm.gc.G1 + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run driver gc.g1.TestMixedGCLiveThreshold 25 false + */ + +/* + * @test id=100percent + * @summary Test G1MixedGCLiveThresholdPercent=100. Fill up a region to at least 33 percent, + * the region should be selected for mixed GC cycle. + * @requires vm.gc.G1 + * @library /test/lib + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run driver gc.g1.TestMixedGCLiveThreshold 100 true */ import java.util.ArrayList; @@ -48,14 +70,9 @@ private static final String pattern = "Remembered Set Tracking update regions total ([0-9]+), selected ([0-9]+)$"; public static void main(String[] args) throws Exception { - // -XX:G1MixedGCLiveThresholdPercent=0 - testMixedGCLiveThresholdPercent(0, false); - - // -XX:G1MixedGCLiveThresholdPercent=25 - testMixedGCLiveThresholdPercent(25, false); - - // -XX:G1MixedGCLiveThresholdPercent=100 - testMixedGCLiveThresholdPercent(100, true); + int liveThresholdPercent = Integer.parseInt(args[0]); + boolean expectRebuild = Boolean.parseBoolean(args[1]); + testMixedGCLiveThresholdPercent(liveThresholdPercent, expectRebuild); } private static void testMixedGCLiveThresholdPercent(int liveThresholdPercent, boolean expectedRebuild) throws Exception { @@ -71,6 +88,7 @@ " no regions should be selected") ); output.shouldHaveExitValue(0); + output.reportDiagnosticSummary(); } private static OutputAnalyzer testWithMixedGCLiveThresholdPercent(int percent) throws Exception { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stress/gclocker/TestGCLockerWithSerial.java 2022-04-19 19:55:43.000000000 +0000 @@ -30,7 +30,7 @@ * @requires vm.gc.Serial * @requires vm.flavor != "minimal" * @summary Stress Serial's GC locker by calling GetPrimitiveArrayCritical while concurrently filling up old gen. - * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xmx1500m -Xmx1500m -XX:+UseSerialGC gc.stress.gclocker.TestGCLockerWithSerial + * @run main/native/othervm/timeout=200 -Xlog:gc*=info -Xms1500m -Xmx1500m -XX:+UseSerialGC gc.stress.gclocker.TestGCLockerWithSerial */ public class TestGCLockerWithSerial { public static void main(String[] args) { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationAgeThreshold.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,31 @@ * @test TestStringDeduplicationAgeThreshold * @summary Test string deduplication age threshold * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold + * @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold G1 + */ + +/* + * @test TestStringDeduplicationAgeThreshold + * @summary Test string deduplication age threshold + * @bug 8029075 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @library / + * @modules java.base/jdk.internal.misc:open + * @modules java.base/java.lang:open + * java.management + * @run driver gc.stringdedup.TestStringDeduplicationAgeThreshold Shenandoah */ public class TestStringDeduplicationAgeThreshold { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testAgeThreshold(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationFullGC.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,31 @@ * @test TestStringDeduplicationFullGC * @summary Test string deduplication during full GC * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationFullGC + * @run driver gc.stringdedup.TestStringDeduplicationFullGC G1 + */ + +/* + * @test TestStringDeduplicationFullGC + * @summary Test string deduplication during full GC + * @bug 8029075 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @library / + * @modules java.base/jdk.internal.misc:open + * @modules java.base/java.lang:open + * java.management + * @run driver gc.stringdedup.TestStringDeduplicationFullGC Shenandoah */ public class TestStringDeduplicationFullGC { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testFullGC(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationInterned.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,18 @@ * @test TestStringDeduplicationInterned * @summary Test string deduplication of interned strings * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationInterned + * @run driver gc.stringdedup.TestStringDeduplicationInterned G1 */ public class TestStringDeduplicationInterned { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testInterned(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationPrintOptions.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,31 @@ * @test TestStringDeduplicationPrintOptions * @summary Test string deduplication print options * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationPrintOptions + * @run driver gc.stringdedup.TestStringDeduplicationPrintOptions G1 + */ + +/* + * @test TestStringDeduplicationPrintOptions + * @summary Test string deduplication print options + * @bug 8029075 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @library / + * @modules java.base/jdk.internal.misc:open + * @modules java.base/java.lang:open + * java.management + * @run driver gc.stringdedup.TestStringDeduplicationPrintOptions Shenandoah */ public class TestStringDeduplicationPrintOptions { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testPrintOptions(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTableResize.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,31 @@ * @test TestStringDeduplicationTableResize * @summary Test string deduplication table resize * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationTableResize + * @run driver gc.stringdedup.TestStringDeduplicationTableResize G1 + */ + +/* + * @test TestStringDeduplicationTableResize + * @summary Test string deduplication table resize + * @bug 8029075 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @library / + * @modules java.base/jdk.internal.misc:open + * @modules java.base/java.lang:open + * java.management + * @run driver gc.stringdedup.TestStringDeduplicationTableResize Shenandoah */ public class TestStringDeduplicationTableResize { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testTableResize(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationTools.java 2022-04-19 19:55:43.000000000 +0000 @@ -50,6 +50,8 @@ private static Unsafe unsafe; private static byte[] dummy; + private static String selectedGC = null; + static { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); @@ -63,6 +65,10 @@ } } + public static void selectGC(String[] args) { + selectedGC = args[0]; + } + private static Object getValue(String string) { try { return valueField.get(string); @@ -226,6 +232,7 @@ }; ArrayList args = new ArrayList(); + args.add("-XX:+Use" + selectedGC + "GC"); args.addAll(Arrays.asList(defaultArgs)); args.addAll(Arrays.asList(extraArgs)); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/gc/stringdedup/TestStringDeduplicationYoungGC.java 2022-04-19 19:55:43.000000000 +0000 @@ -27,17 +27,31 @@ * @test TestStringDeduplicationYoungGC * @summary Test string deduplication during young GC * @bug 8029075 - * @requires vm.gc == "null" | vm.gc == "G1" | vm.gc == "Shenandoah" + * @requires vm.gc.G1 * @library /test/lib * @library / * @modules java.base/jdk.internal.misc:open * @modules java.base/java.lang:open * java.management - * @run driver gc.stringdedup.TestStringDeduplicationYoungGC + * @run driver gc.stringdedup.TestStringDeduplicationYoungGC G1 + */ + +/* + * @test TestStringDeduplicationYoungGC + * @summary Test string deduplication during young GC + * @bug 8029075 + * @requires vm.gc.Shenandoah + * @library /test/lib + * @library / + * @modules java.base/jdk.internal.misc:open + * @modules java.base/java.lang:open + * java.management + * @run driver gc.stringdedup.TestStringDeduplicationYoungGC Shenandoah */ public class TestStringDeduplicationYoungGC { public static void main(String[] args) throws Exception { + TestStringDeduplicationTools.selectGC(args); TestStringDeduplicationTools.testYoungGC(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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,16 @@ * @run main BSMCalledTwice */ +/* + * @test + * @bug 8262134 + * @library /test/lib + * @requires vm.debug + * @modules java.base/jdk.internal.org.objectweb.asm + * @compile -XDignore.symbol.file BSMCalledTwice.java + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestC::* -XX:+DeoptimizeALot -XX:+VerifyStack BSMCalledTwice + */ + import java.io.File; import java.io.FileOutputStream; import java.util.*; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/CompilerConfigFileWarning.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, 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 @@ -48,6 +48,8 @@ pb = ProcessTools.createJavaProcessBuilder("-XX:CompileCommandFile=hs_comp.txt", "-version"); output = new OutputAnalyzer(pb.start()); + // problems in CompileCommandFile are treated as warnings + output.shouldHaveExitValue(0); output.shouldContain("An error occurred during parsing"); output.shouldContain("Unrecognized option 'aaa'"); output.shouldContain("aaa, aaa"); @@ -60,6 +62,7 @@ pb = ProcessTools.createJavaProcessBuilder("-version"); output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); output.shouldContain("warning: .hotspot_compiler file is present but has been ignored. Run with -XX:CompileCommandFile=.hotspot_compiler to load the file."); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/ConfigFileWarning.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, 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,6 +59,7 @@ pb = ProcessTools.createJavaProcessBuilder("-version"); output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); output.shouldContain("warning: .hotspotrc file is present but has been ignored. Run with -XX:Flags=.hotspotrc to load the file."); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/ObsoleteFlagErrorMessage.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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 @@ -52,6 +52,7 @@ "-XX:+" + flag, "-version"); OutputAnalyzer output2 = new OutputAnalyzer(pb2.start()); + output2.shouldHaveExitValue(0); output2.shouldContain("Ignoring option").shouldContain("support was removed"); output2.shouldContain(flag); } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethods.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -48,6 +48,7 @@ // UnlockDiagnostic turned off, should fail OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Error: VM option 'LogTouchedMethods' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); output.shouldContain("Error: Could not create the Java Virtual Machine."); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/PrintTouchedMethodsJcmd.java 2022-04-19 19:55:43.000000000 +0000 @@ -41,10 +41,6 @@ var pb = new ProcessBuilder(); pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.print_touched_methods"}); var output = new OutputAnalyzer(pb.start()); - try { - output.shouldContain("PrintTouchedMethodsJcmd.main:([Ljava/lang/String;)V"); - } catch (RuntimeException e) { - output.shouldContain("Unknown diagnostic command"); - } - } + output.shouldContain("PrintTouchedMethodsJcmd.main:([Ljava/lang/String;)V"); + } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/TestHexArguments.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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,6 +47,7 @@ pb = ProcessTools.createJavaProcessBuilder( "-XX:SharedBaseAddress=1D000000", "-version"); output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Could not create the Java Virtual Machine"); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/TestVMOptions.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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,11 +42,13 @@ "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintFlagsInitial"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); output.shouldContain("bool UseSerialGC"); pb = ProcessTools.createJavaProcessBuilder( "-XX:-PrintVMOptions", "-version"); output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); output.shouldMatch("(openjdk|java)\\sversion"); File dir = new File(System.getProperty("test.src", ".")); @@ -54,6 +56,7 @@ String s = file.getAbsolutePath(); pb = ProcessTools.createJavaProcessBuilder("-XX:Flags="+s); output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("VM option '-IgnoreUnrecognizedVMOptions'"); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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,6 +39,7 @@ public static void main(String[] args) throws Exception { ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+AlwaysSafeConstructors", "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Error: VM option 'AlwaysSafeConstructors' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions."); if (Platform.isDebugBuild()) { @@ -48,14 +49,17 @@ pb = ProcessTools.createJavaProcessBuilder("-XX:+PrintInlining", "-version"); output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Error: VM option 'PrintInlining' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions."); pb = ProcessTools.createJavaProcessBuilder("-XX:+VerifyStack", "-version"); output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Error: VM option 'VerifyStack' is develop and is available only in debug version of VM."); pb = ProcessTools.createJavaProcessBuilder("-XX:+CheckCompressedOops", "-version"); output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); output.shouldContain("Error: VM option 'CheckCompressedOops' is notproduct and is available only in debug version of VM."); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/PrintMetaspaceDcmd.java 2022-04-19 19:55:43.000000000 +0000 @@ -168,6 +168,29 @@ output.shouldContain("Virtual space list"); output.shouldMatch("node.*reserved.*committed.*used.*"); + pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "chunkfreelist"}); + // Output should look somewhat like this... + // vvvvvvvvvvvvvvvv + // Chunk freelist details: + // Non-Class: + // cm non-class-space: 5 chunks, total word size: 402944. + // -- List[lv00]: empty + // -- List[lv01]: - - total : 1 chunks. + // -- List[lv02]: - - total : 1 chunks. + // -- List[lv03]: empty + // ..... + // + // total chunks: 5, total word size: 402944. + // ^^^^^^^^^^^^^^^^^ + // .... but the actual number of chunks in the freelist is difficult to predict and may be low or zero since + // no class unloading happened yet. + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("Chunk freelist details:"); + // ... but we should see at least one one chunk somewhere, the list should never be empty. + output.shouldMatch(".*-- List\\[lv00\\].*"); + output.shouldMatch(".*total chunks.*total word size.*"); + // Test with different scales pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.metaspace", "scale=G"}); output = new OutputAnalyzer(pb.start()); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestArena.java 2022-04-19 19:55:43.000000000 +0000 @@ -25,8 +25,6 @@ import sun.hotspot.WhiteBox; -import java.util.concurrent.atomic.AtomicLong; - public class MetaspaceTestArena { long arena; @@ -38,7 +36,7 @@ long numAllocated = 0; long deallocatedWords = 0; long numDeallocated = 0; - long numAllocationFailures = 0; + volatile long numAllocationFailures = 0; private synchronized boolean reachedCeiling() { return (allocatedWords - deallocatedWords) > allocationCeiling; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestManyArenasManyThreads.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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 @@ -63,7 +63,7 @@ Thread.sleep(200); for (RandomAllocatorThread t: threads) { - if (t.allocator.arena.numAllocationFailures > 0) { + if (t.allocator.arena.numAllocationFailures > 1000) { t.interrupt(); t.join(); context.destroyArena(t.allocator.arena); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/MetaspaceTestWithThreads.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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,6 @@ * */ -import java.util.Set; - public class MetaspaceTestWithThreads { // The context to use. @@ -53,6 +51,8 @@ // Stop all threads. for (Thread t: threads) { t.interrupt(); + } + for (Thread t: threads) { t.join(); } } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocator.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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,11 @@ ArrayList to_dealloc = new ArrayList<>(); long ticks = 0; - boolean allocationError = false; + + // Allocate (breathe in) until arena is full, then - to test the arena deallocator - deallocate some allocations + // and breathe in again until full. + boolean breatheIn = true; + int breatheOutTicks = 0; Random localRandom; @@ -57,7 +61,6 @@ // Allocate a random amount from the arena. If dice hits right, add this to the deallocation list. void allocateRandomly() { - allocationError = false; long word_size = profile.randomAllocationSize(); Allocation a = arena.allocate(word_size); if (a != null) { @@ -65,7 +68,9 @@ to_dealloc.add(a); } } else { - allocationError = true; + // On allocation error, breathe out a bit + breatheIn = false; + breatheOutTicks = 0; } } @@ -80,19 +85,20 @@ } public void tick() { - - if (!allocationError) { + if (breatheIn) { + // allocate until we hit the ceiling allocateRandomly(); - if(rollDice(profile.randomDeallocProbability)) { + if (rollDice(profile.randomDeallocProbability)) { deallocateRandomly(); } } else { - deallocateRandomly(); - allocationError = false; + if (++breatheOutTicks < 100) { + deallocateRandomly(); + } else { + breatheIn = true; + } } - ticks ++; - } public RandomAllocator(MetaspaceTestArena arena) { @@ -102,6 +108,10 @@ this.localRandom = new Random(RandomHelper.random().nextInt()); } + long numAllocationFailures() { + return arena.numAllocationFailures; + } + @Override public String toString() { return arena.toString() + ", ticks=" + ticks; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Metaspace/elastic/RandomAllocatorThread.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2021 SAP SE. All rights reserved. + * Copyright (c) 2021, 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,7 +23,6 @@ * */ -import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; @@ -55,9 +54,7 @@ } while (!Thread.interrupted()) { - for (int i = 0; i < 1000; i++) { - allocator.tick(); - } + allocator.tick(); } // System.out.println("+ [" + id + "] " + allocator); diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Safepoint/TestAbortOnVMOperationTimeout.java 2022-04-19 19:55:43.000000000 +0000 @@ -26,7 +26,7 @@ /* * @test TestAbortOnVMOperationTimeout - * @bug 8181143 + * @bug 8181143 8269523 * @summary Check abort on VM timeout is working * @library /test/lib * @modules java.base/jdk.internal.misc @@ -36,12 +36,17 @@ public class TestAbortOnVMOperationTimeout { + // A static array is unlikely to be optimised away by the JIT. + static Object[] arr; + public static void main(String[] args) throws Exception { if (args.length > 0) { - Object[] arr = new Object[10_000_000]; + arr = new Object[10_000_000]; for (int i = 0; i < arr.length; i++) { arr[i] = new Object(); } + // Try to force at least one full GC cycle. + System.gc(); return; } @@ -51,9 +56,9 @@ testWith(delay, true); } - // These should fail: Serial is not very fast. Traversing 10M objects in 5 ms - // means less than 0.5 ns per object, which is not doable. - for (int delay : new int[]{0, 1, 5}) { + // These should fail: Serial is not very fast but we have seen the test + // execute as quickly as 2ms! + for (int delay : new int[]{0, 1}) { testWith(delay, false); } } @@ -66,6 +71,7 @@ "-Xmx256m", "-XX:+UseSerialGC", "-XX:-CreateCoredumpOnCrash", + "-Xlog:gc", "TestAbortOnVMOperationTimeout", "foo" ); @@ -77,6 +83,6 @@ output.shouldContain("VM operation took too long"); output.shouldNotHaveExitValue(0); } + output.reportDiagnosticSummary(); } } - diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/Thread/TestSpinPause.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright Amazon.com Inc. 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 TestSpinPause + * @summary JVM runtime can use SpinPause function for synchronized statements. + * Check different implementations of JVM SpinPause don't crash JVM. + * @bug 8278241 + * @library /test/lib + * + * @requires os.arch=="aarch64" + * + * @run main/othervm TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xint -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xcomp TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=none TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=nop -XX:OnSpinWaitInstCount=10 TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=isb -XX:OnSpinWaitInstCount=3 TestSpinPause + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:OnSpinWaitInst=yield -XX:OnSpinWaitInstCount=3 TestSpinPause + */ + +public class TestSpinPause { + private Integer[] valueHolder; + + private TestSpinPause () { + valueHolder = new Integer[] {Integer.valueOf(101)}; + } + + private void getSet() { + final int iterCount = 100; + for (int i = 0; i < iterCount; ++i) { + synchronized (valueHolder) { + Integer v = valueHolder[0]; + valueHolder[0] = Integer.reverse(v); + } + } + } + + public static void main(String[] args) throws Exception { + TestSpinPause test = new TestSpinPause(); + Thread t1 = new Thread(test::getSet); + Thread t2 = new Thread(test::getSet); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + System.out.println("Done: " + test.valueHolder[0]); + } +} + diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/LambdaContainsOldInf.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021, 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 8274944 + * @summary VM should not crash during CDS dump when a lambda proxy class + * contains an old version of interface. + * @requires vm.cds + * @library /test/lib + * @compile test-classes/OldProvider.jasm + * @compile test-classes/LambdaContainsOldInfApp.java + * @run driver LambdaContainsOldInf + */ + +import jdk.test.lib.cds.CDSOptions; +import jdk.test.lib.cds.CDSTestUtils; +import jdk.test.lib.process.OutputAnalyzer; + +public class LambdaContainsOldInf { + + public static void main(String[] args) throws Exception { + String mainClass = "LambdaContainsOldInfApp"; + String namePrefix = "lambdacontainsoldinf"; + JarBuilder.build(namePrefix, mainClass, "OldProvider"); + + String appJar = TestCommon.getTestJar(namePrefix + ".jar"); + String classList = namePrefix + ".list"; + String archiveName = namePrefix + ".jsa"; + + // dump class list + CDSTestUtils.dumpClassList(classList, "-cp", appJar, mainClass); + + // create archive with the class list + CDSOptions opts = (new CDSOptions()) + .addPrefix("-XX:ExtraSharedClassListFile=" + classList, + "-cp", appJar, + "-Xlog:class+load,cds") + .setArchiveName(archiveName); + OutputAnalyzer output = CDSTestUtils.createArchiveAndCheck(opts); + TestCommon.checkExecReturn(output, 0, true, + "Skipping OldProvider: Old class has been linked"); + output.shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked"); + + // run with archive + CDSOptions runOpts = (new CDSOptions()) + .addPrefix("-cp", appJar, "-Xlog:class+load,cds=debug") + .setArchiveName(archiveName) + .setUseVersion(false) + .addSuffix(mainClass); + output = CDSTestUtils.runWithArchive(runOpts); + TestCommon.checkExecReturn(output, 0, true, + "[class,load] LambdaContainsOldInfApp source: shared objects file"); + output.shouldMatch(".class.load. OldProvider.source:.*lambdacontainsoldinf.jar") + .shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf"); + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaContainsOldInf.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021, 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 8274944 + * @summary VM should not crash during CDS dump when a lambda proxy class + * contains an old version of interface. + * @requires vm.cds + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes + * @build LambdaContainsOldInfApp sun.hotspot.WhiteBox OldProvider LambdaVerification + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar lambda_contains_old_inf.jar LambdaVerification + * LambdaContainsOldInfApp OldProvider + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. LambdaContainsOldInf + */ + +import jdk.test.lib.helpers.ClassFileInstaller; + +public class LambdaContainsOldInf extends DynamicArchiveTestBase { + public static void main(String[] args) throws Exception { + runTest(LambdaContainsOldInf::test); + } + + static void test() throws Exception { + String topArchiveName = getNewArchiveName(); + String appJar = ClassFileInstaller.getJarPath("lambda_contains_old_inf.jar"); + String mainClass = "LambdaContainsOldInfApp"; + String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar"); + String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar; + + dump(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-Xlog:class+load=debug,cds=debug,cds+dynamic=info", + use_whitebox_jar, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output + .shouldMatch("Skipping.LambdaContainsOldInfApp[$][$]Lambda[$].*0x.*:.*Old.class.has.been.linked") + .shouldHaveExitValue(0); + }); + + run(topArchiveName, + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + use_whitebox_jar, + "-Xlog:class+load=debug", + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + output.shouldContain("[class,load] LambdaContainsOldInfApp source: shared objects file (top)") + .shouldMatch(".class.load. OldProvider.source:.*lambda_contains_old_inf.jar") + .shouldMatch(".class.load. LambdaContainsOldInfApp[$][$]Lambda[$].*/0x.*source:.*LambdaContainsOldInf") + .shouldHaveExitValue(0); + }); + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/loaderConstraints/DynamicLoaderConstraintsTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -52,6 +52,23 @@ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicLoaderConstraintsTest custom */ +/** + * @test id=custom-cl-zgc + * @requires vm.cds.custom.loaders + * @requires vm.gc.Z + * @summary Test dumptime_table entries are removed with zgc eager class unloading + * @bug 8274935 + * @library /test/lib + * /test/hotspot/jtreg/runtime/cds/appcds + * /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive + * @modules java.base/jdk.internal.misc + * jdk.httpserver + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm/timeout=180 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. DynamicLoaderConstraintsTest custom-zgc + */ + import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import jdk.test.lib.Asserts; @@ -83,9 +100,11 @@ * if false, LoaderConstraintsApp will be loaded by the built-in AppClassLoader. */ static boolean useCustomLoader; + static boolean useZGC; public static void main(String[] args) throws Exception { useCustomLoader = (args.length != 0); + useZGC = (args.length != 0 && args[0].equals("custom-zgc")); runTest(DynamicLoaderConstraintsTest::doTest); } @@ -124,8 +143,15 @@ }; if (useCustomLoader) { - cmdLine = TestCommon.concat(cmdLine, "-cp", loaderJar, - loaderMainClass, appJar); + if (useZGC) { + // Add options to force eager class unloading. + cmdLine = TestCommon.concat(cmdLine, "-cp", loaderJar, + "-XX:+UseZGC", "-XX:ZCollectionInterval=0.01", + loaderMainClass, appJar); + } else { + cmdLine = TestCommon.concat(cmdLine, "-cp", loaderJar, + loaderMainClass, appJar); + } } else { cmdLine = TestCommon.concat(cmdLine, "-cp", appJar); } diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/test-classes/LambdaContainsOldInfApp.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, 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. + * + */ +public class LambdaContainsOldInfApp { + public static void main(final String... args) { + getProvider(); + } + + public static OldProvider getProvider() { + return () -> { + return null; + }; + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/cds/appcds/test-classes/OldProvider.jasm 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 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. + * + */ +public interface OldProvider + version 49:0 +{ + public abstract Method get:"()Ljava/lang/Object;"; + +} // end Class OldProvider diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/jsig/Testjsig.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/jsig/Testjsig.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/jsig/Testjsig.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/jsig/Testjsig.java 2022-04-19 19:55:43.000000000 +0000 @@ -29,7 +29,7 @@ * @bug 8022301 * @bug 8025519 * @summary sigaction(sig) results in process hang/timed-out if sig is much greater than SIGRTMAX - * @requires (os.family == "linux") + * @requires os.family != "windows" * @library /test/lib * @compile TestJNI.java * @run driver Testjsig diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/modules/LoadUnloadModuleStress.java 2022-04-19 19:55:43.000000000 +0000 @@ -30,7 +30,7 @@ * @build sun.hotspot.WhiteBox * @compile/module=java.base java/lang/ModuleHelper.java * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xmx64m -Xmx64m LoadUnloadModuleStress 15000 + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xms64m -Xmx64m LoadUnloadModuleStress 15000 */ import java.lang.ref.WeakReference; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/verifier/PutfieldProtectedTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, 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 8274714 + * @summary Make sure error message for protected putfield error is correct. + * @compile putfieldProtected.jasm + * @run main/othervm -Xverify:remote PutfieldProtectedTest + */ + +// Test that an int[] is not assignable to byte[]. +public class PutfieldProtectedTest { + + public static void main(String args[]) throws Throwable { + try { + Class newClass = Class.forName("other.putfieldProtected"); + throw new RuntimeException("Expected VerifyError exception not thrown"); + } catch (java.lang.VerifyError e) { + if (!e.getMessage().contains("Bad access to protected data in putfield")) { + throw new RuntimeException("wrong exception: " + e.getMessage()); + } + } + } +} diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm --- openjdk-17-17.0.2+8/test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/runtime/verifier/putfieldProtected.jasm 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021, 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. + */ + +// This .jasm file is needed for this test to ensure that the illegal protected +// access is done at runtime. Otherwise, javac would detect and complain about +// the illegal access +super public class another/SuperC version 51:0 { + protected Field f:I; + + public Method "":"()V" stack 1 locals 1 { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class another/SuperC + + +super public class other/putfieldProtected extends another/SuperC version 51:0 { + + public Method "":"()V" stack 1 locals 1 { + aload_0; + invokespecial Method another/SuperC."":"()V"; + return; + } + + public static Method run:"()Z" stack 2 locals 2 { + new class another/SuperC; + dup; + invokespecial Method another/SuperC."":"()V"; + iconst_1; + putfield Field another/SuperC.f:"I"; + iconst_1; + ireturn; + } + +} // end Class other/putfieldProtected diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java openjdk-17-17.0.3+7/test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/serviceability/dcmd/framework/TestProcessLauncher.java 2022-04-19 19:55:43.000000000 +0000 @@ -50,7 +50,7 @@ } public TestProcessLauncher(String className) { - this(className, new ArgumentHandler(new String[0])); + this(className, new ArgumentHandler(new String[] {"-transport.address=dynamic"})); } public Process launch() { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/PhantomReference/phantom001/phantom001.java 2022-04-19 19:55:43.000000000 +0000 @@ -94,7 +94,7 @@ } // The class implements the logic of the testcase - class Test implements Runnable { + class Test implements Runnable, OOMStress { int iteration; private volatile boolean finalized; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft001/soft001.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2021, 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 @@ -62,6 +62,7 @@ import nsk.share.gc.GC; import nsk.share.gc.NonbranchyTree; +import nsk.share.gc.OOMStress; import nsk.share.gc.ThreadedGCTest; import nsk.share.gc.gp.GarbageProducer; import nsk.share.gc.gp.GarbageProducerAware; @@ -99,7 +100,7 @@ } // The class implements the logic of the testcase - class Test implements Runnable { + class Test implements Runnable, OOMStress { int iteration; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft003/soft003.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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,7 @@ */ public class soft003 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int arrayLength; private int objectSize = 100; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft004/soft004.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -48,7 +48,7 @@ */ public class soft004 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int arrayLength; private int objectSize = 100; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/SoftReference/soft005/soft005.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -48,7 +48,7 @@ */ public class soft005 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int length = 10000; private int objectSize = 10000; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak001/weak001.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2021, 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 @@ -62,6 +62,7 @@ import nsk.share.gc.GC; import nsk.share.gc.NonbranchyTree; +import nsk.share.gc.OOMStress; import nsk.share.gc.ThreadedGCTest; import nsk.share.gc.gp.GarbageProducer; import nsk.share.gc.gp.GarbageProducerAware; @@ -99,7 +100,7 @@ } // The class implements the logic of the testcase - class Test implements Runnable { + class Test implements Runnable, OOMStress { int iteration; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak003/weak003.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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,7 @@ */ public class weak003 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int arrayLength; private int objectSize = 100; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak004/weak004.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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,7 @@ */ public class weak004 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int arrayLength; private int objectSize = 100; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak005/weak005.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -48,7 +48,7 @@ */ public class weak005 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int length = 10000; private int objectSize = 10000; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak006/weak006.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -53,7 +53,7 @@ */ public class weak006 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int length; private int objectSize = 100; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/gc/gctests/WeakReference/weak007/weak007.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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 @@ -51,7 +51,7 @@ */ public class weak007 extends ThreadedGCTest { - class Worker implements Runnable { + class Worker implements Runnable, OOMStress { private int length = 10000; private int objectSize = 10000; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/jit/t/t105/t105.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, 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,13 +23,18 @@ /* * @test + * @bug 8273277 * * @summary converted from VM Testbase jit/t/t105. * VM Testbase keywords: [jit, quick] * * @library /vmTestbase * /test/lib - * @run main/othervm jit.t.t105.t105 + * @run main/othervm -XX:-OmitStackTraceInFastThrow jit.t.t105.t105 + * @run main/othervm -XX:-OmitStackTraceInFastThrow -Xbatch -XX:Tier0BackedgeNotifyFreqLog=0 -XX:Tier2BackedgeNotifyFreqLog=0 -XX:Tier3BackedgeNotifyFreqLog=0 -XX:Tier2BackEdgeThreshold=1 -XX:Tier3BackEdgeThreshold=1 -XX:Tier4BackEdgeThreshold=1 jit.t.t105.t105 + * + * This test must be run with OmitStackTraceInFastThrow disabled to avoid preallocated + * exceptions. They don't have the detailed message that this test relies on. */ package jit.t.t105; diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy001/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy002/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy003/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy004/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy005/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy006/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy007/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy008/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy009/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy010/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy011/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy012/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy013/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy014/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/metaspace/stressHierarchy/stressHierarchy015/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/jvmti/StopThread/stopthrd007.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2021, 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 @@ -170,8 +170,8 @@ } public void run() { - stopthrd007.startingBarrier.unlock(); try { + stopthrd007.startingBarrier.unlock(); int i = 0; int n = 1000; while (flag) { @@ -216,8 +216,8 @@ } public synchronized void run() { - stopthrd007.startingBarrier.unlock(); try { + stopthrd007.startingBarrier.unlock(); wait(timeout); status = 1; } catch (ThreadDeath t) { @@ -249,8 +249,8 @@ } public void run() { - stopthrd007.startingBarrier.unlock(); try { + stopthrd007.startingBarrier.unlock(); sleep(timeout); status = 1; } catch (ThreadDeath t) { diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree001/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree002/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree003/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree004/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree005/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree006/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree007/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree008/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree009/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree010/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree011/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/btree/btree012/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain001/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain002/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain003/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain004/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain005/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain006/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain007/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties --- openjdk-17-17.0.2+8/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/hotspot/jtreg/vmTestbase/nsk/sysdict/vm/stress/chain/chain008/TEST.properties 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -exclusiveAccess.dirs=. diff -Nru openjdk-17-17.0.2+8/test/jdk/ProblemList.txt openjdk-17-17.0.3+7/test/jdk/ProblemList.txt --- openjdk-17-17.0.2+8/test/jdk/ProblemList.txt 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/ProblemList.txt 2022-04-19 19:55:43.000000000 +0000 @@ -572,6 +572,7 @@ # jdk_io java/io/pathNames/GeneralWin32.java 8180264 windows-all +java/io/File/createTempFile/SpecialTempFile.java 8274122 windows11 ############################################################################ diff -Nru openjdk-17-17.0.2+8/test/jdk/TEST.ROOT openjdk-17-17.0.3+7/test/jdk/TEST.ROOT --- openjdk-17-17.0.2+8/test/jdk/TEST.ROOT 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/TEST.ROOT 2022-04-19 19:55:43.000000000 +0000 @@ -27,7 +27,7 @@ java/rmi/Naming java/util/prefs sun/management/jmxremote \ sun/tools/jstatd sun/tools/jcmd \ sun/tools/jinfo sun/tools/jmap sun/tools/jps sun/tools/jstack sun/tools/jstat \ -com/sun/tools/attach sun/security/mscapi java/util/stream java/util/Arrays/largeMemory \ +com/sun/tools/attach sun/security/mscapi java/util/Arrays/largeMemory \ java/util/BitSet/stream javax/rmi java/net/httpclient/websocket # Group definitions diff -Nru openjdk-17-17.0.2+8/test/jdk/TEST.groups openjdk-17-17.0.3+7/test/jdk/TEST.groups --- openjdk-17-17.0.2+8/test/jdk/TEST.groups 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/TEST.groups 2022-04-19 19:55:43.000000000 +0000 @@ -74,7 +74,9 @@ :build \ :jdk_vector \ :jdk_rmi \ - :jdk_jfr_tier3 + :jdk_svc \ + -:jdk_svc_sanity \ + -:svc_tools # Everything not in other tiers tier4 = \ @@ -283,9 +285,6 @@ jdk_jfr = \ jdk/jfr -jdk_jfr_tier3 = \ - jdk/jfr/event/metadata/TestLookForUntestedEvents.java - # # Catch-all for other areas with a small number of tests # @@ -382,6 +381,9 @@ jfc_demo = \ demo/jfc +jdk_editpad = \ + jdk/editpad + jdk_desktop = \ :jdk_awt \ :jdk_2d \ @@ -391,7 +393,8 @@ :jdk_imageio \ :jdk_accessibility \ :jfc_demo \ - :jdk_client_sanity + :jdk_client_sanity \ + :jdk_editpad # SwingSet3 tests. jdk_client_sanity = \ diff -Nru openjdk-17-17.0.2+8/test/jdk/build/AbsPathsInImage.java openjdk-17-17.0.3+7/test/jdk/build/AbsPathsInImage.java --- openjdk-17-17.0.2+8/test/jdk/build/AbsPathsInImage.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/build/AbsPathsInImage.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; -import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -145,6 +144,15 @@ private void scanFiles(Path root, List searchPatterns) throws IOException { Files.walkFileTree(root, new SimpleFileVisitor<>() { @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + String dirName = dir.toString(); + if (dirName.endsWith(".dSYM")) { + return FileVisitResult.SKIP_SUBTREE; + } + return super.preVisitDirectory(dir, attrs); + } + + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String fileName = file.toString(); if (Files.isSymbolicLink(file)) { diff -Nru openjdk-17-17.0.2+8/test/jdk/com/sun/jdi/TestScaffold.java openjdk-17-17.0.3+7/test/jdk/com/sun/jdi/TestScaffold.java --- openjdk-17-17.0.2+8/test/jdk/com/sun/jdi/TestScaffold.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/com/sun/jdi/TestScaffold.java 2022-04-19 19:55:43.000000000 +0000 @@ -535,16 +535,15 @@ Location loc = ((Locatable)event).location(); ReferenceType rt = loc.declaringType(); String name = rt.name(); - if (name.startsWith("java.") && - !name.startsWith("sun.") && - !name.startsWith("com.")) { + if (name.startsWith("java.") + || name.startsWith("sun.") + || name.startsWith("com.") + || name.startsWith("jdk.")) { if (mainStartClass != null) { redefine(mainStartClass); } } else { - if (!name.startsWith("jdk.")) { - redefine(rt); - } + redefine(rt); } } } diff -Nru openjdk-17-17.0.2+8/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java openjdk-17-17.0.3+7/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java --- openjdk-17-17.0.2+8/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/com/sun/jndi/ldap/LdapPoolTimeoutTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021, 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 8277795 + * @summary Multi-threaded client timeout tests for ldap pool + * @library /test/lib + * lib/ + * @run testng/othervm LdapPoolTimeoutTest + */ + +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.IOException; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.InitialDirContext; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; + +import static jdk.test.lib.Utils.adjustTimeout; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.expectThrows; + +public class LdapPoolTimeoutTest { + /* + * Practical representation of an infinite timeout. + */ + private static final long INFINITY_MILLIS = adjustTimeout(20_000); + /* + * The acceptable variation in timeout measurements. + */ + private static final long TOLERANCE = adjustTimeout( 3_500); + + private static final long CONNECT_MILLIS = adjustTimeout( 3_000); + private static final long READ_MILLIS = adjustTimeout(10_000); + + static { + // a series of checks to make sure this timeouts configuration is + // consistent and the timeouts do not overlap + + assert (TOLERANCE >= 0); + // context creation + assert (2 * CONNECT_MILLIS + TOLERANCE < READ_MILLIS); + // context creation immediately followed by search + assert (2 * CONNECT_MILLIS + READ_MILLIS + TOLERANCE < INFINITY_MILLIS); + } + + @Test + public void test() throws Exception { + List> futures = new ArrayList<>(); + ExecutorService executorService = Executors.newCachedThreadPool(); + + Hashtable env = new Hashtable<>(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put("com.sun.jndi.ldap.read.timeout", String.valueOf(READ_MILLIS)); + env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(CONNECT_MILLIS)); + env.put("com.sun.jndi.ldap.connect.pool", "true"); + env.put(Context.PROVIDER_URL, "ldap://example.com:1234"); + + try { + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + futures.add(executorService.submit(() -> { attemptConnect(env); return null; })); + } finally { + executorService.shutdown(); + } + int failedCount = 0; + for (var f : futures) { + try { + f.get(); + } catch (ExecutionException e) { + failedCount++; + e.getCause().printStackTrace(System.out); + } + } + if (failedCount > 0) + throw new RuntimeException(failedCount + " (sub)tests failed"); + } + + private static void attemptConnect(Hashtable env) throws Exception { + try { + LdapTimeoutTest.assertCompletion(CONNECT_MILLIS - 1000, + 2 * CONNECT_MILLIS + TOLERANCE, + () -> new InitialDirContext(env)); + } catch (RuntimeException e) { + String msg = e.getCause() == null ? e.getMessage() : e.getCause().getMessage(); + System.err.println("MSG RTE: " + msg); + // assertCompletion may wrap a CommunicationException in an RTE + assertTrue(msg != null && msg.contains("Network is unreachable")); + } catch (NamingException ex) { + String msg = ex.getCause() == null ? ex.getMessage() : ex.getCause().getMessage(); + System.err.println("MSG: " + msg); + assertTrue(msg != null && + (msg.contains("Network is unreachable") + || msg.contains("Timed out waiting for lock") + || msg.contains("Connect timed out") + || msg.contains("Timeout exceeded while waiting for a connection"))); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + +} + diff -Nru openjdk-17-17.0.2+8/test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java openjdk-17-17.0.3+7/test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java --- openjdk-17-17.0.2+8/test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/awt/FontClass/DrawStringWithInfiniteXform.java 2022-04-19 19:55:43.000000000 +0000 @@ -35,11 +35,12 @@ import java.util.TimerTask; public class DrawStringWithInfiniteXform { - Timer timer; - boolean done; + + volatile Timer timer; + volatile boolean done; + class ScheduleTask extends TimerTask { public void run() { - timer.cancel(); if (!done) { throw new RuntimeException("drawString with InfiniteXform transform takes long time"); @@ -48,7 +49,7 @@ } public DrawStringWithInfiniteXform() { timer = new Timer(); - timer.schedule(new ScheduleTask(), 10000); + timer.schedule(new ScheduleTask(), 20000); } public static void main(String [] args) { @@ -73,6 +74,7 @@ g2d.drawString("abc", 20, 20); } done = true; + timer.cancel(); System.out.println("Test passed"); } } diff -Nru openjdk-17-17.0.2+8/test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java openjdk-17-17.0.3+7/test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java --- openjdk-17-17.0.2+8/test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/awt/Window/WindowResizingOnDPIChanging/WindowResizingOnMovingToAnotherDisplay.java 2022-04-19 19:55:43.000000000 +0000 @@ -53,7 +53,7 @@ import javax.swing.SwingUtilities; /* @test - * @bug 8147440 8147016 + * @bug 8147440 8147016 8270874 * @summary HiDPI (Windows): Swing components have incorrect sizes after * changing display resolution * @run main/manual/othervm WindowResizingOnMovingToAnotherDisplay diff -Nru openjdk-17-17.0.2+8/test/jdk/java/io/File/GetXSpace.java openjdk-17-17.0.3+7/test/jdk/java/io/File/GetXSpace.java --- openjdk-17-17.0.2+8/test/jdk/java/io/File/GetXSpace.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/io/File/GetXSpace.java 2022-04-19 19:55:43.000000000 +0000 @@ -51,6 +51,7 @@ import static java.lang.System.err; import static java.lang.System.out; +@SuppressWarnings("removal") public class GetXSpace { private static SecurityManager [] sma = { null, new Allow(), new DenyFSA(), @@ -213,8 +214,9 @@ out.format(fmt, "df", s.total(), 0, s.free()); out.format(fmt, "getX", ts, fs, us); - // if the file system can dynamically change size, this check will fail - if (ts != s.total()) { + // If the file system can dynamically change size, this check will fail. + // This can happen on macOS for the /dev files system. + if (ts != s.total() && (!Platform.isOSX() || !s.name().equals("/dev"))) { long blockSize = 1; long numBlocks = 0; try { @@ -231,7 +233,6 @@ throw new RuntimeException(e); } - // On macOS, the number of 1024 byte blocks might be incorrectly // calculated by 'df' using integer division by 2 of the number of // 512 byte blocks, resulting in a size smaller than the actual diff -Nru openjdk-17-17.0.2+8/test/jdk/java/io/File/LastModifiedTest.java openjdk-17-17.0.3+7/test/jdk/java/io/File/LastModifiedTest.java --- openjdk-17-17.0.2+8/test/jdk/java/io/File/LastModifiedTest.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/io/File/LastModifiedTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021, Amazon 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 org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Instant; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; + +/** + * @test + * @library .. + * @run testng LastModifiedTest + * @summary Test to validate that java.nio.Files returns the same value + * as java.io.File + */ +public class LastModifiedTest { + + private static final Instant MILLISECOND_PRECISION = Instant.ofEpochMilli(1999L); + + @Test + public void verifyLastModifiedTime() throws IOException { + File tempFile = Files.createTempFile("MillisecondPrecisionTest", "txt").toFile(); + try { + tempFile.setLastModified(MILLISECOND_PRECISION.toEpochMilli()); + + long ioTimestamp = tempFile.lastModified(); + long nioTimestamp = Files.getLastModifiedTime(tempFile.toPath()).toMillis(); + + assertEquals(ioTimestamp, nioTimestamp); + } finally { + tempFile.delete(); + } + + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/java/lang/ProcessBuilder/Basic.java openjdk-17-17.0.3+7/test/jdk/java/lang/ProcessBuilder/Basic.java --- openjdk-17-17.0.2+8/test/jdk/java/lang/ProcessBuilder/Basic.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/lang/ProcessBuilder/Basic.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2022, 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,13 @@ * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 * 4947220 7018606 7034570 4244896 5049299 8003488 8054494 8058464 - * 8067796 8224905 8263729 8265173 + * 8067796 8224905 8263729 8265173 8272600 8231297 8282219 * @key intermittent * @summary Basic tests for Process and Environment Variable code * @modules java.base/java.lang:open * @library /test/lib - * @run main/othervm/timeout=300 -Djava.security.manager=allow Basic - * @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic + * @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic + * @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic * @author Martin Buchholz */ @@ -50,8 +50,8 @@ import static java.lang.ProcessBuilder.Redirect.*; import java.io.*; -import java.lang.reflect.Field; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.*; @@ -85,7 +85,7 @@ /** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a - * call to {@link System.nanoTime()}. + * call to {@link System#nanoTime()}. */ private static long millisElapsedSince(long startNanoTime) { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime); @@ -1870,6 +1870,8 @@ String[] envpOth = {"=ExitValue=3", "=C:=\\"}; if (Windows.is()) { envp = envpWin; + } else if (AIX.is()) { + envp = new String[] {"=ExitValue=3", "=C:=\\", "LIBPATH=" + libpath}; } else { envp = envpOth; } @@ -1918,6 +1920,9 @@ String[] envp; if (Windows.is()) { envp = envpWin; + } else if (AIX.is()) { + envp = new String[] {"LC_ALL=C\u0000\u0000", // Yuck! + "FO\u0000=B\u0000R", "LIBPATH=" + libpath}; } else { envp = envpOth; } @@ -2137,34 +2142,8 @@ final int cases = 4; for (int i = 0; i < cases; i++) { final int action = i; - List childArgs = new ArrayList<>(javaChildArgs); + List childArgs = getSleepArgs(); final ProcessBuilder pb = new ProcessBuilder(childArgs); - { - // Redirect any child VM error output away from the stream being tested - // and to the log file. For background see: - // 8231297: java/lang/ProcessBuilder/Basic.java test fails intermittently - // Destroying the process may, depending on the timing, cause some output - // from the child VM. - // This test requires the thread reading from the subprocess be blocked - // in the read from the subprocess; there should be no bytes to read. - // Modify the argument list shared with ProcessBuilder to redirect VM output. - assert (childArgs.get(1).equals("-XX:+DisplayVMOutputToStderr")) : "Expected arg 1 to be \"-XX:+DisplayVMOutputToStderr\""; - switch (action & 0x1) { - case 0: - childArgs.set(1, "-XX:+DisplayVMOutputToStderr"); - childArgs.add(2, "-Xlog:all=warning:stderr"); - pb.redirectError(INHERIT); - break; - case 1: - childArgs.set(1, "-XX:+DisplayVMOutputToStdout"); - childArgs.add(2, "-Xlog:all=warning:stdout"); - pb.redirectOutput(INHERIT); - break; - default: - throw new Error(); - } - } - childArgs.add("sleep"); final byte[] bytes = new byte[10]; final Process p = pb.start(); final CountDownLatch latch = new CountDownLatch(1); @@ -2237,9 +2216,10 @@ // our child) but not our grandchild (i.e. '/bin/sleep'). So // pay attention that the grandchild doesn't run too long to // avoid polluting the process space with useless processes. - // Running the grandchild for 60s should be more than enough. - final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" }; - final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" }; + // Running the grandchild for 59s should be more than enough. + // A unique (59s) time is needed to avoid killing other sleep processes. + final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 59)" }; + final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 59\")" }; final ProcessBuilder pb = new ProcessBuilder(cmd); final Process p = pb.start(); final InputStream stdout = p.getInputStream(); @@ -2441,8 +2421,7 @@ // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); long start = System.nanoTime(); if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) { @@ -2471,17 +2450,19 @@ // works as expected. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); long start = System.nanoTime(); - p.waitFor(10, TimeUnit.MILLISECONDS); - - long end = System.nanoTime(); - if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10)) - fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)"); - + if (p.waitFor(10, TimeUnit.MILLISECONDS)) { + var msg = "External sleep process terminated early: exitValue: %d, (%dns)%n" + .formatted(p.exitValue(), (System.nanoTime() - start)); + fail(msg); + } else { + long end = System.nanoTime(); + if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10)) + fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)"); + } p.destroy(); } catch (Throwable t) { unexpected(t); } @@ -2490,8 +2471,7 @@ // interrupt works as expected, if interrupted while waiting. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); final long start = System.nanoTime(); final CountDownLatch aboutToWaitFor = new CountDownLatch(1); @@ -2522,8 +2502,7 @@ // interrupt works as expected, if interrupted while waiting. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); final long start = System.nanoTime(); final CountDownLatch aboutToWaitFor = new CountDownLatch(1); @@ -2554,8 +2533,7 @@ // interrupt works as expected, if interrupted before waiting. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); final long start = System.nanoTime(); final CountDownLatch threadStarted = new CountDownLatch(1); @@ -2586,8 +2564,7 @@ // Check that Process.waitFor(timeout, null) throws NPE. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); THROWS(NullPointerException.class, () -> p.waitFor(10L, null)); @@ -2610,8 +2587,7 @@ // Check that default implementation of Process.waitFor(timeout, null) throws NPE. //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process proc = new ProcessBuilder(childArgs).start(); final DelegatingProcess p = new DelegatingProcess(proc); @@ -2637,24 +2613,75 @@ // Process.waitFor(long, TimeUnit) //---------------------------------------------------------------- try { - List childArgs = new ArrayList(javaChildArgs); - childArgs.add("sleep"); + List childArgs = getSleepArgs(); final Process proc = new ProcessBuilder(childArgs).start(); DelegatingProcess p = new DelegatingProcess(proc); long start = System.nanoTime(); - p.waitFor(1000, TimeUnit.MILLISECONDS); - - long end = System.nanoTime(); - if ((end - start) < 500000000) - fail("Test failed: waitFor didn't take long enough"); - + if (p.waitFor(1000, TimeUnit.MILLISECONDS)) { + var msg = "External sleep process terminated early: exitValue: %02x, (%dns)" + .formatted(p.exitValue(), (System.nanoTime() - start)); + fail(msg); + } else { + long end = System.nanoTime(); + if ((end - start) < 500000000) + fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)"); + } p.destroy(); p.waitFor(1000, TimeUnit.MILLISECONDS); } catch (Throwable t) { unexpected(t); } } + // Path to native executables, if any + private static final String TEST_NATIVEPATH = System.getProperty("test.nativepath"); + + // Path where "sleep" program may be found" or null + private static final Path SLEEP_PATH = initSleepPath(); + + /** + * Compute the Path to a sleep executable. + * @return a Path to sleep or BasicSleep(.exe) or null if none + */ + private static Path initSleepPath() { + if (Windows.is() && TEST_NATIVEPATH != null) { + // exeBasicSleep is equivalent to sleep on Unix + Path exePath = Path.of(TEST_NATIVEPATH).resolve("BasicSleep.exe"); + if (Files.isExecutable(exePath)) { + return exePath; + } + } + + List binPaths = List.of("/bin", "/usr/bin"); + for (String dir : binPaths) { + Path exePath = Path.of(dir).resolve("sleep"); + if (Files.isExecutable(exePath)) { + return exePath; + } + } + return null; + } + + /** + * Return the list of process arguments for a child to sleep 10 minutes (600 seconds). + * + * @return A list of process arguments to sleep 10 minutes. + */ + private static List getSleepArgs() { + List childArgs = null; + if (SLEEP_PATH != null) { + childArgs = List.of(SLEEP_PATH.toString(), "600"); + } else { + // Fallback to the JavaChild ; its 'sleep' command is for 10 minutes. + // The fallback the Java$Child is used if the test is run without building + // the BasicSleep native executable (for Windows). + childArgs = new ArrayList<>(javaChildArgs); + childArgs.add("sleep"); + System.out.println("Sleep not found, fallback to JavaChild: " + childArgs); + } + return childArgs; + } + static void closeStreams(Process p) { try { p.getOutputStream().close(); diff -Nru openjdk-17-17.0.2+8/test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c openjdk-17-17.0.3+7/test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c --- openjdk-17-17.0.2+8/test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, 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 +#include + +#ifdef _WIN32 + #include +#else + #include +#endif +/** + * Command line program to sleep at least given number of seconds. + * The behavior should equivalent to the Unix sleep command. + * Actual time sleeping may vary if interrupted, the remaining time + * returned from sleep has limited accuracy. + * + * Note: the file name prefix "exe" identifies the source should be built into BasicSleep(.exe). + */ +int main(int argc, char** argv) { + int seconds; + + if (argc < 2 || (seconds = atoi(argv[1])) < 0) { + fprintf(stderr, "usage: BasicSleep \n"); + exit(1); + } + +#ifdef _WIN32 + Sleep(seconds * 1000); +#else + while ((seconds = sleep(seconds)) > 0) { + // until no more to sleep + } +#endif +} diff -Nru openjdk-17-17.0.2+8/test/jdk/java/math/BigInteger/BitLengthOverflow.java openjdk-17-17.0.3+7/test/jdk/java/math/BigInteger/BitLengthOverflow.java --- openjdk-17-17.0.2+8/test/jdk/java/math/BigInteger/BitLengthOverflow.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/math/BigInteger/BitLengthOverflow.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, 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,28 +23,43 @@ /* * @test - * @bug 6910473 + * @bug 6910473 8272541 * @summary Test that bitLength() is not negative * @author Dmitry Nadezhin */ import java.math.BigInteger; +import java.util.function.Supplier; public class BitLengthOverflow { - - public static void main(String[] args) { + private static void test(Supplier s) { try { - BigInteger x = BigInteger.ONE.shiftLeft(Integer.MAX_VALUE); // x = pow(2,Integer.MAX_VALUE) - if (x.bitLength() != (1L << 31)) { - throw new RuntimeException("Incorrect bitLength() " + x.bitLength()); - } - System.out.println("Surprisingly passed with correct bitLength() " + x.bitLength()); + BigInteger x = s.get(); + System.out.println("Surprisingly passed with correct bitLength() " + + x.bitLength()); } catch (ArithmeticException e) { // expected - System.out.println("Overflow is reported by ArithmeticException, as expected"); + System.out.println("Overflow reported by ArithmeticException, as expected"); } catch (OutOfMemoryError e) { // possible System.err.println("BitLengthOverflow skipped: OutOfMemoryError"); System.err.println("Run jtreg with -javaoption:-Xmx8g"); } } + + public static void main(String[] args) { + test(() -> { + // x = pow(2,Integer.MAX_VALUE) + BigInteger x = BigInteger.ONE.shiftLeft(Integer.MAX_VALUE); + if (x.bitLength() != (1L << 31)) { + throw new RuntimeException("Incorrect bitLength() " + + x.bitLength()); + } + return x; + }); + test(() -> { + BigInteger a = BigInteger.ONE.shiftLeft(1073742825); + BigInteger b = BigInteger.ONE.shiftLeft(1073742825); + return a.multiply(b); + }); + } } diff -Nru openjdk-17-17.0.2+8/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java openjdk-17-17.0.3+7/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java --- openjdk-17-17.0.2+8/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/nio/channels/FileChannel/BlockDeviceSize.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, 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 * @bug 8054029 + * @requires (os.family == "linux") * @summary Block devices should not report size=0 on Linux */ diff -Nru openjdk-17-17.0.2+8/test/jdk/java/nio/file/Files/probeContentType/Basic.java openjdk-17-17.0.3+7/test/jdk/java/nio/file/Files/probeContentType/Basic.java --- openjdk-17-17.0.2+8/test/jdk/java/nio/file/Files/probeContentType/Basic.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/nio/file/Files/probeContentType/Basic.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, 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,7 +22,7 @@ */ /* @test - * @bug 4313887 8129632 8129633 8162624 8146215 8162745 + * @bug 4313887 8129632 8129633 8162624 8146215 8162745 8273655 8274171 * @summary Unit test for probeContentType method * @library ../.. * @build Basic SimpleFileTypeDetector @@ -31,6 +31,7 @@ import java.io.*; import java.nio.file.*; +import java.util.List; import java.util.stream.Stream; /** @@ -95,16 +96,12 @@ return 0; } - static int checkContentTypes(String[] extensions, String[] expectedTypes) + static int checkContentTypes(ExType[] exTypes) throws IOException { - if (extensions.length != expectedTypes.length) { - System.err.println("Parameter array lengths differ"); - return 1; - } - int failures = 0; - for (int i = 0; i < extensions.length; i++) { - String extension = extensions[i]; + for (int i = 0; i < exTypes.length; i++) { + String extension = exTypes[i].extension(); + List expectedTypes = exTypes[i].expectedTypes(); Path file = Files.createTempFile("foo", "." + extension); try { String type = Files.probeContentType(file); @@ -112,9 +109,9 @@ System.err.println("Content type of " + extension + " cannot be determined"); failures++; - } else if (!type.equals(expectedTypes[i])) { - System.err.printf("Content type: %s; expected: %s%n", - type, expectedTypes[i]); + } else if (!expectedTypes.contains(type)) { + System.err.printf("For extension %s we got content type: %s; expected: %s%n", + extension, type, expectedTypes); failures++; } } finally { @@ -155,27 +152,46 @@ Files.delete(file); } - // Verify that certain media extensions are mapped to the correct type. - String[] extensions = new String[]{ - "jpg", - "mp3", - "mp4", - "pdf", - "png", - "webm" + // Verify that certain extensions are mapped to the correct type. + var exTypes = new ExType[] { + new ExType("adoc", List.of("text/plain")), + new ExType("bz2", List.of("application/bz2", "application/x-bzip2", "application/x-bzip")), + new ExType("css", List.of("text/css")), + new ExType("csv", List.of("text/csv")), + new ExType("doc", List.of("application/msword")), + new ExType("docx", List.of("application/vnd.openxmlformats-officedocument.wordprocessingml.document")), + new ExType("gz", List.of("application/gzip", "application/x-gzip")), + new ExType("jar", List.of("application/java-archive", "application/x-java-archive")), + new ExType("jpg", List.of("image/jpeg")), + new ExType("js", List.of("text/javascript", "application/javascript")), + new ExType("json", List.of("application/json")), + new ExType("markdown", List.of("text/markdown")), + new ExType("md", List.of("text/markdown", "application/x-genesis-rom")), + new ExType("mp3", List.of("audio/mpeg")), + new ExType("mp4", List.of("video/mp4")), + new ExType("odp", List.of("application/vnd.oasis.opendocument.presentation")), + new ExType("ods", List.of("application/vnd.oasis.opendocument.spreadsheet")), + new ExType("odt", List.of("application/vnd.oasis.opendocument.text")), + new ExType("pdf", List.of("application/pdf")), + new ExType("php", List.of("text/plain", "text/php", "application/x-php")), + new ExType("png", List.of("image/png")), + new ExType("ppt", List.of("application/vnd.ms-powerpoint")), + new ExType("pptx",List.of("application/vnd.openxmlformats-officedocument.presentationml.presentation")), + new ExType("py", List.of("text/plain", "text/x-python", "text/x-python-script")), + new ExType("rar", List.of("application/rar", "application/vnd.rar", "application/x-rar")), + new ExType("rtf", List.of("application/rtf", "text/rtf")), + new ExType("webm", List.of("video/webm")), + new ExType("webp", List.of("image/webp")), + new ExType("xls", List.of("application/vnd.ms-excel")), + new ExType("xlsx", List.of("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")), + new ExType("7z", List.of("application/x-7z-compressed")), }; - String[] expectedTypes = new String[] { - "image/jpeg", - "audio/mpeg", - "video/mp4", - "application/pdf", - "image/png", - "video/webm" - }; - failures += checkContentTypes(extensions, expectedTypes); + failures += checkContentTypes(exTypes); if (failures > 0) { throw new RuntimeException("Test failed!"); } } + + record ExType(String extension, List expectedTypes) { } } diff -Nru openjdk-17-17.0.2+8/test/jdk/java/security/testlibrary/CertificateBuilder.java openjdk-17-17.0.3+7/test/jdk/java/security/testlibrary/CertificateBuilder.java --- openjdk-17-17.0.2+8/test/jdk/java/security/testlibrary/CertificateBuilder.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/security/testlibrary/CertificateBuilder.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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,6 +38,7 @@ import sun.security.util.DerOutputStream; import sun.security.util.DerValue; import sun.security.util.ObjectIdentifier; +import sun.security.util.SignatureUtil; import sun.security.x509.AccessDescription; import sun.security.x509.AlgorithmId; import sun.security.x509.AuthorityInfoAccessExtension; @@ -364,8 +365,7 @@ throws IOException, CertificateException, NoSuchAlgorithmException { // TODO: add some basic checks (key usage, basic constraints maybe) - AlgorithmId signAlg = AlgorithmId.get(algName); - byte[] encodedCert = encodeTopLevel(issuerCert, issuerKey, signAlg); + byte[] encodedCert = encodeTopLevel(issuerCert, issuerKey, algName); ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert); return (X509Certificate)factory.generateCertificate(bais); } @@ -392,18 +392,24 @@ * @throws IOException if an encoding error occurs. */ private byte[] encodeTopLevel(X509Certificate issuerCert, - PrivateKey issuerKey, AlgorithmId signAlg) - throws CertificateException, IOException { + PrivateKey issuerKey, String algName) + throws CertificateException, IOException, NoSuchAlgorithmException { + + AlgorithmId signAlg = AlgorithmId.get(algName); DerOutputStream outerSeq = new DerOutputStream(); DerOutputStream topLevelItems = new DerOutputStream(); - tbsCertBytes = encodeTbsCert(issuerCert, signAlg); - topLevelItems.write(tbsCertBytes); try { - signatureBytes = signCert(issuerKey, signAlg); + Signature sig = SignatureUtil.fromKey(signAlg.getName(), issuerKey, (Provider)null); + // Rewrite signAlg, RSASSA-PSS needs some parameters. + signAlg = SignatureUtil.fromSignature(sig, issuerKey); + tbsCertBytes = encodeTbsCert(issuerCert, signAlg); + sig.update(tbsCertBytes); + signatureBytes = sig.sign(); } catch (GeneralSecurityException ge) { throw new CertificateException(ge); } + topLevelItems.write(tbsCertBytes); signAlg.derEncode(topLevelItems); topLevelItems.putBitString(signatureBytes); outerSeq.write(DerValue.tag_Sequence, topLevelItems); @@ -518,22 +524,4 @@ (byte)3), extSequence); } - /** - * Digitally sign the X.509 certificate. - * - * @param issuerKey The private key of the issuing authority - * @param signAlg The signature algorithm object - * - * @return The digital signature bytes. - * - * @throws GeneralSecurityException If any errors occur during the - * digital signature process. - */ - private byte[] signCert(PrivateKey issuerKey, AlgorithmId signAlg) - throws GeneralSecurityException { - Signature sig = Signature.getInstance(signAlg.getName()); - sig.initSign(issuerKey); - sig.update(tbsCertBytes); - return sig.sign(); - } - } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/java/security/testlibrary/SimpleOCSPServer.java openjdk-17-17.0.3+7/test/jdk/java/security/testlibrary/SimpleOCSPServer.java --- openjdk-17-17.0.2+8/test/jdk/java/security/testlibrary/SimpleOCSPServer.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/security/testlibrary/SimpleOCSPServer.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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,8 +174,7 @@ issuerAlias + " not found"); } } - - sigAlgId = AlgorithmId.get("Sha256withRSA"); + sigAlgId = AlgorithmId.get(SignatureUtil.getDefaultSigAlgForKey(signerKey)); respId = new ResponderId(signerCert.getSubjectX500Principal()); listenAddress = addr; listenPort = port; @@ -1348,13 +1347,14 @@ basicORItemStream.write(tbsResponseBytes); try { - sigAlgId.derEncode(basicORItemStream); - // Create the signature - Signature sig = Signature.getInstance(sigAlgId.getName()); - sig.initSign(signerKey); + Signature sig = SignatureUtil.fromKey( + sigAlgId.getName(), signerKey, (Provider)null); sig.update(tbsResponseBytes); signature = sig.sign(); + // Rewrite signAlg, RSASSA-PSS needs some parameters. + sigAlgId = SignatureUtil.fromSignature(sig, signerKey); + sigAlgId.derEncode(basicORItemStream); basicORItemStream.putBitString(signature); } catch (GeneralSecurityException exc) { err(exc); diff -Nru openjdk-17-17.0.2+8/test/jdk/java/text/Format/DateFormat/DateFormatTest.java openjdk-17-17.0.3+7/test/jdk/java/text/Format/DateFormat/DateFormatTest.java --- openjdk-17-17.0.2+8/test/jdk/java/text/Format/DateFormat/DateFormatTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/text/Format/DateFormat/DateFormatTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2021, 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 * @bug 4052223 4089987 4469904 4326988 4486735 8008577 8045998 8140571 - * 8216969 + * 8190748 8216969 * @summary test DateFormat and SimpleDateFormat. * @library /java/text/testlib * @modules jdk.localedata @@ -342,7 +342,7 @@ // Test pattern with runs things together public void TestRunTogetherPattern985() { - String format = "yyyyMMddHHmmssSSS"; + String format = "yyyyMMddHHmmssSSSzzzz"; String now, then; SimpleDateFormat formatter = new SimpleDateFormat(format); diff -Nru openjdk-17-17.0.2+8/test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java openjdk-17-17.0.3+7/test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java --- openjdk-17-17.0.2+8/test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/text/Format/DateFormat/NonGregorianFormatTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2021, 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,12 +23,13 @@ /* * @test - * @bug 4833268 6253991 8008577 + * @bug 4833268 6253991 8008577 8190748 * @summary Test formatting and parsing with non-Gregorian calendars * @modules jdk.localedata * @run main/othervm -Djava.locale.providers=COMPAT,SPI NonGregorianFormatTest */ +import java.time.ZoneId; import java.util.*; import java.text.*; import static java.util.Calendar.*; @@ -160,10 +161,15 @@ private static void testRoundTrip(DateFormat df, Date orig) { try { + var defZone = ZoneId.systemDefault(); + if (defZone.getRules().getTransition(orig.toInstant().atZone(defZone).toLocalDateTime()) != null) { + System.out.println("At the offset transition. Round trip test skipped."); + return; + } String s = df.format(orig); Date parsed = df.parse(s); if (!orig.equals(parsed)) { - error("testRoundTrip: bad date: origianl: '%s', parsed '%s'%n", orig, parsed); + error("testRoundTrip: bad date: original: '%s', parsed '%s'%n", orig, parsed); } } catch (Exception e) { error("Unexpected exception: %s%n", e); diff -Nru openjdk-17-17.0.2+8/test/jdk/java/util/Currency/ValidateISO4217.java openjdk-17-17.0.3+7/test/jdk/java/util/Currency/ValidateISO4217.java --- openjdk-17-17.0.2+8/test/jdk/java/util/Currency/ValidateISO4217.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/util/Currency/ValidateISO4217.java 2022-04-19 19:55:43.000000000 +0000 @@ -24,7 +24,7 @@ * @test * @bug 4691089 4819436 4942982 5104960 6544471 6627549 7066203 7195759 * 8039317 8074350 8074351 8145952 8187946 8193552 8202026 8204269 - * 8208746 8209775 + * 8208746 8209775 8274658 * @summary Validate ISO 4217 data for Currency class. * @modules java.base/java.util:open * jdk.localedata @@ -94,11 +94,11 @@ {"MF", "EUR", "978", "2"}, // Saint Martin }; - /* Codes that are obsolete, do not have related country */ + /* Codes that are obsolete, do not have related country, extra currency */ static final String otherCodes = "ADP-AFA-ATS-AYM-AZM-BEF-BGL-BOV-BYB-BYR-CHE-CHW-CLF-COU-CUC-CYP-" + "DEM-EEK-ESP-FIM-FRF-GHC-GRD-GWP-IEP-ITL-LTL-LUF-LVL-MGF-MRO-MTL-MXV-MZM-NLG-" - + "PTE-ROL-RUR-SDD-SIT-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-" + + "PTE-ROL-RUR-SDD-SIT-SKK-SRG-STD-TMM-TPE-TRL-VEF-UYI-USN-USS-VEB-VED-" + "XAG-XAU-XBA-XBB-XBC-XBD-XDR-XFO-XFU-XPD-XPT-XSU-XTS-XUA-XXX-" + "YUM-ZMK-ZWD-ZWN-ZWR"; diff -Nru openjdk-17-17.0.2+8/test/jdk/java/util/Currency/tablea1.txt openjdk-17-17.0.3+7/test/jdk/java/util/Currency/tablea1.txt --- openjdk-17-17.0.2+8/test/jdk/java/util/Currency/tablea1.txt 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/java/util/Currency/tablea1.txt 2022-04-19 19:55:43.000000000 +0000 @@ -1,12 +1,12 @@ # # -# Amendments up until ISO 4217 AMENDMENT NUMBER 169 -# (As of 27 Aug 2018) +# Amendments up until ISO 4217 AMENDMENT NUMBER 170 +# (As of 1 Oct 2021) # # Version FILEVERSION=3 -DATAVERSION=169 +DATAVERSION=170 # ISO 4217 currency data AF AFN 971 2 diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java openjdk-17-17.0.3+7/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java --- openjdk-17-17.0.2+8/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/net/ssl/Stapling/HttpsUrlConnClient.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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,8 @@ * @summary OCSP Stapling for TLS * @library ../../../../java/security/testlibrary * @build CertificateBuilder SimpleOCSPServer - * @run main/othervm HttpsUrlConnClient + * @run main/othervm HttpsUrlConnClient RSA SHA256withRSA + * @run main/othervm HttpsUrlConnClient RSASSA-PSS RSASSA-PSS */ import java.io.*; @@ -60,7 +61,6 @@ import sun.security.testlibrary.SimpleOCSPServer; import sun.security.testlibrary.CertificateBuilder; -import sun.security.validator.ValidatorException; public class HttpsUrlConnClient { @@ -73,6 +73,9 @@ static final byte[] LINESEP = { 10 }; static final Base64.Encoder B64E = Base64.getMimeEncoder(64, LINESEP); + static String SIGALG; + static String KEYALG; + // Turn on TLS debugging static boolean debug = true; @@ -137,6 +140,9 @@ System.setProperty("javax.net.ssl.trustStore", ""); System.setProperty("javax.net.ssl.trustStorePassword", ""); + KEYALG = args[0]; + SIGALG = args[1]; + // Create the PKI we will use for the test and start the OCSP servers createPKI(); utcDateFmt.setTimeZone(TimeZone.getTimeZone("GMT")); @@ -514,7 +520,7 @@ */ private static void createPKI() throws Exception { CertificateBuilder cbld = new CertificateBuilder(); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KEYALG); keyGen.initialize(2048); KeyStore.Builder keyStoreBuilder = KeyStore.Builder.newInstance("PKCS12", null, @@ -540,7 +546,7 @@ addCommonCAExts(cbld); // Make our Root CA Cert! X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), - "SHA256withRSA"); + SIGALG); log("Root CA Created:\n" + certInfo(rootCert)); // Now build a keystore and add the keys and cert @@ -582,7 +588,7 @@ cbld.addAIAExt(Collections.singletonList(rootRespURI)); // Make our Intermediate CA Cert! X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), - "SHA256withRSA"); + SIGALG); log("Intermediate CA Created:\n" + certInfo(intCaCert)); // Provide intermediate CA cert revocation info to the Root CA @@ -644,7 +650,7 @@ cbld.addAIAExt(Collections.singletonList(intCaRespURI)); // Make our SSL Server Cert! X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), - "SHA256withRSA"); + SIGALG); log("SSL Certificate Created:\n" + certInfo(sslCert)); // Provide SSL server cert revocation info to the Intermeidate CA diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java openjdk-17-17.0.3+7/test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java --- openjdk-17-17.0.2+8/test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/swing/JFileChooser/FileSystemView/ShellFolderStackOverflow.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, 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 8277299 + * @requires (os.family == "windows") + * @summary STACK_OVERFLOW in Java_sun_awt_shell_Win32ShellFolder2_getIconBits + * @run main/othervm -Xss320k ShellFolderStackOverflow + */ +import javax.swing.UIManager; + +public class ShellFolderStackOverflow { + public static void main(final String... args) throws Exception { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + // With default stack size for 32-bit VM next call will cause VM crash + UIManager.getIcon("Tree.openIcon"); + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java openjdk-17-17.0.3+7/test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java --- openjdk-17-17.0.2+8/test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/swing/UIDefaults/6302464/bug6302464.java 2022-04-19 19:55:43.000000000 +0000 @@ -119,7 +119,7 @@ setMetalLookAndFeel(); boolean isMacOSX14 = false; - boolean isMacOSXBigSur = false; + boolean isMacOSXBigSurOrAbove = false; if (System.getProperty("os.name").contains("OS X")) { String version = System.getProperty("os.version", ""); if (version.startsWith("10.")) { @@ -133,11 +133,17 @@ isMacOSX14 = (v >= 14); } catch (NumberFormatException e) { } - } else if (version.startsWith("11.")) { - isMacOSXBigSur = true; + } else { + String majorVersion = version.substring(0, version.indexOf(".")); + System.out.println(majorVersion); + try { + int ver = Integer.parseInt(majorVersion); + isMacOSXBigSurOrAbove = (ver >= 11); + } catch (NumberFormatException e){ + } } } - if (!isMacOSX14 && !isMacOSXBigSur) { + if (!isMacOSX14 && !isMacOSXBigSurOrAbove) { HashSet colorsAAOff = getAntialiasedColors(VALUE_TEXT_ANTIALIAS_OFF, 100); if (colorsAAOff.size() > 2) { diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java openjdk-17-17.0.3+7/test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java --- openjdk-17-17.0.2+8/test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/swing/text/FlowView/6318524/bug6318524.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2005, 2021, 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.FontMetrics; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import javax.imageio.ImageIO; +import javax.swing.JFrame; +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.Document; +import javax.swing.text.Position; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; + +import static java.awt.image.BufferedImage.TYPE_INT_RGB; + +/* + * @test + * @bug 6318524 8239502 + * @summary Tests that children of ParagraphView do not mess up their parents + * @run main bug6318524 + */ +/* + * Test parameters: + * -show: Show frame for visual inspection + * -save: Save the start image after the first paragraph is justified, + * and the last image before it's checked that the first paragraph + * remains justified + * -saveAll: Save images for all the intermediate steps + */ +public class bug6318524 { + private static final String LONG_WORD = "consequences"; + private static final String TEXT = "Justified: " + + LONG_WORD + " " + LONG_WORD; + private static final int REPEAT_COUNT = 18; + + private static JTextPane textPane; + private static Dimension bounds; + + private static int step = 0; + + private static Shape firstLineEndsAt; + + public static void main(String[] args) throws Throwable { + List argList = Arrays.asList(args); + + // Show frame for visual inspection + final boolean showFrame = argList.contains("-show"); + // Save images for all the intermediate steps + final boolean saveAllImages = argList.contains("-saveAll"); + // Save the start and last image only + final boolean saveImage = saveAllImages || argList.contains("-save"); + + SwingUtilities.invokeAndWait(() -> { + createUI(showFrame); + paintToImage(step++, saveAllImages); + makeLineJustified(); + paintToImage(step++, saveImage); + + firstLineEndsAt = getEndOfFirstLine(); + + moveCursorToStart(); + pressEnter(saveAllImages); + + paintToImage(step++, saveImage); + checkLineJustified(); + }); + } + + private static void createUI(boolean showFrame) { + textPane = new JTextPane(); + textPane.setText(TEXT); + + FontMetrics fm = textPane.getFontMetrics(textPane.getFont()); + int textWidth = fm.stringWidth(LONG_WORD); + int textHeight = fm.getHeight(); + bounds = new Dimension(2 * textWidth, + (REPEAT_COUNT + 3) * textHeight); + textPane.setPreferredSize(bounds); + textPane.setSize(bounds); + + if (showFrame) { + JFrame frame = new JFrame("bug6318524"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + frame.getContentPane().add(textPane); + + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + } + + private static void makeLineJustified() { + SimpleAttributeSet sas = new SimpleAttributeSet(); + StyleConstants.setAlignment(sas, StyleConstants.ALIGN_JUSTIFIED); + textPane.setParagraphAttributes(sas, false); + } + + private static void moveCursorToStart() { + // Move cursor to the beginning + Caret caret = textPane.getCaret(); + caret.setDot(0); + } + + private static void pressEnter(boolean saveImages) { + Document doc = textPane.getDocument(); + try { + for (int i = 0; i < REPEAT_COUNT; i++) { + // Add a new paragraph at the beginning + doc.insertString(0, "\n", null); + // Paint the textPane after each change + paintToImage(step++, saveImages); + } + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } + + private static void checkLineJustified() { + Shape newPosition = getEndOfFirstLine(); + if (((Rectangle) firstLineEndsAt).x != ((Rectangle) newPosition).x) { + System.err.println("Old: " + firstLineEndsAt); + System.err.println("New: " + newPosition); + throw new RuntimeException("The first line of the paragraph is not justified"); + } + } + + private static Shape getEndOfFirstLine() { + try { + final View rootView = textPane.getUI().getRootView(textPane); + final View boxView = rootView.getView(0); + final View paragraphView = boxView.getView(boxView.getViewCount() - 1); + assert paragraphView.getViewCount() == 2; + final View rowView = paragraphView.getView(0); + return rowView.getView(0) + .modelToView(rowView.getEndOffset() - 1, + textPane.getBounds(), + Position.Bias.Backward); + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } + + private static void paintToImage(final int step, boolean saveImage) { + BufferedImage im = new BufferedImage(bounds.width, bounds.height, + TYPE_INT_RGB); + Graphics g = im.getGraphics(); + textPane.paint(g); + g.dispose(); + if (saveImage) { + saveImage(im, String.format("%02d.png", step)); + } + } + + private static void saveImage(BufferedImage image, String fileName) { + try { + ImageIO.write(image, "png", new File(fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java openjdk-17-17.0.3+7/test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java --- openjdk-17-17.0.2+8/test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/swing/text/ParagraphView/6364882/bug6364882.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2006, 2021, 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.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.text.AbstractDocument; +import javax.swing.text.BadLocationException; +import javax.swing.text.Position; +import javax.swing.text.View; +import javax.swing.text.html.HTMLEditorKit; + +import static java.awt.image.BufferedImage.TYPE_INT_RGB; + +/* + * @test + * @bug 6364882 8273634 + * @summary tests if broken and last lines in paragraph are not justified + * @run main bug6364882 + */ +public class bug6364882 { + private static final String TEXT = + "

    " + + "should be justified should be justified should be justified " + + "should be justified should be justified should be justified " + + "should be justified should be justified should be justified " + + "should be justified should be justified should be justified " + + "should be justified should be justified should be justified " + + "should be justified should be justified should be justified " + + "
    " + + "should not be justified
    " + + "should not be justified" + + ""; + + private static final int WIDTH = 580; + private static final int HEIGHT = 300; + + public static final String IMAGE_FILENAME = "editorPane.png"; + + private static JEditorPane editorPane; + + private static volatile List errors; + + public static void main(String[] args) throws Exception { + List argList = Arrays.asList(args); + // Show frame for visual inspection + final boolean showFrame = argList.contains("-show"); + // Save the rendered image even if the test passes + // If the test fails, the image is always saved + final boolean saveImage = argList.contains("-save"); + + SwingUtilities.invokeAndWait(() -> { + createUI(showFrame); + + BufferedImage image = paintToImage(); + + boolean exceptionThrown = false; + try { + errors = checkJustification(); + } catch (Throwable t) { + exceptionThrown = true; + throw t; + } finally { + if (exceptionThrown || errors.size() > 0 || saveImage) { + saveImage(image); + dumpViews(); + } + } + }); + + if (errors != null && errors.size() > 0) { + String message = "Test failed: " + errors.size() + " error(s)"; + System.err.println(message); + for (Error e : errors) { + e.printStackTrace(); + } + throw new RuntimeException(message + " - " + errors.get(0).getMessage()); + } + + System.out.println("Test passed"); + } + + private static void createUI(boolean showFrame) { + editorPane = new JEditorPane(); + editorPane.setEditorKit(new HTMLEditorKit()); + ((AbstractDocument) editorPane.getDocument()).setAsynchronousLoadPriority(-1); + editorPane.setText(TEXT); + + editorPane.setSize(WIDTH, HEIGHT); + + if (showFrame) { + JFrame frame = new JFrame("bug6364882"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + frame.getContentPane().add(editorPane); + + frame.setSize(WIDTH, HEIGHT); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + } + + private static List checkJustification() { + final List errors = new ArrayList<>(15); + try { + final View rootView = editorPane.getUI().getRootView(editorPane); + final View blockView = rootView.getView(0); + assert blockView.getViewCount() == 2 + : "blockView doesn't have 2 child views"; + final View bodyView = blockView.getView(1); + final View paragraphView = bodyView.getView(0); + + final int rowCount = paragraphView.getViewCount(); + if (rowCount < 4) { + errors.add(new Error("Less than 4 lines of text: no justified lines")); + return errors; + } + + final Rectangle bounds = editorPane.getBounds(); + final int rightMargin = bounds.width - 15; + + // First lines should be justified + int lineNo = 0; + final int oneX = getEndOfLineX(paragraphView.getView(lineNo++), bounds); + if (oneX < rightMargin) { + errors.add(new Error("Text is not justified at line " + lineNo + ": " + + oneX + " < " + rightMargin)); + } + // Justified lines should have the same width + while (lineNo < rowCount - 3) { + int lineX = getEndOfLineX(paragraphView.getView(lineNo++), + bounds); + if (oneX != lineX) { + errors.add(new Error("Text is not justified at line " + lineNo + + ": " + oneX + " != " + lineX)); + } + } + + // The last line of the wrapped text, before the first
    , + // should not be justified + final int twoX = getEndOfLineX(paragraphView.getView(lineNo++), bounds); + if (oneX == twoX) { + errors.add(new Error("Line " + lineNo + " is justified: " + + oneX + " vs " + twoX)); + } + if (twoX > rightMargin) { + errors.add(new Error("Line " + lineNo + " is justified: " + + twoX + " > " + rightMargin)); + } + + // The next two lines are created by line break
    + // They should not be justified and should be of the same width + final int threeX = getEndOfLineX(paragraphView.getView(lineNo++), bounds); + if (oneX == threeX) { + errors.add(new Error("Line " + lineNo + " is justified: " + + oneX + " == " + threeX)); + } + if (threeX > bounds.width / 2) { + errors.add(new Error("Line " + lineNo + " is justified: " + + threeX + " > " + (bounds.width / 2))); + } + + final int lastX = getEndOfLineX(paragraphView.getView(lineNo), bounds); + if (threeX != lastX) { + errors.add(new Error("Line " + lineNo + " and " + (lineNo + 1) + + " have different width: " + + threeX + " != " + lastX)); + } + + return errors; + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } + + private static int getEndOfLineX(final View rowView, + final Rectangle bounds) + throws BadLocationException { + final View inlineView = rowView.getView(0); + Shape loc = inlineView.modelToView(inlineView.getEndOffset() - 1, + bounds, + Position.Bias.Backward); + return loc instanceof Rectangle + ? ((Rectangle) loc).x + : loc.getBounds().x; + } + + private static BufferedImage paintToImage() { + Dimension bounds = editorPane.getSize(); + BufferedImage im = new BufferedImage(bounds.width, bounds.height, + TYPE_INT_RGB); + Graphics g = im.getGraphics(); + editorPane.paint(g); + g.dispose(); + return im; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", new File(IMAGE_FILENAME)); + } catch (IOException e) { + // Don't propagate the exception + e.printStackTrace(); + } + } + + private static void dumpViews() { + final View view = editorPane.getUI().getRootView(editorPane); + dumpViews(view, ""); + } + + private static void dumpViews(final View view, final String indent) { + System.err.println(indent + view.getClass().getName() + ": " + + view.getStartOffset() + ", " + view.getEndOffset() + + "; span: " + view.getPreferredSpan(View.X_AXIS)); + final String nestedIndent = indent + " "; + for (int i = 0; i < view.getViewCount(); i++) { + dumpViews(view.getView(i), nestedIndent); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java openjdk-17-17.0.3+7/test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java --- openjdk-17-17.0.2+8/test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/javax/xml/jaxp/XPath/InvalidXPath.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, 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 + * 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 8284548 + * @summary Test whether the expected exception is thrown when + * trying to compile an invalid XPath expression. + * @run main InvalidXPath + */ + +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +public class InvalidXPath { + + public static void main(String... args) { + // define an invalid XPath expression + final String invalidXPath = ">>"; + + // expect XPathExpressionException when the invalid XPath expression is compiled + try { + XPathFactory.newInstance().newXPath().compile(invalidXPath); + } catch (XPathExpressionException e) { + System.out.println("Caught expected exception: " + e.getClass().getName() + + "(" + e.getMessage() + ")."); + } catch (Exception e) { + System.out.println("Caught unexpected exception: " + e.getClass().getName() + + "(" + e.getMessage() + ")!"); + throw e; + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/editpad/EditPadTest.java openjdk-17-17.0.3+7/test/jdk/jdk/editpad/EditPadTest.java --- openjdk-17-17.0.2+8/test/jdk/jdk/editpad/EditPadTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/editpad/EditPadTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -23,6 +23,7 @@ /* * @test + * @key headful * @bug 8167636 8167639 8168972 * @summary Testing built-in editor. * @modules java.desktop/java.awt diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/jline/AbstractWindowsTerminalTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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,11 +58,6 @@ }; var t = new AbstractWindowsTerminal(out, "test", "vt100", null, -1, false, SignalHandler.SIG_DFL, isWrapper) { @Override - protected int getConsoleOutputCP() { - throw new UnsupportedOperationException("unexpected."); - } - - @Override protected int getConsoleMode() { return -1; } diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/jline/KeyConversionTest.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/jline/KeyConversionTest.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/jline/KeyConversionTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/jline/KeyConversionTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ import java.nio.charset.Charset; import jdk.internal.org.jline.terminal.Size; +import jdk.internal.org.jline.terminal.Terminal.SignalHandler; import jdk.internal.org.jline.terminal.impl.AbstractWindowsTerminal; public class KeyConversionTest { @@ -58,11 +59,7 @@ void checkKeyConversion(KeyEvent event, String expected) throws IOException { StringBuilder result = new StringBuilder(); new AbstractWindowsTerminal(new StringWriter(), "", "windows", Charset.forName("UTF-8"), - 0, true, null, in -> in) { - @Override - protected int getConsoleOutputCP() { - throw new UnsupportedOperationException("Not supported yet."); - } + 0, true, SignalHandler.SIG_DFL, in -> in) { @Override protected int getConsoleMode() { return 0; diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -54,7 +54,7 @@ // container include the Java test class to be run along with the // resource to be examined and expected result. - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { int numCpus = CPUSetsReader.getNumCpus(); diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -52,7 +52,7 @@ // container include the Java test class to be run along with the // resource to be examined and expected result. - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { testMemoryLimit("200m"); testMemoryLimit("1g"); diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestGetFreeSwapSpaceSize.java 2022-04-19 19:55:43.000000000 +0000 @@ -43,7 +43,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { testGetFreeSwapSpaceSize( diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestPidsLimit.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021 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 + * 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 + * @key cgroups + * @summary Test JDK Metrics class when running inside a docker container with limited pids + * @bug 8266490 + * @requires docker.support + * @library /test/lib + * @build TestPidsLimit + * @run driver TestPidsLimit + */ +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.containers.docker.Common; +import jdk.test.lib.containers.docker.DockerRunOptions; +import jdk.test.lib.containers.docker.DockerTestUtils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Asserts; +import jdk.test.lib.Container; + +public class TestPidsLimit { + private static final String imageName = Common.imageName("pids"); + private static final boolean IS_PODMAN = Container.ENGINE_COMMAND.contains("podman"); + private static final int UNLIMITED_PIDS_PODMAN = 0; + private static final int UNLIMITED_PIDS_DOCKER = -1; + + public static void main(String[] args) throws Exception { + if (!DockerTestUtils.canTestDocker()) { + return; + } + + DockerTestUtils.buildJdkContainerImage(imageName); + + try { + testPidsLimit("1000"); + testPidsLimit("2000"); + testPidsLimit("Unlimited"); + } finally { + if (!DockerTestUtils.RETAIN_IMAGE_AFTER_TEST) { + DockerTestUtils.removeDockerImage(imageName); + } + } + } + + private static void checkResult(List lines, String lineMarker, String expectedValue) { + boolean lineMarkerFound = false; + + for (String line : lines) { + if (line.contains("WARNING: Your kernel does not support pids limit capabilities")) { + System.out.println("Docker pids limitation seems not to work, avoiding check"); + return; + } + + if (line.contains(lineMarker)) { + lineMarkerFound = true; + String[] parts = line.split(":"); + System.out.println("DEBUG: line = " + line); + System.out.println("DEBUG: parts.length = " + parts.length); + + Asserts.assertEquals(parts.length, 2); + String actual = parts[1].replaceAll("\\s",""); + // Unlimited pids leads on some setups not to "max" in the output, but to a high number + if (expectedValue.equals("Unlimited")) { + if (actual.equals("Unlimited")) { + System.out.println("Found expected value for unlimited pids."); + } else { + try { + int ai = Integer.parseInt(actual); + if (ai > 20000) { + System.out.println("Limit value " + ai + " got accepted as unlimited, log line was " + line); + } else { + throw new RuntimeException("Limit value " + ai + " is not accepted as unlimited, log line was " + line); + } + } catch (NumberFormatException ex) { + throw new RuntimeException("Could not convert " + actual + " to an integer, log line was " + line); + } + } + } else { + Asserts.assertEquals(actual, expectedValue); + } + break; + } + } + Asserts.assertTrue(lineMarkerFound); + } + + private static void testPidsLimit(String pidsLimit) throws Exception { + Common.logNewTestCase("testPidsLimit (limit: " + pidsLimit + ")"); + DockerRunOptions opts = Common.newOptsShowSettings(imageName); + if (pidsLimit.equals("Unlimited")) { + int unlimited = IS_PODMAN ? UNLIMITED_PIDS_PODMAN : UNLIMITED_PIDS_DOCKER; + opts.addDockerOpts("--pids-limit=" + unlimited); + } else { + opts.addDockerOpts("--pids-limit="+pidsLimit); + } + + OutputAnalyzer out = DockerTestUtils.dockerRunJava(opts); + out.shouldHaveExitValue(0); + // some docker enviroments do not have the pids limit capabilities + String sdr = out.getOutput(); + if (sdr.contains("WARNING: Your kernel does not support pids limit capabilities")) { + System.out.println("Docker pids limitation seems not to work, avoiding check"); + } else { + List lines = new ArrayList<>(); + sdr.lines().forEach(s -> lines.add(s)); + checkResult(lines, "Maximum Processes Limit: ", pidsLimit); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestSystemMetrics.java 2022-04-19 19:55:43.000000000 +0000 @@ -45,7 +45,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { Common.logNewTestCase("Test SystemMetrics"); diff -Nru openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java --- openjdk-17-17.0.2+8/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/jdk/internal/platform/docker/TestUseContainerSupport.java 2022-04-19 19:55:43.000000000 +0000 @@ -44,7 +44,7 @@ return; } - DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); + DockerTestUtils.buildJdkContainerImage(imageName); try { testUseContainerSupport(true); diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java openjdk-17-17.0.3+7/test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java --- openjdk-17-17.0.2+8/test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/java2d/cmm/ColorConvertOp/MTPerLineTransformValidation.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,146 @@ +/* + * Copyright Amazon.com Inc. 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.image.BufferedImage; +import java.awt.image.ColorConvertOp; + +/** + * @test + * @bug 8273972 + * @summary Verifies that ColorConvertOp works fine if shared between threads + * @run main/othervm/timeout=600 MTTransformValidation + */ +public final class MTPerLineTransformValidation { + + private volatile static BufferedImage[] lines; + + public static final int SIZE = 255; + private static volatile boolean failed = false; + + private static final int[] spaces = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, + ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + private static final int[] types = new int[]{ + BufferedImage.TYPE_INT_RGB, BufferedImage.TYPE_INT_ARGB, + BufferedImage.TYPE_INT_ARGB_PRE, BufferedImage.TYPE_INT_BGR, + BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_4BYTE_ABGR, + BufferedImage.TYPE_4BYTE_ABGR_PRE, + BufferedImage.TYPE_USHORT_565_RGB, + BufferedImage.TYPE_USHORT_555_RGB, BufferedImage.TYPE_BYTE_GRAY, + BufferedImage.TYPE_USHORT_GRAY, BufferedImage.TYPE_BYTE_BINARY, + BufferedImage.TYPE_BYTE_INDEXED + }; + + /** + * For all possible combinations of color spaces and image types, convert + * the source image using one shared ColorConvertOp per line on the + * different threads. The result is validated against images converted on + * one thread only. + */ + public static void main(String[] args) throws Exception { + for (int srcCS : spaces) { + for (int dstCS : spaces) { + if(srcCS != dstCS) { + for (int type : types) { + checkTypes(ColorSpace.getInstance(srcCS), + ColorSpace.getInstance(dstCS), type); + } + } + } + } + } + + private static void checkTypes(ColorSpace srcCS, ColorSpace dstCS, int type) + throws Exception { + lines = new BufferedImage[SIZE]; + ColorConvertOp goldOp = new ColorConvertOp(srcCS, dstCS, null); + BufferedImage src = createSrc(type); + BufferedImage gold = goldOp.filter(src, null); + + // we do not share the goldOp since it is already initialized and used + // for the whole image, instead we will create a separate sharedOp and + // use it for each line of a different threads + ColorConvertOp sharedOp = new ColorConvertOp(srcCS, dstCS, null); + Thread[] threads = new Thread[SIZE]; + for (int y = 0; y < SIZE; ++y) { + BufferedImage line = src.getSubimage(0, y, SIZE, 1); + threads[y] = test(sharedOp, line, y); + } + + for (Thread t: threads) { + t.start(); + } + for (Thread t: threads) { + t.join(); + } + for (int y = 0; y < SIZE; ++y) { + validate(gold, lines[y], y); + } + if (failed) { + throw new RuntimeException("Unexpected exception"); + } + } + + private static Thread test(ColorConvertOp sharedOp, + BufferedImage line, int y){ + return new Thread(() -> { + try { + BufferedImage image = sharedOp.filter(line, null); + lines[y] = image; + } catch (Throwable t) { + t.printStackTrace(); + failed = true; + } + }); + } + + private static BufferedImage createSrc(int type) { + BufferedImage img = new BufferedImage(SIZE, SIZE, type); + fill(img); + return img; + } + + private static void fill(BufferedImage image) { + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + image.setRGB(i, j, + (i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1)); + } + } + } + + private static void validate(BufferedImage full, BufferedImage line, int y) { + for (int i = 0; i < SIZE; i++) { + int rgb1 = full.getRGB(i, y); + int rgb2 = line.getRGB(i, 0); + if (rgb1 != rgb2) { + System.err.println("rgb1 = " + Integer.toHexString(rgb1)); + System.err.println("rgb2 = " + Integer.toHexString(rgb2)); + throw new RuntimeException(); + } + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java openjdk-17-17.0.3+7/test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java --- openjdk-17-17.0.2+8/test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/java2d/cmm/ColorConvertOp/MTTransformValidation.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright Amazon.com Inc. 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.image.BufferedImage; +import java.awt.image.ColorConvertOp; +import java.util.concurrent.CountDownLatch; + +/** + * @test + * @bug 8273972 + * @summary Verifies that ColorConvertOp works fine if shared between threads + * @run main/othervm/timeout=600 MTTransformValidation + */ +public final class MTTransformValidation { + + public static final int SIZE = 255; + private static volatile boolean failed = false; + + private static final int[] spaces = { + ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, + ColorSpace.CS_PYCC, ColorSpace.CS_sRGB + }; + + private static final int[] types = new int[]{ + BufferedImage.TYPE_INT_RGB, BufferedImage.TYPE_INT_ARGB, + BufferedImage.TYPE_INT_ARGB_PRE, BufferedImage.TYPE_INT_BGR, + BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_4BYTE_ABGR, + BufferedImage.TYPE_4BYTE_ABGR_PRE, + BufferedImage.TYPE_USHORT_565_RGB, + BufferedImage.TYPE_USHORT_555_RGB, BufferedImage.TYPE_BYTE_GRAY, + BufferedImage.TYPE_USHORT_GRAY, BufferedImage.TYPE_BYTE_BINARY, + BufferedImage.TYPE_BYTE_INDEXED + }; + + /** + * For all possible combinations of color spaces and image types, convert + * the source image using one shared ColorConvertOp. The result is validated + * against images converted on one thread only. + */ + public static void main(String[] args) throws Exception { + for (int srcCS : spaces) { + for (int dstCS : spaces) { + if(srcCS != dstCS) { + for (int type : types) { + checkTypes(ColorSpace.getInstance(srcCS), + ColorSpace.getInstance(dstCS), type); + } + } + } + } + } + + private static void checkTypes(ColorSpace srcCS, ColorSpace dstCS, int type) + throws Exception { + ColorConvertOp goldOp = new ColorConvertOp(srcCS, dstCS, null); + BufferedImage gold = goldOp.filter(createSrc(type), null); + // we do not share the goldOp since it is already initialized, but + // instead we will trigger initialization/usage of the new sharedOp on + // different threads at once + ColorConvertOp sharedOp = new ColorConvertOp(srcCS, dstCS, null); + test(gold, sharedOp, type); + + if (failed) { + throw new RuntimeException("Unexpected exception"); + } + } + + private static void test(BufferedImage gold, ColorConvertOp sharedOp, + int type) throws Exception { + Thread[] ts = new Thread[7]; + CountDownLatch latch = new CountDownLatch(ts.length); + for (int i = 0; i < ts.length; i++) { + ts[i] = new Thread(() -> { + BufferedImage local = createSrc(type); + latch.countDown(); + try { + latch.await(); + BufferedImage image = sharedOp.filter(local, null); + validate(image, gold); + } catch (Throwable t) { + t.printStackTrace(); + failed = true; + } + }); + } + for (Thread t : ts) { + t.start(); + } + for (Thread t : ts) { + t.join(); + } + } + + private static BufferedImage createSrc(int type) { + BufferedImage img = new BufferedImage(SIZE, SIZE, type); + fill(img); + return img; + } + + private static void fill(BufferedImage image) { + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + image.setRGB(i, j, + (i << 24) | (i << 16) | (j << 8) | ((i + j) >> 1)); + } + } + } + + private static void validate(BufferedImage img1, BufferedImage img2) { + for (int i = 0; i < SIZE; i++) { + for (int j = 0; j < SIZE; j++) { + int rgb1 = img1.getRGB(i, j); + int rgb2 = img2.getRGB(i, j); + if (rgb1 != rgb2) { + System.err.println("rgb1 = " + Integer.toHexString(rgb1)); + System.err.println("rgb2 = " + Integer.toHexString(rgb2)); + throw new RuntimeException(); + } + } + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java openjdk-17-17.0.3+7/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java --- openjdk-17-17.0.2+8/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/security/lib/cacerts/VerifyCACerts.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, 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 @@ -264,6 +264,8 @@ add("luxtrustglobalrootca [jdk]"); // Valid until: Wed Mar 17 11:33:33 PDT 2021 add("quovadisrootca [jdk]"); + // Valid until: Sat May 21 04:00:00 GMT 2022 + add("geotrustglobalca [jdk]"); } }; diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java openjdk-17-17.0.3+7/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java --- openjdk-17-17.0.2+8/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/security/pkcs12/KeytoolOpensslInteropTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2022, 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 @@ -462,7 +462,7 @@ "pkcs12", "-in", "ksnormal", "-passin", "pass:changeit", "-info", "-nokeys", "-nocerts"); output1.shouldHaveExitValue(0) - .shouldContain("MAC: sha256, Iteration 10000") + .shouldMatch("MAC:.*sha256.*Iteration 10000") .shouldContain("Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC," + " Iteration 10000, PRF hmacWithSHA256") .shouldContain("PKCS7 Encrypted data: PBES2, PBKDF2, AES-256-CBC," @@ -505,7 +505,7 @@ "ksnewic", "-passin", "pass:changeit", "-info", "-nokeys", "-nocerts"); output1.shouldHaveExitValue(0) - .shouldContain("MAC: sha256, Iteration 5555") + .shouldMatch("MAC:.*sha256.*Iteration 5555") .shouldContain("Shrouded Keybag: PBES2, PBKDF2, AES-256-CBC," + " Iteration 7777, PRF hmacWithSHA256") .shouldContain("Shrouded Keybag: pbeWithSHA1And128BitRC4," diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java openjdk-17-17.0.3+7/test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java --- openjdk-17-17.0.2+8/test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/security/ssl/SSLSocketImpl/ClientSocketCloseHang.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021, 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 8274524 + * @summary 8274524: SSLSocket.close() hangs if it is called during the ssl handshake + * @library /javax/net/ssl/templates + * @run main/othervm ClientSocketCloseHang TLSv1.2 + * @run main/othervm ClientSocketCloseHang TLSv1.3 + */ + + +import javax.net.ssl.*; +import java.net.InetAddress; + +public class ClientSocketCloseHang implements SSLContextTemplate { + + public static void main(String[] args) throws Exception { + System.setProperty("jdk.tls.client.protocols", args[0]); + for (int i = 0; i<= 20; i++) { + System.err.println("==================================="); + System.err.println("loop " + i); + System.err.println("==================================="); + new ClientSocketCloseHang().test(); + } + } + + private void test() throws Exception { + SSLServerSocket listenSocket = null; + SSLSocket serverSocket = null; + ClientSocket clientSocket = null; + try { + SSLServerSocketFactory serversocketfactory = + createServerSSLContext().getServerSocketFactory(); + listenSocket = + (SSLServerSocket)serversocketfactory.createServerSocket(0); + listenSocket.setNeedClientAuth(false); + listenSocket.setEnableSessionCreation(true); + listenSocket.setUseClientMode(false); + + + System.err.println("Starting client"); + clientSocket = new ClientSocket(listenSocket.getLocalPort()); + clientSocket.start(); + + System.err.println("Accepting client requests"); + serverSocket = (SSLSocket) listenSocket.accept(); + + serverSocket.startHandshake(); + } finally { + if (clientSocket != null) { + clientSocket.close(); + } + if (listenSocket != null) { + listenSocket.close(); + } + + if (serverSocket != null) { + serverSocket.close(); + } + } + } + + private class ClientSocket extends Thread{ + int serverPort = 0; + SSLSocket clientSocket = null; + + public ClientSocket(int serverPort) { + this.serverPort = serverPort; + } + + @Override + public void run() { + try { + System.err.println( + "Connecting to server at port " + serverPort); + SSLSocketFactory sslSocketFactory = + createClientSSLContext().getSocketFactory(); + clientSocket = (SSLSocket)sslSocketFactory.createSocket( + InetAddress.getLocalHost(), serverPort); + clientSocket.setSoLinger(true, 3); + clientSocket.startHandshake(); + } catch (Exception e) { + } + } + + public void close() { + Thread t = new Thread() { + @Override + public void run() { + try { + if (clientSocket != null) { + clientSocket.close(); + } + } catch (Exception ex) { + } + } + }; + try { + // Close client connection + t.start(); + t.join(2000); // 2 sec + } catch (InterruptedException ex) { + return; + } + + if (t.isAlive()) { + throw new RuntimeException("SSL Client hangs on close"); + } + } + } +} + diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/text/resources/LocaleData openjdk-17-17.0.3+7/test/jdk/sun/text/resources/LocaleData --- openjdk-17-17.0.2+8/test/jdk/sun/text/resources/LocaleData 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/text/resources/LocaleData 2022-04-19 19:55:43.000000000 +0000 @@ -8319,7 +8319,8 @@ # bug #8193552 CurrencyNames//mru=Mauritanian Ouguiya -# bug #8208746 +# bug #8208746, #8274658 +CurrencyNames//ved=Venezuelan Bol\u00edvar Soberano CurrencyNames//ves=Venezuelan Bol\u00edvar Soberano # bug #8206879 diff -Nru openjdk-17-17.0.2+8/test/jdk/sun/text/resources/LocaleDataTest.java openjdk-17-17.0.3+7/test/jdk/sun/text/resources/LocaleDataTest.java --- openjdk-17-17.0.2+8/test/jdk/sun/text/resources/LocaleDataTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/sun/text/resources/LocaleDataTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2021, 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,7 @@ * 8145136 8145952 8164784 8037111 8081643 7037368 8178872 8185841 8190918 * 8187946 8195478 8181157 8179071 8193552 8202026 8204269 8202537 8208746 * 8209775 8221432 8227127 8230284 8231273 8233579 8234288 8250665 8255086 - * 8251317 + * 8251317 8274658 * @summary Verify locale data * @modules java.base/sun.util.resources * @modules jdk.localedata diff -Nru openjdk-17-17.0.2+8/test/jdk/tools/jar/ContentOrder.java openjdk-17-17.0.3+7/test/jdk/tools/jar/ContentOrder.java --- openjdk-17-17.0.2+8/test/jdk/tools/jar/ContentOrder.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/tools/jar/ContentOrder.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2021, 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 8276764 + * @summary test that the jar content ordering is sorted + * @library /test/lib + * @modules jdk.jartool + * @build jdk.test.lib.Platform + * jdk.test.lib.util.FileUtils + * @run testng ContentOrder + */ + +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; +import java.util.zip.ZipException; + +import jdk.test.lib.util.FileUtils; + +public class ContentOrder { + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() -> + new RuntimeException("jar tool not found") + ); + + private final String nl = System.lineSeparator(); + private final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private final PrintStream out = new PrintStream(baos); + private Runnable onCompletion; + + @BeforeMethod + public void reset() { + onCompletion = null; + } + + @AfterMethod + public void run() { + if (onCompletion != null) { + onCompletion.run(); + } + } + + // Test that the jar content ordering when processing a single directory is sorted + @Test + public void testSingleDir() throws IOException { + mkdir("testjar/Ctest1", "testjar/Btest2/subdir1", "testjar/Atest3"); + touch("testjar/Ctest1/testfile1", "testjar/Ctest1/testfile2", "testjar/Ctest1/testfile3"); + touch("testjar/Btest2/subdir1/testfileC", "testjar/Btest2/subdir1/testfileB", "testjar/Btest2/subdir1/testfileA"); + touch("testjar/Atest3/fileZ", "testjar/Atest3/fileY", "testjar/Atest3/fileX"); + + onCompletion = () -> rm("test.jar", "testjar"); + + jar("cf test.jar testjar"); + jar("tf test.jar"); + System.out.println(new String(baos.toByteArray())); + String output = "META-INF/" + nl + + "META-INF/MANIFEST.MF" + nl + + "testjar/" + nl + + "testjar/Atest3/" + nl + + "testjar/Atest3/fileX" + nl + + "testjar/Atest3/fileY" + nl + + "testjar/Atest3/fileZ" + nl + + "testjar/Btest2/" + nl + + "testjar/Btest2/subdir1/" + nl + + "testjar/Btest2/subdir1/testfileA" + nl + + "testjar/Btest2/subdir1/testfileB" + nl + + "testjar/Btest2/subdir1/testfileC" + nl + + "testjar/Ctest1/" + nl + + "testjar/Ctest1/testfile1" + nl + + "testjar/Ctest1/testfile2" + nl + + "testjar/Ctest1/testfile3" + nl; + Assert.assertEquals(baos.toByteArray(), output.getBytes()); + } + + // Test that when specifying multiple directories or releases that the sort + // ordering is done on each directory and release, reserving the order of + // the directories/releases specified on the command line + @Test + public void testMultiDirWithReleases() throws IOException { + mkdir("testjar/foo/classes", + "testjar/foo11/classes/Zclasses", + "testjar/foo11/classes/Yclasses", + "testjar/foo17/classes/Bclasses", + "testjar/foo17/classes/Aclasses"); + touch("testjar/foo/classes/testfile1", "testjar/foo/classes/testfile2"); + touch("testjar/foo11/classes/Zclasses/testfile1", "testjar/foo11/classes/Zclasses/testfile2"); + touch("testjar/foo11/classes/Yclasses/testfileA", "testjar/foo11/classes/Yclasses/testfileB"); + touch("testjar/foo17/classes/Bclasses/testfile1", "testjar/foo17/classes/Bclasses/testfile2"); + touch("testjar/foo17/classes/Aclasses/testfileA", "testjar/foo17/classes/Aclasses/testfileB"); + + onCompletion = () -> rm("test.jar", "testjar"); + + jar("cf test.jar -C testjar/foo classes " + + "--release 17 -C testjar/foo17 classes/Bclasses -C testjar/foo17 classes/Aclasses " + + "--release 11 -C testjar/foo11 classes/Zclasses -C testjar/foo11 classes/Yclasses"); + jar("tf test.jar"); + System.out.println(new String(baos.toByteArray())); + String output = "META-INF/" + nl + + "META-INF/MANIFEST.MF" + nl + + "classes/" + nl + + "classes/testfile1" + nl + + "classes/testfile2" + nl + + "META-INF/versions/17/classes/Bclasses/" + nl + + "META-INF/versions/17/classes/Bclasses/testfile1" + nl + + "META-INF/versions/17/classes/Bclasses/testfile2" + nl + + "META-INF/versions/17/classes/Aclasses/" + nl + + "META-INF/versions/17/classes/Aclasses/testfileA" + nl + + "META-INF/versions/17/classes/Aclasses/testfileB" + nl + + "META-INF/versions/11/classes/Zclasses/" + nl + + "META-INF/versions/11/classes/Zclasses/testfile1" + nl + + "META-INF/versions/11/classes/Zclasses/testfile2" + nl + + "META-INF/versions/11/classes/Yclasses/" + nl + + "META-INF/versions/11/classes/Yclasses/testfileA" + nl + + "META-INF/versions/11/classes/Yclasses/testfileB" + nl; + Assert.assertEquals(baos.toByteArray(), output.getBytes()); + } + + private Stream mkpath(String... args) { + return Arrays.stream(args).map(d -> Paths.get(".", d.split("/"))); + } + + private void mkdir(String... dirs) { + System.out.println("mkdir -p " + Arrays.toString(dirs)); + Arrays.stream(dirs).forEach(p -> { + try { + Files.createDirectories((new File(p)).toPath()); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void touch(String... files) { + System.out.println("touch " + Arrays.toString(files)); + Arrays.stream(files).forEach(p -> { + try { + Files.createFile((new File(p)).toPath()); + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void rm(String... files) { + System.out.println("rm -rf " + Arrays.toString(files)); + Arrays.stream(files).forEach(p -> { + try { + Path path = (new File(p)).toPath(); + if (Files.isDirectory(path)) { + FileUtils.deleteFileTreeWithRetry(path); + } else { + FileUtils.deleteFileIfExistsWithRetry(path); + } + } catch (IOException x) { + throw new UncheckedIOException(x); + } + }); + } + + private void jar(String cmdline) throws IOException { + System.out.println("jar " + cmdline); + baos.reset(); + + // the run method catches IOExceptions, we need to expose them + ByteArrayOutputStream baes = new ByteArrayOutputStream(); + PrintStream err = new PrintStream(baes); + PrintStream saveErr = System.err; + System.setErr(err); + int rc = JAR_TOOL.run(out, err, cmdline.split(" +")); + System.setErr(saveErr); + if (rc != 0) { + String s = baes.toString(); + if (s.startsWith("java.util.zip.ZipException: duplicate entry: ")) { + throw new ZipException(s); + } + throw new IOException(s); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/tools/jar/ReproducibleJar.java openjdk-17-17.0.3+7/test/jdk/tools/jar/ReproducibleJar.java --- openjdk-17-17.0.2+8/test/jdk/tools/jar/ReproducibleJar.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/tools/jar/ReproducibleJar.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2021, 2022, 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 8276766 + * @requires vm.bits == 64 + * @summary Test jar --date source date of entries and that jars are + * reproducible + * @modules jdk.jartool + * @run testng/othervm ReproducibleJar + */ + +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.attribute.FileTime; +import java.util.Date; +import java.util.TimeZone; +import java.util.spi.ToolProvider; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +public class ReproducibleJar { + private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar") + .orElseThrow(() -> + new RuntimeException("jar tool not found") + ); + + // ZipEntry's mod date has 2 seconds precision: give extra time to + // allow for e.g. rounding/truncation and networked/samba drives. + private static final long PRECISION = 10000L; + + private static final TimeZone TZ = TimeZone.getDefault(); + private static final boolean DST = TZ.inDaylightTime(new Date()); + private static final String UNIX_2038_ROLLOVER_TIME = "2038-01-19T03:14:07Z"; + private static final Instant UNIX_2038_ROLLOVER = Instant.parse(UNIX_2038_ROLLOVER_TIME); + private static final File DIR_OUTER = new File("outer"); + private static final File DIR_INNER = new File(DIR_OUTER, "inner"); + private static final File FILE_INNER = new File(DIR_INNER, "foo.txt"); + private static final File JAR_FILE_SOURCE_DATE1 = new File("JarEntryTimeSourceDate1.jar"); + private static final File JAR_FILE_SOURCE_DATE2 = new File("JarEntryTimeSourceDate2.jar"); + + // Valid --date values for jar + @DataProvider + private Object[][] validSourceDates() { + return new Object[][]{ + {"1980-01-01T00:00:02+00:00"}, + {"1986-06-24T01:02:03+00:00"}, + {"2022-03-15T00:00:00+00:00"}, + {"2022-03-15T00:00:00+06:00"}, + {"2021-12-25T09:30:00-08:00[America/Los_Angeles]"}, + {"2021-12-31T23:59:59Z"}, + {"2024-06-08T14:24Z"}, + {"2026-09-24T16:26-05:00"}, + {"2038-11-26T06:06:06+00:00"}, + {"2098-02-18T00:00:00-08:00"}, + {"2099-12-31T23:59:59+00:00"} + }; + } + + // Invalid --date values for jar + @DataProvider + private Object[][] invalidSourceDates() { + return new Object[][]{ + {"1976-06-24T01:02:03+00:00"}, + {"1980-01-01T00:00:01+00:00"}, + {"2100-01-01T00:00:00+00:00"}, + {"2138-02-18T00:00:00-11:00"}, + {"2006-04-06T12:38:00"}, + {"2012-08-24T16"} + }; + } + + @BeforeMethod + public void runBefore() throws IOException { + runAfter(); + createOuterInnerDirs(); + } + + @AfterMethod + public void runAfter() { + cleanup(DIR_INNER); + cleanup(DIR_OUTER); + JAR_FILE_SOURCE_DATE1.delete(); + JAR_FILE_SOURCE_DATE2.delete(); + TimeZone.setDefault(TZ); + } + + /** + * Test jar tool with various valid --date + */ + @Test(dataProvider = "validSourceDates") + public void testValidSourceDate(String sourceDate) { + if (isInTransition()) return; + + // Test --date source date + Assert.assertEquals(JAR_TOOL.run(System.out, System.err, + "--create", + "--file", JAR_FILE_SOURCE_DATE1.getName(), + "--date", sourceDate, + DIR_OUTER.getName()), 0); + Assert.assertTrue(JAR_FILE_SOURCE_DATE1.exists()); + + // Extract JAR_FILE_SOURCE_DATE1 and check last modified values + Assert.assertEquals(JAR_TOOL.run(System.out, System.err, + "--extract", + "--file", JAR_FILE_SOURCE_DATE1.getName()), 0); + Assert.assertTrue(DIR_OUTER.exists()); + Assert.assertTrue(DIR_INNER.exists()); + Assert.assertTrue(FILE_INNER.exists()); + LocalDateTime expectedLdt = ZonedDateTime.parse(sourceDate, + DateTimeFormatter.ISO_DATE_TIME) + .withZoneSameInstant(ZoneOffset.UTC) + .toLocalDateTime(); + System.out.format("Checking jar entries local date time for --date %s, is %s%n", + sourceDate, expectedLdt); + long sourceDateEpochMillis = TimeUnit.MILLISECONDS.convert( + expectedLdt.toEpochSecond(ZoneId.systemDefault().getRules() + .getOffset(expectedLdt)), TimeUnit.SECONDS); + checkFileTime(DIR_OUTER.lastModified(), sourceDateEpochMillis); + checkFileTime(DIR_INNER.lastModified(), sourceDateEpochMillis); + checkFileTime(FILE_INNER.lastModified(), sourceDateEpochMillis); + } + + /** + * Test jar tool with various invalid --date + */ + @Test(dataProvider = "invalidSourceDates") + public void testInvalidSourceDate(String sourceDate) { + // Negative Tests --date out of range or wrong format source date + Assert.assertNotEquals(JAR_TOOL.run(System.out, System.err, + "--create", + "--file", JAR_FILE_SOURCE_DATE1.getName(), + "--date", sourceDate, + DIR_OUTER.getName()), 0); + } + + /** + * Test jar produces deterministic reproducible output + */ + @Test(dataProvider = "validSourceDates") + public void testJarsReproducible(String sourceDate) throws IOException { + // Test jars are reproducible across timezones + TimeZone tzAsia = TimeZone.getTimeZone("Asia/Shanghai"); + TimeZone tzLA = TimeZone.getTimeZone("America/Los_Angeles"); + TimeZone.setDefault(tzAsia); + Assert.assertEquals(JAR_TOOL.run(System.out, System.err, + "--create", + "--file", JAR_FILE_SOURCE_DATE1.getName(), + "--date", sourceDate, + DIR_OUTER.getName()), 0); + Assert.assertTrue(JAR_FILE_SOURCE_DATE1.exists()); + + try { + // Sleep 5 seconds to ensure jar timestamps might be different if they could be + Thread.sleep(5000); + } catch (InterruptedException ex) { + } + + TimeZone.setDefault(tzLA); + Assert.assertEquals(JAR_TOOL.run(System.out, System.err, + "--create", + "--file", JAR_FILE_SOURCE_DATE2.getName(), + "--date", sourceDate, + DIR_OUTER.getName()), 0); + Assert.assertTrue(JAR_FILE_SOURCE_DATE2.exists()); + + // Check jars are identical + Assert.assertEquals(Files.readAllBytes(JAR_FILE_SOURCE_DATE1.toPath()), + Files.readAllBytes(JAR_FILE_SOURCE_DATE2.toPath())); + } + + /** + * Create the standard directory structure used by the test: + * outer/ + * inner/ + * foo.txt + */ + static void createOuterInnerDirs() throws IOException { + Assert.assertTrue(DIR_OUTER.mkdir()); + Assert.assertTrue(DIR_INNER.mkdir()); + try (PrintWriter pw = new PrintWriter(FILE_INNER)) { + pw.println("hello, world"); + } + Assert.assertTrue(DIR_OUTER.exists()); + Assert.assertTrue(DIR_INNER.exists()); + Assert.assertTrue(FILE_INNER.exists()); + } + + /** + * Check the extracted and original millis since Epoch file times are + * within the zip precision time period. + */ + static void checkFileTime(long now, long original) { + if (isTimeSettingChanged()) { + return; + } + + if (Math.abs(now - original) > PRECISION) { + // If original time is after UNIX 2038 32bit rollover + // and the now time is exactly the rollover time, then assume + // running on a file system that only supports to 2038 (e.g.XFS) and pass test + if (FileTime.fromMillis(original).toInstant().isAfter(UNIX_2038_ROLLOVER) && + FileTime.fromMillis(now).toInstant().equals(UNIX_2038_ROLLOVER)) { + System.out.println("Checking file time after Unix 2038 rollover," + + " and extracted file time is " + UNIX_2038_ROLLOVER_TIME + ", " + + " Assuming restricted file system, pass file time check."); + } else { + throw new AssertionError("checkFileTime failed," + + " extracted to " + FileTime.fromMillis(now) + + ", expected to be close to " + FileTime.fromMillis(original)); + } + } + } + + /** + * Has the timezone or DST changed during the test? + */ + private static boolean isTimeSettingChanged() { + TimeZone currentTZ = TimeZone.getDefault(); + boolean currentDST = currentTZ.inDaylightTime(new Date()); + if (!currentTZ.equals(TZ) || currentDST != DST) { + System.out.println("Timezone or DST has changed during " + + "ReproducibleJar testcase execution. Test skipped"); + return true; + } else { + return false; + } + } + + /** + * Is the Zone currently within the transition change period? + */ + private static boolean isInTransition() { + var inTransition = false; + var date = new Date(); + var defZone = ZoneId.systemDefault(); + if (defZone.getRules().getTransition( + date.toInstant().atZone(defZone).toLocalDateTime()) != null) { + System.out.println("ReproducibleJar testcase being run during Zone offset transition. Test skipped."); + inTransition = true; + } + return inTransition; + } + + /** + * Remove the directory and its contents + */ + static void cleanup(File dir) { + File[] x = dir.listFiles(); + if (x != null) { + for (File f : x) { + f.delete(); + } + } + dir.delete(); + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/tools/jimage/JImageNonAsciiNameTest.java openjdk-17-17.0.3+7/test/jdk/tools/jimage/JImageNonAsciiNameTest.java --- openjdk-17-17.0.2+8/test/jdk/tools/jimage/JImageNonAsciiNameTest.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/tools/jimage/JImageNonAsciiNameTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, 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.nio.file.Path; +import jdk.internal.jimage.BasicImageReader; +import jdk.internal.jimage.ImageLocation; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.util.JarBuilder; + +import tests.Helper; +import tests.JImageGenerator; +import tests.Result; + +/* + * @test + * @bug 8278185 + * @summary Test non-ASCII path in custom JRE + * @library ../lib + * /test/lib + * @modules java.base/jdk.internal.jimage + * jdk.jdeps/com.sun.tools.classfile + * jdk.jlink/jdk.tools.jimage + * @build tests.* + * @run main/othervm JImageNonAsciiNameTest + */ + +public class JImageNonAsciiNameTest { + private final static String moduleName = "A_module"; + private final static String packageName = "test.\u3042"; //non-ASCII + private final static String className = "A"; + private final static String fullName = packageName + "." + className; + private static Helper helper; + + public static void main(String[] args) throws Exception { + helper = Helper.newHelper(); + if (helper == null) { + System.err.println("Test not run"); + return; + } + + String source = + "package "+packageName+";" + + "public class "+className+" {" + + " public static void main(String[] args) {}" + + "}"; + String moduleInfo = "module " + moduleName + " {}"; + + // Using InMemory features to avoid generating non-ASCII name file + byte[] byteA = InMemoryJavaCompiler.compile(fullName, source); + byte[] byteModule = InMemoryJavaCompiler.compile( + "module-info", moduleInfo); + + Path jarDir = helper.getJarDir(); + JarBuilder jb = new JarBuilder( + jarDir.resolve(moduleName + ".jar").toString()); + jb.addEntry(fullName.replace(".","/") + ".class", byteA); + jb.addEntry("module-info.class", byteModule); + jb.build(); + + Path outDir = helper.createNewImageDir(moduleName); + + Result result = JImageGenerator.getJLinkTask() + .modulePath(helper.defaultModulePath()) + .output(outDir) + .addMods(moduleName) + .call(); + Path testImage = result.assertSuccess(); + + BasicImageReader bir = BasicImageReader.open( + testImage.resolve("lib").resolve("modules")); + ImageLocation loc = bir.findLocation(moduleName, + fullName.replace(".","/") + ".class"); + if (loc == null) { + throw new RuntimeException("Failed to find " + + fullName + " in module " +moduleName); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/jdk/tools/jmod/JmodTest.java openjdk-17-17.0.3+7/test/jdk/tools/jmod/JmodTest.java --- openjdk-17-17.0.2+8/test/jdk/tools/jmod/JmodTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/jdk/tools/jmod/JmodTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, 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,7 +23,7 @@ /* * @test - * @bug 8142968 8166568 8166286 8170618 8168149 8240910 + * @bug 8142968 8166568 8166286 8170618 8168149 8240910 8276764 8276766 * @summary Basic test for jmod * @library /test/lib * @modules jdk.compiler @@ -183,13 +183,15 @@ @Test public void testList() throws IOException { String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); + Path jmod = MODS_DIR.resolve("foo.jmod"); + FileUtils.deleteFileIfExistsWithRetry(jmod); jmod("create", "--class-path", cp, - MODS_DIR.resolve("foo.jmod").toString()) + jmod.toString()) .assertSuccess(); jmod("list", - MODS_DIR.resolve("foo.jmod").toString()) + jmod.toString()) .assertSuccess() .resultChecker(r -> { // asserts dependent on the exact contents of foo @@ -197,6 +199,75 @@ assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class"); assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); + + // JDK-8276764: Ensure the order is sorted for reproducible jmod content + // module-info, followed by + int mod_info_i = r.output.indexOf(CLASSES_PREFIX + "module-info.class"); + int foo_cls_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/Foo.class"); + int msg_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/internal/Message.class"); + int res_i = r.output.indexOf(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties"); + System.out.println("jmod classes sort order check:\n"+r.output); + assertTrue(mod_info_i < foo_cls_i); + assertTrue(foo_cls_i < msg_i); + assertTrue(msg_i < res_i); + }); + } + + @Test + public void testSourceDateReproducible() throws IOException { + String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString(); + Path jmod1 = MODS_DIR.resolve("foo1.jmod"); + Path jmod2 = MODS_DIR.resolve("foo2.jmod"); + Path jmod3 = MODS_DIR.resolve("foo3.jmod"); + FileUtils.deleteFileIfExistsWithRetry(jmod1); + FileUtils.deleteFileIfExistsWithRetry(jmod2); + FileUtils.deleteFileIfExistsWithRetry(jmod3); + + // Use source date of 15/03/2022 + String sourceDate = "2022-03-15T00:00:00+00:00"; + + jmod("create", + "--class-path", cp, + "--date", sourceDate, + jmod1.toString()) + .assertSuccess(); + + try { + // Sleep 5 seconds to ensure zip timestamps might be different if they could be + Thread.sleep(5000); + } catch(InterruptedException ex) {} + + jmod("create", + "--class-path", cp, + "--date", sourceDate, + jmod2.toString()) + .assertSuccess(); + + // Compare file byte content to see if they are identical + assertSameContent(jmod1, jmod2); + + // Use a date before 1980 and assert failure error + sourceDate = "1976-03-15T00:00:00+00:00"; + + jmod("create", + "--class-path", cp, + "--date", sourceDate, + jmod3.toString()) + .assertFailure() + .resultChecker(r -> { + assertContains(r.output, "is out of the valid range"); + }); + + // Use a date after 2099 and assert failure error + sourceDate = "2100-03-15T00:00:00+00:00"; + + jmod("create", + "--class-path", cp, + "--date", sourceDate, + jmod3.toString()) + .assertFailure() + .resultChecker(r -> { + assertContains(r.output, "is out of the valid range"); }); } diff -Nru openjdk-17-17.0.2+8/test/langtools/jdk/jshell/CommandCompletionTest.java openjdk-17-17.0.3+7/test/langtools/jdk/jshell/CommandCompletionTest.java --- openjdk-17-17.0.2+8/test/langtools/jdk/jshell/CommandCompletionTest.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/langtools/jdk/jshell/CommandCompletionTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -23,7 +23,7 @@ /* * @test - * @bug 8144095 8164825 8169818 8153402 8165405 8177079 8178013 8167554 8166232 + * @bug 8144095 8164825 8169818 8153402 8165405 8177079 8178013 8167554 8166232 8277328 * @summary Test Command Completion * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.main @@ -332,14 +332,31 @@ } @Test + public void testClassPathWithSpace() throws IOException { + Compiler compiler = new Compiler(); + Path outDir = compiler.getPath("testClassPathWithSpace"); + Path dirWithSpace = Files.createDirectories(outDir.resolve("dir with space")); + Files.createDirectories(dirWithSpace.resolve("nested with space")); + String[] pathArray = new String[] {"dir\\ with\\ space/"}; + String[] pathArray2 = new String[] {"nested\\ with\\ space/"}; + testNoStartUp( + a -> assertCompletion(a, "/env -class-path " + outDir + "/|", false, pathArray), + a -> assertCompletion(a, "/env -class-path " + outDir + "/dir|", false, pathArray), + a -> assertCompletion(a, "/env -class-path " + outDir + "/dir\\ with|", false, pathArray), + a -> assertCompletion(a, "/env -class-path " + outDir + "/dir\\ with\\ space/|", false, pathArray2) + ); + } + + @Test public void testUserHome() throws IOException { List completions; Path home = Paths.get(System.getProperty("user.home")); String selectedFile; try (Stream content = Files.list(home)) { selectedFile = content.filter(CLASSPATH_FILTER) + .filter(file -> file.getFileName().toString().contains(" ")) .findAny() - .map(file -> file.getFileName().toString()) + .map(file -> file.getFileName().toString().replace(" ", "\\ ")) .orElse(null); } if (selectedFile == null) { @@ -347,8 +364,8 @@ } try (Stream content = Files.list(home)) { completions = content.filter(CLASSPATH_FILTER) - .filter(file -> file.getFileName().toString().startsWith(selectedFile)) - .map(file -> file.getFileName().toString() + (Files.isDirectory(file) ? "/" : "")) + .filter(file -> file.getFileName().toString().startsWith(selectedFile.replace("\\ ", " "))) + .map(file -> file.getFileName().toString().replace(" ", "\\ ") + (Files.isDirectory(file) ? "/" : "")) .sorted() .collect(Collectors.toList()); } diff -Nru openjdk-17-17.0.2+8/test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java openjdk-17-17.0.3+7/test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java --- openjdk-17-17.0.2+8/test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/langtools/tools/javac/generics/diamond/protectedConstructor/ProtectedConstructorTest.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021, 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 8225559 + * @summary assertion error at TransTypes.visitApply + * @compile ProtectedConstructorTest.java + */ + +import pkg.Bar; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +class ProtectedConstructorTest { + public void foo() { + supply(getSupplier(new Bar<>(){})); + CompletableFuture> completableFuture = getCompletableFuture(getSupplier(new Bar<>(){})); + completableFuture = getCompletableFuture(() -> getList(null, new Bar<>() {})); + } + + static Supplier getSupplier(Bar t) { + return null; + } + + static void supply(Supplier supplier) {} + static CompletableFuture getCompletableFuture(Supplier supplier) { return null; } + List getList(final Supplier> supplier, Bar t) { return null; } +} diff -Nru openjdk-17-17.0.2+8/test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java openjdk-17-17.0.3+7/test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java --- openjdk-17-17.0.2+8/test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/langtools/tools/javac/generics/diamond/protectedConstructor/pkg/Bar.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, 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 pkg; + +public abstract class Bar { + protected Bar() {} + protected Bar(Class c) {} +} diff -Nru openjdk-17-17.0.2+8/test/lib/jdk/test/lib/SecurityTools.java openjdk-17-17.0.3+7/test/lib/jdk/test/lib/SecurityTools.java --- openjdk-17-17.0.2+8/test/lib/jdk/test/lib/SecurityTools.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/lib/jdk/test/lib/SecurityTools.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, 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 @@ package jdk.test.lib; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -275,5 +276,63 @@ } return result; } + + // Create a temporary keychain in macOS and use it. The original + // keychains will be restored when the object is closed. + public static class TemporaryKeychain implements Closeable { + // name of new keychain + private final String newChain; + // names of the original keychains + private final List oldChains; + + public TemporaryKeychain(String name) { + Path p = Path.of(name + ".keychain-db"); + newChain = p.toAbsolutePath().toString(); + try { + oldChains = ProcessTools.executeProcess("security", "list-keychains") + .shouldHaveExitValue(0) + .getStdout() + .lines() + .map(String::trim) + .map(x -> x.startsWith("\"") ? x.substring(1, x.length() - 1) : x) + .collect(Collectors.toList()); + if (!Files.exists(p)) { + ProcessTools.executeProcess("security", "create-keychain", "-p", "changeit", newChain) + .shouldHaveExitValue(0); + } + ProcessTools.executeProcess("security", "unlock-keychain", "-p", "changeit", newChain) + .shouldHaveExitValue(0); + ProcessTools.executeProcess("security", "list-keychains", "-s", newChain) + .shouldHaveExitValue(0); + } catch (Throwable t) { + if (t instanceof RuntimeException re) { + throw re; + } else { + throw new RuntimeException(t); + } + } + } + + public String chain() { + return newChain; + } + + @Override + public void close() throws IOException { + List cmds = new ArrayList<>(); + cmds.addAll(List.of("security", "list-keychains", "-s")); + cmds.addAll(oldChains); + try { + ProcessTools.executeProcess(cmds.toArray(new String[0])) + .shouldHaveExitValue(0); + } catch (Throwable t) { + if (t instanceof RuntimeException re) { + throw re; + } else { + throw new RuntimeException(t); + } + } + } + } } diff -Nru openjdk-17-17.0.2+8/test/lib/jdk/test/lib/containers/docker/Common.java openjdk-17-17.0.3+7/test/lib/jdk/test/lib/containers/docker/Common.java --- openjdk-17-17.0.2+8/test/lib/jdk/test/lib/containers/docker/Common.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/lib/jdk/test/lib/containers/docker/Common.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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,6 +57,10 @@ .addJavaOpts("-Xlog:os+container=trace"); } + public static DockerRunOptions newOptsShowSettings(String imageNameAndTag) { + return new DockerRunOptions(imageNameAndTag, "/jdk/bin/java", "-version", "-XshowSettings:system"); + } + // create commonly used options with class to be launched inside container public static DockerRunOptions newOpts(String imageNameAndTag, String testClass) { diff -Nru openjdk-17-17.0.2+8/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java openjdk-17-17.0.3+7/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java --- openjdk-17-17.0.2+8/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/lib/jdk/test/lib/containers/docker/DockerTestUtils.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2021, 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 @@ -120,59 +120,66 @@ return true; } + /** + * Build a container image that contains JDK under test. + * The jdk will be placed under the "/jdk/" folder inside the image/container file system. + * + * @param imageName name of the image to be created, including version tag + * @throws Exception + */ + public static void buildJdkContainerImage(String imageName) throws Exception { + buildJdkContainerImage(imageName, null); + } - /** - * Build a docker image that contains JDK under test. - * The jdk will be placed under the "/jdk/" folder inside the docker file system. + /** + * Build a container image that contains JDK under test. + * The jdk will be placed under the "/jdk/" folder inside the image/container file system. * - * @param imageName name of the image to be created, including version tag - * @param dockerfile name of the dockerfile residing in the test source; - * we check for a platform specific dockerfile as well - * and use this one in case it exists - * @param buildDirName name of the docker build/staging directory, which will - * be created in the jtreg's scratch folder - * @throws Exception - */ - public static void - buildJdkDockerImage(String imageName, String dockerfile, String buildDirName) - throws Exception { + * @param imageName name of the image to be created, including version tag + * @param dockerfileContent content of the Dockerfile; use null to generate default content + * @throws Exception + */ + public static void buildJdkContainerImage(String imageName, String dockerfileContent) throws Exception { + // image name may contain tag, hence replace ':' + String imageDirName = imageName.replace(":", "-"); - Path buildDir = Paths.get(".", buildDirName); + // Create an image build/staging directory + Path buildDir = Paths.get(imageDirName); if (Files.exists(buildDir)) { throw new RuntimeException("The docker build directory already exists: " + buildDir); } + Files.createDirectories(buildDir); - Path jdkSrcDir = Paths.get(JDK_UNDER_TEST); - Path jdkDstDir = buildDir.resolve("jdk"); - - Files.createDirectories(jdkDstDir); + // Generate Dockerfile + if (dockerfileContent != null) { + Files.writeString(buildDir.resolve("Dockerfile"), dockerfileContent); + } else { + generateDockerFile(buildDir.resolve("Dockerfile"), + DockerfileConfig.getBaseImageName(), + DockerfileConfig.getBaseImageVersion()); + } // Copy JDK-under-test tree to the docker build directory. // This step is required for building a docker image. + Path jdkSrcDir = Paths.get(JDK_UNDER_TEST); + Path jdkDstDir = buildDir.resolve("jdk"); + Files.createDirectories(jdkDstDir); Files.walkFileTree(jdkSrcDir, new CopyFileVisitor(jdkSrcDir, jdkDstDir)); - buildDockerImage(imageName, Paths.get(Utils.TEST_SRC, dockerfile), buildDir); + + buildImage(imageName, buildDir); } /** - * Build a docker image based on given docker file and docker build directory. + * Build a container image based on image build directory. * * @param imageName name of the image to be created, including version tag - * @param dockerfile path to the Dockerfile to be used for building the docker - * image. The specified dockerfile will be copied to the docker build - * directory as 'Dockerfile' - * @param buildDir build directory; it should already contain all the content - * needed to build the docker image. + * @param buildDir build directory; it should already contain all the content + * needed to build the image. * @throws Exception */ - public static void - buildDockerImage(String imageName, Path dockerfile, Path buildDir) throws Exception { - - generateDockerFile(buildDir.resolve("Dockerfile"), - DockerfileConfig.getBaseImageName(), - DockerfileConfig.getBaseImageVersion()); + private static void buildImage(String imageName, Path buildDir) throws Exception { try { - // Build the docker execute(Container.ENGINE_COMMAND, "build", "--no-cache", "--tag", imageName, buildDir.toString()) .shouldHaveExitValue(0); } catch (Exception e) { diff -Nru openjdk-17-17.0.2+8/test/lib/sun/hotspot/WhiteBox.java openjdk-17-17.0.3+7/test/lib/sun/hotspot/WhiteBox.java --- openjdk-17-17.0.2+8/test/lib/sun/hotspot/WhiteBox.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/lib/sun/hotspot/WhiteBox.java 2022-04-19 19:55:43.000000000 +0000 @@ -311,7 +311,7 @@ makeMethodNotCompilable0(method, compLevel, isOsr); } public int getMethodCompilationLevel(Executable method) { - return getMethodCompilationLevel(method, false /*not ost*/); + return getMethodCompilationLevel(method, false /*not osr*/); } private native int getMethodCompilationLevel0(Executable method, boolean isOsr); public int getMethodCompilationLevel(Executable method, boolean isOsr) { diff -Nru openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/StringEncode.java openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/StringEncode.java --- openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/StringEncode.java 2021-12-07 18:03:22.000000000 +0000 +++ openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/StringEncode.java 2022-04-19 19:55:43.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2022, 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,59 +30,97 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) -@Fork(value = 3, jvmArgs = "-Xmx1g") +@Fork(value = 3) @Warmup(iterations = 5, time = 2) @Measurement(iterations = 5, time = 3) @State(Scope.Thread) public class StringEncode { - @BenchmarkMode(Mode.AverageTime) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - @Fork(value = 3, jvmArgs = "-Xmx1g") - @Warmup(iterations = 5, time = 2) - @Measurement(iterations = 5, time = 2) - @State(Scope.Thread) - public static class WithCharset { - - @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6"}) - private String charsetName; - - private Charset charset; - private String asciiString; - private String utf16String; - - @Setup - public void setup() { - charset = Charset.forName(charsetName); - asciiString = "ascii string"; - utf16String = "UTF-\uFF11\uFF16 string"; - } - - @Benchmark - public void encodeCharsetName(Blackhole bh) throws Exception { - bh.consume(asciiString.getBytes(charsetName)); - bh.consume(utf16String.getBytes(charsetName)); - } - - @Benchmark - public void encodeCharset(Blackhole bh) throws Exception { - bh.consume(asciiString.getBytes(charset)); - bh.consume(utf16String.getBytes(charset)); - } - } - - private String asciiDefaultString; - private String utf16DefaultString; + @Param({"US-ASCII", "ISO-8859-1", "UTF-8", "MS932", "ISO-8859-6"}) + private String charsetName; + private Charset charset; + private String asciiString; + private String utf16String; + private String longUtf16String; + private String longUtf16StartString; @Setup public void setup() { - asciiDefaultString = "ascii string"; - utf16DefaultString = "UTF-\uFF11\uFF16 string"; + charset = Charset.forName(charsetName); + asciiString = "ascii string"; + utf16String = "UTF-\uFF11\uFF16 string"; + longUtf16String = """ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu + urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. + Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et + sapien in magna porta ultricies. Sed vel pellentesque nibh. Pellentesque dictum + dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent + per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla + sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida + efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. + Suspendisse potenti. + + Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis + nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet + sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum + consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. + Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id + hendrerit sapien. Donec aliquam mattis lectus eu ultrices. Duis eu nisl + euismod, blandit mauris vel, placerat urna. Etiam malesuada enim purus, + tristique mollis odio blandit quis. Vivamus posuere. + \uFF11 + """; + longUtf16StartString = """ + \uFF11 + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ac sem eu + urna egestas placerat. Etiam finibus ipsum nulla, non mattis dolor cursus a. + Nulla nec nisl consectetur, lacinia neque id, accumsan ante. Curabitur et + sapien in magna porta ultricies. Sed vel pellentesque nibh. Pellentesque dictum + dignissim diam eu ultricies. Class aptent taciti sociosqu ad litora torquent + per conubia nostra, per inceptos himenaeos. Suspendisse erat diam, fringilla + sed massa sed, posuere viverra orci. Suspendisse tempor libero non gravida + efficitur. Vivamus lacinia risus non orci viverra, at consectetur odio laoreet. + Suspendisse potenti. + + Phasellus vel nisi iaculis, accumsan quam sed, bibendum eros. Sed venenatis + nulla tortor, et eleifend urna sodales id. Nullam tempus ac metus sit amet + sollicitudin. Nam sed ex diam. Praesent vitae eros et neque condimentum + consectetur eget non tortor. Praesent bibendum vel felis nec dignissim. + Maecenas a enim diam. Suspendisse quis ligula at nisi accumsan lacinia id + hendrerit sapien. Donec aliquam mattis lectus eu ultrices. Duis eu nisl + euismod, blandit mauris vel, placerat urna. Etiam malesuada enim purus, + tristique mollis odio blandit quis. Vivamus posuere. + """; + } + + @Benchmark + public byte[] encodeAsciiCharsetName() throws Exception { + return asciiString.getBytes(charset); + } + + @Benchmark + public byte[] encodeAscii() throws Exception { + return asciiString.getBytes(charset); + } + + @Benchmark + public void encodeMix(Blackhole bh) throws Exception { + bh.consume(asciiString.getBytes(charset)); + bh.consume(utf16String.getBytes(charset)); + } + + @Benchmark + public byte[] encodeUTF16LongEnd() throws Exception { + return longUtf16String.getBytes(charset); + } + + @Benchmark + public byte[] encodeUTF16LongStart() throws Exception { + return longUtf16StartString.getBytes(charset); } @Benchmark - public void encodeDefault(Blackhole bh) throws Exception { - bh.consume(asciiDefaultString.getBytes()); - bh.consume(utf16DefaultString.getBytes()); + public byte[] encodeUTF16() throws Exception { + return utf16String.getBytes(charset); } } diff -Nru openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java --- openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWait.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. 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; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Threads; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +public class ThreadOnSpinWait { + @Benchmark + @Threads(1) + public void testOnSpinWait() { + Thread.onSpinWait(); + } + + @Benchmark + @Threads(1) + public void testSleep0() throws InterruptedException { + Thread.sleep(0); + } + + @Benchmark + @Threads(1) + public void testEmpty() { + } +} diff -Nru openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java --- openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitProducerConsumer.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021, Amazon.com Inc. 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; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +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 org.openjdk.jmh.annotations.Threads; + +import org.openjdk.jmh.infra.Blackhole; + +import java.math.BigInteger; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +/** + * This microbenchmark models producer-consumer. + * + * The microbenchmark uses two thread: 1 for a producer, 1 for a consumer. + * The microbenchmark uses BigInteger to have latencies of producing/consuming + * data comparable with synchronization operations. + * + * Thread.onSpinWait is used in a spin loop which is used to avoid heavy locks. + * In the spin loop volatile fields are checked. To reduce overhead accessing them + * they are only checked after a number of iterations. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Threads(1) +public class ThreadOnSpinWaitProducerConsumer { + @Param({"100"}) + public int maxNum; + + @Param({"125"}) + public int spinNum; + + @Param({"10"}) + public int checkSpinCondAfterIters; + + @Param({"256"}) + public int dataBitLength; + + private Thread threadProducer; + private Thread threadConsumer; + private Object monitor; + + private BigInteger a; + private BigInteger b; + private Blackhole bh; + + private volatile int dataId; + private volatile int seenDataId; + + private int producedDataCount; + private int consumedDataCount; + + private void produceData() { + if (!isDataSeen()) { + return; + } + + b = a.not(); + ++dataId; + ++producedDataCount; + } + + private void consumeData() { + if (isDataSeen()) { + return; + } + bh.consume(a.equals(b.not())); + seenDataId = dataId; + ++consumedDataCount; + } + + private boolean isDataSeen() { + return seenDataId == dataId; + } + + private boolean isNewData() { + return seenDataId != dataId; + } + + private boolean spinWaitForCondition(int spinNum, BooleanSupplier cond) { + for (int i = 0; i < spinNum; ++i) { + if ((i % checkSpinCondAfterIters) == 0 && cond.getAsBoolean()) { + return true; + } + Thread.onSpinWait(); + } + return cond.getAsBoolean(); + } + + void produce() { + try { + while (dataId < maxNum) { + if (spinWaitForCondition(this.spinNum, this::isDataSeen)) { + synchronized (monitor) { + produceData(); + monitor.notify(); + } + } else { + synchronized (monitor) { + while (!isDataSeen()) { + monitor.wait(); + } + + produceData(); + monitor.notify(); + } + } + } + } catch (InterruptedException e) {} + } + + void consume() { + try { + for (;;) { + if (spinWaitForCondition(this.spinNum, this::isNewData)) { + synchronized (monitor) { + consumeData(); + monitor.notify(); + } + } else { + synchronized (monitor) { + while (isDataSeen()) { + monitor.wait(); + } + + consumeData(); + monitor.notify(); + } + } + } + } catch (InterruptedException e) {} + } + + @Setup(Level.Trial) + public void setup01() { + Random rnd = new Random(111); + a = BigInteger.probablePrime(dataBitLength, rnd); + monitor = new Object(); + } + + @Setup(Level.Invocation) + public void setup02() { + threadProducer = new Thread(this::produce); + threadConsumer = new Thread(this::consume); + } + + @Benchmark + public void trial(Blackhole bh) throws Exception { + this.bh = bh; + producedDataCount = 0; + consumedDataCount = 0; + dataId = 0; + seenDataId = 0; + threadProducer.start(); + threadConsumer.start(); + threadProducer.join(); + + synchronized (monitor) { + while (!isDataSeen()) { + monitor.wait(); + } + } + threadConsumer.interrupt(); + + if (producedDataCount != maxNum) { + throw new RuntimeException("Produced: " + producedDataCount + ". Expected: " + maxNum); + } + if (producedDataCount != consumedDataCount) { + throw new RuntimeException("produced != consumed: " + producedDataCount + " != " + consumedDataCount); + } + } +} diff -Nru openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java --- openjdk-17-17.0.2+8/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java 1970-01-01 00:00:00.000000000 +0000 +++ openjdk-17-17.0.3+7/test/micro/org/openjdk/bench/java/lang/ThreadOnSpinWaitSharedCounter.java 2022-04-19 19:55:43.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021, 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.lang; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Level; +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.concurrent.atomic.AtomicInteger; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +public class ThreadOnSpinWaitSharedCounter { + @Param({"1000000"}) + public int maxNum; + + @Param({"4"}) + public int threadCount; + + AtomicInteger theCounter; + + Thread threads[]; + + void work() { + for (;;) { + int prev = theCounter.get(); + if (prev >= maxNum) { + break; + } + if (theCounter.compareAndExchange(prev, prev + 1) != prev) { + Thread.onSpinWait(); + } + } + } + + @Setup(Level.Trial) + public void foo() { + theCounter = new AtomicInteger(); + } + + @Setup(Level.Invocation) + public void setup() { + theCounter.set(0); + threads = new Thread[threadCount]; + + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread(this::work); + } + } + + @Benchmark + public void trial() throws Exception { + for (int i = 0; i < threads.length; i++) { + threads[i].start(); + } + for (int i = 0; i < threads.length; i++) { + threads[i].join(); + } + } +}