archive state to custom binary format

This commit is contained in:
Vladimir Krivosheev
2016-02-03 07:25:48 +01:00
parent 49973e9305
commit e17f17cdb3
4 changed files with 144 additions and 8 deletions
@@ -0,0 +1,78 @@
package com.intellij.configurationStore
import com.intellij.util.containers.isNullOrEmpty
import org.jdom.*
import java.io.DataOutputStream
private enum class TypeMarker {
ELEMENT, CDATA, TEXT, ELEMENT_END
}
fun output(doc: Document, out: DataOutputStream) {
val content = doc.content
val size = content.size
for (i in 0..size - 1) {
val obj = content[i]
when (obj) {
is Element -> printElement(out, doc.rootElement)
}
}
out.flush()
}
fun writeElement(element: Element, out: DataOutputStream) {
printElement(out, element)
out.flush()
}
private fun printElement(out: DataOutputStream, element: Element) {
out.writeByte(TypeMarker.ELEMENT.ordinal)
out.writeUTF(element.name)
val content = element.content
printAttributes(out, element.attributes)
for (item in content) {
if (item is Element) {
printElement(out, item)
}
else if (item is Text) {
if (!isAllWhitespace(item)) {
out.writeByte(TypeMarker.TEXT.ordinal)
out.writeUTF(item.text)
}
}
else if (item is CDATA) {
out.writeByte(TypeMarker.CDATA.ordinal)
out.writeUTF(item.text)
}
}
out.writeByte(TypeMarker.ELEMENT_END.ordinal)
}
private fun printAttributes(out: DataOutputStream, attributes: List<Attribute>?) {
if (attributes.isNullOrEmpty()) {
val size = attributes?.size ?: 0
if (size > 255) {
throw UnsupportedOperationException("attributes size > 255")
}
out.writeByte(size)
return
}
for (attribute in attributes!!) {
out.writeUTF(attribute.name)
out.writeUTF(attribute.value)
}
}
private fun isAllWhitespace(obj: Content): Boolean {
val str = (obj as? Text)?.text ?: return false
for (i in 0..str.length - 1) {
if (!Verifier.isXMLWhitespace(str[i])) {
return false
}
}
return true
}
@@ -15,8 +15,10 @@
*/
package com.intellij.configurationStore
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream
import com.intellij.openapi.util.registry.Registry
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.CharsetToolkit
import com.intellij.util.ArrayUtil
@@ -27,30 +29,63 @@ import org.iq80.snappy.SnappyOutputStream
import org.jdom.Element
import org.jdom.output.Format
import java.io.ByteArrayInputStream
import java.io.DataOutputStream
import java.io.OutputStreamWriter
import java.util.*
import java.util.concurrent.atomic.AtomicReferenceArray
private val XML_FORMAT = Format.getRawFormat().setTextMode(Format.TextMode.TRIM).setOmitEncoding(true).setOmitDeclaration(true)
private fun archiveState(state: Element): ByteArray {
// must be mot modified during app life
private val isUseNewSaving by lazy { ApplicationManager.getApplication().isUnitTestMode || Registry.`is`("configuration.saving.v3", false) }
private fun archiveState(state: Element): BufferExposingByteArrayOutputStream {
if (isUseNewSaving) {
return archiveStateBinary(state)
}
else {
return archiveStateXml(state)
}
}
fun archiveStateBinary(state: Element): BufferExposingByteArrayOutputStream {
val byteOut = BufferExposingByteArrayOutputStream()
DataOutputStream(SnappyOutputStream(byteOut)).use {
writeElement(state, it)
}
return byteOut
}
fun archiveStateXml(state: Element): BufferExposingByteArrayOutputStream {
val byteOut = BufferExposingByteArrayOutputStream()
OutputStreamWriter(SnappyOutputStream(byteOut), CharsetToolkit.UTF8_CHARSET).use {
val xmlOutputter = JDOMUtil.MyXMLOutputter()
xmlOutputter.format = XML_FORMAT
xmlOutputter.output(state, it)
}
return ArrayUtil.realloc(byteOut.internalBuffer, byteOut.size())
return byteOut
}
private fun unarchiveState(state: ByteArray) = JDOMUtil.load(SnappyInputStream(ByteArrayInputStream(state)))
fun getNewByteIfDiffers(key: String, newState: Any, oldState: ByteArray): ByteArray? {
val newBytes = if (newState is Element) archiveState(newState) else newState as ByteArray
if (Arrays.equals(newBytes, oldState)) {
return null
val newBytes: ByteArray
if (newState is Element) {
val byteOut = archiveState(newState)
if (arrayEquals(byteOut.internalBuffer, oldState, byteOut.size())) {
return null
}
newBytes = ArrayUtil.realloc(byteOut.internalBuffer, byteOut.size())
}
else if (SystemProperties.getBooleanProperty("idea.log.changed.components", false)) {
else {
newBytes = newState as ByteArray
if (Arrays.equals(newBytes, oldState)) {
return null
}
}
if (SystemProperties.getBooleanProperty("idea.log.changed.components", false)) {
fun stateToString(state: Any) = JDOMUtil.writeParent(state as? Element ?: unarchiveState(state as ByteArray), "\n")
val before = stateToString(oldState)
@@ -75,8 +110,7 @@ fun stateToElement(key: String, state: Any?, newLiveStates: Map<String, Element>
}
class StateMap private constructor(private val names: Array<String>, private val states: AtomicReferenceArray<Any?>) {
override fun toString(): String =
if (this == EMPTY) "EMPTY" else states.toString();
override fun toString() = if (this == EMPTY) "EMPTY" else states.toString()
companion object {
val EMPTY = StateMap(emptyArray(), AtomicReferenceArray(0))
@@ -225,5 +259,24 @@ internal fun updateState(states: MutableMap<String, Any>, key: String, newState:
}
states.put(key, newBytes ?: newState)
return true
}
fun arrayEquals(a: ByteArray, a2: ByteArray, aSize: Int = a.size): Boolean {
if (a == a2) {
return true
}
val length = aSize
if (a2.size != length) {
return false
}
for (i in 0..length - 1) {
if (a[i] != a2[i]) {
return false
}
}
return true
}
@@ -26,6 +26,8 @@ import org.xml.sax.EntityResolver
import org.xml.sax.InputSource
import java.io.CharArrayReader
import java.io.Reader
import java.nio.file.Files
import java.nio.file.Path
private val cachedSaxBuilder = ThreadLocal<SoftReference<SAXBuilder>>()
@@ -44,6 +46,8 @@ private fun getSaxBuilder(): SAXBuilder {
fun loadElement(chars: CharSequence) = loadDocument(CharSequenceReader(chars)).detachRootElement()
fun loadElement(path: Path) = loadDocument(Files.newInputStream(path).bufferedReader()).detachRootElement()
private fun loadDocument(reader: Reader): Document {
if (Registry.`is`("jdom.ignoring.whitespace", false) || (ApplicationManager.getApplication()?.isUnitTestMode ?: false)) {
try {
@@ -686,6 +686,7 @@ ruby.remote.debugger.supports.catchpoint.removal=true
use.read.action.to.init.service=false
use.loaded.state.as.existing=true
saving.state.in.new.format.is.allowed=false
configuration.saving.v3=false
jdom.ignoring.whitespace=false
ide.mac.new.color.picker=false