Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions binding/exported-functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ _xmlCtxtSetErrorHandler
_xmlCtxtValidateDtd
_xmlDocGetRootElement
_xmlDocSetRootElement
_xmlDOMWrapCloneNode
_xmlFreeDoc
_xmlFreeDtd
_xmlFreeNode
Expand Down
26 changes: 26 additions & 0 deletions src/libxml2.mts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,31 @@ function allocUTF8Buffer(str: string | null) {
return [buf, len];
}

class PointerRef<T extends Pointer> {
ptr: Pointer;

constructor(ptr: Pointer) {
this.ptr = ptr;
}

dereference(): T {
return libxml2.getValue(this.ptr, '*') as T;
}
}

// TODO: find out actual pointer size
const POINTER_SIZE = 8;

export function withPointerRef<T extends Pointer, R>(process: (pointerRef: PointerRef<T>) => R): R {
const buf = libxml2._malloc(POINTER_SIZE);

try {
return process(new PointerRef<T>(buf));
} finally {
libxml2._free(buf);
}
}

function withStrings<R>(process: (...buf: number[]) => R, ...strings: (string | null)[]): R {
const args = strings.map((str) => {
const [buf] = allocUTF8Buffer(str);
Expand Down Expand Up @@ -725,6 +750,7 @@ export const xmlXPathFreeContext = libxml2._xmlXPathFreeContext;
export const xmlXPathFreeObject = libxml2._xmlXPathFreeObject;
export const xmlXPathNewContext = libxml2._xmlXPathNewContext;
export const xmlXPathSetContextNode = libxml2._xmlXPathSetContextNode;
export const xmlDOMWrapCloneNode = libxml2._xmlDOMWrapCloneNode;

/**
* Create an output buffer using I/O callbacks (same pattern as xmlSaveToIO)
Expand Down
11 changes: 11 additions & 0 deletions src/libxml2raw.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ type Pointer = number;
type CString = Pointer;
type XmlAttrPtr = Pointer;
type XmlCharEncodingHandlerPtr = Pointer;
type XMLDomWrapCtxtPtr = Pointer;
type XmlParserCtxtPtr = Pointer;
type XmlParserInputBufferPtr = Pointer;
type XmlDocPtr = Pointer;
Expand Down Expand Up @@ -62,6 +63,16 @@ export class LibXml2 {
_xmlCtxtValidateDtd(ctxt: XmlParserCtxtPtr, doc: XmlDocPtr, dtd: XmlDtdPtr): number;
_xmlFreeNode(node: XmlNodePtr): void;
_xmlFreeParserCtxt(ctxt: XmlParserCtxtPtr): void;
_xmlDOMWrapCloneNode(
ctxt: XMLDomWrapCtxtPtr,
sourceDoc: XmlDocPtr,
node: XmlNodePtr,
resNode: Pointer,
destDoc: XmlDocPtr,
destParent: XmlDocPtr,
deep: number,
options: number,
): number;
_xmlDocGetRootElement(doc: XmlDocPtr): XmlNodePtr;
_xmlDocSetRootElement(doc: XmlDocPtr, root: XmlNodePtr): XmlNodePtr;
_xmlFreeDoc(Doc: XmlDocPtr): void;
Expand Down
33 changes: 33 additions & 0 deletions src/nodes.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {
XmlNsStruct,
XmlOutputBufferHandler,
XmlXPathObjectStruct,
withPointerRef,
xmlAddChild,
xmlAddNextSibling,
xmlAddPrevSibling,
xmlDOMWrapCloneNode,
xmlFreeNode,
xmlGetNsList,
xmlHasNsProp,
Expand Down Expand Up @@ -798,6 +800,37 @@ export class XmlElement extends XmlTreeNode {
);
}

/**
* Insert a clone of the given source node to the end of the children list.
*/
addClone(srcNode: XmlElement, deep: boolean = true): XmlElement {
return withPointerRef((resNodePtr) => {
const result = xmlDOMWrapCloneNode(
0,
srcNode.doc._ptr,
srcNode._nodePtr,
resNodePtr.ptr,
this.doc._ptr,
this._nodePtr,
deep ? 1 : 0,
0,
);

switch (result) {
case 0:
{
const resNode = resNodePtr.dereference();
xmlAddChild(this._nodePtr, resNode);
return new XmlElement(resNode);
}
case 1:
throw new Error('Unsupported node type given to addClone');
default:
throw new Error('Internal error in addClone');
}
});
}

/**
* Save the XmlElement to a buffer and invoke the callbacks to process.
*
Expand Down
Loading