Skip to Content.
Sympa Menu

xom-interest - [XOM-interest] Optimizing character checking

xom-interest AT

Subject: XOM API for Processing XML with Java

List archive

Chronological Thread  
  • From: Elliotte Rusty Harold <elharo AT>
  • To: xom-interest AT
  • Subject: [XOM-interest] Optimizing character checking
  • Date: Mon, 12 May 2003 07:56:04 -0400

The next release of XOM (1.0d14) will have removed almost all verification from the document parsing process, which results in significant speed ups. It was redundant because the parser does that checking anyway. No need to do it twice.

However, verification will still be done when building new or changing existing nodes and documents in memory, and it can be a noticeable performance hit. Thus it pays to think about optimization. In my initial tests two related methods stand out as particularly worthy of optimization and as usual these aren't the ones I would have predicted before measuring:

public void checkXMLCharacters(String text)
private static boolean isXMLCharacter(int c)

Even small optimizations in these two methods might have a big impact on certain kinds of programs. The isXMLCharacter method is pretty simple already but it's called a lot:

private static boolean isXMLCharacter(int c) {
if (c <= 0xD7FF) {
if (c >= 0x20) return true;
else {
if (c == '\n') return true;
if (c == '\r') return true;
if (c == '\t') return true;
return false;

if (c < 0xE000) return false; if (c <= 0xFFFD) return true;
if (c < 0x10000) return false; if (c <= 0x10FFFF) return true;
return false;

Note that what's passed to this method is a Unicode code point, not a Java char. It needs to handle the full range of Unicode. The checkXMLCharacters method handles the decomposition of Java strings into Unicode chars:

public static void checkCharacterData(String text) {
if (text == null) {
throw new IllegalDataException(
"A null is not a legal XML value"

// do check
char[] data = text.toCharArray();
for (int i = 0, len = data.length; i < len; i++) {
char c = data[i];
int result = c;
// high surrogate
if (result >= 0xD800 && result <= 0xDBFF) {
// Decode surrogate pair
int high = c;
try {
int low = text.charAt(i+1);
if (low < 0xDC00 || low > 0xDFFF) {
throw new IllegalDataException(
"Bad surrogate pair"
// Algorithm defined in Unicode spec
result = (high-0xD800)*0x400 + (low-0xDC00) + 0x10000;
catch (IndexOutOfBoundsException e) {
throw new IllegalDataException(
"Bad surrogate pair"

if (!isXMLCharacter(result)) {
// Likely this character can't be easily displayed
// because it's a control so we use its hexadecimal
// representation in the reason.
throw new IllegalDataException("0x"
+ Integer.toHexString(result)
+ " is not a legal XML character"); } }

// If we got here, everything is OK

Although this code is somewhat complex, the complex parts (decoding surrogate pairs) are not the common cases. In fact, I'm not even sure my timing tests are exercising those at all yet. Likely any effective optimizations performed here will need to be in the simple parts of the code. Hmm, looking at this now I can probably eliminate the local variable result and replace the call to charAt with an array access. Does anyone see anything else?

Elliotte Rusty Harold
elharo AT
Processing XML with Java (Addison-Wesley, 2002)

Archive powered by MHonArc 2.6.24.

Top of Page