xsl-list
[Top] [All Lists]

[xsl] RE: String conversion problem when string is large

2012-03-20 14:12:13
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) &lt; 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() &lt;= $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>
--~--

<Prev in Thread] Current Thread [Next in Thread>