All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.plantuml.zopfli.Squeeze Maven / Gradle / Ivy

There is a newer version: 1.2024.8
Show newest version
// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
 * |
 * |      PlantUML : a free UML diagram generator
 * |
 * +=======================================================================
 *
 * (C) Copyright 2009-2024, Arnaud Roques
 *
 * Project Info:  https://plantuml.com
 *
 * If you like this project or if you find it useful, you can support us at:
 *
 * https://plantuml.com/patreon (only 1$ per month!)
 * https://plantuml.com/liberapay (only 1€ per month!)
 * https://plantuml.com/paypal
 *
 *
 * PlantUML is free software; you can redistribute it and/or modify it
 * under the terms of the MIT License.
 *
 * See http://opensource.org/licenses/MIT
 *
 * 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.
 *
 * PlantUML can occasionally display sponsored or advertising messages. Those
 * messages are usually generated on welcome or error images and never on
 * functional diagrams.
 * See https://plantuml.com/professional if you want to remove them
 *
 * Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
 * are owned by the author of their corresponding sources code (that is, their
 * textual description in PlantUML language). Those images are not covered by
 * this MIT license.
 *
 * The generated images can then be used without any reference to the MIT license.
 * It is not even necessary to stipulate that they have been generated with PlantUML,
 * although this will be appreciated by the PlantUML team.
 *
 * There is an exception : if the textual description in PlantUML language is also covered
 * by any license, then the generated images are logically covered
 * by the very same license.
 *
 * This is the IGY distribution (Install GraphViz by Yourself).
 * You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
 * (see https://plantuml.com/graphviz-dot )
 *
 * Icons provided by OpenIconic :  https://useiconic.com/open
 * Archimate sprites provided by Archi :  http://www.archimatetool.com
 * Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
 * Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
 * ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
 * ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
 * CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
 * Brotli (c) by the Brotli Authors https://github.com/google/brotli
 * Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
 * Twemoji (c) by Twitter at https://twemoji.twitter.com/
 *
 */

package net.sourceforge.plantuml.zopfli;

class Squeeze {

	static LzStore optimal(Cookie cookie, int numIterations, LongestMatchCache lmc, byte[] input, int from, int to) {
		LzStore currentStore = cookie.store1;
		currentStore.reset();
		LzStore store = cookie.store2;
		Deflate.greedy(cookie, lmc, input, from, to, currentStore);
		SymbolStats stats = cookie.stats;
		SymbolStats bestStats = cookie.bestStats;
		SymbolStats lastStats = cookie.lastStats;
		stats.getFreqs(currentStore);

		char[] lengthArray = cookie.lengthArray;
		long[] costs = cookie.costs;

		int cost;
		int bestCost = Integer.MAX_VALUE;
		int lastCost = 0;
		int lastRandomStep = -1;

		for (int i = 0; i < numIterations; i++) {
			currentStore.reset();
			bestLengths(cookie, lmc, from, input, from, to, stats.minCost(), stats, lengthArray, costs);
			optimalRun(cookie, lmc, input, from, to, lengthArray, currentStore);
			cost = Deflate.calculateBlockSize(cookie, currentStore.litLens, currentStore.dists, 0, currentStore.size);
			if (cost < bestCost) {
				store.copy(currentStore);
				bestStats.copy(stats);
				bestCost = cost;
			}
			lastStats.copy(stats);
			stats.getFreqs(currentStore);
			if (lastRandomStep != -1) {
				stats.alloy(lastStats);
				stats.calculate();
			}
			if (i > 5 && cost == lastCost) {
				stats.copy(bestStats);
				cookie.rnd = stats.randomizeFreqs(cookie.rnd);
				stats.calculate();
				lastRandomStep = i;
			}
			lastCost = cost;
		}
		return store;
	}

	static void optimalRun(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to, char[] lengthArray,
			LzStore store) {
		// assert from != to
		char[] path = cookie.path;
		int pathSize = 0;
		int size = to - from;
		do {
			char las = lengthArray[size];
			path[pathSize++] = las;
			size -= las;
		} while (size != 0);

		int windowStart = Math.max(from - 0x8000, 0);
		Hash h = cookie.h;
		h.init(input, windowStart, from, to);
		int pos = from;

		do {
			h.updateHash(input, pos, to);
			int length = path[--pathSize];
			if (length >= 3) {
				Deflate.findLongestMatch(cookie, lmc, from, h, input, pos, to, length, null);
				store.append((char) length, (char) cookie.distVal);
			} else {
				length = 1;
				store.append((char) (input[pos] & 0xFF), (char) 0);
			}

			for (int j = 1; j < length; ++j) {
				h.updateHash(input, pos + j, to);
			}
			pos += length;
		} while (pathSize != 0);
	}

	private static long fixedCost(int litLen, int dist) {
		if (dist == 0) {
			if (litLen <= 143) {
				return 8;
			}
			return 9;
		} else {
			long cost = 12
					+ (dist < 4097 ? Util.CACHED_DIST_EXTRA_BITS[dist] : dist < 16385 ? dist < 8193 ? 11 : 12 : 13)
					+ Util.LENGTH_EXTRA_BITS[litLen];
			if (Util.LENGTH_SYMBOL[litLen] > 279) {
				return cost + 1;
			}
			return cost;
		}
	}

	private static void bestLengths(Cookie cookie, LongestMatchCache lmc, int blockStart, byte[] input, int from,
			int to, long minCost, SymbolStats stats, char[] lengthArray, long[] costs) {
		// # WINDOW_SIZE = 0x8000
		// # WINDOW_MASK = 0x7FFF
		// # MAX_MATCH = 258

		int windowStart = Math.max(from - 0x8000, 0);
		Hash h = cookie.h;
		h.init(input, windowStart, from, to);
		Cookie.fillCostMax(costs, to - from + 1);
		costs[0] = 0L;
		lengthArray[0] = 0;
		int[] same = h.same;

		char[] subLen = cookie.c259a;
		System.arraycopy(Cookie.charZeroes, 0, subLen, 0, 259);

		long[] slLiterals = stats.lLiterals;
		long[] slLengths = stats.lLengths;
		long[] sdSymbols = stats.dSymbols;
		long stepCost = slLengths[258] + sdSymbols[0];

		int[] cachedDistSymbol = Util.CACHED_DIST_SYMBOL;

		int i = from;
		int j = 0;
		while (i < to) {
			h.updateHash(input, i, to);

			if (same[i & 0x7FFF] > 516 && i > from + 259 && i + 517 < to && same[(i - 258) & 0x7FFF] > 258) {
				for (int k = 0; k < 258; ++k) {
					costs[j + 258] = costs[j] + stepCost;
					lengthArray[j + 258] = 258;
					i++;
					j++;
					h.updateHash(input, i, to);
				}
			}

			Deflate.findLongestMatch(cookie, lmc, blockStart, h, input, i, to, 258, subLen);

			long costsJ = costs[j];
			if (i + 1 <= to) {
				long newCost = costsJ + slLiterals[input[i] & 0xFF];
				if (newCost < costs[j + 1]) {
					costs[j + 1] = newCost;
					lengthArray[j + 1] = 1;
				}
			}
			int lenValue = cookie.lenVal;
			long baseCost = minCost + costsJ;
			if (lenValue > to - i) {
				lenValue = to - i;
			}
			int jpk = j + 3;
			for (char k = 3; k <= lenValue; k++, jpk++) {
				if (costs[jpk] > baseCost) {
					long newCost = costsJ + (slLengths[k] + sdSymbols[cachedDistSymbol[subLen[k]]]);
					if (costs[jpk] > newCost) {
						costs[jpk] = newCost;
						lengthArray[jpk] = k;
					}
				}
			}

			i++;
			j++;
		}
	}

	static void bestFixedLengths(Cookie cookie, LongestMatchCache lmc, byte[] input, int from, int to,
			char[] lengthArray, long[] costs) {
		int windowStart = Math.max(from - 0x8000, 0);
		Hash h = cookie.h;
		h.init(input, windowStart, from, to);
		Cookie.fillCostMax(costs, to - from + 1);
		costs[0] = 0L;
		lengthArray[0] = 0;

		char[] subLen = cookie.c259a;
		for (int i = from; i < to; i++) {
			int j = i - from;
			h.updateHash(input, i, to);

			if (h.same[i & 0x7FFF] > 258 * 2 && i > from + 258 + 1 && i + 258 * 2 + 1 < to
					&& h.same[(i - 258) & 0x7FFF] > 258) {
				long symbolCost = fixedCost(258, 1);
				for (int k = 0; k < 258; k++) {
					costs[j + 258] = costs[j] + symbolCost;
					lengthArray[j + 258] = 258;
					i++;
					j++;
					h.updateHash(input, i, to);
				}
			}

			Deflate.findLongestMatch(cookie, lmc, from, h, input, i, to, 258, subLen);

			if (i + 1 <= to) {
				long newCost = costs[j] + fixedCost(input[i] & 0xFF, 0);
				if (newCost < costs[j + 1]) {
					costs[j + 1] = newCost;
					lengthArray[j + 1] = 1;
				}
			}
			int lenValue = cookie.lenVal;
			for (char k = 3; k <= lenValue && i + k <= to; k++) {
				if (costs[j + k] - costs[j] <= 12.0) {
					continue;
				}

				long newCost = costs[j] + fixedCost(k, subLen[k]);
				if (newCost < costs[j + k]) {
					costs[j + k] = newCost;
					lengthArray[j + k] = k;
				}
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy