Recipe3.10.Testing Bits


Recipe 3.10. Testing Bits

Problem

You want to treat numbers as bit masks even though XSLT does not have integers or associated bitwise operators.

When working with XML, don't go out of your way to encode information in bits. Use this solution only when you have no control over the encoding of the data.


Solution

The following solution works on 16-bit numbers, but can easily be extended up to 32:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"      version="1.0" >     <!--powers of two--> <xsl:variable name="bit15" select="32768"/> <xsl:variable name="bit14" select="16384"/> <xsl:variable name="bit13" select="8192"/> <xsl:variable name="bit12" select="4096"/> <xsl:variable name="bit11" select="2048"/> <xsl:variable name="bit10" select="1024"/> <xsl:variable name="bit9"  select="512"/> <xsl:variable name="bit8"  select="256"/> <xsl:variable name="bit7"  select="128"/> <xsl:variable name="bit6"  select="64"/> <xsl:variable name="bit5"  select="32"/> <xsl:variable name="bit4"  select="16"/> <xsl:variable name="bit3"  select="8"/> <xsl:variable name="bit2"  select="4"/> <xsl:variable name="bit1"  select="2"/> <xsl:variable name="bit0"  select="1"/>     <xsl:template name="bitTest">   <xsl:param name="num"/>   <xsl:param name="bit" select="$bit0"/>   <xsl:choose>     <xsl:when test="( $num mod ( $bit * 2 ) ) -                     ( $num mod ( $bit ) )">1</xsl:when>     <xsl:otherwise>0</xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitAnd">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="test" select="$bit15"/>      <xsl:variable name="nextN1"        select="($num1 >= $test) * ($num1 - $test) + not($num1 >= $test) * $num1"/>   <xsl:variable name="nextN2"        select="($num2 >= $test) * ($num2 - $test) + not($num2 >= $test) * $num2"/>      <xsl:choose>     <xsl:when test="$test &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num1 >= $test and $num2 >= $test">       <xsl:call-template name="bitAnd">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result + $test"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitAnd">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitOr">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="test" select="$bit15"/>       <xsl:variable name="nextN1"        select="($num1 >= $test) * ($num1 - $test) + not($num1 >= $test) * $num1"/>   <xsl:variable name="nextN2"        select="($num2 >= $test) * ($num2 - $test) + not($num2 >= $test) * $num2"/>      <xsl:choose>     <xsl:when test="$test &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num1 >= $test or $num2 >= $test">       <xsl:call-template name="bitOr">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result + $test"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitOr">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitXor">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="test" select="$bit15"/>       <xsl:variable name="nextN1"        select="($num1 >= $test) * ($num1 - $test) + not($num1 >= $test) * $num1"/>   <xsl:variable name="nextN2"        select="($num2 >= $test) * ($num2 - $test) + not($num2 >= $test) * $num2"/>      <xsl:choose>     <xsl:when test="$test &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num1 >= $test and not($num2 >= $test)          or not($num1 >= $test) and $num2 >= $test">       <xsl:call-template name="bitXor">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result + $test"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitXor">         <xsl:with-param name="num1" select="$nextN1"/>         <xsl:with-param name="num2" select="$nextN2"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitNot">   <xsl:param name="num"/>   <xsl:param name="result" select="0"/>   <xsl:param name="test" select="$bit15"/>      <xsl:choose>     <xsl:when test="$test &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num >= $test">       <xsl:call-template name="bitNot">         <xsl:with-param name="num" select="$num - $test"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitNot">         <xsl:with-param name="num" select="$num"/>         <xsl:with-param name="result" select="$result + $test"/>         <xsl:with-param name="test" select="$test div 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     </xsl:stylesheet>

Discussion

This solution for testing bits ( bitTest template) based on modulo arithmetic was discussed at XML DevCon London in February 2001. We implemented the bitwise logical operations recursively using comparison and subtraction, but you could also use division and mod, as shown in the following code:

<xsl:template name="bitAnd">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="pow2" select="$bit0"/>      <xsl:choose>     <xsl:when test="$num1 &lt; 1 or $num2 &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num1 mod 2 and $num2 mod 2">       <xsl:call-template name="bitAnd">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result + $pow2"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitAnd">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitOr">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="pow2" select="$bit0"/>      <xsl:choose>     <xsl:when test="$num1 &lt; 1 and $num2 &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="boolean($num1 mod 2) or boolean($num2 mod 2)">       <xsl:call-template name="bitOr">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result + $pow2"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitOr">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>     <xsl:template name="bitXor">   <xsl:param name="num1"/>   <xsl:param name="num2"/>   <xsl:param name="result" select="0"/>   <xsl:param name="pow2" select="$bit0"/>      <xsl:choose>     <xsl:when test="$num1 &lt; 1 and $num2 &lt; 1">       <xsl:value-of select="$result"/>     </xsl:when>       <xsl:when test="$num1 mod 2 + $num2 mod 2 = 1">       <xsl:call-template name="bitXor">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result + $pow2"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:when>     <xsl:otherwise>       <xsl:call-template name="bitXor">         <xsl:with-param name="num1" select="floor($num1 div 2)"/>         <xsl:with-param name="num2" select="floor($num2 div 2)"/>         <xsl:with-param name="result" select="$result"/>         <xsl:with-param name="pow2" select="$pow2 * 2"/>       </xsl:call-template>     </xsl:otherwise>   </xsl:choose> </xsl:template>

XSLT 2.0

I leave the XSLT 2.0 solution as an exercise for the reader. As with the other recipes, you should convert the templates into functions and replace Boolean hackery like ($num1 >= $test) * ($num1 - $test) + not($num1 >= $test) * $num1 with if-then-else expressions.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net