Bits testen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie wollen Zahlen als Bitmasken behandeln, obwohl XSLT nicht über Integer oder zugehörige bitweise Operatoren verfügt.

Achtung!
Wenn Sie mit XML arbeiten, sollten Sie sich nicht darum bemühen, Informationen in Bits zu kodieren. Verwenden Sie diese Lösung nur, wenn Sie keine Kontrolle über die Kodierung der Daten haben.

Lösung

Die folgende Lösung funktioniert bei 16-Bit-Zahlen. Sie kann einfach auf 32-Bit-Zahlen erweitert werden:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" id="bittesting">
  <!--Zweierpotenzen-->
  <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 &gt;= $test) * ($num1 - $test) + not($num1 &gt;= $test) * $num1"/>
    <xsl:variable name="nextN2" select="($num2 &gt;= $test) * ($num2 - $test) + not($num2 &gt;= $test) * $num2"/>
    <xsl:choose>
      <xsl:when test="$test &lt; 1">
        <xsl:value-of select="$result"/>
      </xsl:when>
      <xsl:when test="$num1 &gt;= $test and $num2 &gt;= $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 &gt;= $test) * ($num1 - $test) + not($num1 &gt;= $test) * $num1"/>
    <xsl:variable name="nextN2" select="($num2 &gt;= $test) * ($num2 - $test) + not($num2 &gt;= $test) * $num2"/>
    <xsl:choose>
      <xsl:when test="$test &lt; 1">
        <xsl:value-of select="$result"/>
      </xsl:when>
      <xsl:when test="$num1 &gt;= $test or $num2 &gt;= $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 &gt;= $test) * ($num1 - $test) + not($num1 &gt;= $test) * $num1"/>
    <xsl:variable name="nextN2" select="($num2 &gt;= $test) * ($num2 - $test) + not($num2 &gt;= $test) * $num2"/>
    <xsl:choose>
      <xsl:when test="$test &lt; 1">
        <xsl:value-of select="$result"/>
      </xsl:when>
      <xsl:when test="$num1 &gt;= $test and not($num2 &gt;= $test) or not($num1 &gt;= $test) and $num2 &gt;= $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 &gt;= $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>

Diskussion

Diese Lösung zum Testen von Bits (bitTest-Template), die auf Modulo-Arithmetik basiert, wurde auf der XML DevCon London im Februar 2001 diskutiert. Wir haben die bitweisen logischen Operationen rekursiv mit Hilfe von Vergleich und Subtraktion implementiert. Sie können aber auch Division und Modulo verwenden, wie der folgende Code zeigt:

<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

Die XSLT-2.0-Lösung überlasse ich dem Leser zur Übung. Wie bei den anderen Rezepten sollten Sie die Templates in Funktionen umwandeln und Boolesche Hacks wie ($num1 >= $test) * ($num1 - $test) + not($num1 >= $test) * $num1 durch if-then-else-Ausdrücke ersetzen.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de