Here's a template I just recently rewrote, to use bisection instead of forward
recursion. You can probably easily retool it to do your hex-to-dec logic
instead of the string-replace that it does now.
<!-- Replaces all instances of $search in $string with $replace. -->
<xsl:template name="str-replace">
<xsl:param name="string" select="''" />
<xsl:param name="search" select="''" />
<xsl:param name="replace" select="''" />
<!-- Variables for bisection -->
<xsl:variable name="half" select="floor(string-length($string) div 2)" />
<xsl:variable name="midpoint1" select="$half - (string-length($search) - 1)"
/>
<xsl:variable name="midpoint2" select="$half + (string-length($search) - 1)"
/>
<xsl:variable name="mid-string" select="substring($string, $midpoint1 + 1,
$midpoint2 - $midpoint1)" />
<xsl:choose>
<xsl:when test="not(string($string) and string($search) and
contains($string, $search))">
<xsl:value-of select="$string" />
</xsl:when>
<xsl:when test="string-length($string) < string-length($search) * 2">
<xsl:value-of select="concat(substring-before($string, $search),
$replace, substring-after($string, $search))" />
</xsl:when>
<!-- Check the string around the bisection point for occurrence of the
search string -->
<xsl:when test="$mid-string and contains($mid-string, $search)">
<xsl:call-template name="str-replace">
<xsl:with-param name="string" select="substring($string, 1, $midpoint1
+ string-length(substring-before($mid-string, $search)))" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
<xsl:value-of select="$replace" />
<xsl:call-template name="str-replace">
<xsl:with-param name="string" select="substring($string, $midpoint2 -
string-length(substring-after($mid-string, $search)) + 1)" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str-replace">
<xsl:with-param name="string" select="substring($string, 1, $half)" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
<xsl:call-template name="str-replace">
<xsl:with-param name="string" select="substring($string, $half + 1)" />
<xsl:with-param name="search" select="$search" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
~ Scott
-----Original Message-----
From: Bulgrien, Kevin [mailto:Kevin(_dot_)Bulgrien(_at_)GDSATCOM(_dot_)com]
Sent: Tuesday, March 20, 2012 1:22 PM
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] String conversion problem when string is large
I have an XML file produced by a third-party tool. An XSL Transform is used to
transform data in this file to a CSV that is fed into another third-party tool.
The XSL Transform has worked fine for a very long time. A recent use,
however, exposed a weakness in the implementation that causes most processors
to crash as a result of recursion depth.
The recursion depth problem occurs in an element that contains hex-encoded
binary data like:
<container Count="3">0x00,0x01,0xFF</container>
The CSV file needs to be generated with decimal values instead of hexadecimal
values.
...,0,1,255
The XML data that needs to be translated has not usually been very large, so no
particular problems have been observed for a number of years, but a problem
arose when a data set came up where a container has ~50,000 hex-encoded bytes.
The translator converts the first value, and recursively calls the translation
on the remaining string. This becomes a problem when 50,000 values need to be
translated.
Perhaps this data set means that XSLT is no longer the right tool for this
particular job, and possibly the XSL transform should simply dump the hex data
for post-processing by a different tool. As my experience with XSL Is limited
I'm interested in opinions on whether there are translation methods that might
not be so expensive.
At risk of exposing my ignorance, this was the original code:
<!-- =======================================================================
!
! Convert REG_BINARY data stream from a comma-separated list of hex numbers
! in the form "0xhh,0xhh,...", to a comma-separated list of decimal numbers
! in the form "n,n,...".
!
! TODO: Pass in @count and verify that the data stream length matches the
! actual length of the datasream.
!
! -->
<xsl:template name="HexToDec">
<xsl:param name="HexData" />
<!-- Convert first number of HexData -->
<xsl:variable name="Base" select="'0123456789ABCDEF'" />
<xsl:variable name="Num1"
select="string-length(substring-before($Base,substring($HexData,3,1)))" />
<xsl:variable name="Num2"
select="string-length(substring-before($Base,substring($HexData,4,1)))" />
<xsl:choose>
<xsl:when test="string-length($HexData) = 0">
<!-- Do nothing -->
</xsl:when>
<xsl:otherwise>
<xsl:text>,</xsl:text>
<xsl:value-of select="$Num1 * 16 + $Num2" />
<xsl:call-template name="BinToDec">
<xsl:with-param name="HexData">
<xsl:value-of select="substring-after($HexData, ',')" />
</xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
A rewrite using a different algorithm proved even less efficient, and was a
tutorial on arcane and probably unwise use of xsl:for-each:
<xsl:template name="HexToDec">
<xsl:param name="HexData" />
<!-- Find the length of the data stream to convert. -->
<xsl:variable name="HexSize" select="(string-length($HexData)+1) div 5" />
<!-- String used to convert each byte of HexData to decimal. -->
<xsl:variable name="Hex" select="'0123456789ABCDEF'" />
<!--
! All but the final byte has a comma, so when using the string length
! to determine how many bytes there are, add 1 to the string length
! before dividing by the normal length of a hex-encoded byte with
! separator.
-->
<xsl:for-each select="(//*)[position() <= $HexSize]" >
<!--
! Emulated "for" loop repeated content here. Use position() to get
! loop index. position() is one-based.
!
-->
<xsl:text>,</xsl:text>
<xsl:value-of select="string-length
(
substring-before
(
$Hex, substring
(
$HexData, (position() - 1) * 5 + 3, 1
)
)
) * 16 +
string-length
(
substring-before
(
$Hex, substring
(
$HexData, (position() - 1) * 5 + 4, 1
)
)
) * 1"
/>
</xsl:for-each>
</xsl:template>
It probably will not help much to regurgitate yet another rewrite based on use
of replace, as it blows the stack too. I confess I have a hard time picturing
in my head what happens when the XSL transform runs, so I appear to be
repeating the same mistakes over and over the code is respun in different ways.
If it was possible to read to this point without screaming in horror, or
falling off your chair laughing, perhaps a comment on use or avoiding use of
XSL in this scenario could help me raise a level in XSL understanding.
I have both XSLT 1.0 and 2.0 engines at my disposal. Apologies in advance if I
have failed to identify a prior response to a similar problem in the archives
or at the most helpful http://www.dpawson.co.uk/xsl site.
Kevin Bulgrien
This message and/or attachments may include information subject to GD Corporate
Policy 07-105 and is intended to be accessed only by authorized personnel of
General Dynamics and approved service providers. Use, storage and transmission
are governed by General Dynamics and its policies. Contractual restrictions
apply to third parties. Recipients should refer to the policies or contract to
determine proper handling. Unauthorized review, use, disclosure or
distribution is prohibited. If you are not an intended recipient, please
contact the sender and destroy all copies of the original message.
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--
--~------------------------------------------------------------------
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--